diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java b/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java index 3ee35997fd1b..6bb84f86ec2c 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java @@ -264,15 +264,19 @@ public enum SpelMessage { /** @since 5.2.23 */ MAX_REPEATED_TEXT_SIZE_EXCEEDED(Kind.ERROR, 1076, - "Repeated text results in too many characters, exceeding the threshold of ''{0}''"), + "Repeated text is too long, exceeding the threshold of ''{0}'' characters"), /** @since 5.2.23 */ MAX_REGEX_LENGTH_EXCEEDED(Kind.ERROR, 1077, - "Regular expression contains too many characters, exceeding the threshold of ''{0}''"), + "Regular expression is too long, exceeding the threshold of ''{0}'' characters"), /** @since 5.2.24 */ MAX_CONCATENATED_STRING_LENGTH_EXCEEDED(Kind.ERROR, 1078, - "Concatenated string is too long, exceeding the threshold of ''{0}'' characters"); + "Concatenated string is too long, exceeding the threshold of ''{0}'' characters"), + + /** @since 5.2.24 */ + MAX_EXPRESSION_LENGTH_EXCEEDED(Kind.ERROR, 1079, + "SpEL expression is too long, exceeding the threshold of ''{0}'' characters"); private final Kind kind; diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java b/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java index 6ca3781d0fa1..23a09f719772 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java @@ -29,6 +29,7 @@ import org.springframework.expression.ParserContext; import org.springframework.expression.common.TemplateAwareExpressionParser; import org.springframework.expression.spel.InternalParseException; +import org.springframework.expression.spel.SpelEvaluationException; import org.springframework.expression.spel.SpelMessage; import org.springframework.expression.spel.SpelParseException; import org.springframework.expression.spel.SpelParserConfiguration; @@ -92,6 +93,12 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { private static final Pattern VALID_QUALIFIED_ID_PATTERN = Pattern.compile("[\\p{L}\\p{N}_$]+"); + /** + * Maximum length permitted for a SpEL expression. + * @since 5.2.24 + */ + private static final int MAX_EXPRESSION_LENGTH = 10_000; + private final SpelParserConfiguration configuration; @@ -127,6 +134,8 @@ public InternalSpelExpressionParser(SpelParserConfiguration configuration) { protected SpelExpression doParseExpression(String expressionString, @Nullable ParserContext context) throws ParseException { + checkExpressionLength(expressionString); + try { this.expressionString = expressionString; Tokenizer tokenizer = new Tokenizer(expressionString); @@ -148,6 +157,12 @@ protected SpelExpression doParseExpression(String expressionString, @Nullable Pa } } + private void checkExpressionLength(String string) { + if (string.length() > MAX_EXPRESSION_LENGTH) { + throw new SpelEvaluationException(SpelMessage.MAX_EXPRESSION_LENGTH_EXCEEDED, MAX_EXPRESSION_LENGTH); + } + } + // expression // : logicalOrExpression // ( (ASSIGN^ logicalOrExpression) diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java index 75d2a25f551f..4a2f596ee27a 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java @@ -59,6 +59,20 @@ */ public class EvaluationTests extends AbstractExpressionTests { + @Test + void expressionLength() { + String expression = String.format("'X' + '%s'", repeat(" ", 9_992)); + assertThat(expression).hasSize(10_000); + Expression expr = parser.parseExpression(expression); + String result = expr.getValue(context, String.class); + assertThat(result).hasSize(9_993); + assertThat(result.trim()).isEqualTo("X"); + + expression = String.format("'X' + '%s'", repeat(" ", 9_993)); + assertThat(expression).hasSize(10_001); + evaluateAndCheckError(expression, String.class, SpelMessage.MAX_EXPRESSION_LENGTH_EXCEEDED); + } + @Test public void testCreateListsOnAttemptToIndexNull01() throws EvaluationException, ParseException { ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); @@ -211,14 +225,6 @@ void matchesWithPatternLengthThreshold() { evaluateAndCheckError("'abc' matches '" + pattern + "'", Boolean.class, SpelMessage.MAX_REGEX_LENGTH_EXCEEDED); } - private String repeat(String str, int count) { - String result = ""; - for (int i = 0; i < count; i++) { - result += str; - } - return result; - } - // mixing operators @Test public void testMixingOperators01() { @@ -1373,6 +1379,15 @@ public List filter(List methods) { } + private static String repeat(String str, int count) { + String result = ""; + for (int i = 0; i < count; i++) { + result += str; + } + return result; + } + + @SuppressWarnings("rawtypes") static class TestClass {