Skip to content

Commit

Permalink
Merge pull request #3 from cesarhernandezgt/v.4.3.30.RELEASE-TT.x-patch
Browse files Browse the repository at this point in the history
  • Loading branch information
cesarhernandezgt authored Apr 21, 2023
2 parents 739e6ad + 820b701 commit 5742f3a
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -262,13 +262,21 @@ public enum SpelMessage {
MAX_ARRAY_ELEMENTS_THRESHOLD_EXCEEDED(Kind.ERROR, 1075,
"Array declares too many elements, exceeding the threshold of ''{0}''"),

/** @since 5.3.26 */
/** @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.3.26 */
/** @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"),

/** @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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
//import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.NumberUtils;

Expand All @@ -49,6 +52,12 @@
*/
public class OpPlus extends Operator {

/**
* Maximum number of characters permitted in a concatenated string.
* @since 5.2.24
*/
private static final int MAX_CONCATENATED_STRING_LENGTH = 100000;

public OpPlus(int pos, SpelNodeImpl... operands) {
super("+", pos, operands);
Assert.notEmpty(operands, "Operands must not be empty");
Expand Down Expand Up @@ -123,22 +132,45 @@ else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNume

if (leftOperand instanceof String && rightOperand instanceof String) {
this.exitTypeDescriptor = "Ljava/lang/String";
return new TypedValue((String) leftOperand + rightOperand);
String leftString = (String) leftOperand;
String rightString = (String) rightOperand;
checkStringLength(leftString);
checkStringLength(rightString);
return concatenate(leftString, rightString);
}

if (leftOperand instanceof String) {
return new TypedValue(
leftOperand + (rightOperand == null ? "null" : convertTypedValueToString(operandTwoValue, state)));
String leftString = (String) leftOperand;
checkStringLength(leftString);
String rightString = (rightOperand == null ? "null" : convertTypedValueToString(operandTwoValue, state));
checkStringLength(rightString);
return concatenate(leftString, rightString);
}

if (rightOperand instanceof String) {
return new TypedValue(
(leftOperand == null ? "null" : convertTypedValueToString(operandOneValue, state)) + rightOperand);
String rightString = (String) rightOperand;
checkStringLength(rightString);
String leftString = (leftOperand == null ? "null" : convertTypedValueToString(operandOneValue, state));
checkStringLength(leftString);
return concatenate(leftString, rightString);
}

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

private void checkStringLength(String string) {
if (string.length() > MAX_CONCATENATED_STRING_LENGTH) {
throw new SpelEvaluationException(getStartPosition(),
SpelMessage.MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, MAX_CONCATENATED_STRING_LENGTH);
}
}

private TypedValue concatenate(String leftString, String rightString) {
String result = leftString + rightString;
checkStringLength(result);
return new TypedValue(result);
}

@Override
public String toStringAST() {
if (this.children.length < 2) { // unary plus
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public class OperatorMatches extends Operator {
* Maximum number of characters permitted in a regular expression.
* @since 5.3.26
*/
private static final int MAX_REGEX_LENGTH = 256;
private static final int MAX_REGEX_LENGTH = 1000;

private final ConcurrentMap<String, Pattern> patternCache;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,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;
Expand Down Expand Up @@ -90,6 +91,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 = 10000;


private final SpelParserConfiguration configuration;

Expand Down Expand Up @@ -123,6 +130,9 @@ public InternalSpelExpressionParser(SpelParserConfiguration configuration) {

@Override
protected SpelExpression doParseExpression(String expressionString, ParserContext context) throws ParseException {

checkExpressionLength(expressionString);

try {
this.expressionString = expressionString;
Tokenizer tokenizer = new Tokenizer(expressionString);
Expand All @@ -142,6 +152,12 @@ protected SpelExpression doParseExpression(String expressionString, ParserContex
}
}

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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,20 @@
*/
public class EvaluationTests extends AbstractExpressionTests {

@Test
public void expressionLength() {
String expression = String.format("'X' + '%s'", repeat(" ", 9992));
assertEquals(10000, expression.length());
Expression expr = parser.parseExpression(expression);
String result = expr.getValue(context, String.class);
assertEquals(9993, result.length());
assertEquals("X", result.trim());

expression = String.format("'X' + '%s'", repeat(" ", 9993));
assertEquals(10001, expression.length());
evaluateAndCheckError(expression, String.class, SpelMessage.MAX_EXPRESSION_LENGTH_EXCEEDED);
}

@Test
public void testCreateListsOnAttemptToIndexNull01() throws EvaluationException, ParseException {
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
Expand Down Expand Up @@ -212,15 +226,13 @@ public void testMatchesWithPatternAccessThreshold() {

@Test
public void matchesWithPatternLengthThreshold() {
String pattern = "(0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
"01234567890123456789012345678901234567890123456789|abc)";
assertEquals(256, pattern.length());
Expression expr = parser.parseExpression("'abc' matches '" + pattern + "'");
String pattern = String.format("^(%s|X)", repeat("12345", 199));
assertEquals(1000, pattern.length());
Expression expr = parser.parseExpression("'X' matches '" + pattern + "'");
assertTrue(expr.getValue(context, Boolean.class));

pattern += "?";
assertEquals(257, pattern.length());
assertEquals(1001, pattern.length());
evaluateAndCheckError("'abc' matches '" + pattern + "'", Boolean.class, SpelMessage.MAX_REGEX_LENGTH_EXCEEDED);
}

Expand Down Expand Up @@ -1438,6 +1450,15 @@ public List<Method> filter(List<Method> 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 {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

import static org.junit.Assert.*;
import static org.springframework.expression.spel.SpelMessage.MAX_REPEATED_TEXT_SIZE_EXCEEDED;
import static org.springframework.expression.spel.SpelMessage.MAX_CONCATENATED_STRING_LENGTH_EXCEEDED;

/**
* Tests the evaluation of expressions using relational operators.
Expand Down Expand Up @@ -389,11 +390,7 @@ public void testPlus() throws Exception {
evaluate("3.0f + 5.0f", 8.0f, Float.class);
evaluate("3.0d + 5.0d", 8.0d, Double.class);
evaluate("3 + new java.math.BigDecimal('5')", new BigDecimal("8"), BigDecimal.class);

evaluate("'ab' + 2", "ab2", String.class);
evaluate("2 + 'a'", "2a", String.class);
evaluate("'ab' + null", "abnull", String.class);
evaluate("null + 'ab'", "nullab", String.class);
evaluate("5 + new Integer('37')", 42, Integer.class);

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

// use as a unary operator
evaluate("+5d",5d,Double.class);
evaluate("+5L",5L,Long.class);
evaluate("+5",5,Integer.class);
evaluate("+new java.math.BigDecimal('5')", new BigDecimal("5"),BigDecimal.class);
evaluateAndCheckError("+'abc'",SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
evaluate("+5d", 5d, Double.class);
evaluate("+5L", 5L, Long.class);
evaluate("+5", 5, Integer.class);
evaluate("+new java.math.BigDecimal('5')", new BigDecimal("5"), BigDecimal.class);
evaluateAndCheckError("+'abc'", SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);

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

@Test
public void stringConcatenation() {
evaluate("'' + ''", "", String.class);
evaluate("'' + null", "null", String.class);
evaluate("null + ''", "null", String.class);
evaluate("'ab' + null", "abnull", String.class);
evaluate("null + 'ab'", "nullab", String.class);
evaluate("'ab' + 2", "ab2", String.class);
evaluate("2 + 'ab'", "2ab", String.class);
evaluate("'abc' + 'def'", "abcdef", String.class);

// Text is big but not too big
final int maxSize = 100_000;
context.setVariable("text1", createString(maxSize));
Expression expr = parser.parseExpression("#text1 + ''");
//assertThat(expr.getValue(context, String.class)).hasSize(maxSize);
assertEquals(maxSize, expr.getValue(context, String.class).length());

expr = parser.parseExpression("'' + #text1");
//assertThat(expr.getValue(context, String.class)).hasSize(maxSize);
assertEquals(maxSize, expr.getValue(context, String.class).length());

context.setVariable("text1", createString(maxSize / 2));
expr = parser.parseExpression("#text1 + #text1");
//assertThat(expr.getValue(context, String.class)).hasSize(maxSize);
assertEquals(maxSize, expr.getValue(context, String.class).length());

// Text is too big
context.setVariable("text1", createString(maxSize + 1));
evaluateAndCheckError("#text1 + ''", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7);
evaluateAndCheckError("#text1 + true", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7);
evaluateAndCheckError("'' + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 3);
evaluateAndCheckError("true + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 5);

context.setVariable("text1", createString(maxSize / 2));
context.setVariable("text2", createString((maxSize / 2) + 1));
evaluateAndCheckError("#text1 + #text2", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7);
evaluateAndCheckError("#text1 + #text2 + true", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7);
evaluateAndCheckError("#text1 + true + #text2", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 14);
evaluateAndCheckError("true + #text1 + #text2", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 14);

evaluateAndCheckError("#text2 + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7);
evaluateAndCheckError("#text2 + #text1 + true", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7);
evaluateAndCheckError("#text2 + true + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 14);
evaluateAndCheckError("true + #text2 + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 14);

context.setVariable("text1", createString((maxSize / 3) + 1));
evaluateAndCheckError("#text1 + #text1 + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 16);
evaluateAndCheckError("(#text1 + #text1) + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 18);
evaluateAndCheckError("#text1 + (#text1 + #text1)", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7);
}

private static String createString(int size) {
return new String(new char[size]);
}

@Test
public void testLongs() {
evaluate("3L == 4L", false, Boolean.class);
Expand Down

0 comments on commit 5742f3a

Please sign in to comment.