Add 'uris', 'address' and 'addresses' to keys to sanitize.
See gh-19999
This commit is contained in:
parent
45fd60337d
commit
badc83d368
|
@ -16,8 +16,10 @@
|
||||||
|
|
||||||
package org.springframework.boot.actuate.endpoint;
|
package org.springframework.boot.actuate.endpoint;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
@ -32,18 +34,23 @@ import org.springframework.util.StringUtils;
|
||||||
* @author Nicolas Lejeune
|
* @author Nicolas Lejeune
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
* @author HaiTao Zhang
|
* @author HaiTao Zhang
|
||||||
|
* @author Chris Bono
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
public class Sanitizer {
|
public class Sanitizer {
|
||||||
|
|
||||||
private static final String[] REGEX_PARTS = { "*", "$", "^", "+" };
|
private static final String[] REGEX_PARTS = { "*", "$", "^", "+" };
|
||||||
|
|
||||||
|
private static final String[] DEFAULT_KEYS_TO_SANITIZE = { "password", "secret", "key", "token", ".*credentials.*", "vcap_services", "sun.java.command", "uri", "uris", "address", "addresses" };
|
||||||
|
|
||||||
|
private static final String[] URI_USERINFO_KEYS = { "uri", "uris", "address", "addresses" };
|
||||||
|
|
||||||
private static final Pattern URI_USERINFO_PATTERN = Pattern.compile("[A-Za-z]+://.+:(.*)@.+$");
|
private static final Pattern URI_USERINFO_PATTERN = Pattern.compile("[A-Za-z]+://.+:(.*)@.+$");
|
||||||
|
|
||||||
private Pattern[] keysToSanitize;
|
private Pattern[] keysToSanitize;
|
||||||
|
|
||||||
public Sanitizer() {
|
public Sanitizer() {
|
||||||
this("password", "secret", "key", "token", ".*credentials.*", "vcap_services", "sun.java.command", "uri");
|
this(DEFAULT_KEYS_TO_SANITIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Sanitizer(String... keysToSanitize) {
|
public Sanitizer(String... keysToSanitize) {
|
||||||
|
@ -91,8 +98,8 @@ public class Sanitizer {
|
||||||
}
|
}
|
||||||
for (Pattern pattern : this.keysToSanitize) {
|
for (Pattern pattern : this.keysToSanitize) {
|
||||||
if (pattern.matcher(key).matches()) {
|
if (pattern.matcher(key).matches()) {
|
||||||
if (pattern.matcher("uri").matches()) {
|
if (keyIsUriWithUserInfo(pattern)) {
|
||||||
return sanitizeUri(value);
|
return sanitizeUris(value.toString());
|
||||||
}
|
}
|
||||||
return "******";
|
return "******";
|
||||||
}
|
}
|
||||||
|
@ -100,14 +107,28 @@ public class Sanitizer {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object sanitizeUri(Object value) {
|
private boolean keyIsUriWithUserInfo(Pattern pattern) {
|
||||||
String uriString = value.toString();
|
for (String uriKey : URI_USERINFO_KEYS) {
|
||||||
|
if (pattern.matcher(uriKey).matches()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object sanitizeUris(String uriString) {
|
||||||
|
// Treat each uri value as possibly containing multiple uris (comma separated)
|
||||||
|
return Arrays.stream(uriString.split(","))
|
||||||
|
.map(this::sanitizeUri)
|
||||||
|
.collect(Collectors.joining(","));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String sanitizeUri(String uriString) {
|
||||||
Matcher matcher = URI_USERINFO_PATTERN.matcher(uriString);
|
Matcher matcher = URI_USERINFO_PATTERN.matcher(uriString);
|
||||||
String password = matcher.matches() ? matcher.group(1) : null;
|
String password = matcher.matches() ? matcher.group(1) : null;
|
||||||
if (password != null) {
|
if (password != null) {
|
||||||
return StringUtils.replace(uriString, ":" + password + "@", ":******@");
|
return StringUtils.replace(uriString, ":" + password + "@", ":******@");
|
||||||
}
|
}
|
||||||
return value;
|
return uriString;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.springframework.boot.actuate.context.properties;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -51,6 +52,7 @@ import static org.assertj.core.api.Assertions.entry;
|
||||||
* @author Andy Wilkinson
|
* @author Andy Wilkinson
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
* @author HaiTao Zhang
|
* @author HaiTao Zhang
|
||||||
|
* @author Chris Bono
|
||||||
*/
|
*/
|
||||||
class ConfigurationPropertiesReportEndpointTests {
|
class ConfigurationPropertiesReportEndpointTests {
|
||||||
|
|
||||||
|
@ -170,19 +172,26 @@ class ConfigurationPropertiesReportEndpointTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void sanitizedUriWithSensitiveInfo() {
|
void sanitizeUriWithSensitiveInfo() {
|
||||||
this.contextRunner.withUserConfiguration(SensiblePropertiesConfiguration.class)
|
this.contextRunner.withUserConfiguration(SensiblePropertiesConfiguration.class)
|
||||||
.run(assertProperties("sensible", (properties) -> assertThat(properties.get("sensitiveUri"))
|
.run(assertProperties("sensible", (properties) -> assertThat(properties.get("sensitiveUri"))
|
||||||
.isEqualTo("http://user:******@localhost:8080")));
|
.isEqualTo("http://user:******@localhost:8080")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void sanitizedUriWithNoPassword() {
|
void sanitizeUriWithNoPassword() {
|
||||||
this.contextRunner.withUserConfiguration(SensiblePropertiesConfiguration.class)
|
this.contextRunner.withUserConfiguration(SensiblePropertiesConfiguration.class)
|
||||||
.run(assertProperties("sensible", (properties) -> assertThat(properties.get("noPasswordUri"))
|
.run(assertProperties("sensible", (properties) -> assertThat(properties.get("noPasswordUri"))
|
||||||
.isEqualTo("http://user:******@localhost:8080")));
|
.isEqualTo("http://user:******@localhost:8080")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void sanitizeAddressesFieldContainingMultipleRawSensitiveUris() {
|
||||||
|
this.contextRunner.withUserConfiguration(SensiblePropertiesConfiguration.class)
|
||||||
|
.run(assertProperties("sensible", (properties) -> assertThat(properties.get("rawSensitiveAddresses"))
|
||||||
|
.isEqualTo("http://user:******@localhost:8080,http://user2:******@localhost:8082")));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void sanitizeLists() {
|
void sanitizeLists() {
|
||||||
this.contextRunner.withUserConfiguration(SensiblePropertiesConfiguration.class)
|
this.contextRunner.withUserConfiguration(SensiblePropertiesConfiguration.class)
|
||||||
|
@ -574,6 +583,8 @@ class ConfigurationPropertiesReportEndpointTests {
|
||||||
|
|
||||||
private URI noPasswordUri = URI.create("http://user:@localhost:8080");
|
private URI noPasswordUri = URI.create("http://user:@localhost:8080");
|
||||||
|
|
||||||
|
private String rawSensitiveAddresses = "http://user:password@localhost:8080,http://user2:password2@localhost:8082";
|
||||||
|
|
||||||
private List<ListItem> listItems = new ArrayList<>();
|
private List<ListItem> listItems = new ArrayList<>();
|
||||||
|
|
||||||
private List<List<ListItem>> listOfListItems = new ArrayList<>();
|
private List<List<ListItem>> listOfListItems = new ArrayList<>();
|
||||||
|
@ -599,6 +610,14 @@ class ConfigurationPropertiesReportEndpointTests {
|
||||||
return this.noPasswordUri;
|
return this.noPasswordUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getRawSensitiveAddresses() {
|
||||||
|
return this.rawSensitiveAddresses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRawSensitiveAddresses(final String rawSensitiveAddresses) {
|
||||||
|
this.rawSensitiveAddresses = rawSensitiveAddresses;
|
||||||
|
}
|
||||||
|
|
||||||
public List<ListItem> getListItems() {
|
public List<ListItem> getListItems() {
|
||||||
return this.listItems;
|
return this.listItems;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,10 @@
|
||||||
package org.springframework.boot.actuate.endpoint;
|
package org.springframework.boot.actuate.endpoint;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
@ -25,11 +29,12 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||||
*
|
*
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
|
* @author Chris Bono
|
||||||
*/
|
*/
|
||||||
class SanitizerTests {
|
class SanitizerTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void defaults() {
|
void defaultNonUriKeys() {
|
||||||
Sanitizer sanitizer = new Sanitizer();
|
Sanitizer sanitizer = new Sanitizer();
|
||||||
assertThat(sanitizer.sanitize("password", "secret")).isEqualTo("******");
|
assertThat(sanitizer.sanitize("password", "secret")).isEqualTo("******");
|
||||||
assertThat(sanitizer.sanitize("my-password", "secret")).isEqualTo("******");
|
assertThat(sanitizer.sanitize("my-password", "secret")).isEqualTo("******");
|
||||||
|
@ -40,21 +45,64 @@ class SanitizerTests {
|
||||||
assertThat(sanitizer.sanitize("sometoken", "secret")).isEqualTo("******");
|
assertThat(sanitizer.sanitize("sometoken", "secret")).isEqualTo("******");
|
||||||
assertThat(sanitizer.sanitize("find", "secret")).isEqualTo("secret");
|
assertThat(sanitizer.sanitize("find", "secret")).isEqualTo("secret");
|
||||||
assertThat(sanitizer.sanitize("sun.java.command", "--spring.redis.password=pa55w0rd")).isEqualTo("******");
|
assertThat(sanitizer.sanitize("sun.java.command", "--spring.redis.password=pa55w0rd")).isEqualTo("******");
|
||||||
assertThat(sanitizer.sanitize("my.uri", "http://user:password@localhost:8080"))
|
|
||||||
.isEqualTo("http://user:******@localhost:8080");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "key = {0}")
|
||||||
void uriWithNoPasswordShouldNotBeSanitized() {
|
@MethodSource("matchingUriUserInfoKeys")
|
||||||
|
void uriWithSingleEntryWithPasswordShouldBeSanitized(String key) {
|
||||||
Sanitizer sanitizer = new Sanitizer();
|
Sanitizer sanitizer = new Sanitizer();
|
||||||
assertThat(sanitizer.sanitize("my.uri", "http://localhost:8080")).isEqualTo("http://localhost:8080");
|
assertThat(sanitizer.sanitize(key, "http://user:password@localhost:8080")).isEqualTo("http://user:******@localhost:8080");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "key = {0}")
|
||||||
void uriWithPasswordMatchingOtherPartsOfString() {
|
@MethodSource("matchingUriUserInfoKeys")
|
||||||
|
void uriWithSingleEntryWithNoPasswordShouldNotBeSanitized(String key) {
|
||||||
Sanitizer sanitizer = new Sanitizer();
|
Sanitizer sanitizer = new Sanitizer();
|
||||||
assertThat(sanitizer.sanitize("my.uri", "http://user://@localhost:8080"))
|
assertThat(sanitizer.sanitize(key, "http://localhost:8080")).isEqualTo("http://localhost:8080");
|
||||||
.isEqualTo("http://user:******@localhost:8080");
|
assertThat(sanitizer.sanitize(key, "http://user@localhost:8080")).isEqualTo("http://user@localhost:8080");
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest(name = "key = {0}")
|
||||||
|
@MethodSource("matchingUriUserInfoKeys")
|
||||||
|
void uriWithSingleEntryWithPasswordMatchingOtherPartsOfStringShouldBeSanitized(String key) {
|
||||||
|
Sanitizer sanitizer = new Sanitizer();
|
||||||
|
assertThat(sanitizer.sanitize(key, "http://user://@localhost:8080")).isEqualTo("http://user:******@localhost:8080");
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest(name = "key = {0}")
|
||||||
|
@MethodSource("matchingUriUserInfoKeys")
|
||||||
|
void uriWithMultipleEntriesEachWithPasswordShouldHaveAllSanitized(String key) {
|
||||||
|
Sanitizer sanitizer = new Sanitizer();
|
||||||
|
assertThat(sanitizer.sanitize(key, "http://user1:password1@localhost:8080,http://user2:password2@localhost:8082"))
|
||||||
|
.isEqualTo("http://user1:******@localhost:8080,http://user2:******@localhost:8082");
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest(name = "key = {0}")
|
||||||
|
@MethodSource("matchingUriUserInfoKeys")
|
||||||
|
void uriWithMultipleEntriesNoneWithPasswordShouldHaveNoneSanitized(String key) {
|
||||||
|
Sanitizer sanitizer = new Sanitizer();
|
||||||
|
assertThat(sanitizer.sanitize(key, "http://user@localhost:8080,http://localhost:8082"))
|
||||||
|
.isEqualTo("http://user@localhost:8080,http://localhost:8082");
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest(name = "key = {0}")
|
||||||
|
@MethodSource("matchingUriUserInfoKeys")
|
||||||
|
void uriWithMultipleEntriesSomeWithPasswordShouldHaveThoseSanitized(String key) {
|
||||||
|
Sanitizer sanitizer = new Sanitizer();
|
||||||
|
assertThat(sanitizer.sanitize(key, "http://user1:password1@localhost:8080,http://user2@localhost:8082,http://localhost:8083"))
|
||||||
|
.isEqualTo("http://user1:******@localhost:8080,http://user2@localhost:8082,http://localhost:8083");
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest(name = "key = {0}")
|
||||||
|
@MethodSource("matchingUriUserInfoKeys")
|
||||||
|
void uriWithMultipleEntriesWithPasswordMatchingOtherPartsOfStringShouldBeSanitized(String key) {
|
||||||
|
Sanitizer sanitizer = new Sanitizer();
|
||||||
|
assertThat(sanitizer.sanitize(key, "http://user1://@localhost:8080,http://user2://@localhost:8082"))
|
||||||
|
.isEqualTo("http://user1:******@localhost:8080,http://user2:******@localhost:8082");
|
||||||
|
}
|
||||||
|
|
||||||
|
static private Stream<String> matchingUriUserInfoKeys() {
|
||||||
|
return Stream.of("uri", "my.uri", "myuri", "uris", "my.uris", "myuris", "address", "my.address", "myaddress", "addresses", "my.addresses", "myaddresses");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -63,5 +111,4 @@ class SanitizerTests {
|
||||||
assertThat(sanitizer.sanitize("verylOCkish", "secret")).isEqualTo("******");
|
assertThat(sanitizer.sanitize("verylOCkish", "secret")).isEqualTo("******");
|
||||||
assertThat(sanitizer.sanitize("veryokish", "secret")).isEqualTo("secret");
|
assertThat(sanitizer.sanitize("veryokish", "secret")).isEqualTo("secret");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||||
* @author Madhura Bhave
|
* @author Madhura Bhave
|
||||||
* @author Andy Wilkinson
|
* @author Andy Wilkinson
|
||||||
* @author HaiTao Zhang
|
* @author HaiTao Zhang
|
||||||
|
* @author Chris Bono
|
||||||
*/
|
*/
|
||||||
class EnvironmentEndpointTests {
|
class EnvironmentEndpointTests {
|
||||||
|
|
||||||
|
@ -246,13 +247,21 @@ class EnvironmentEndpointTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void uriPropertryWithSensitiveInfo() {
|
void uriPropertyWithSensitiveInfo() {
|
||||||
ConfigurableEnvironment environment = new StandardEnvironment();
|
ConfigurableEnvironment environment = new StandardEnvironment();
|
||||||
TestPropertyValues.of("sensitive.uri=http://user:password@localhost:8080").applyTo(environment);
|
TestPropertyValues.of("sensitive.uri=http://user:password@localhost:8080").applyTo(environment);
|
||||||
EnvironmentEntryDescriptor descriptor = new EnvironmentEndpoint(environment).environmentEntry("sensitive.uri");
|
EnvironmentEntryDescriptor descriptor = new EnvironmentEndpoint(environment).environmentEntry("sensitive.uri");
|
||||||
assertThat(descriptor.getProperty().getValue()).isEqualTo("http://user:******@localhost:8080");
|
assertThat(descriptor.getProperty().getValue()).isEqualTo("http://user:******@localhost:8080");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void addressesPropertyWithMultipleEntriesEachWithSensitiveInfo() {
|
||||||
|
ConfigurableEnvironment environment = new StandardEnvironment();
|
||||||
|
TestPropertyValues.of("sensitive.addresses=http://user:password@localhost:8080,http://user2:password2@localhost:8082").applyTo(environment);
|
||||||
|
EnvironmentEntryDescriptor descriptor = new EnvironmentEndpoint(environment).environmentEntry("sensitive.addresses");
|
||||||
|
assertThat(descriptor.getProperty().getValue()).isEqualTo("http://user:******@localhost:8080,http://user2:******@localhost:8082");
|
||||||
|
}
|
||||||
|
|
||||||
private static ConfigurableEnvironment emptyEnvironment() {
|
private static ConfigurableEnvironment emptyEnvironment() {
|
||||||
StandardEnvironment environment = new StandardEnvironment();
|
StandardEnvironment environment = new StandardEnvironment();
|
||||||
environment.getPropertySources().remove(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME);
|
environment.getPropertySources().remove(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME);
|
||||||
|
|
Loading…
Reference in New Issue