diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/StringMatchFilterTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/StringMatchFilterTest.java new file mode 100644 index 00000000000..24a428f1626 --- /dev/null +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/StringMatchFilterTest.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you 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 + * + * http://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.apache.logging.log4j.core.filter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.test.junit.LoggerContextSource; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link StringMatchFilter}. + */ +class StringMatchFilterTest { + + /** + * Test the normal valid programmatic instantiation of a {@link StringMatchFilter} via its builder. + */ + @Test + void testFilterBuilderOK() { + StringMatchFilter.Builder stringMatchFilterBuilder = StringMatchFilter.newBuilder(); + stringMatchFilterBuilder.setText("foo"); + StringMatchFilter stringMatchFilter = stringMatchFilterBuilder.build(); + assertNotNull(stringMatchFilter, "The filter should not be null."); + assertEquals("foo", stringMatchFilter.getText()); + } + + /** + * Test that if no match-string is set on the builder, the '{@link StringMatchFilter.Builder#build()}' returns + * {@code null}. + */ + @Test + void testFilterBuilderFailsWithNullText() { + StringMatchFilter.Builder stringMatchFilterBuilder = StringMatchFilter.newBuilder(); + Assertions.assertNull(stringMatchFilterBuilder.build()); + } + + /** + * Test that if a {@code null} string is set as a match-pattern, a {@link NullPointerException} is thrown. + */ + @Test + @SuppressWarnings({"DataFlowIssue" // invalid null parameter explicitly being tested + }) + void testFilterBuilderFailsWithExceptionOnNullText() { + StringMatchFilter.Builder stringMatchFilterBuilder = StringMatchFilter.newBuilder(); + Assertions.assertThrows(NullPointerException.class, () -> stringMatchFilterBuilder.setText(null)); + } + + /** + * Test that if an empty ({@code ""}) string is set as a match-pattern, an {@code IllegalArgumentException} is thrown. + */ + @Test + void testFilterBuilderFailsWithExceptionOnEmptyText() { + StringMatchFilter.Builder stringMatchFilterBuilder = StringMatchFilter.newBuilder(); + Assertions.assertThrows(IllegalArgumentException.class, () -> stringMatchFilterBuilder.setText("")); + } + + /** + * Test that if a {@link StringMatchFilter} is specified with a 'text' attribute it is correctly instantiated. + * + * @param configuration the configuration + */ + @Test + @LoggerContextSource("log4j2-stringmatchfilter-3153-ok.xml") + void testConfigurationWithTextPOS(final Configuration configuration) { + final Filter filter = configuration.getFilter(); + assertNotNull(filter, "The filter should not be null."); + assertInstanceOf( + StringMatchFilter.class, filter, "Expected a StringMatchFilter, but got: " + filter.getClass()); + assertEquals("FooBar", ((StringMatchFilter) filter).getText()); + } + + /** + * Test that if a {@link StringMatchFilter} is specified without a 'text' attribute it is not instantiated. + * + * + * @param configuration the configuration + */ + @Test + @LoggerContextSource("log4j2-stringmatchfilter-3153-nok.xml") + void testConfigurationWithTextNEG(final Configuration configuration) { + final Filter filter = configuration.getFilter(); + assertNull(filter, "The filter should be null."); + } +} diff --git a/log4j-core-test/src/test/resources/log4j2-stringmatchfilter-3153-nok.xml b/log4j-core-test/src/test/resources/log4j2-stringmatchfilter-3153-nok.xml new file mode 100644 index 00000000000..607a6ada7e0 --- /dev/null +++ b/log4j-core-test/src/test/resources/log4j2-stringmatchfilter-3153-nok.xml @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/log4j-core-test/src/test/resources/log4j2-stringmatchfilter-3153-ok.xml b/log4j-core-test/src/test/resources/log4j2-stringmatchfilter-3153-ok.xml new file mode 100644 index 00000000000..598dfaa3025 --- /dev/null +++ b/log4j-core-test/src/test/resources/log4j2-stringmatchfilter-3153-ok.xml @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/StringMatchFilter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/StringMatchFilter.java index 51ee3b2a201..155c59ee450 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/StringMatchFilter.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/StringMatchFilter.java @@ -16,6 +16,7 @@ */ package org.apache.logging.log4j.core.filter; +import java.util.Objects; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.core.Filter; @@ -26,7 +27,10 @@ import org.apache.logging.log4j.plugins.Plugin; import org.apache.logging.log4j.plugins.PluginBuilderAttribute; import org.apache.logging.log4j.plugins.PluginFactory; +import org.apache.logging.log4j.plugins.util.Assert; +import org.apache.logging.log4j.plugins.validation.constraints.Required; import org.apache.logging.log4j.util.PerformanceSensitive; +import org.jspecify.annotations.NonNull; /** * This filter returns the onMatch result if the message in the event matches the specified text @@ -37,12 +41,15 @@ @PerformanceSensitive("allocation") public final class StringMatchFilter extends AbstractFilter { - public static final String ATTR_MATCH = "match"; private final String text; - private StringMatchFilter(final String text, final Result onMatch, final Result onMismatch) { - super(onMatch, onMismatch); - this.text = text; + private StringMatchFilter(final Builder builder) { + super(builder.getOnMatch(), builder.getOnMismatch()); + this.text = Assert.requireNonEmpty(builder.text, "The 'text' argument must not be null or empty."); + } + + public String getText() { + return text; } @Override @@ -230,28 +237,35 @@ public String toString() { } @PluginFactory - public static StringMatchFilter.Builder newBuilder() { - return new StringMatchFilter.Builder(); + public static Builder newBuilder() { + return new Builder(); } - public static class Builder extends AbstractFilterBuilder - implements org.apache.logging.log4j.core.util.Builder { + public static class Builder extends AbstractFilterBuilder + implements org.apache.logging.log4j.plugins.util.Builder { + @PluginBuilderAttribute - private String text = ""; + @Required(message = "No text provided for StringMatchFilter") + private String text; /** * Sets the text to search in event messages. * @param text the text to search in event messages. * @return this instance. */ - public StringMatchFilter.Builder setMatchString(final String text) { - this.text = text; + public Builder setText(@NonNull final String text) { + Objects.requireNonNull(text, "The 'text' argument must not be null."); + this.text = Assert.requireNonEmpty(text, "The 'text' argument must not be empty."); return this; } @Override public StringMatchFilter build() { - return new StringMatchFilter(this.text, this.getOnMatch(), this.getOnMismatch()); + if (this.text == null) { + LOGGER.error("Unable to create StringMatchFilter: The 'text' attribute must be configured."); + return null; + } + return new StringMatchFilter(this); } } } diff --git a/src/changelog/.3.x.x/3509_fix_StringMatchFilter_Validation.xml b/src/changelog/.3.x.x/3509_fix_StringMatchFilter_Validation.xml new file mode 100644 index 00000000000..897c339bdcc --- /dev/null +++ b/src/changelog/.3.x.x/3509_fix_StringMatchFilter_Validation.xml @@ -0,0 +1,8 @@ + + + + Fix validation in StringMatchFilter. +