Add convenience methods to SanitizingFunction
Add a `sanitizeValue()` factory method and `if...` methods that can be used to quickly build a `SanitizingFunction`. Closes gh-39243
This commit is contained in:
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.boot.actuate.endpoint;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.springframework.core.env.PropertySource;
|
||||
|
||||
/**
|
||||
@@ -36,6 +38,8 @@ public final class SanitizableData {
|
||||
|
||||
private final String key;
|
||||
|
||||
private String lowerCaseKey;
|
||||
|
||||
private final Object value;
|
||||
|
||||
/**
|
||||
@@ -67,6 +71,20 @@ public final class SanitizableData {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key as a lowercase value.
|
||||
* @return the key as a lowercase value
|
||||
* @since 3.5.0
|
||||
*/
|
||||
public String getLowerCaseKey() {
|
||||
String result = this.lowerCaseKey;
|
||||
if (result == null && this.key != null) {
|
||||
result = this.key.toLowerCase(Locale.getDefault());
|
||||
this.lowerCaseKey = result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the data.
|
||||
* @return the data value
|
||||
|
||||
@@ -72,7 +72,7 @@ public class Sanitizer {
|
||||
return SanitizableData.SANITIZED_VALUE;
|
||||
}
|
||||
for (SanitizingFunction sanitizingFunction : this.sanitizingFunctions) {
|
||||
data = sanitizingFunction.apply(data);
|
||||
data = sanitizingFunction.applyUnlessFiltered(data);
|
||||
Object sanitizedValue = data.getValue();
|
||||
if (!value.equals(sanitizedValue)) {
|
||||
return sanitizedValue;
|
||||
|
||||
@@ -16,12 +16,27 @@
|
||||
|
||||
package org.springframework.boot.actuate.endpoint;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Function that takes a {@link SanitizableData} and applies sanitization to the value, if
|
||||
* necessary. Can be used by a {@link Sanitizer} to determine the sanitized value.
|
||||
* <p>
|
||||
* This interface also provides convenience methods that can help build a
|
||||
* {@link SanitizingFunction} instances, for example to return from a {@code @Bean}
|
||||
* method. See {@link #sanitizeValue()} for an example.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @author Phillip Webb
|
||||
* @since 2.6.0
|
||||
* @see Sanitizer
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface SanitizingFunction {
|
||||
@@ -33,4 +48,387 @@ public interface SanitizingFunction {
|
||||
*/
|
||||
SanitizableData apply(SanitizableData data);
|
||||
|
||||
/**
|
||||
* Return an optional filter that determines if the sanitizing function applies.
|
||||
* @return a predicate used to filter functions or {@code null} if no filter is
|
||||
* declared
|
||||
* @since 3.5.0
|
||||
* @see #applyUnlessFiltered(SanitizableData)
|
||||
*/
|
||||
default Predicate<SanitizableData> filter() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the sanitizing function as long as the filter passes or there is no filter.
|
||||
* @param data the data to sanitize
|
||||
* @return the sanitized data or the original instance is no sanitization is applied
|
||||
* @since 3.5.0
|
||||
*/
|
||||
default SanitizableData applyUnlessFiltered(SanitizableData data) {
|
||||
return (filter() == null || filter().test(data)) ? apply(data) : data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new function with a filter that <em>also</em> applies if the data is
|
||||
* likely to contain a sensitive value. This method can help construct a useful
|
||||
* sanitizing function, but may not catch all sensitive data so care should be taken
|
||||
* to test the results for your specific environment.
|
||||
* @return a new sanitizing function with an updated {@link #filter()}
|
||||
* @since 3.5.0
|
||||
* @see #filter()
|
||||
* @see #sanitizeValue()
|
||||
*/
|
||||
default SanitizingFunction ifLikelySenstive() {
|
||||
return ifLikelyCredential().ifLikelyUri().ifLikelySenstiveEnvironmentVariable().ifVcapServices();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new function with a filter that <em>also</em> applies if the data is
|
||||
* likely to contain a credential. This method can help construct a useful sanitizing
|
||||
* function, but may not catch all sensitive data so care should be taken to test the
|
||||
* results for your specific environment.
|
||||
* @return a new sanitizing function with an updated {@link #filter()}
|
||||
* @since 3.5.0
|
||||
* @see #filter()
|
||||
* @see #sanitizeValue()
|
||||
*/
|
||||
default SanitizingFunction ifLikelyCredential() {
|
||||
return ifKeyEndsWith("password", "secret", "key", "token").ifKeyContains("credentials");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new function with a filter that <em>also</em> applies if the data is
|
||||
* likely to contain a URI. This method can help construct a useful sanitizing
|
||||
* function, but may not catch all sensitive data so care should be taken to test the
|
||||
* results for your specific environment.
|
||||
* @return a new sanitizing function with an updated {@link #filter()}
|
||||
* @since 3.5.0
|
||||
* @see #filter()
|
||||
* @see #sanitizeValue()
|
||||
*/
|
||||
default SanitizingFunction ifLikelyUri() {
|
||||
return ifKeyEndsWith("uri", "uris", "url", "urls", "address", "addresses");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new function with a filter that <em>also</em> applies if the data is
|
||||
* likely to sensitive environment variable value. This method can help construct a
|
||||
* useful sanitizing function, but may not catch all sensitive data so care should be
|
||||
* taken to test the results for your specific environment.
|
||||
* @return a new sanitizing function with an updated {@link #filter()}
|
||||
* @since 3.5.0
|
||||
* @see #filter()
|
||||
* @see #sanitizeValue()
|
||||
*/
|
||||
default SanitizingFunction ifLikelySenstiveEnvironmentVariable() {
|
||||
return ifKeyMatches("sun.java.command", "^spring[._]application[._]json$");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new function with a filter that <em>also</em> applies if the data is for
|
||||
* VCAP services.
|
||||
* @return a new sanitizing function with an updated {@link #filter()}
|
||||
* @since 3.5.0
|
||||
* @see #filter()
|
||||
* @see #sanitizeValue()
|
||||
*/
|
||||
|
||||
default SanitizingFunction ifVcapServices() {
|
||||
return ifKeyEquals("vcap_services").ifKeyMatches("^vcap\\.services.*$");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new function with a filter that <em>also</em> applies if the data key is
|
||||
* equal to any of the given values (ignoring case).
|
||||
* @param values the case insensitive values that the key can equal
|
||||
* @return a new sanitizing function with an updated {@link #filter()}
|
||||
* @since 3.5.0
|
||||
* @see #filter()
|
||||
* @see #sanitizeValue()
|
||||
*/
|
||||
default SanitizingFunction ifKeyEquals(String... values) {
|
||||
Assert.notNull(values, "'values' must not be null");
|
||||
return ifKeyMatchesIgnoringCase(String::equals, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new function with a filter that <em>also</em> applies if the data key ends
|
||||
* with any of the given values (ignoring case).
|
||||
* @param suffixes the case insensitive suffixes that they key can end with
|
||||
* @return a new sanitizing function with an updated {@link #filter()}
|
||||
* @since 3.5.0
|
||||
* @see #filter()
|
||||
* @see #sanitizeValue()
|
||||
*/
|
||||
default SanitizingFunction ifKeyEndsWith(String... suffixes) {
|
||||
Assert.notNull(suffixes, "'suffixes' must not be null");
|
||||
return ifKeyMatchesIgnoringCase(String::endsWith, suffixes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new function with a filter that <em>also</em> applies if the data key
|
||||
* contains any of the given values (ignoring case).
|
||||
* @param values the case insensitive values that the key can contain
|
||||
* @return a new sanitizing function with an updated {@link #filter()}
|
||||
* @since 3.5.0
|
||||
* @see #filter()
|
||||
* @see #sanitizeValue()
|
||||
*/
|
||||
default SanitizingFunction ifKeyContains(String... values) {
|
||||
Assert.notNull(values, "'values' must not be null");
|
||||
return ifKeyMatchesIgnoringCase(String::contains, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new function with a filter that <em>also</em> applies if the data key and
|
||||
* any of the values match the given predicate. The predicate is only called with
|
||||
* lower case values.
|
||||
* @param predicate the predicate used to check the key against a value. The key is
|
||||
* the first argument and the value is the second. Both are converted to lower case
|
||||
* @param values the case insensitive values that the key can match
|
||||
* @return a new sanitizing function with an updated {@link #filter()}
|
||||
* @since 3.5.0
|
||||
* @see #filter()
|
||||
* @see #sanitizeValue()
|
||||
*/
|
||||
default SanitizingFunction ifKeyMatchesIgnoringCase(BiPredicate<String, String> predicate, String... values) {
|
||||
Assert.notNull(predicate, "'predicate' must not be null");
|
||||
Assert.notNull(values, "'values' must not be null");
|
||||
return ifMatches(Arrays.stream(values).map((value) -> onKeyIgnoringCase(predicate, value)).toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new function with a filter that <em>also</em> applies if the data key
|
||||
* matches any of the given regex patterns (ignoring case).
|
||||
* @param regexes the case insensitive regexes that the key can match
|
||||
* @return a new sanitizing function with an updated {@link #filter()}
|
||||
* @since 3.5.0
|
||||
* @see #filter()
|
||||
* @see #sanitizeValue()
|
||||
*/
|
||||
default SanitizingFunction ifKeyMatches(String... regexes) {
|
||||
Assert.notNull(regexes, "'regexes' must not be null");
|
||||
return ifKeyMatches(Arrays.stream(regexes).map(this::caseInsensitivePattern).toArray(Pattern[]::new));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new function with a filter that <em>also</em> applies if the data key
|
||||
* matches any of the given patterns.
|
||||
* @param patterns the patterns that the key can match
|
||||
* @return a new sanitizing function with an updated {@link #filter()}
|
||||
* @since 3.5.0
|
||||
* @see #filter()
|
||||
* @see #sanitizeValue()
|
||||
*/
|
||||
default SanitizingFunction ifKeyMatches(Pattern... patterns) {
|
||||
Assert.notNull(patterns, "'patterns' must not be null");
|
||||
return ifKeyMatches(Arrays.stream(patterns).map(Pattern::asMatchPredicate).toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new function with a filter that <em>also</em> applies if the data key
|
||||
* matches any of the given predicates.
|
||||
* @param predicates the predicates that the key can match
|
||||
* @return a new sanitizing function with an updated {@link #filter()}
|
||||
* @since 3.5.0
|
||||
* @see #filter()
|
||||
* @see #sanitizeValue()
|
||||
*/
|
||||
default SanitizingFunction ifKeyMatches(List<Predicate<String>> predicates) {
|
||||
Assert.notNull(predicates, "'predicates' must not be null");
|
||||
return ifMatches(predicates.stream().map(this::onKey).toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new function with a filter that <em>also</em> applies if the data key
|
||||
* matches any of the given predicate.
|
||||
* @param predicate the predicate that the key can match
|
||||
* @return a new sanitizing function with an updated {@link #filter()}
|
||||
* @since 3.5.0
|
||||
* @see #filter()
|
||||
* @see #sanitizeValue()
|
||||
*/
|
||||
default SanitizingFunction ifKeyMatches(Predicate<String> predicate) {
|
||||
Assert.notNull(predicate, "'predicate' must not be null");
|
||||
return ifMatches(onKey(predicate));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new function with a filter that <em>also</em> applies if the data string
|
||||
* value matches any of the given regex patterns (ignoring case).
|
||||
* @param regexes the case insensitive regexes that the values string can match
|
||||
* @return a new sanitizing function with an updated {@link #filter()}
|
||||
* @since 3.5.0
|
||||
* @see #filter()
|
||||
* @see #sanitizeValue()
|
||||
*/
|
||||
default SanitizingFunction ifValueStringMatches(String... regexes) {
|
||||
Assert.notNull(regexes, "'regexes' must not be null");
|
||||
return ifValueStringMatches(Arrays.stream(regexes).map(this::caseInsensitivePattern).toArray(Pattern[]::new));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new function with a filter that <em>also</em> applies if the data string
|
||||
* value matches any of the given patterns.
|
||||
* @param patterns the patterns that the value string can match
|
||||
* @return a new sanitizing function with an updated {@link #filter()}
|
||||
* @since 3.5.0
|
||||
* @see #filter()
|
||||
* @see #sanitizeValue()
|
||||
*/
|
||||
default SanitizingFunction ifValueStringMatches(Pattern... patterns) {
|
||||
Assert.notNull(patterns, "'patterns' must not be null");
|
||||
return ifValueStringMatches(Arrays.stream(patterns).map(Pattern::asMatchPredicate).toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new function with a filter that <em>also</em> applies if the data string
|
||||
* value matches any of the given predicates.
|
||||
* @param predicates the predicates that the value string can match
|
||||
* @return a new sanitizing function with an updated {@link #filter()}
|
||||
* @since 3.5.0
|
||||
* @see #filter()
|
||||
* @see #sanitizeValue()
|
||||
*/
|
||||
|
||||
default SanitizingFunction ifValueStringMatches(List<Predicate<String>> predicates) {
|
||||
Assert.notNull(predicates, "'predicates' must not be null");
|
||||
return ifMatches(predicates.stream().map(this::onValueString).toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new function with a filter that <em>also</em> applies if the data value
|
||||
* matches any of the given predicates.
|
||||
* @param predicates the predicates that the value can match
|
||||
* @return a new sanitizing function with an updated {@link #filter()}
|
||||
* @since 3.5.0
|
||||
* @see #filter()
|
||||
* @see #sanitizeValue()
|
||||
*/
|
||||
default SanitizingFunction ifValueMatches(List<Predicate<Object>> predicates) {
|
||||
Assert.notNull(predicates, "'predicates' must not be null");
|
||||
return ifMatches(predicates.stream().map(this::onValue).toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new function with a filter that <em>also</em> applies if the data string
|
||||
* value matches the given predicate.
|
||||
* @param predicate the predicate that the value string can match
|
||||
* @return a new sanitizing function with an updated {@link #filter()}
|
||||
* @since 3.5.0
|
||||
* @see #filter()
|
||||
* @see #sanitizeValue()
|
||||
*/
|
||||
|
||||
default SanitizingFunction ifValueStringMatches(Predicate<String> predicate) {
|
||||
Assert.notNull(predicate, "'predicate' must not be null");
|
||||
return ifMatches(onValueString(predicate));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new function with a filter that <em>also</em> applies if the data value
|
||||
* matches the given predicate.
|
||||
* @param predicate the predicate that the value can match
|
||||
* @return a new sanitizing function with an updated {@link #filter()}
|
||||
* @since 3.5.0
|
||||
* @see #filter()
|
||||
* @see #sanitizeValue()
|
||||
*/
|
||||
default SanitizingFunction ifValueMatches(Predicate<Object> predicate) {
|
||||
Assert.notNull(predicate, "'predicate' must not be null");
|
||||
return ifMatches((data) -> predicate.test(data.getValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new function with a filter that <em>also</em> applies if the data matches
|
||||
* any of the given predicates.
|
||||
* @param predicates the predicates that the data can match
|
||||
* @return a new sanitizing function with an updated {@link #filter()}
|
||||
* @since 3.5.0
|
||||
* @see #filter()
|
||||
* @see #sanitizeValue()
|
||||
*/
|
||||
default SanitizingFunction ifMatches(List<Predicate<SanitizableData>> predicates) {
|
||||
Assert.notNull(predicates, "'predicates' must not be null");
|
||||
Predicate<SanitizableData> combined = null;
|
||||
for (Predicate<SanitizableData> predicate : predicates) {
|
||||
combined = (combined != null) ? combined.or(predicate) : predicate;
|
||||
}
|
||||
return ifMatches(combined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new function with a filter that <em>also</em> applies if the data matches
|
||||
* the given predicate.
|
||||
* @param predicate the predicate that the data can match
|
||||
* @return a new sanitizing function with an updated {@link #filter()}
|
||||
* @since 3.5.0
|
||||
* @see #filter()
|
||||
* @see #sanitizeValue()
|
||||
*/
|
||||
default SanitizingFunction ifMatches(Predicate<SanitizableData> predicate) {
|
||||
Assert.notNull(predicate, "'predicate' must not be null");
|
||||
Predicate<SanitizableData> filter = (filter() != null) ? filter().or(predicate) : predicate;
|
||||
return new SanitizingFunction() {
|
||||
|
||||
@Override
|
||||
public Predicate<SanitizableData> filter() {
|
||||
return filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SanitizableData apply(SanitizableData data) {
|
||||
return SanitizingFunction.this.apply(data);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
private Pattern caseInsensitivePattern(String regex) {
|
||||
Assert.notNull(regex, "'regex' must not be null");
|
||||
return Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
|
||||
}
|
||||
|
||||
private Predicate<SanitizableData> onKeyIgnoringCase(BiPredicate<String, String> predicate, String value) {
|
||||
Assert.notNull(predicate, "'predicate' must not be null");
|
||||
Assert.notNull(value, "'value' must not be null");
|
||||
String lowerCaseValue = value.toLowerCase(Locale.getDefault());
|
||||
return (data) -> nullSafeTest(data.getLowerCaseKey(),
|
||||
(lowerCaseKey) -> predicate.test(lowerCaseKey, lowerCaseValue));
|
||||
}
|
||||
|
||||
private Predicate<SanitizableData> onKey(Predicate<String> predicate) {
|
||||
Assert.notNull(predicate, "'predicate' must not be null");
|
||||
return (data) -> nullSafeTest(data.getKey(), predicate);
|
||||
}
|
||||
|
||||
private Predicate<SanitizableData> onValue(Predicate<Object> predicate) {
|
||||
Assert.notNull(predicate, "'predicate' must not be null");
|
||||
return (data) -> nullSafeTest(data.getValue(), predicate);
|
||||
}
|
||||
|
||||
private Predicate<SanitizableData> onValueString(Predicate<String> predicate) {
|
||||
Assert.notNull(predicate, "'predicate' must not be null");
|
||||
return (data) -> nullSafeTest((data.getValue() != null) ? data.getValue().toString() : null, predicate);
|
||||
}
|
||||
|
||||
private <T> boolean nullSafeTest(T value, Predicate<T> predicate) {
|
||||
return value != null && predicate.test(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to return a {@link SanitizingFunction} that sanitizes the value.
|
||||
* This method is often chained with one or more {@code if...} methods. For example:
|
||||
* <pre class="code">
|
||||
* return SanitizingFunction.sanitizeValue()
|
||||
* .ifKeyContains("password", "secret")
|
||||
* .ifValueStringMatches("^gh._[a-zA-Z0-9]{36}$");
|
||||
* </pre>
|
||||
* @return a {@link SanitizingFunction} that sanitizes values.
|
||||
*/
|
||||
static SanitizingFunction sanitizeValue() {
|
||||
return SanitizableData::withSanitizedValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -93,6 +93,15 @@ class SanitizerTests {
|
||||
assertThat(sanitizer.sanitize(password, true)).isEqualTo("------");
|
||||
}
|
||||
|
||||
@Test
|
||||
void overridingDefaultSanitizingFunctionWithFiltered() {
|
||||
Sanitizer sanitizer = new Sanitizer(List.of(SanitizingFunction.sanitizeValue().ifLikelySenstive()));
|
||||
SanitizableData other = new SanitizableData(null, "other", "123456");
|
||||
SanitizableData password = new SanitizableData(null, "password", "123456");
|
||||
assertThat(sanitizer.sanitize(other, true)).isEqualTo("123456");
|
||||
assertThat(sanitizer.sanitize(password, true)).isEqualTo(SanitizableData.SANITIZED_VALUE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenValueSanitizedLaterSanitizingFunctionsShouldBeSkipped() {
|
||||
final String sameKey = "custom";
|
||||
|
||||
@@ -0,0 +1,338 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.assertj.core.api.Condition;
|
||||
import org.assertj.core.api.ObjectAssert;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link SanitizingFunction}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class SanitizingFunctionTests {
|
||||
|
||||
private static final SanitizableData data = data("key");
|
||||
|
||||
@Test
|
||||
void applyUnlessFilteredWhenHasNoFilterReturnsFiltered() {
|
||||
SanitizingFunction function = SanitizingFunction.sanitizeValue();
|
||||
assertThat(function.apply(data)).has(sanitizedValue());
|
||||
assertThat(function.applyUnlessFiltered(data)).has(sanitizedValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyUnlessFilteredWhenHasFilterTestingTrueReturnsFiltered() {
|
||||
SanitizingFunction function = SanitizingFunction.sanitizeValue().ifMatches((data) -> true);
|
||||
assertThat(function.apply(data)).has(sanitizedValue());
|
||||
assertThat(function.applyUnlessFiltered(data)).has(sanitizedValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyUnlessFilteredWhenHasFilterTestingFalseReturnsUnfiltered() {
|
||||
SanitizingFunction function = SanitizingFunction.sanitizeValue().ifMatches((data) -> false);
|
||||
assertThat(function.apply(data)).has(sanitizedValue());
|
||||
assertThat(function.applyUnlessFiltered(data)).has(unsanitizedValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ifLikelySenstiveFiltersExpected() {
|
||||
SanitizingFunction function = SanitizingFunction.sanitizeValue().ifLikelySenstive();
|
||||
assertThat(function).satisfies(this::likelyCredentialChecks, this::likelyUriChecks,
|
||||
this::likelySenstiveEnvironmentVariableChecks, this::vcapServicesChecks);
|
||||
}
|
||||
|
||||
@Test
|
||||
void ifLikelyCredentialFiltersExpected() {
|
||||
SanitizingFunction function = SanitizingFunction.sanitizeValue().ifLikelyCredential();
|
||||
assertThat(function).satisfies(this::likelyCredentialChecks);
|
||||
}
|
||||
|
||||
private void likelyCredentialChecks(SanitizingFunction function) {
|
||||
assertThatApplyingToKey(function, "password").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "database.password").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "PASSWORD").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "secret").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "key").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "token").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "credentials").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "thecredentialssecret").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "some.credentials.here").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "test").has(unsanitizedValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ifLikelyUriFiltersExpected() {
|
||||
SanitizingFunction function = SanitizingFunction.sanitizeValue().ifLikelyUri();
|
||||
assertThat(function).satisfies(this::likelyUriChecks);
|
||||
}
|
||||
|
||||
private void likelyUriChecks(SanitizingFunction function) {
|
||||
assertThatApplyingToKey(function, "uri").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "URI").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "database.uri").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "uris").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "url").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "urls").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "address").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "addresses").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "test").has(unsanitizedValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ifLikelySenstiveEnvironmentVariableFiltersExpected() {
|
||||
SanitizingFunction function = SanitizingFunction.sanitizeValue().ifLikelySenstiveEnvironmentVariable();
|
||||
assertThat(function).satisfies(this::likelySenstiveEnvironmentVariableChecks);
|
||||
}
|
||||
|
||||
private void likelySenstiveEnvironmentVariableChecks(SanitizingFunction function) {
|
||||
assertThatApplyingToKey(function, "sun.java.command").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "spring.application.json").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "SPRING_APPLICATION_JSON").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "some.other.json").has(unsanitizedValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ifVcapServicesFiltersExpected() {
|
||||
SanitizingFunction function = SanitizingFunction.sanitizeValue().ifVcapServices();
|
||||
assertThat(function).satisfies(this::vcapServicesChecks);
|
||||
}
|
||||
|
||||
private void vcapServicesChecks(SanitizingFunction function) {
|
||||
assertThatApplyingToKey(function, "vcap_services").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "vcap.services").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "vcap.services.whatever").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "notvcap.services").has(unsanitizedValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ifKeyEqualsFiltersExpected() {
|
||||
SanitizingFunction function = SanitizingFunction.sanitizeValue().ifKeyEquals("spring", "test");
|
||||
assertThatApplyingToKey(function, "spring").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "SPRING").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "SpRiNg").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "test").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "boot").has(unsanitizedValue());
|
||||
assertThatApplyingToKey(function, "xspring").has(unsanitizedValue());
|
||||
assertThatApplyingToKey(function, "springx").has(unsanitizedValue());
|
||||
assertThatApplyingToKey(function, null).has(unsanitizedValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ifKeyEndsWithFiltersExpected() {
|
||||
SanitizingFunction function = SanitizingFunction.sanitizeValue().ifKeyEndsWith("boot", "test");
|
||||
assertThatApplyingToKey(function, "springboot").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "SPRINGboot").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "springBOOT").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "boot").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "atest").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "bootx").has(unsanitizedValue());
|
||||
assertThatApplyingToKey(function, null).has(unsanitizedValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ifKeyContainsFiltersExpected() {
|
||||
SanitizingFunction function = SanitizingFunction.sanitizeValue().ifKeyContains("oo", "ee");
|
||||
assertThatApplyingToKey(function, "oo").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "OO").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "bOOt").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "boot").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "beet").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "spring").has(unsanitizedValue());
|
||||
assertThatApplyingToKey(function, null).has(unsanitizedValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ifKeyMatchesIgnoringCaseFiltersExpected() {
|
||||
SanitizingFunction function = SanitizingFunction.sanitizeValue()
|
||||
.ifKeyMatchesIgnoringCase((key, value) -> key.startsWith(value) && key.endsWith(value), "x", "y");
|
||||
assertThatApplyingToKey(function, "xtestx").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "XtestX").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "YY").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "xy").has(unsanitizedValue());
|
||||
assertThatApplyingToKey(function, null).has(unsanitizedValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ifKeyMatchesWithRegexFiltersExpected() {
|
||||
SanitizingFunction function = SanitizingFunction.sanitizeValue().ifKeyMatches("^sp.*$", "^bo.*$");
|
||||
assertThatApplyingToKey(function, "spring").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "spin").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "SPRING").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "BOOT").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "xspring").has(unsanitizedValue());
|
||||
assertThatApplyingToKey(function, null).has(unsanitizedValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ifKeyMatchesWithPatternFiltersExpected() {
|
||||
SanitizingFunction function = SanitizingFunction.sanitizeValue().ifKeyMatches(Pattern.compile("^sp.*$"));
|
||||
assertThatApplyingToKey(function, "spring").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "spin").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "SPRING").has(unsanitizedValue());
|
||||
assertThatApplyingToKey(function, "xspring").has(unsanitizedValue());
|
||||
assertThatApplyingToKey(function, null).has(unsanitizedValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ifKeyMatchesWithPredicatesFiltersExpected() {
|
||||
SanitizingFunction function = SanitizingFunction.sanitizeValue()
|
||||
.ifKeyMatches(List.of((key) -> key.startsWith("sp"), (key) -> key.startsWith("BO")));
|
||||
assertThatApplyingToKey(function, "spring").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "spin").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "BO").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "SPRING").has(unsanitizedValue());
|
||||
assertThatApplyingToKey(function, "boot").has(unsanitizedValue());
|
||||
assertThatApplyingToKey(function, null).has(unsanitizedValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ifKeyMatchesWithPredicateFiltersExpected() {
|
||||
SanitizingFunction function = SanitizingFunction.sanitizeValue().ifKeyMatches((key) -> key.startsWith("sp"));
|
||||
assertThatApplyingToKey(function, "spring").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "spin").has(sanitizedValue());
|
||||
assertThatApplyingToKey(function, "boot").has(unsanitizedValue());
|
||||
assertThatApplyingToKey(function, null).has(unsanitizedValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ifValueStringMatchesWithRegexesFiltersExpected() {
|
||||
SanitizingFunction function = SanitizingFunction.sanitizeValue().ifValueStringMatches("^sp.*$", "^bo.*$");
|
||||
assertThatApplyingToValue(function, "spring").has(sanitizedValue());
|
||||
assertThatApplyingToValue(function, "SPRING").has(sanitizedValue());
|
||||
assertThatApplyingToValue(function, "boot").has(sanitizedValue());
|
||||
assertThatApplyingToValue(function, "other").has(unsanitizedValue());
|
||||
assertThatApplyingToKey(function, null).has(unsanitizedValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ifValueStringMatchesWithPatternsFiltersExpected() {
|
||||
SanitizingFunction function = SanitizingFunction.sanitizeValue()
|
||||
.ifValueStringMatches(Pattern.compile("^sp.*$"));
|
||||
assertThatApplyingToValue(function, "spring").has(sanitizedValue());
|
||||
assertThatApplyingToValue(function, "spin").has(sanitizedValue());
|
||||
assertThatApplyingToValue(function, "SPRING").has(unsanitizedValue());
|
||||
assertThatApplyingToValue(function, "xspring").has(unsanitizedValue());
|
||||
assertThatApplyingToValue(function, null).has(unsanitizedValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ifValueStringStringMatchesWithPredicatesFiltersExpected() {
|
||||
SanitizingFunction function = SanitizingFunction.sanitizeValue()
|
||||
.ifValueStringMatches(List.of((value) -> value.startsWith("sp"), (value) -> value.startsWith("BO")));
|
||||
assertThatApplyingToValue(function, "spring").has(sanitizedValue());
|
||||
assertThatApplyingToValue(function, "spin").has(sanitizedValue());
|
||||
assertThatApplyingToValue(function, "BO").has(sanitizedValue());
|
||||
assertThatApplyingToValue(function, "SPRING").has(unsanitizedValue());
|
||||
assertThatApplyingToValue(function, "boot").has(unsanitizedValue());
|
||||
assertThatApplyingToValue(function, null).has(unsanitizedValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ifValueStringMatchesWithPredicateFiltersExpected() {
|
||||
SanitizingFunction function = SanitizingFunction.sanitizeValue()
|
||||
.ifValueStringMatches((value) -> value.startsWith("sp"));
|
||||
assertThatApplyingToValue(function, "spring").has(sanitizedValue());
|
||||
assertThatApplyingToValue(function, "spin").has(sanitizedValue());
|
||||
assertThatApplyingToValue(function, "boot").has(unsanitizedValue());
|
||||
assertThatApplyingToValue(function, null).has(unsanitizedValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ifValueMatchesWithPredicatesFiltersExpected() {
|
||||
SanitizingFunction function = SanitizingFunction.sanitizeValue()
|
||||
.ifValueMatches(List.of((value) -> value instanceof String string && string.startsWith("sp"),
|
||||
(value) -> value instanceof String string && string.startsWith("BO")));
|
||||
assertThatApplyingToValue(function, "spring").has(sanitizedValue());
|
||||
assertThatApplyingToValue(function, "spin").has(sanitizedValue());
|
||||
assertThatApplyingToValue(function, "BO").has(sanitizedValue());
|
||||
assertThatApplyingToValue(function, "SPRING").has(unsanitizedValue());
|
||||
assertThatApplyingToValue(function, "boot").has(unsanitizedValue());
|
||||
assertThatApplyingToValue(function, 123).has(unsanitizedValue());
|
||||
assertThatApplyingToValue(function, null).has(unsanitizedValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ifValueMatchesWithPredicateFiltersExpected() {
|
||||
SanitizingFunction function = SanitizingFunction.sanitizeValue()
|
||||
.ifValueMatches((value) -> value instanceof String string && string.startsWith("sp"));
|
||||
assertThatApplyingToValue(function, "spring").has(sanitizedValue());
|
||||
assertThatApplyingToValue(function, "spin").has(sanitizedValue());
|
||||
assertThatApplyingToValue(function, "boot").has(unsanitizedValue());
|
||||
assertThatApplyingToValue(function, 123).has(unsanitizedValue());
|
||||
assertThatApplyingToKey(function, null).has(unsanitizedValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ifMatchesPredicatesFiltersExpected() {
|
||||
SanitizingFunction function = SanitizingFunction.sanitizeValue()
|
||||
.ifMatches(List.of((data) -> data.getKey().startsWith("sp") && "boot".equals(data.getValue()),
|
||||
(data) -> data.getKey().startsWith("sp") && "framework".equals(data.getValue())));
|
||||
assertThatApplying(function, data("spring", "boot")).is(sanitizedValue());
|
||||
assertThatApplying(function, data("spring", "framework")).is(sanitizedValue());
|
||||
assertThatApplying(function, data("spring", "data")).is(unsanitizedValue());
|
||||
assertThatApplying(function, data("spring", null)).is(unsanitizedValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ifMatchesPredicateFiltersExpected() {
|
||||
SanitizingFunction function = SanitizingFunction.sanitizeValue()
|
||||
.ifMatches((data) -> data.getKey().startsWith("sp") && "boot".equals(data.getValue()));
|
||||
assertThatApplying(function, data("spring", "boot")).is(sanitizedValue());
|
||||
assertThatApplying(function, data("spring", "framework")).is(unsanitizedValue());
|
||||
assertThatApplying(function, data("spring", "data")).is(unsanitizedValue());
|
||||
assertThatApplying(function, data("spring", null)).is(unsanitizedValue());
|
||||
}
|
||||
|
||||
private ObjectAssert<SanitizableData> assertThatApplyingToKey(SanitizingFunction function, String key) {
|
||||
return assertThatApplying(function, data(key));
|
||||
}
|
||||
|
||||
private ObjectAssert<SanitizableData> assertThatApplyingToValue(SanitizingFunction function, Object value) {
|
||||
return assertThatApplying(function, data("key", value));
|
||||
}
|
||||
|
||||
private ObjectAssert<SanitizableData> assertThatApplying(SanitizingFunction function, SanitizableData data) {
|
||||
return assertThat(function.applyUnlessFiltered(data)).as("%s:%s", data.getKey(), data.getValue());
|
||||
}
|
||||
|
||||
private Condition<SanitizableData> sanitizedValue() {
|
||||
return new Condition<>((data) -> Objects.equals(data.getValue(), SanitizableData.SANITIZED_VALUE),
|
||||
"sanitized value");
|
||||
}
|
||||
|
||||
private Condition<SanitizableData> unsanitizedValue() {
|
||||
return new Condition<>((data) -> !Objects.equals(data.getValue(), SanitizableData.SANITIZED_VALUE),
|
||||
"unsanitized value");
|
||||
}
|
||||
|
||||
private static SanitizableData data(String key) {
|
||||
return data(key, "value");
|
||||
}
|
||||
|
||||
private static SanitizableData data(String key, Object value) {
|
||||
return new SanitizableData(null, key, value);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user