Skip to content

Commit

Permalink
Make maximum SpEL expression length configurable
Browse files Browse the repository at this point in the history
Closes gh-30380
  • Loading branch information
sbrannen committed May 10, 2023
1 parent 120c228 commit aefcb9d
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 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.
Expand Down Expand Up @@ -30,6 +30,12 @@
*/
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;

/** System property to configure the default compiler mode for SpEL expression parsers: {@value}. */
public static final String SPRING_EXPRESSION_COMPILER_MODE_PROPERTY_NAME = "spring.expression.compiler.mode";

Expand All @@ -54,6 +60,8 @@ public class SpelParserConfiguration {

private final int maximumAutoGrowSize;

private final int maximumExpressionLength;


/**
* Create a new {@code SpelParserConfiguration} instance with default settings.
Expand Down Expand Up @@ -102,11 +110,30 @@ public SpelParserConfiguration(boolean autoGrowNullReferences, boolean autoGrowC
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;
}


Expand Down Expand Up @@ -146,4 +173,12 @@ public int getMaximumAutoGrowSize() {
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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -158,8 +151,9 @@ 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);
int maxLength = this.configuration.getMaximumExpressionLength();
if (string.length() > maxLength) {
throw new SpelEvaluationException(SpelMessage.MAX_EXPRESSION_LENGTH_EXCEEDED, maxLength);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,24 @@ protected void evaluateAndCheckError(String expression, SpelMessage expectedMess
*/
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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,26 @@ void expressionLength() {
evaluateAndCheckError(expression, String.class, SpelMessage.MAX_EXPRESSION_LENGTH_EXCEEDED);
}

@Test
void maxExpressionLengthIsConfigurable() {
int maximumExpressionLength = 20_000;

String expression = "'%s'".formatted("Y".repeat(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 = "'%s'".formatted("Y".repeat(25_000));
assertThat(expression).hasSize(25_002);
evaluateAndCheckError(parser, expression, String.class, SpelMessage.MAX_EXPRESSION_LENGTH_EXCEEDED);
}

@Test
void createListsOnAttemptToIndexNull01() throws EvaluationException, ParseException {
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
Expand Down

0 comments on commit aefcb9d

Please sign in to comment.