Skip to content

Commit badc83d

Browse files
cbo-indeedmbhave
authored andcommitted
Add 'uris', 'address' and 'addresses' to keys to sanitize.
See gh-19999
1 parent 45fd603 commit badc83d

File tree

4 files changed

+117
-21
lines changed

4 files changed

+117
-21
lines changed

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/Sanitizer.java

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616

1717
package org.springframework.boot.actuate.endpoint;
1818

19+
import java.util.Arrays;
1920
import java.util.regex.Matcher;
2021
import java.util.regex.Pattern;
22+
import java.util.stream.Collectors;
2123

2224
import org.springframework.util.Assert;
2325
import org.springframework.util.StringUtils;
@@ -32,18 +34,23 @@
3234
* @author Nicolas Lejeune
3335
* @author Stephane Nicoll
3436
* @author HaiTao Zhang
37+
* @author Chris Bono
3538
* @since 2.0.0
3639
*/
3740
public class Sanitizer {
3841

3942
private static final String[] REGEX_PARTS = { "*", "$", "^", "+" };
4043

44+
private static final String[] DEFAULT_KEYS_TO_SANITIZE = { "password", "secret", "key", "token", ".*credentials.*", "vcap_services", "sun.java.command", "uri", "uris", "address", "addresses" };
45+
46+
private static final String[] URI_USERINFO_KEYS = { "uri", "uris", "address", "addresses" };
47+
4148
private static final Pattern URI_USERINFO_PATTERN = Pattern.compile("[A-Za-z]+://.+:(.*)@.+$");
4249

4350
private Pattern[] keysToSanitize;
4451

4552
public Sanitizer() {
46-
this("password", "secret", "key", "token", ".*credentials.*", "vcap_services", "sun.java.command", "uri");
53+
this(DEFAULT_KEYS_TO_SANITIZE);
4754
}
4855

4956
public Sanitizer(String... keysToSanitize) {
@@ -91,23 +98,37 @@ public Object sanitize(String key, Object value) {
9198
}
9299
for (Pattern pattern : this.keysToSanitize) {
93100
if (pattern.matcher(key).matches()) {
94-
if (pattern.matcher("uri").matches()) {
95-
return sanitizeUri(value);
101+
if (keyIsUriWithUserInfo(pattern)) {
102+
return sanitizeUris(value.toString());
96103
}
97104
return "******";
98105
}
99106
}
100107
return value;
101108
}
102109

103-
private Object sanitizeUri(Object value) {
104-
String uriString = value.toString();
110+
private boolean keyIsUriWithUserInfo(Pattern pattern) {
111+
for (String uriKey : URI_USERINFO_KEYS) {
112+
if (pattern.matcher(uriKey).matches()) {
113+
return true;
114+
}
115+
}
116+
return false;
117+
}
118+
119+
private Object sanitizeUris(String uriString) {
120+
// Treat each uri value as possibly containing multiple uris (comma separated)
121+
return Arrays.stream(uriString.split(","))
122+
.map(this::sanitizeUri)
123+
.collect(Collectors.joining(","));
124+
}
125+
126+
private String sanitizeUri(String uriString) {
105127
Matcher matcher = URI_USERINFO_PATTERN.matcher(uriString);
106128
String password = matcher.matches() ? matcher.group(1) : null;
107129
if (password != null) {
108130
return StringUtils.replace(uriString, ":" + password + "@", ":******@");
109131
}
110-
return value;
132+
return uriString;
111133
}
112-
113134
}

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointTests.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.net.URI;
2020
import java.time.Duration;
2121
import java.util.ArrayList;
22+
import java.util.Arrays;
2223
import java.util.Collections;
2324
import java.util.HashMap;
2425
import java.util.List;
@@ -51,6 +52,7 @@
5152
* @author Andy Wilkinson
5253
* @author Stephane Nicoll
5354
* @author HaiTao Zhang
55+
* @author Chris Bono
5456
*/
5557
class ConfigurationPropertiesReportEndpointTests {
5658

@@ -170,19 +172,26 @@ void sanitizeWithCustomPatternUsingCompositeKeys() {
170172
}
171173

172174
@Test
173-
void sanitizedUriWithSensitiveInfo() {
175+
void sanitizeUriWithSensitiveInfo() {
174176
this.contextRunner.withUserConfiguration(SensiblePropertiesConfiguration.class)
175177
.run(assertProperties("sensible", (properties) -> assertThat(properties.get("sensitiveUri"))
176178
.isEqualTo("http://user:******@localhost:8080")));
177179
}
178180

179181
@Test
180-
void sanitizedUriWithNoPassword() {
182+
void sanitizeUriWithNoPassword() {
181183
this.contextRunner.withUserConfiguration(SensiblePropertiesConfiguration.class)
182184
.run(assertProperties("sensible", (properties) -> assertThat(properties.get("noPasswordUri"))
183185
.isEqualTo("http://user:******@localhost:8080")));
184186
}
185187

188+
@Test
189+
void sanitizeAddressesFieldContainingMultipleRawSensitiveUris() {
190+
this.contextRunner.withUserConfiguration(SensiblePropertiesConfiguration.class)
191+
.run(assertProperties("sensible", (properties) -> assertThat(properties.get("rawSensitiveAddresses"))
192+
.isEqualTo("http://user:******@localhost:8080,http://user2:******@localhost:8082")));
193+
}
194+
186195
@Test
187196
void sanitizeLists() {
188197
this.contextRunner.withUserConfiguration(SensiblePropertiesConfiguration.class)
@@ -574,6 +583,8 @@ public static class SensibleProperties {
574583

575584
private URI noPasswordUri = URI.create("http://user:@localhost:8080");
576585

586+
private String rawSensitiveAddresses = "http://user:password@localhost:8080,http://user2:password2@localhost:8082";
587+
577588
private List<ListItem> listItems = new ArrayList<>();
578589

579590
private List<List<ListItem>> listOfListItems = new ArrayList<>();
@@ -599,6 +610,14 @@ public URI getNoPasswordUri() {
599610
return this.noPasswordUri;
600611
}
601612

613+
public String getRawSensitiveAddresses() {
614+
return this.rawSensitiveAddresses;
615+
}
616+
617+
public void setRawSensitiveAddresses(final String rawSensitiveAddresses) {
618+
this.rawSensitiveAddresses = rawSensitiveAddresses;
619+
}
620+
602621
public List<ListItem> getListItems() {
603622
return this.listItems;
604623
}

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/SanitizerTests.java

Lines changed: 58 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
package org.springframework.boot.actuate.endpoint;
1818

1919
import org.junit.jupiter.api.Test;
20+
import org.junit.jupiter.params.ParameterizedTest;
21+
import org.junit.jupiter.params.provider.MethodSource;
22+
23+
import java.util.stream.Stream;
2024

2125
import static org.assertj.core.api.Assertions.assertThat;
2226

@@ -25,11 +29,12 @@
2529
*
2630
* @author Phillip Webb
2731
* @author Stephane Nicoll
32+
* @author Chris Bono
2833
*/
2934
class SanitizerTests {
3035

3136
@Test
32-
void defaults() {
37+
void defaultNonUriKeys() {
3338
Sanitizer sanitizer = new Sanitizer();
3439
assertThat(sanitizer.sanitize("password", "secret")).isEqualTo("******");
3540
assertThat(sanitizer.sanitize("my-password", "secret")).isEqualTo("******");
@@ -40,21 +45,64 @@ void defaults() {
4045
assertThat(sanitizer.sanitize("sometoken", "secret")).isEqualTo("******");
4146
assertThat(sanitizer.sanitize("find", "secret")).isEqualTo("secret");
4247
assertThat(sanitizer.sanitize("sun.java.command", "--spring.redis.password=pa55w0rd")).isEqualTo("******");
43-
assertThat(sanitizer.sanitize("my.uri", "http://user:password@localhost:8080"))
44-
.isEqualTo("http://user:******@localhost:8080");
4548
}
4649

47-
@Test
48-
void uriWithNoPasswordShouldNotBeSanitized() {
50+
@ParameterizedTest(name = "key = {0}")
51+
@MethodSource("matchingUriUserInfoKeys")
52+
void uriWithSingleEntryWithPasswordShouldBeSanitized(String key) {
4953
Sanitizer sanitizer = new Sanitizer();
50-
assertThat(sanitizer.sanitize("my.uri", "http://localhost:8080")).isEqualTo("http://localhost:8080");
54+
assertThat(sanitizer.sanitize(key, "http://user:password@localhost:8080")).isEqualTo("http://user:******@localhost:8080");
5155
}
5256

53-
@Test
54-
void uriWithPasswordMatchingOtherPartsOfString() {
57+
@ParameterizedTest(name = "key = {0}")
58+
@MethodSource("matchingUriUserInfoKeys")
59+
void uriWithSingleEntryWithNoPasswordShouldNotBeSanitized(String key) {
60+
Sanitizer sanitizer = new Sanitizer();
61+
assertThat(sanitizer.sanitize(key, "http://localhost:8080")).isEqualTo("http://localhost:8080");
62+
assertThat(sanitizer.sanitize(key, "http://user@localhost:8080")).isEqualTo("http://user@localhost:8080");
63+
}
64+
65+
@ParameterizedTest(name = "key = {0}")
66+
@MethodSource("matchingUriUserInfoKeys")
67+
void uriWithSingleEntryWithPasswordMatchingOtherPartsOfStringShouldBeSanitized(String key) {
5568
Sanitizer sanitizer = new Sanitizer();
56-
assertThat(sanitizer.sanitize("my.uri", "http://user://@localhost:8080"))
57-
.isEqualTo("http://user:******@localhost:8080");
69+
assertThat(sanitizer.sanitize(key, "http://user://@localhost:8080")).isEqualTo("http://user:******@localhost:8080");
70+
}
71+
72+
@ParameterizedTest(name = "key = {0}")
73+
@MethodSource("matchingUriUserInfoKeys")
74+
void uriWithMultipleEntriesEachWithPasswordShouldHaveAllSanitized(String key) {
75+
Sanitizer sanitizer = new Sanitizer();
76+
assertThat(sanitizer.sanitize(key, "http://user1:password1@localhost:8080,http://user2:password2@localhost:8082"))
77+
.isEqualTo("http://user1:******@localhost:8080,http://user2:******@localhost:8082");
78+
}
79+
80+
@ParameterizedTest(name = "key = {0}")
81+
@MethodSource("matchingUriUserInfoKeys")
82+
void uriWithMultipleEntriesNoneWithPasswordShouldHaveNoneSanitized(String key) {
83+
Sanitizer sanitizer = new Sanitizer();
84+
assertThat(sanitizer.sanitize(key, "http://user@localhost:8080,http://localhost:8082"))
85+
.isEqualTo("http://user@localhost:8080,http://localhost:8082");
86+
}
87+
88+
@ParameterizedTest(name = "key = {0}")
89+
@MethodSource("matchingUriUserInfoKeys")
90+
void uriWithMultipleEntriesSomeWithPasswordShouldHaveThoseSanitized(String key) {
91+
Sanitizer sanitizer = new Sanitizer();
92+
assertThat(sanitizer.sanitize(key, "http://user1:password1@localhost:8080,http://user2@localhost:8082,http://localhost:8083"))
93+
.isEqualTo("http://user1:******@localhost:8080,http://user2@localhost:8082,http://localhost:8083");
94+
}
95+
96+
@ParameterizedTest(name = "key = {0}")
97+
@MethodSource("matchingUriUserInfoKeys")
98+
void uriWithMultipleEntriesWithPasswordMatchingOtherPartsOfStringShouldBeSanitized(String key) {
99+
Sanitizer sanitizer = new Sanitizer();
100+
assertThat(sanitizer.sanitize(key, "http://user1://@localhost:8080,http://user2://@localhost:8082"))
101+
.isEqualTo("http://user1:******@localhost:8080,http://user2:******@localhost:8082");
102+
}
103+
104+
static private Stream<String> matchingUriUserInfoKeys() {
105+
return Stream.of("uri", "my.uri", "myuri", "uris", "my.uris", "myuris", "address", "my.address", "myaddress", "addresses", "my.addresses", "myaddresses");
58106
}
59107

60108
@Test
@@ -63,5 +111,4 @@ void regex() {
63111
assertThat(sanitizer.sanitize("verylOCkish", "secret")).isEqualTo("******");
64112
assertThat(sanitizer.sanitize("veryokish", "secret")).isEqualTo("secret");
65113
}
66-
67114
}

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/env/EnvironmentEndpointTests.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
* @author Madhura Bhave
5151
* @author Andy Wilkinson
5252
* @author HaiTao Zhang
53+
* @author Chris Bono
5354
*/
5455
class EnvironmentEndpointTests {
5556

@@ -246,13 +247,21 @@ void multipleSourcesWithSameProperty() {
246247
}
247248

248249
@Test
249-
void uriPropertryWithSensitiveInfo() {
250+
void uriPropertyWithSensitiveInfo() {
250251
ConfigurableEnvironment environment = new StandardEnvironment();
251252
TestPropertyValues.of("sensitive.uri=http://user:password@localhost:8080").applyTo(environment);
252253
EnvironmentEntryDescriptor descriptor = new EnvironmentEndpoint(environment).environmentEntry("sensitive.uri");
253254
assertThat(descriptor.getProperty().getValue()).isEqualTo("http://user:******@localhost:8080");
254255
}
255256

257+
@Test
258+
void addressesPropertyWithMultipleEntriesEachWithSensitiveInfo() {
259+
ConfigurableEnvironment environment = new StandardEnvironment();
260+
TestPropertyValues.of("sensitive.addresses=http://user:password@localhost:8080,http://user2:password2@localhost:8082").applyTo(environment);
261+
EnvironmentEntryDescriptor descriptor = new EnvironmentEndpoint(environment).environmentEntry("sensitive.addresses");
262+
assertThat(descriptor.getProperty().getValue()).isEqualTo("http://user:******@localhost:8080,http://user2:******@localhost:8082");
263+
}
264+
256265
private static ConfigurableEnvironment emptyEnvironment() {
257266
StandardEnvironment environment = new StandardEnvironment();
258267
environment.getPropertySources().remove(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME);

0 commit comments

Comments
 (0)