From cc91db9a76e4d65cdc5bc42b01bac38590780ad5 Mon Sep 17 00:00:00 2001 From: Sebastian-Ion TINCU <Sebastian-Ion.TINCU@ext.ec.europa.eu> Date: Thu, 28 Mar 2024 18:39:53 +0100 Subject: [PATCH] EDELIVERY-12927 Resources Search Filter Treats % Character as Placeholder Escape SQL characters before wrapping with the final % LIKE characters. --- .../smp/data/model/doc/DBResourceFilter.java | 4 +- .../data/model/doc/DBResourceFilterTest.java | 46 ++++++++++++ .../edelivery/smp/filter/FilterHandler.java | 12 ++-- .../smp/filter/FilterHandlerTest.java | 71 +++++++++++++++++++ 4 files changed, 127 insertions(+), 6 deletions(-) create mode 100644 smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/model/doc/DBResourceFilterTest.java create mode 100644 smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/filter/FilterHandlerTest.java diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBResourceFilter.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBResourceFilter.java index e2c219b35..ddc62892f 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBResourceFilter.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBResourceFilter.java @@ -29,7 +29,7 @@ import java.util.Arrays; import java.util.List; import static org.apache.commons.lang3.StringUtils.trim; -import static org.apache.commons.lang3.StringUtils.wrapIfMissing; +import static org.apache.commons.lang3.StringUtils.wrap; public class DBResourceFilter { private static final List<MembershipRoleType> ALL_ROLES = Arrays.asList(MembershipRoleType.values()); @@ -145,7 +145,7 @@ public class DBResourceFilter { } public Builder identifierFilter(String identifierFilter) { - this.identifierFilter = wrapIfMissing(trim(identifierFilter), "%"); + this.identifierFilter = wrap(trim(identifierFilter), "%"); return this; } diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/model/doc/DBResourceFilterTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/model/doc/DBResourceFilterTest.java new file mode 100644 index 000000000..773ac44fb --- /dev/null +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/model/doc/DBResourceFilterTest.java @@ -0,0 +1,46 @@ +/*- + * #START_LICENSE# + * smp-webapp + * %% + * Copyright (C) 2017 - 2023 European Commission | eDelivery | DomiSMP + * %% + * Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent + * versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * + * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence is + * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and limitations under the Licence. + * #END_LICENSE# + */ +package eu.europa.ec.edelivery.smp.data.model.doc; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Sebastian-Ion TINCU + * @since 5.1 + */ +public class DBResourceFilterTest { + + @Test + public void wrapsFilterValueHavingPercentageCharacters() { + // GIVEN + DBResourceFilter filter = DBResourceFilter + .createBuilder() + .identifierFilter("\\%") + .build(); + + // WHEN + String result = filter.getIdentifierFilter(); + + // THEN + Assertions.assertEquals("%\\%%", result, "Should have wrapped the escaped '%' character around with two extra '%' characters"); + } +} \ No newline at end of file diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/filter/FilterHandler.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/filter/FilterHandler.java index 71a167a5f..e87fea783 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/filter/FilterHandler.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/filter/FilterHandler.java @@ -18,6 +18,7 @@ */ package eu.europa.ec.edelivery.smp.filter; +import org.apache.commons.lang3.RegExUtils; import org.springframework.format.AnnotationFormatterFactory; import org.springframework.format.Formatter; import org.springframework.format.Parser; @@ -50,15 +51,15 @@ public class FilterHandler implements AnnotationFormatterFactory<Filter> { @Override public Printer<?> getPrinter(Filter annotation, Class<?> fieldType) { - return getFormatter(annotation); + return getFormatter(); } @Override public Parser<?> getParser(Filter annotation, Class<?> fieldType) { - return getFormatter(annotation); + return getFormatter(); } - private Formatter<String> getFormatter(Filter annotation) { + private Formatter<String> getFormatter() { return new Formatter<String>() { @Override public String print(String object, Locale locale) { @@ -67,7 +68,10 @@ public class FilterHandler implements AnnotationFormatterFactory<Filter> { @Override public String parse(String encoded, Locale locale) throws ParseException { - return URLDecoder.decode(encoded); + String decoded = URLDecoder.decode(encoded); + // We need to escape the following characters that have special meaning when used in a LIKE statement + // inside an SQL query: '%', '\', '_', ''', '"', '[' and ']' + return RegExUtils.replaceAll(decoded, "([%\\\\_'\"\\[\\]])", "\\\\$1"); } }; } diff --git a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/filter/FilterHandlerTest.java b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/filter/FilterHandlerTest.java new file mode 100644 index 000000000..21fc414f2 --- /dev/null +++ b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/filter/FilterHandlerTest.java @@ -0,0 +1,71 @@ +/*- + * #START_LICENSE# + * smp-webapp + * %% + * Copyright (C) 2017 - 2023 European Commission | eDelivery | DomiSMP + * %% + * Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent + * versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * + * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence is + * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and limitations under the Licence. + * #END_LICENSE# + */ +package eu.europa.ec.edelivery.smp.filter; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.format.Parser; + +import java.net.URLEncoder; + +/** + * @author Sebastian-Ion TINCU + * @since 5.1 + */ +public class FilterHandlerTest { + + private static final Logger LOG = LoggerFactory.getLogger(FilterHandlerTest.class); + + private Parser<?> filterHandlerParser; + + @BeforeEach + public void setup() { + filterHandlerParser = new FilterHandler().getParser(null, null); + + } + + @ParameterizedTest + @CsvSource({ + "'%','\\%'", // % + "'\\','\\\\'", // \ + "'_','\\_'", // _ + "'''','\\'''", // ' + "'\"','\\\"'", // " + "'[','\\['", // [ + "']','\\]'", // ] + }) + public void getEscapedDecodedFilterValue(String unencodedFilterValue, String expectedParsedFilterValue) throws Exception { + // GIVEN + String encodedFilterValue = URLEncoder.encode(unencodedFilterValue); + LOG.info("Testing if the original filter value [{}], encoded as [{}] can be decoded successfully to [{}] " + + "having its SQL query parameters escaped", unencodedFilterValue, encodedFilterValue, expectedParsedFilterValue); + + // WHEN + Object result = filterHandlerParser.parse(encodedFilterValue, null); + + // THEN + Assertions.assertEquals(expectedParsedFilterValue, result, + "Should have parsed the [" + encodedFilterValue + "] correctly to [" + expectedParsedFilterValue + + "], decoding the original filter value and escaping any special SQL query characters that are to be used in a LIKE statement" ); + } +} \ No newline at end of file -- GitLab