From accde46c4437ec7224a6fe2f4f9ff311c96fab1f Mon Sep 17 00:00:00 2001 From: hengyu_laptop <1183660933@qq.com> Date: Fri, 24 Apr 2026 17:42:31 +0800 Subject: [PATCH 1/2] fix admin : add uri validation for regex and other operators. --- .../shenyu/admin/service/RuleService.java | 21 +++--- .../validator/UriConditionValidator.java | 66 +++++++++++++++++++ 2 files changed, 76 insertions(+), 11 deletions(-) create mode 100644 shenyu-admin/src/main/java/org/apache/shenyu/admin/validation/validator/UriConditionValidator.java diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/RuleService.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/RuleService.java index 5c759f26e819..a04aa572a3b4 100644 --- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/RuleService.java +++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/RuleService.java @@ -28,10 +28,9 @@ import org.apache.shenyu.admin.model.result.ConfigImportResult; import org.apache.shenyu.admin.model.vo.RuleVO; import org.apache.shenyu.admin.service.configs.ConfigsImportContext; +import org.apache.shenyu.admin.validation.validator.UriConditionValidator; import org.apache.shenyu.common.dto.RuleData; -import org.apache.shenyu.common.enums.OperatorEnum; import org.apache.shenyu.common.enums.ParamTypeEnum; -import org.springframework.web.util.pattern.PathPatternParser; import java.util.List; @@ -63,9 +62,9 @@ default int createOrUpdate(final RuleDTO ruleDTO) { final List ruleConditions = ruleDTO.getRuleConditions(); ruleConditions.stream() .filter(conditionData -> ParamTypeEnum.URI.getName().equals(conditionData.getParamType())) - .filter(conditionData -> OperatorEnum.PATH_PATTERN.getAlias().equals(conditionData.getOperator())) - .map(RuleConditionDTO::getParamValue) - .forEach(PathPatternParser.defaultInstance::parse); + .forEach(conditionData -> { + UriConditionValidator.validate(conditionData.getOperator(), conditionData.getParamValue()); + }); } catch (Exception e) { throw new ShenyuAdminException("uri validation of Condition failed, please check.", e); } @@ -91,7 +90,7 @@ default int createOrUpdate(final RuleDTO ruleDTO) { /** * delete rules by ids and namespaceId. * - * @param ids primary key. + * @param ids primary key. * @param namespaceId namespaceId. * @return rows int */ @@ -171,7 +170,7 @@ default int createOrUpdate(final RuleDTO ruleDTO) { * Find by selector id and name rule do. * * @param selectorId selector id - * @param name rule name + * @param name rule name * @return {@link RuleDO} */ RuleDO findBySelectorIdAndName(String selectorId, String name); @@ -188,8 +187,8 @@ default int createOrUpdate(final RuleDTO ruleDTO) { * Import data. * * @param namespace namespace - * @param ruleList rule list - * @param context import context + * @param ruleList rule list + * @param context import context * @return config import result */ ConfigImportResult importData(String namespace, List ruleList, ConfigsImportContext context); @@ -197,8 +196,8 @@ default int createOrUpdate(final RuleDTO ruleDTO) { /** * Enabled string by ids and namespaceId. * - * @param ids the ids - * @param enabled the enabled + * @param ids the ids + * @param enabled the enabled * @param namespaceId the namespaceId. * @return the result */ diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/validation/validator/UriConditionValidator.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/validation/validator/UriConditionValidator.java new file mode 100644 index 000000000000..af51379546b4 --- /dev/null +++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/validation/validator/UriConditionValidator.java @@ -0,0 +1,66 @@ +/* + * 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.shenyu.admin.validation.validator; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.regex.Pattern; + +import org.apache.commons.lang3.StringUtils; +import org.apache.shenyu.common.enums.OperatorEnum; +import org.springframework.web.util.pattern.PathPatternParser; + +public class UriConditionValidator { + + private static final Map> VALIDATOR_MAP = new HashMap<>(); + + static { + VALIDATOR_MAP.put(OperatorEnum.PATH_PATTERN.getAlias(), + PathPatternParser.defaultInstance::parse); + VALIDATOR_MAP.put(OperatorEnum.REGEX.getAlias(), Pattern::compile); + + Consumer commonPathValidator = value -> { + if (!value.startsWith("/")) { + throw new IllegalArgumentException("The URI must start with '/'"); + } + if (StringUtils.containsAny(value, " ", "\t", "\n")) { + throw new IllegalArgumentException( + "The URI cannot contain whitespaces. Current value: " + value); + } + }; + VALIDATOR_MAP.put(OperatorEnum.EQ.getAlias(), commonPathValidator); + VALIDATOR_MAP.put(OperatorEnum.STARTS_WITH.getAlias(), commonPathValidator); + VALIDATOR_MAP.put(OperatorEnum.ENDS_WITH.getAlias(), commonPathValidator); + } + + public static void validate(final String operator, final String value) { + if (StringUtils.isBlank(value)) { + throw new IllegalArgumentException("The URI condition value cannot be empty."); + } + Consumer validator = VALIDATOR_MAP.get(operator); + if (Objects.nonNull(validator)) { + validator.accept(value); + } else { + throw new IllegalArgumentException("No such operator: " + operator); + } + } + + +} From 7454dadface7058195e26e91cb58fad894936900 Mon Sep 17 00:00:00 2001 From: hengyu_laptop <1183660933@qq.com> Date: Sat, 25 Apr 2026 11:37:45 +0800 Subject: [PATCH 2/2] fix admin : fix url condition and add test --- .../shenyu/admin/service/RuleService.java | 4 - .../validator/UriConditionValidator.java | 14 ++- .../validator/UriConditionValidatorTest.java | 99 +++++++++++++++++++ 3 files changed, 109 insertions(+), 8 deletions(-) create mode 100644 shenyu-admin/src/test/java/org/apache/shenyu/admin/validation/validator/UriConditionValidatorTest.java diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/RuleService.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/RuleService.java index a04aa572a3b4..1993b8700222 100644 --- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/RuleService.java +++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/RuleService.java @@ -54,10 +54,6 @@ public interface RuleService extends PageService { * @return rows int */ default int createOrUpdate(final RuleDTO ruleDTO) { - - // now, only check rule uri condition in pathPattern mode - // todo check uri in other modes - try { final List ruleConditions = ruleDTO.getRuleConditions(); ruleConditions.stream() diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/validation/validator/UriConditionValidator.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/validation/validator/UriConditionValidator.java index af51379546b4..4798bc3afedc 100644 --- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/validation/validator/UriConditionValidator.java +++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/validation/validator/UriConditionValidator.java @@ -45,22 +45,28 @@ public class UriConditionValidator { "The URI cannot contain whitespaces. Current value: " + value); } }; + Consumer blankPathValidator = value -> { + if (StringUtils.isNotBlank(value)) { + throw new IllegalArgumentException("The URI must be blank"); + } + }; VALIDATOR_MAP.put(OperatorEnum.EQ.getAlias(), commonPathValidator); VALIDATOR_MAP.put(OperatorEnum.STARTS_WITH.getAlias(), commonPathValidator); VALIDATOR_MAP.put(OperatorEnum.ENDS_WITH.getAlias(), commonPathValidator); + VALIDATOR_MAP.put(OperatorEnum.MATCH.getAlias(), commonPathValidator); + VALIDATOR_MAP.put(OperatorEnum.EXCLUDE.getAlias(), commonPathValidator); + VALIDATOR_MAP.put(OperatorEnum.CONTAINS.getAlias(), commonPathValidator); + VALIDATOR_MAP.put(OperatorEnum.IS_BLANK.getAlias(), blankPathValidator); } public static void validate(final String operator, final String value) { - if (StringUtils.isBlank(value)) { + if (!OperatorEnum.IS_BLANK.getAlias().equals(operator) && StringUtils.isBlank(value)) { throw new IllegalArgumentException("The URI condition value cannot be empty."); } Consumer validator = VALIDATOR_MAP.get(operator); if (Objects.nonNull(validator)) { validator.accept(value); - } else { - throw new IllegalArgumentException("No such operator: " + operator); } } - } diff --git a/shenyu-admin/src/test/java/org/apache/shenyu/admin/validation/validator/UriConditionValidatorTest.java b/shenyu-admin/src/test/java/org/apache/shenyu/admin/validation/validator/UriConditionValidatorTest.java new file mode 100644 index 000000000000..dcc756b7daea --- /dev/null +++ b/shenyu-admin/src/test/java/org/apache/shenyu/admin/validation/validator/UriConditionValidatorTest.java @@ -0,0 +1,99 @@ +/* + * 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.shenyu.admin.validation.validator; + +import org.apache.shenyu.common.enums.OperatorEnum; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.web.util.pattern.PatternParseException; + +import java.util.regex.PatternSyntaxException; + +/** + * Test cases for {@link UriConditionValidator}. + */ +public class UriConditionValidatorTest { + + @Test + public void testValidateErrorRegex() { + String pattern = "[abc"; + Assertions.assertThrows(PatternSyntaxException.class, + () -> UriConditionValidator.validate(OperatorEnum.REGEX.getAlias(), pattern)); + } + + @Test + public void testValidateHappyRegex() { + String pattern = "^\\/[a-zA-Z0-9\\-_\\/]+$"; + Assertions.assertDoesNotThrow(() -> UriConditionValidator.validate(OperatorEnum.REGEX.getAlias(), pattern)); + } + + @Test + public void testValidateHappyPathPattern() { + String pattern = "/http/**"; + Assertions.assertDoesNotThrow(() -> UriConditionValidator.validate(OperatorEnum.PATH_PATTERN.getAlias(), pattern)); + } + + @Test + public void testValidateErrorPathPattern() { + String pattern = "/http/{abc"; + Assertions.assertThrows(PatternParseException.class, + () -> UriConditionValidator.validate(OperatorEnum.PATH_PATTERN.getAlias(), pattern)); + } + + @Test + public void testValidateHappyIsBlank() { + String pattern = ""; + Assertions.assertDoesNotThrow(() -> UriConditionValidator.validate(OperatorEnum.IS_BLANK.getAlias(), pattern)); + } + + @Test + public void testValidateErrorIsBlank() { + String pattern = "/http"; + Assertions.assertThrows(IllegalArgumentException.class, + () -> UriConditionValidator.validate(OperatorEnum.IS_BLANK.getAlias(), pattern)); + } + + @Test + public void testValidateEmptyValueForNormalOperator() { + String pattern = " "; + Assertions.assertThrows(IllegalArgumentException.class, + () -> UriConditionValidator.validate(OperatorEnum.EQ.getAlias(), pattern)); + } + + @Test + public void testValidateHappyOtherCondition() { + String pattern = "/http/test"; + Assertions.assertDoesNotThrow(() -> UriConditionValidator.validate(OperatorEnum.STARTS_WITH.getAlias(), pattern)); + Assertions.assertDoesNotThrow(() -> UriConditionValidator.validate(OperatorEnum.ENDS_WITH.getAlias(), pattern)); + Assertions.assertDoesNotThrow(() -> UriConditionValidator.validate(OperatorEnum.MATCH.getAlias(), pattern)); + Assertions.assertDoesNotThrow(() -> UriConditionValidator.validate(OperatorEnum.EQ.getAlias(), pattern)); + Assertions.assertDoesNotThrow(() -> UriConditionValidator.validate(OperatorEnum.EXCLUDE.getAlias(), pattern)); + Assertions.assertDoesNotThrow(() -> UriConditionValidator.validate(OperatorEnum.CONTAINS.getAlias(), pattern)); + } + + @Test + public void testValidateErrorOtherCondition() { + String pattern = "http/test"; + Assertions.assertThrows(IllegalArgumentException.class, () -> UriConditionValidator.validate(OperatorEnum.STARTS_WITH.getAlias(), pattern)); + Assertions.assertThrows(IllegalArgumentException.class, () -> UriConditionValidator.validate(OperatorEnum.ENDS_WITH.getAlias(), pattern)); + Assertions.assertThrows(IllegalArgumentException.class, () -> UriConditionValidator.validate(OperatorEnum.MATCH.getAlias(), pattern)); + Assertions.assertThrows(IllegalArgumentException.class, () -> UriConditionValidator.validate(OperatorEnum.EQ.getAlias(), pattern)); + Assertions.assertThrows(IllegalArgumentException.class, () -> UriConditionValidator.validate(OperatorEnum.EXCLUDE.getAlias(), pattern)); + Assertions.assertThrows(IllegalArgumentException.class, () -> UriConditionValidator.validate(OperatorEnum.CONTAINS.getAlias(), pattern)); + } +} \ No newline at end of file