From 7c8fed7f3b173fd3c485ebb10f1e903675b83c4f Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Wed, 10 May 2023 11:20:07 +0200 Subject: [PATCH] Make maximum SpEL expression length configurable Closes gh-30452 --- .../spel/SpelParserConfiguration.java | 39 ++++++++++++++++++- .../InternalSpelExpressionParser.java | 14 +++---- .../spel/AbstractExpressionTests.java | 20 +++++++++- .../expression/spel/EvaluationTests.java | 20 ++++++++++ 4 files changed, 82 insertions(+), 11 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/SpelParserConfiguration.java b/spring-expression/src/main/java/org/springframework/expression/spel/SpelParserConfiguration.java index fb1260570d4..9e4ccfd68cc 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/SpelParserConfiguration.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/SpelParserConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2023 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. @@ -25,11 +25,19 @@ import org.springframework.lang.Nullable; * @author Juergen Hoeller * @author Phillip Webb * @author Andy Clement + * @author Sam Brannen * @since 3.0 * @see org.springframework.expression.spel.standard.SpelExpressionParser#SpelExpressionParser(SpelParserConfiguration) */ public class SpelParserConfiguration { + /** + * Default maximum length permitted for a SpEL expression. + * @since 5.2.24 + */ + private static final int DEFAULT_MAX_EXPRESSION_LENGTH = 10_000; + + private static final SpelCompilerMode defaultCompilerMode; static { @@ -50,6 +58,8 @@ public class SpelParserConfiguration { private final int maximumAutoGrowSize; + private final int maximumExpressionLength; + /** * Create a new {@code SpelParserConfiguration} instance with default settings. @@ -98,11 +108,30 @@ public class SpelParserConfiguration { public SpelParserConfiguration(@Nullable SpelCompilerMode compilerMode, @Nullable ClassLoader compilerClassLoader, boolean autoGrowNullReferences, boolean autoGrowCollections, int maximumAutoGrowSize) { + this(compilerMode, compilerClassLoader, autoGrowNullReferences, autoGrowCollections, + maximumAutoGrowSize, DEFAULT_MAX_EXPRESSION_LENGTH); + } + + /** + * Create a new {@code SpelParserConfiguration} instance. + * @param compilerMode the compiler mode that parsers using this configuration object should use + * @param compilerClassLoader the ClassLoader to use as the basis for expression compilation + * @param autoGrowNullReferences if null references should automatically grow + * @param autoGrowCollections if collections should automatically grow + * @param maximumAutoGrowSize the maximum size that a collection can auto grow + * @param maximumExpressionLength the maximum length of a SpEL expression; + * must be a positive number + * @since 5.2.25 + */ + public SpelParserConfiguration(@Nullable SpelCompilerMode compilerMode, @Nullable ClassLoader compilerClassLoader, + boolean autoGrowNullReferences, boolean autoGrowCollections, int maximumAutoGrowSize, int maximumExpressionLength) { + this.compilerMode = (compilerMode != null ? compilerMode : defaultCompilerMode); this.compilerClassLoader = compilerClassLoader; this.autoGrowNullReferences = autoGrowNullReferences; this.autoGrowCollections = autoGrowCollections; this.maximumAutoGrowSize = maximumAutoGrowSize; + this.maximumExpressionLength = maximumExpressionLength; } @@ -142,4 +171,12 @@ public class SpelParserConfiguration { return this.maximumAutoGrowSize; } + /** + * Return the maximum number of characters that a SpEL expression can contain. + * @since 5.2.25 + */ + public int getMaximumExpressionLength() { + return this.maximumExpressionLength; + } + } 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 0d1a50472c8..062a82f12b6 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 @@ -93,13 +93,6 @@ 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; // For rules that build nodes, they are stacked here for return @@ -158,8 +151,11 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { } private void checkExpressionLength(String string) { - if (string != null && string.length() > MAX_EXPRESSION_LENGTH) { - throw new SpelEvaluationException(SpelMessage.MAX_EXPRESSION_LENGTH_EXCEEDED, MAX_EXPRESSION_LENGTH); + if (string != null) { + int maxLength = this.configuration.getMaximumExpressionLength(); + if (string.length() > maxLength) { + throw new SpelEvaluationException(SpelMessage.MAX_EXPRESSION_LENGTH_EXCEEDED, maxLength); + } } } diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/AbstractExpressionTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/AbstractExpressionTests.java index 7a682dbd4e5..eade0737456 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/AbstractExpressionTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/AbstractExpressionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2023 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. @@ -164,6 +164,24 @@ public abstract class AbstractExpressionTests { */ protected void evaluateAndCheckError(String expression, Class expectedReturnType, SpelMessage expectedMessage, Object... otherProperties) { + + evaluateAndCheckError(this.parser, expression, expectedReturnType, expectedMessage, otherProperties); + } + + /** + * Evaluate the specified expression and ensure the expected message comes out. + * The message may have inserts and they will be checked if otherProperties is specified. + * The first entry in otherProperties should always be the position. + * @param parser the expression parser to use + * @param expression the expression to evaluate + * @param expectedReturnType ask the expression return value to be of this type if possible + * ({@code null} indicates don't ask for conversion) + * @param expectedMessage the expected message + * @param otherProperties the expected inserts within the message + */ + protected void evaluateAndCheckError(ExpressionParser parser, String expression, Class expectedReturnType, SpelMessage expectedMessage, + Object... otherProperties) { + assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> { Expression expr = parser.parseExpression(expression); assertThat(expr).as("expression").isNotNull(); 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 f84a439badf..1b0bc6bf14f 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 @@ -76,6 +76,26 @@ public class EvaluationTests extends AbstractExpressionTests { evaluateAndCheckError(expression, String.class, SpelMessage.MAX_EXPRESSION_LENGTH_EXCEEDED); } + @Test + void maxExpressionLengthIsConfigurable() { + int maximumExpressionLength = 20_000; + + String expression = String.format("'%s'", repeat("Y", 19_998)); + assertThat(expression).hasSize(maximumExpressionLength); + + SpelParserConfiguration configuration = + new SpelParserConfiguration(null, null, false, false, 0, maximumExpressionLength); + ExpressionParser parser = new SpelExpressionParser(configuration); + + Expression expr = parser.parseExpression(expression); + String result = expr.getValue(String.class); + assertThat(result).hasSize(19_998); + + expression = String.format("'%s'", repeat("Y", 25_000)); + assertThat(expression).hasSize(25_002); + evaluateAndCheckError(parser, expression, String.class, SpelMessage.MAX_EXPRESSION_LENGTH_EXCEEDED); + } + @Test public void testCreateListsOnAttemptToIndexNull01() throws EvaluationException, ParseException { ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));