Skip to content

Commit 6a727e1

Browse files
committed
OperatorMatches flags misguided evaluation attempts as FLAWED_PATTERN
Issue: SPR-16731 (cherry picked from commit d4a55a2)
1 parent 246a6db commit 6a727e1

File tree

4 files changed

+94
-21
lines changed

4 files changed

+94
-21
lines changed

spring-context/src/main/java/org/springframework/cache/interceptor/VariableNotAvailableException.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -30,13 +30,15 @@ class VariableNotAvailableException extends EvaluationException {
3030

3131
private final String name;
3232

33+
3334
public VariableNotAvailableException(String name) {
34-
super("Variable '" + name + "' is not available");
35+
super("Variable not available");
3536
this.name = name;
3637
}
3738

3839

39-
public String getName() {
40+
public final String getName() {
4041
return this.name;
4142
}
43+
4244
}

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

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,13 @@ public enum SpelMessage {
5757
"Property or field ''{0}'' cannot be found on null"),
5858

5959
PROPERTY_OR_FIELD_NOT_READABLE(Kind.ERROR, 1008,
60-
"Property or field ''{0}'' cannot be found on object of type ''{1}'' - maybe not public?"),
60+
"Property or field ''{0}'' cannot be found on object of type ''{1}'' - maybe not public or not valid?"),
6161

6262
PROPERTY_OR_FIELD_NOT_WRITABLE_ON_NULL(Kind.ERROR, 1009,
6363
"Property or field ''{0}'' cannot be set on null"),
6464

6565
PROPERTY_OR_FIELD_NOT_WRITABLE(Kind.ERROR, 1010,
66-
"Property or field ''{0}'' cannot be set on object of type ''{1}'' - maybe not public?"),
66+
"Property or field ''{0}'' cannot be set on object of type ''{1}'' - maybe not public or not writable?"),
6767

6868
METHOD_CALL_ON_NULL_OBJECT_NOT_ALLOWED(Kind.ERROR, 1011,
6969
"Method call: Attempted to call method {0} on null context object"),
@@ -156,7 +156,7 @@ public enum SpelMessage {
156156
NOT_A_REAL(Kind.ERROR, 1040,
157157
"The value ''{0}'' cannot be parsed as a double"),
158158

159-
MORE_INPUT(Kind.ERROR,1041,
159+
MORE_INPUT(Kind.ERROR, 1041,
160160
"After parsing a valid expression, there is still more data in the expression: ''{0}''"),
161161

162162
RIGHT_OPERAND_PROBLEM(Kind.ERROR, 1042,
@@ -226,30 +226,36 @@ public enum SpelMessage {
226226
"A required array dimension has not been specified"),
227227

228228
INITIALIZER_LENGTH_INCORRECT(Kind.ERROR, 1064,
229-
"array initializer size does not match array dimensions"),
229+
"Array initializer size does not match array dimensions"),
230230

231-
UNEXPECTED_ESCAPE_CHAR(Kind.ERROR, 1065, "unexpected escape character."),
231+
UNEXPECTED_ESCAPE_CHAR(Kind.ERROR, 1065,
232+
"Unexpected escape character"),
232233

233234
OPERAND_NOT_INCREMENTABLE(Kind.ERROR, 1066,
234-
"the expression component ''{0}'' does not support increment"),
235+
"The expression component ''{0}'' does not support increment"),
235236

236237
OPERAND_NOT_DECREMENTABLE(Kind.ERROR, 1067,
237-
"the expression component ''{0}'' does not support decrement"),
238+
"The expression component ''{0}'' does not support decrement"),
238239

239240
NOT_ASSIGNABLE(Kind.ERROR, 1068,
240-
"the expression component ''{0}'' is not assignable"),
241+
"The expression component ''{0}'' is not assignable"),
241242

242243
MISSING_CHARACTER(Kind.ERROR, 1069,
243-
"missing expected character ''{0}''"),
244+
"Missing expected character ''{0}''"),
244245

245246
LEFT_OPERAND_PROBLEM(Kind.ERROR, 1070,
246247
"Problem parsing left operand"),
247248

248249
MISSING_SELECTION_EXPRESSION(Kind.ERROR, 1071,
249250
"A required selection expression has not been specified"),
250251

251-
EXCEPTION_RUNNING_COMPILED_EXPRESSION(Kind.ERROR,1072,
252-
"An exception occurred whilst evaluating a compiled expression");
252+
/** @since 4.1 */
253+
EXCEPTION_RUNNING_COMPILED_EXPRESSION(Kind.ERROR, 1072,
254+
"An exception occurred whilst evaluating a compiled expression"),
255+
256+
/** @since 4.3.17 */
257+
FLAWED_PATTERN(Kind.ERROR, 1073,
258+
"Failed to efficiently evaluate pattern ''{0}'': consider redesigning it");
253259

