diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpoint.java index 1d629b98a3c..f60bfc8537e 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpoint.java @@ -111,7 +111,7 @@ public class ConfigurationPropertiesReportEndpoint Map root = new HashMap(); String prefix = extractPrefix(context, beanFactoryMetaData, beanName, bean); root.put("prefix", prefix); - root.put("properties", sanitize(safeSerialize(mapper, bean, prefix))); + root.put("properties", sanitize(prefix, safeSerialize(mapper, bean, prefix))); result.put(beanName, root); } if (context.getParent() != null) { @@ -231,15 +231,18 @@ public class ConfigurationPropertiesReportEndpoint * @return the sanitized map */ @SuppressWarnings("unchecked") - private Map sanitize(Map map) { + private Map sanitize(String prefix, Map map) { for (Map.Entry entry : map.entrySet()) { String key = entry.getKey(); + String qualifiedKey = (prefix.length() == 0 ? prefix : prefix + ".") + key; Object value = entry.getValue(); if (value instanceof Map) { - map.put(key, sanitize((Map) value)); + map.put(key, sanitize(qualifiedKey, (Map) value)); } else { - map.put(key, this.sanitizer.sanitize(key, value)); + value = this.sanitizer.sanitize(key, value); + value = this.sanitizer.sanitize(qualifiedKey, value); + map.put(key, value); } } return map; diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpointTests.java index ee9bd1d7d6f..350e8e5c715 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpointTests.java @@ -16,6 +16,7 @@ package org.springframework.boot.actuate.endpoint; +import java.util.HashMap; import java.util.Map; import org.junit.Test; @@ -135,8 +136,8 @@ public class ConfigurationPropertiesReportEndpointTests assertEquals("654321", nestedProperties.get("myTestProperty")); } - @SuppressWarnings("unchecked") @Test + @SuppressWarnings("unchecked") public void testKeySanitizationWithCustomPatternAndKeyByEnvironment() throws Exception { this.context = new AnnotationConfigApplicationContext(); @@ -153,6 +154,29 @@ public class ConfigurationPropertiesReportEndpointTests assertEquals("******", nestedProperties.get("myTestProperty")); } + @Test + @SuppressWarnings("unchecked") + public void testKeySanitizationWithCustomPatternUsingCompositeKeys() + throws Exception { + // gh-4415 + this.context = new AnnotationConfigApplicationContext(); + EnvironmentTestUtils.addEnvironment(this.context, + "endpoints.configprops.keys-to-sanitize: .*\\.secrets\\..*, .*\\.hidden\\..*"); + this.context.register(Config.class); + this.context.refresh(); + ConfigurationPropertiesReportEndpoint report = getEndpointBean(); + Map properties = report.invoke(); + Map nestedProperties = (Map) ((Map) properties + .get("testProperties")).get("properties"); + assertNotNull(nestedProperties); + Map secrets = (Map) nestedProperties + .get("secrets"); + Map hidden = (Map) nestedProperties.get("hidden"); + assertEquals("******", secrets.get("mine")); + assertEquals("******", secrets.get("yours")); + assertEquals("******", hidden.get("mine")); + } + @Test @SuppressWarnings("unchecked") public void mixedBoolean() throws Exception { @@ -197,6 +221,15 @@ public class ConfigurationPropertiesReportEndpointTests private Boolean mixedBoolean = true; + private Map secrets = new HashMap(); + + private Hidden hidden = new Hidden(); + + public TestProperties() { + this.secrets.put("mine", "myPrivateThing"); + this.secrets.put("yours", "yourPrivateThing"); + } + public String getDbPassword() { return this.dbPassword; } @@ -221,5 +254,35 @@ public class ConfigurationPropertiesReportEndpointTests this.mixedBoolean = mixedBoolean; } + public Map getSecrets() { + return this.secrets; + } + + public void setSecrets(Map secrets) { + this.secrets = secrets; + } + + public Hidden getHidden() { + return this.hidden; + } + + public void setHidden(Hidden hidden) { + this.hidden = hidden; + } + + public static class Hidden { + + private String mine = "mySecret"; + + public String getMine() { + return this.mine; + } + + public void setMine(String mine) { + this.mine = mine; + } + + } + } }