Skip to content

Commit 9d37c09

Browse files
committed
OperatorMatches flags misguided evaluation attempts as FLAWED_PATTERN
Issue: SPR-16731 (cherry picked from commit d4a55a2)
1 parent eb573d8 commit 9d37c09

File tree

4 files changed

+89
-15
lines changed

4 files changed

+89
-15
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: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -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: 53 additions & 3 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<>();
4446

4547

@@ -79,11 +81,59 @@ public BooleanTypedValue getValueInternal(ExpressionState state) throws Evaluati
7981
pattern = Pattern.compile(rightString);
8082
this.patternCache.putIfAbsent(rightString, pattern);
8183
}
82-
Matcher matcher = pattern.matcher(left);
84+
Matcher matcher = pattern.matcher(new MatcherInput(left, new AccessCount()));
8385
return BooleanTypedValue.forValue(matcher.matches());
8486
}
8587
catch (PatternSyntaxException ex) {
86-
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();
87137
}
88138
}
89139

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)