Skip to content

Commit

Permalink
Prevent serialization exception from Env actuator
Browse files Browse the repository at this point in the history
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
  • Loading branch information
scottfrederick committed Oct 26, 2020
1 parent 008ab4d commit 1a3f810
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 5 deletions.
Expand Up @@ -57,6 +57,7 @@
* @author Christian Dupuis
* @author Madhura Bhave
* @author Stephane Nicoll
* @author Scott Frederick
* @since 2.0.0
*/
@Endpoint(id = "env")
Expand Down Expand Up @@ -143,7 +144,7 @@ private PropertyValueDescriptor describeValueOf(String name, PropertySource<?> s
PlaceholdersResolver resolver) {
Object resolved = resolver.resolvePlaceholders(source.getProperty(name));
Origin origin = ((source instanceof OriginLookup) ? ((OriginLookup<Object>) source).getOrigin(name) : null);
return new PropertyValueDescriptor(sanitize(name, resolved), origin);
return new PropertyValueDescriptor(stringifyIfNecessary(sanitize(name, resolved)), origin);
}

private PlaceholdersResolver getResolver() {
Expand Down Expand Up @@ -182,6 +183,16 @@ public Object sanitize(String name, Object object) {
return this.sanitizer.sanitize(name, object);
}

protected Object stringifyIfNecessary(Object value) {
if (value == null || value.getClass().isPrimitive()) {
return value;
}
if (CharSequence.class.isAssignableFrom(value.getClass())) {
return value.toString();
}
return "Complex property type " + value.getClass().getName();
}

/**
* {@link PropertySourcesPlaceholdersResolver} that sanitizes sensitive placeholders
* if present.
Expand Down
Expand Up @@ -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;
Expand All @@ -39,6 +42,7 @@
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;
Expand All @@ -54,6 +58,7 @@
* @author Andy Wilkinson
* @author HaiTao Zhang
* @author Chris Bono
* @author Scott Frederick
*/
class EnvironmentEndpointTests {

Expand Down Expand Up @@ -194,15 +199,22 @@ void propertyWithSensitivePlaceholderNotResolved() {
}

@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
Expand Down Expand Up @@ -360,4 +372,35 @@ EnvironmentEndpoint environmentEndpoint(Environment environment) {

}

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());
}

}

}

0 comments on commit 1a3f810

Please sign in to comment.