Skip to content

Commit 5742f3a

Browse files
Merge pull request #3 from cesarhernandezgt/v.4.3.30.RELEASE-TT.x-patch
Backport for spring-projectsgh-30330, spring-projectsgh-30332, spring-projectsgh-30265
2 parents 739e6ad + 820b701 commit 5742f3a

File tree

6 files changed

+156
-26
lines changed

6 files changed

+156
-26
lines changed

spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java

+12-4
Original file line numberDiff line numberDiff line change
@@ -262,13 +262,21 @@ public enum SpelMessage {
262262
MAX_ARRAY_ELEMENTS_THRESHOLD_EXCEEDED(Kind.ERROR, 1075,
263263
"Array declares too many elements, exceeding the threshold of ''{0}''"),
264264

265-
/** @since 5.3.26 */
265+
/** @since 5.2.23 */
266266
MAX_REPEATED_TEXT_SIZE_EXCEEDED(Kind.ERROR, 1076,
267-
"Repeated text results in too many characters, exceeding the threshold of ''{0}''"),
267+
"Repeated text is too long, exceeding the threshold of ''{0}'' characters"),
268268

269-
/** @since 5.3.26 */
269+
/** @since 5.2.23 */
270270
MAX_REGEX_LENGTH_EXCEEDED(Kind.ERROR, 1077,
271-
"Regular expression contains too many characters, exceeding the threshold of ''{0}''");
271+
"Regular expression is too long, exceeding the threshold of ''{0}'' characters"),
272+
273+
/** @since 5.2.24 */
274+
MAX_CONCATENATED_STRING_LENGTH_EXCEEDED(Kind.ERROR, 1078,
275+
"Concatenated string is too long, exceeding the threshold of ''{0}'' characters"),
276+
277+
/** @since 5.2.24 */
278+
MAX_EXPRESSION_LENGTH_EXCEEDED(Kind.ERROR, 1079,
279+
"SpEL expression is too long, exceeding the threshold of ''{0}'' characters");
272280

273281

274282
private final Kind kind;

spring-expression/src/main/java/org/springframework/expression/spel/ast/OpPlus.java

+37-5
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
import org.springframework.expression.TypedValue;
2828
import org.springframework.expression.spel.CodeFlow;
2929
import org.springframework.expression.spel.ExpressionState;
30+
import org.springframework.expression.spel.SpelEvaluationException;
31+
import org.springframework.expression.spel.SpelMessage;
32+
//import org.springframework.lang.Nullable;
3033
import org.springframework.util.Assert;
3134
import org.springframework.util.NumberUtils;
3235

