diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/context/properties/ConfigurationPropertiesReportEndpointAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/context/properties/ConfigurationPropertiesReportEndpointAutoConfiguration.java index e620eba1d7..8c029bfb6b 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/context/properties/ConfigurationPropertiesReportEndpointAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/context/properties/ConfigurationPropertiesReportEndpointAutoConfiguration.java @@ -16,6 +16,8 @@ package org.springframework.boot.actuate.autoconfigure.context.properties; +import java.util.stream.Collectors; + import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint; import org.springframework.boot.actuate.autoconfigure.endpoint.expose.EndpointExposure; @@ -48,7 +50,8 @@ public class ConfigurationPropertiesReportEndpointAutoConfiguration { public ConfigurationPropertiesReportEndpoint configurationPropertiesReportEndpoint( ConfigurationPropertiesReportEndpointProperties properties, ObjectProvider sanitizingFunctions) { - ConfigurationPropertiesReportEndpoint endpoint = new ConfigurationPropertiesReportEndpoint(sanitizingFunctions); + ConfigurationPropertiesReportEndpoint endpoint = new ConfigurationPropertiesReportEndpoint( + sanitizingFunctions.orderedStream().collect(Collectors.toList())); String[] keysToSanitize = properties.getKeysToSanitize(); if (keysToSanitize != null) { endpoint.setKeysToSanitize(keysToSanitize); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/env/EnvironmentEndpointAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/env/EnvironmentEndpointAutoConfiguration.java index 570b81e594..e6af2bc2f6 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/env/EnvironmentEndpointAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/env/EnvironmentEndpointAutoConfiguration.java @@ -16,6 +16,8 @@ package org.springframework.boot.actuate.autoconfigure.env; +import java.util.stream.Collectors; + import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint; import org.springframework.boot.actuate.autoconfigure.endpoint.expose.EndpointExposure; @@ -46,7 +48,8 @@ public class EnvironmentEndpointAutoConfiguration { @ConditionalOnMissingBean public EnvironmentEndpoint environmentEndpoint(Environment environment, EnvironmentEndpointProperties properties, ObjectProvider sanitizingFunctions) { - EnvironmentEndpoint endpoint = new EnvironmentEndpoint(environment, sanitizingFunctions); + EnvironmentEndpoint endpoint = new EnvironmentEndpoint(environment, + sanitizingFunctions.orderedStream().collect(Collectors.toList())); String[] keysToSanitize = properties.getKeysToSanitize(); if (keysToSanitize != null) { endpoint.setKeysToSanitize(keysToSanitize); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/env/EnvironmentEndpointAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/env/EnvironmentEndpointAutoConfigurationTests.java index 467e77f5f1..fcecb19dab 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/env/EnvironmentEndpointAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/env/EnvironmentEndpointAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -82,7 +82,7 @@ class EnvironmentEndpointAutoConfigurationTests { Map systemProperties = getSource("systemProperties", env) .getProperties(); assertThat(systemProperties.get("custom").getValue()).isEqualTo("$$$"); - assertThat(systemProperties.get("password").getValue()).isEqualTo("******"); + assertThat(systemProperties.get("password").getValue()).isEqualTo("$$$"); }); } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/Sanitizer.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/Sanitizer.java index cc5c2b941b..02e3ac96bc 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/Sanitizer.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/Sanitizer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -184,13 +184,18 @@ public class Sanitizer { * @since 2.6.0 */ public Object sanitize(SanitizableData data) { - if (data.getValue() == null) { + Object value = data.getValue(); + if (value == null) { return null; } for (SanitizingFunction sanitizingFunction : this.sanitizingFunctions) { data = sanitizingFunction.apply(data); + Object sanitizedValue = data.getValue(); + if (!value.equals(sanitizedValue)) { + return sanitizedValue; + } } - return data.getValue(); + return value; } private boolean keyIsUriWithUserInfo(Pattern pattern) { diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointTests.java index b98c72b311..69f533593b 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointTests.java @@ -293,7 +293,7 @@ class ConfigurationPropertiesReportEndpointTests { new ApplicationContextRunner().withUserConfiguration(CustomSanitizingEndpointConfig.class, SanitizingFunctionConfiguration.class, TestPropertiesConfiguration.class) .run(assertProperties("test", (properties) -> { - assertThat(properties.get("dbPassword")).isEqualTo("******"); + assertThat(properties.get("dbPassword")).isEqualTo("$$$"); assertThat(properties.get("myTestProperty")).isEqualTo("$$$"); })); } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/SanitizerTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/SanitizerTests.java index 7d30f24c0b..a29771f058 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/SanitizerTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/SanitizerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,9 @@ package org.springframework.boot.actuate.endpoint; +import java.util.ArrayList; import java.util.Collections; +import java.util.List; import java.util.stream.Stream; import org.junit.jupiter.api.Test; @@ -87,6 +89,39 @@ class SanitizerTests { assertThat(sanitizer.sanitize(hello)).isEqualTo("abc"); } + @Test + void overridingDefaultSanitizingFunction() { + Sanitizer sanitizer = new Sanitizer(Collections.singletonList((data) -> { + if (data.getKey().equals("password")) { + return data.withValue("------"); + } + return data; + })); + SanitizableData password = new SanitizableData(null, "password", "123456"); + assertThat(sanitizer.sanitize(password)).isEqualTo("------"); + } + + @Test + void whenValueSanitizedLaterSanitizingFunctionsShouldBeSkipped() { + final String sameKey = "custom"; + List sanitizingFunctions = new ArrayList<>(); + sanitizingFunctions.add((data) -> { + if (data.getKey().equals(sameKey)) { + return data.withValue("------"); + } + return data; + }); + sanitizingFunctions.add((data) -> { + if (data.getKey().equals(sameKey)) { + return data.withValue("******"); + } + return data; + }); + Sanitizer sanitizer = new Sanitizer(sanitizingFunctions); + SanitizableData custom = new SanitizableData(null, sameKey, "123456"); + assertThat(sanitizer.sanitize(custom)).isEqualTo("------"); + } + @ParameterizedTest(name = "key = {0}") @MethodSource("matchingUriUserInfoKeys") void uriWithSingleValueWithPasswordShouldBeSanitized(String key) {