254260

255261
private final Kind kind;

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

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -40,6 +40,8 @@
4040
*/
4141
public class OperatorMatches extends Operator {
4242

43+
private static final int PATTERN_ACCESS_THRESHOLD = 1000000;
44+
4345
private final ConcurrentMap<String, Pattern> patternCache = new ConcurrentHashMap<String, Pattern>();
4446

4547

@@ -60,10 +62,10 @@ public OperatorMatches(int pos, SpelNodeImpl... operands) {
6062
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
6163
SpelNodeImpl leftOp = getLeftOperand();
6264
SpelNodeImpl rightOp = getRightOperand();
63-
Object left = leftOp.getValue(state, String.class);
64-
Object right = getRightOperand().getValueInternal(state).getValue();
65+
String left = leftOp.getValue(state, String.class);
66+
Object right = getRightOperand().getValue(state);
6567

66-
if (!(left instanceof String)) {
68+
if (left == null) {
6769
throw new SpelEvaluationException(leftOp.getStartPosition(),
6870
SpelMessage.INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR, left);
6971
}
@@ -73,18 +75,65 @@ public BooleanTypedValue getValueInternal(ExpressionState state) throws Evaluati
7375
}
7476

7577
try {
76-
String leftString = (String) left;
7778
String rightString = (String) right;
7879
Pattern pattern = this.patternCache.get(rightString);
7980
if (pattern == null) {
8081
pattern = Pattern.compile(rightString);
8182
this.patternCache.putIfAbsent(rightString, pattern);
8283
}
83-
Matcher matcher = pattern.matcher(leftString);
84+
Matcher matcher = pattern.matcher(new MatcherInput(left, new AccessCount()));
8485
return BooleanTypedValue.forValue(matcher.matches());
8586
}
8687
catch (PatternSyntaxException ex) {
87-
throw new SpelEvaluationException(rightOp.getStartPosition(), ex, SpelMessage.INVALID_PATTERN, right);
88+
throw new SpelEvaluationException(
89+
rightOp.getStartPosition(), ex, SpelMessage.INVALID_PATTERN, right);
90+
}
91+
catch (IllegalStateException ex) {
92+
throw new SpelEvaluationException(
93+
rightOp.getStartPosition(), ex, SpelMessage.FLAWED_PATTERN, right);
94+
}
95+
}
96+
97+
98+
private static class AccessCount {
99+
100+
private int count;
101+
102+
public void check() throws IllegalStateException {
103+
if (this.count++ > PATTERN_ACCESS_THRESHOLD) {
104+
throw new IllegalStateException("Pattern access threshold exceeded");
105+
}
106+
}
107+
}
108+
109+
110+
private static class MatcherInput implements CharSequence {
111+
112+
private final CharSequence value;
113+
114+
private AccessCount access;
115+
116+
public MatcherInput(CharSequence value, AccessCount access) {
117+
this.value = value;
118+
this.access = access;
119+
}
120+
121+
public char charAt(int index) {
122+
this.access.check();
123+
return this.value.charAt(index);
124+
}
125+
126+
public CharSequence subSequence(int start, int end) {
127+
return new MatcherInput(this.value.subSequence(start, end), this.access);
128+
}
129+
130+
public int length() {
131+
return this.value.length();
132+
}
133+
134+
@Override
135+
public String toString() {
136+
return this.value.toString();
88137
}
89138
}
90139

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,22 @@ public void testRelOperatorsMatches05() {
194194
evaluate("27 matches '^.*2.*$'", true, Boolean.class); // conversion int>string
195195
}
196196

197+
@Test // SPR-16731
198+
public void testMatchesWithPatternAccessThreshold() {
199+
String pattern = "^(?=[a-z0-9-]{1,47})([a-z0-9]+[-]{0,1}){1,47}[a-z0-9]{1}$";
200+
String expression = "'abcde-fghijklmn-o42pasdfasdfasdf.qrstuvwxyz10x.xx.yyy.zasdfasfd' matches \'" + pattern + "\'";
201+
Expression expr = parser.parseExpression(expression);
202+
try {
203+
expr.getValue();
204+
fail("Should have exceeded threshold");
205+
}
206+
catch (EvaluationException ee) {
207+
SpelEvaluationException see = (SpelEvaluationException) ee;
208+
assertEquals(SpelMessage.FLAWED_PATTERN, see.getMessageCode());
209+
assertTrue(see.getCause() instanceof IllegalStateException);
210+
}
211+
}
212+
197213
// mixing operators
198214
@Test
199215
public void testMixingOperators01() {

0 commit comments

Comments
 (0)