@@ -49,6 +52,12 @@
4952
*/
5053
public class OpPlus extends Operator {
5154

55+
/**
56+
* Maximum number of characters permitted in a concatenated string.
57+
* @since 5.2.24
58+
*/
59+
private static final int MAX_CONCATENATED_STRING_LENGTH = 100000;
60+
5261
public OpPlus(int pos, SpelNodeImpl... operands) {
5362
super("+", pos, operands);
5463
Assert.notEmpty(operands, "Operands must not be empty");
@@ -123,22 +132,45 @@ else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNume
123132

124133
if (leftOperand instanceof String && rightOperand instanceof String) {
125134
this.exitTypeDescriptor = "Ljava/lang/String";
126-
return new TypedValue((String) leftOperand + rightOperand);
135+
String leftString = (String) leftOperand;
136+
String rightString = (String) rightOperand;
137+
checkStringLength(leftString);
138+
checkStringLength(rightString);
139+
return concatenate(leftString, rightString);
127140
}
128141

129142
if (leftOperand instanceof String) {
130-
return new TypedValue(
131-
leftOperand + (rightOperand == null ? "null" : convertTypedValueToString(operandTwoValue, state)));
143+
String leftString = (String) leftOperand;
144+
checkStringLength(leftString);
145+
String rightString = (rightOperand == null ? "null" : convertTypedValueToString(operandTwoValue, state));
146+
checkStringLength(rightString);
147+
return concatenate(leftString, rightString);
132148
}
133149

134150
if (rightOperand instanceof String) {
135-
return new TypedValue(
136-
(leftOperand == null ? "null" : convertTypedValueToString(operandOneValue, state)) + rightOperand);
151+
String rightString = (String) rightOperand;
152+
checkStringLength(rightString);
153+
String leftString = (leftOperand == null ? "null" : convertTypedValueToString(operandOneValue, state));
154+
checkStringLength(leftString);
155+
return concatenate(leftString, rightString);
137156
}
138157

139158
return state.operate(Operation.ADD, leftOperand, rightOperand);
140159
}
141160

161+
private void checkStringLength(String string) {
162+
if (string.length() > MAX_CONCATENATED_STRING_LENGTH) {
163+
throw new SpelEvaluationException(getStartPosition(),
164+
SpelMessage.MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, MAX_CONCATENATED_STRING_LENGTH);
165+
}
166+
}
167+
168+
private TypedValue concatenate(String leftString, String rightString) {
169+
String result = leftString + rightString;
170+
checkStringLength(result);
171+
return new TypedValue(result);
172+
}
173+
142174
@Override
143175
public String toStringAST() {
144176
if (this.children.length < 2) { // unary plus

spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public class OperatorMatches extends Operator {
4646
* Maximum number of characters permitted in a regular expression.
4747
* @since 5.3.26
4848
*/
49-
private static final int MAX_REGEX_LENGTH = 256;
49+
private static final int MAX_REGEX_LENGTH = 1000;
5050

5151
private final ConcurrentMap<String, Pattern> patternCache;
5252

spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java

+16
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.springframework.expression.ParserContext;
2929
import org.springframework.expression.common.TemplateAwareExpressionParser;
3030
import org.springframework.expression.spel.InternalParseException;
31+
import org.springframework.expression.spel.SpelEvaluationException;
3132
import org.springframework.expression.spel.SpelMessage;
3233
import org.springframework.expression.spel.SpelParseException;
3334
import org.springframework.expression.spel.SpelParserConfiguration;
@@ -90,6 +91,12 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
9091

9192
private static final Pattern VALID_QUALIFIED_ID_PATTERN = Pattern.compile("[\\p{L}\\p{N}_$]+");
9293

94+
/**
95+
* Maximum length permitted for a SpEL expression.
96+
* @since 5.2.24
97+
*/
98+
private static final int MAX_EXPRESSION_LENGTH = 10000;
99+
93100

94101
private final SpelParserConfiguration configuration;
95102

@@ -123,6 +130,9 @@ public InternalSpelExpressionParser(SpelParserConfiguration configuration) {
123130

124131
@Override
125132
protected SpelExpression doParseExpression(String expressionString, ParserContext context) throws ParseException {
133+
134+
checkExpressionLength(expressionString);
135+
126136
try {
127137
this.expressionString = expressionString;
128138
Tokenizer tokenizer = new Tokenizer(expressionString);
@@ -142,6 +152,12 @@ protected SpelExpression doParseExpression(String expressionString, ParserContex
142152
}
143153
}
144154

155+
private void checkExpressionLength(String string) {
156+
if (string.length() > MAX_EXPRESSION_LENGTH) {
157+
throw new SpelEvaluationException(SpelMessage.MAX_EXPRESSION_LENGTH_EXCEEDED, MAX_EXPRESSION_LENGTH);
158+
}
159+
}
160+
145161
// expression
146162
// : logicalOrExpression
147163
// ( (ASSIGN^ logicalOrExpression)

spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java

+27-6
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,20 @@
5757
*/
5858
public class EvaluationTests extends AbstractExpressionTests {
5959

60+
@Test
61+
public void expressionLength() {
62+
String expression = String.format("'X' + '%s'", repeat(" ", 9992));
63+
assertEquals(10000, expression.length());
64+
Expression expr = parser.parseExpression(expression);
65+
String result = expr.getValue(context, String.class);
66+
assertEquals(9993, result.length());
67+
assertEquals("X", result.trim());
68+
69+
expression = String.format("'X' + '%s'", repeat(" ", 9993));
70+
assertEquals(10001, expression.length());
71+
evaluateAndCheckError(expression, String.class, SpelMessage.MAX_EXPRESSION_LENGTH_EXCEEDED);
72+
}
73+
6074
@Test
6175
public void testCreateListsOnAttemptToIndexNull01() throws EvaluationException, ParseException {
6276
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
@@ -212,15 +226,13 @@ public void testMatchesWithPatternAccessThreshold() {
212226

213227
@Test
214228
public void matchesWithPatternLengthThreshold() {
215-
String pattern = "(0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
216-
"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
217-
"01234567890123456789012345678901234567890123456789|abc)";
218-
assertEquals(256, pattern.length());
219-
Expression expr = parser.parseExpression("'abc' matches '" + pattern + "'");
229+
String pattern = String.format("^(%s|X)", repeat("12345", 199));
230+
assertEquals(1000, pattern.length());
231+
Expression expr = parser.parseExpression("'X' matches '" + pattern + "'");
220232
assertTrue(expr.getValue(context, Boolean.class));
221233

222234
pattern += "?";
223-
assertEquals(257, pattern.length());
235+
assertEquals(1001, pattern.length());
224236
evaluateAndCheckError("'abc' matches '" + pattern + "'", Boolean.class, SpelMessage.MAX_REGEX_LENGTH_EXCEEDED);
225237
}
226238

@@ -1438,6 +1450,15 @@ public List<Method> filter(List<Method> methods) {
14381450
}
14391451

14401452

1453+
private static String repeat(String str, int count) {
1454+
String result = "";
1455+
for (int i = 0; i < count; i++) {
1456+
result += str;
1457+
}
1458+
return result;
1459+
}
1460+
1461+
14411462
@SuppressWarnings("rawtypes")
14421463
static class TestClass {
14431464

spring-expression/src/test/java/org/springframework/expression/spel/OperatorTests.java

+63-10
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import static org.junit.Assert.*;
2929
import static org.springframework.expression.spel.SpelMessage.MAX_REPEATED_TEXT_SIZE_EXCEEDED;
30+
import static org.springframework.expression.spel.SpelMessage.MAX_CONCATENATED_STRING_LENGTH_EXCEEDED;
3031

3132
/**
3233
* Tests the evaluation of expressions using relational operators.
@@ -389,11 +390,7 @@ public void testPlus() throws Exception {
389390
evaluate("3.0f + 5.0f", 8.0f, Float.class);
390391
evaluate("3.0d + 5.0d", 8.0d, Double.class);
391392
evaluate("3 + new java.math.BigDecimal('5')", new BigDecimal("8"), BigDecimal.class);
392-
393-
evaluate("'ab' + 2", "ab2", String.class);
394-
evaluate("2 + 'a'", "2a", String.class);
395-
evaluate("'ab' + null", "abnull", String.class);
396-
evaluate("null + 'ab'", "nullab", String.class);
393+
evaluate("5 + new Integer('37')", 42, Integer.class);
397394

398395
// AST:
399396
SpelExpression expr = (SpelExpression)parser.parseExpression("+3");
@@ -402,11 +399,11 @@ public void testPlus() throws Exception {
402399
assertEquals("(2 + 3)",expr.toStringAST());
403400

404401
// use as a unary operator
405-
evaluate("+5d",5d,Double.class);
406-
evaluate("+5L",5L,Long.class);
407-
evaluate("+5",5,Integer.class);
408-
evaluate("+new java.math.BigDecimal('5')", new BigDecimal("5"),BigDecimal.class);
409-
evaluateAndCheckError("+'abc'",SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
402+
evaluate("+5d", 5d, Double.class);
403+
evaluate("+5L", 5L, Long.class);
404+
evaluate("+5", 5, Integer.class);
405+
evaluate("+new java.math.BigDecimal('5')", new BigDecimal("5"), BigDecimal.class);
406+
evaluateAndCheckError("+'abc'", SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
410407

411408
// string concatenation
412409
evaluate("'abc'+'def'","abcdef",String.class);
@@ -585,6 +582,62 @@ public void stringRepeat() {
585582
evaluateAndCheckError("'a' * 257", String.class, MAX_REPEATED_TEXT_SIZE_EXCEEDED, 4);
586583
}
587584

585+
@Test
586+
public void stringConcatenation() {
587+
evaluate("'' + ''", "", String.class);
588+
evaluate("'' + null", "null", String.class);
589+
evaluate("null + ''", "null", String.class);
590+
evaluate("'ab' + null", "abnull", String.class);
591+
evaluate("null + 'ab'", "nullab", String.class);
592+
evaluate("'ab' + 2", "ab2", String.class);
593+
evaluate("2 + 'ab'", "2ab", String.class);
594+
evaluate("'abc' + 'def'", "abcdef", String.class);
595+
596+
// Text is big but not too big
597+
final int maxSize = 100_000;
598+
context.setVariable("text1", createString(maxSize));
599+
Expression expr = parser.parseExpression("#text1 + ''");
600+
//assertThat(expr.getValue(context, String.class)).hasSize(maxSize);
601+
assertEquals(maxSize, expr.getValue(context, String.class).length());
602+
603+
expr = parser.parseExpression("'' + #text1");
604+
//assertThat(expr.getValue(context, String.class)).hasSize(maxSize);
605+
assertEquals(maxSize, expr.getValue(context, String.class).length());
606+
607+
context.setVariable("text1", createString(maxSize / 2));
608+
expr = parser.parseExpression("#text1 + #text1");
609+
//assertThat(expr.getValue(context, String.class)).hasSize(maxSize);
610+
assertEquals(maxSize, expr.getValue(context, String.class).length());
611+
612+
// Text is too big
613+
context.setVariable("text1", createString(maxSize + 1));
614+
evaluateAndCheckError("#text1 + ''", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7);
615+
evaluateAndCheckError("#text1 + true", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7);
616+
evaluateAndCheckError("'' + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 3);
617+
evaluateAndCheckError("true + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 5);
618+
619+
context.setVariable("text1", createString(maxSize / 2));
620+
context.setVariable("text2", createString((maxSize / 2) + 1));
621+
evaluateAndCheckError("#text1 + #text2", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7);
622+
evaluateAndCheckError("#text1 + #text2 + true", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7);
623+
evaluateAndCheckError("#text1 + true + #text2", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 14);
624+
evaluateAndCheckError("true + #text1 + #text2", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 14);
625+
626+
evaluateAndCheckError("#text2 + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7);
627+
evaluateAndCheckError("#text2 + #text1 + true", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7);
628+
evaluateAndCheckError("#text2 + true + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 14);
629+
evaluateAndCheckError("true + #text2 + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 14);
630+
631+
context.setVariable("text1", createString((maxSize / 3) + 1));
632+
evaluateAndCheckError("#text1 + #text1 + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 16);
633+
evaluateAndCheckError("(#text1 + #text1) + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 18);
634+
evaluateAndCheckError("#text1 + (#text1 + #text1)", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7);
635+
}
636+
637+
private static String createString(int size) {
638+
return new String(new char[size]);
639+
}
640+
588641
@Test
589642
public void testLongs() {
590643
evaluate("3L == 4L", false, Boolean.class);

0 commit comments

Comments
 (0)