Skip to content

Commit 91f4662

Browse files
committed
Refactor SpelInjection.qll
1 parent 94f32d2 commit 91f4662

File tree

4 files changed

+121
-96
lines changed

4 files changed

+121
-96
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* Provides classes for working with the Spring Expression Language (SpEL).
3+
*/
4+
5+
import java
6+
7+
/**
8+
* Methods that trigger the evaluation of a SpEL expression.
9+
*/
10+
class ExpressionEvaluationMethod extends Method {
11+
ExpressionEvaluationMethod() {
12+
this.getDeclaringType().getASupertype*() instanceof Expression and
13+
this.hasName(["getValue", "getValueTypeDescriptor", "getValueType", "setValue"])
14+
}
15+
}
16+
17+
/**
18+
* The class `org.springframework.expression.ExpressionParser`.
19+
*/
20+
class ExpressionParser extends RefType {
21+
ExpressionParser() { hasQualifiedName("org.springframework.expression", "ExpressionParser") }
22+
}
23+
24+
/**
25+
* The class `org.springframework.expression.spel.support."SimpleEvaluationContext$Builder`.
26+
*/
27+
class SimpleEvaluationContextBuilder extends RefType {
28+
SimpleEvaluationContextBuilder() {
29+
hasQualifiedName("org.springframework.expression.spel.support",
30+
"SimpleEvaluationContext$Builder")
31+
}
32+
}
33+
34+
/**
35+
* The class `org.springframework.expression.Expression`.
36+
*/
37+
class Expression extends RefType {
38+
Expression() { hasQualifiedName("org.springframework.expression", "Expression") }
39+
}
40+
41+
/**
42+
* The class `org.springframework.expression.spel.support.SimpleEvaluationContext`.
43+
*/
44+
class SimpleEvaluationContext extends RefType {
45+
SimpleEvaluationContext() {
46+
hasQualifiedName("org.springframework.expression.spel.support", "SimpleEvaluationContext")
47+
}
48+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/** Provides classes to reason about SpEL injection attacks. */
2+
3+
import java
4+
import semmle.code.java.dataflow.DataFlow
5+
import semmle.code.java.dataflow.ExternalFlow
6+
import semmle.code.java.frameworks.spring.SpringExpression
7+
8+
/** A data flow sink for unvalidated user input that is used to construct SpEL expressions. */
9+
abstract class SpelExpressionEvaluationSink extends DataFlow::ExprNode { }
10+
11+
private class SpelExpressionEvaluationModel extends SinkModelCsv {
12+
override predicate row(string row) {
13+
row =
14+
[
15+
"org.springframework.expression;Expression;true;getValue;;;Argument[-1];spel",
16+
"org.springframework.expression;Expression;true;getValueTypeDescriptor;;;Argument[-1];spel",
17+
"org.springframework.expression;Expression;true;getValueType;;;Argument[-1];spel",
18+
"org.springframework.expression;Expression;true;setValue;;;Argument[-1];spel"
19+
]
20+
}
21+
}
22+
23+
/**
24+
* A unit class for adding additional taint steps.
25+
*
26+
* Extend this class to add additional taint steps that should apply to the `SpELInjectionConfig`.
27+
*/
28+
class SpelExpressionInjectionAdditionalTaintStep extends Unit {
29+
/**
30+
* Holds if the step from `node1` to `node2` should be considered a taint
31+
* step for the `SpELInjectionConfig` configuration.
32+
*/
33+
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
34+
}
35+
36+
/** A set of additional taint steps to consider when taint tracking SpEL related data flows. */
37+
private class DefaultSpelExpressionInjectionAdditionalTaintStep extends SpelExpressionInjectionAdditionalTaintStep {
38+
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
39+
expressionParsingStep(node1, node2)
40+
}
41+
}
42+
43+
/**
44+
* Holds if `node1` to `node2` is a dataflow step that parses a SpEL expression,
45+
* by calling `parser.parseExpression(tainted)`.
46+
*/
47+
private predicate expressionParsingStep(DataFlow::Node node1, DataFlow::Node node2) {
48+
exists(MethodAccess ma, Method m | ma.getMethod() = m |
49+
m.getDeclaringType().getASupertype*() instanceof ExpressionParser and
50+
m.hasName(["parseExpression", "parseRaw"]) and
51+
ma.getAnArgument() = node1.asExpr() and
52+
node2.asExpr() = ma
53+
)
54+
}
Lines changed: 19 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,10 @@
1-
/** Provides classes to reason about SpEL injection attacks. */
1+
/** Provides taint tracking and dataflow configurations to be used in SpEL injection queries. */
22

33
import java
44
import semmle.code.java.dataflow.DataFlow
55
import semmle.code.java.dataflow.FlowSources
6-
7-
/** A data flow sink for unvalidated user input that is used to construct SpEL expressions. */
8-
abstract class SpelExpressionEvaluationSink extends DataFlow::ExprNode { }
9-
10-
/** Default sink for SpEL injection vulnerabilities. */
11-
private class DefaultSpelExpressionEvaluationSink extends SpelExpressionEvaluationSink {
12-
DefaultSpelExpressionEvaluationSink() {
13-
exists(MethodAccess ma |
14-
sinkNode(this, "spel") and
15-
this.asExpr() = ma.getQualifier() and
16-
not exists(SafeEvaluationContextFlowConfig config |
17-
config.hasFlowTo(DataFlow::exprNode(ma.getArgument(0)))
18-
)
19-
)
20-
}
21-
}
22-
23-
/**
24-
* A unit class for adding additional taint steps.
25-
*
26-
* Extend this class to add additional taint steps that should apply to the `SpELInjectionConfig`.
27-
*/
28-
class SpelExpressionInjectionAdditionalTaintStep extends Unit {
29-
/**
30-
* Holds if the step from `node1` to `node2` should be considered a taint
31-
* step for the `SpELInjectionConfig` configuration.
32-
*/
33-
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
34-
}
35-
36-
/** A set of additional taint steps to consider when taint tracking SpEL related data flows. */
37-
private class DefaultSpelExpressionInjectionAdditionalTaintStep extends SpelExpressionInjectionAdditionalTaintStep {
38-
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
39-
expressionParsingStep(node1, node2)
40-
}
41-
}
6+
import semmle.code.java.frameworks.spring.SpringExpression
7+
import semmle.code.java.security.SpelInjection
428

439
/**
4410
* A taint-tracking configuration for unsafe user input
@@ -56,6 +22,19 @@ class SpelInjectionConfig extends TaintTracking::Configuration {
5622
}
5723
}
5824

25+
/** Default sink for SpEL injection vulnerabilities. */
26+
private class DefaultSpelExpressionEvaluationSink extends SpelExpressionEvaluationSink {
27+
DefaultSpelExpressionEvaluationSink() {
28+
exists(MethodAccess ma |
29+
sinkNode(this, "spel") and
30+
this.asExpr() = ma.getQualifier() and
31+
not exists(SafeEvaluationContextFlowConfig config |
32+
config.hasFlowTo(DataFlow::exprNode(ma.getArgument(0)))
33+
)
34+
)
35+
}
36+
}
37+
5938
/**
6039
* A configuration for safe evaluation context that may be used in expression evaluation.
6140
*/
@@ -74,6 +53,9 @@ private class SafeEvaluationContextFlowConfig extends DataFlow2::Configuration {
7453
override int fieldFlowBranchLimit() { result = 0 }
7554
}
7655

56+
/**
57+
* A `ContextSource` that is safe from SpEL injection
58+
*/
7759
private class SafeContextSource extends DataFlow::ExprNode {
7860
SafeContextSource() {
7961
isSimpleEvaluationContextConstructorCall(getExpr()) or
@@ -102,47 +84,3 @@ private predicate isSimpleEvaluationContextBuilderCall(Expr expr) {
10284
ma = expr
10385
)
10486
}
105-
106-
/**
107-
* Methods that trigger evaluation of an expression.
108-
*/
109-
private class ExpressionEvaluationMethod extends Method {
110-
ExpressionEvaluationMethod() {
111-
this.getDeclaringType().getASupertype*() instanceof Expression and
112-
this.hasName(["getValue", "getValueTypeDescriptor", "getValueType", "setValue"])
113-
}
114-
}
115-
116-
/**
117-
* Holds if `node1` to `node2` is a dataflow step that parses a SpEL expression,
118-
* by calling `parser.parseExpression(tainted)`.
119-
*/
120-
private predicate expressionParsingStep(DataFlow::Node node1, DataFlow::Node node2) {
121-
exists(MethodAccess ma, Method m | ma.getMethod() = m |
122-
m.getDeclaringType().getASupertype*() instanceof ExpressionParser and
123-
m.hasName(["parseExpression", "parseRaw"]) and
124-
ma.getAnArgument() = node1.asExpr() and
125-
node2.asExpr() = ma
126-
)
127-
}
128-
129-
private class SimpleEvaluationContext extends RefType {
130-
SimpleEvaluationContext() {
131-
hasQualifiedName("org.springframework.expression.spel.support", "SimpleEvaluationContext")
132-
}
133-
}
134-
135-
private class SimpleEvaluationContextBuilder extends RefType {
136-
SimpleEvaluationContextBuilder() {
137-
hasQualifiedName("org.springframework.expression.spel.support",
138-
"SimpleEvaluationContext$Builder")
139-
}
140-
}
141-
142-
private class Expression extends RefType {
143-
Expression() { hasQualifiedName("org.springframework.expression", "Expression") }
144-
}
145-
146-
private class ExpressionParser extends RefType {
147-
ExpressionParser() { hasQualifiedName("org.springframework.expression", "ExpressionParser") }
148-
}

java/ql/src/semmle/code/java/security/SpelInjectionSinkModels.qll

Lines changed: 0 additions & 15 deletions
This file was deleted.

0 commit comments

Comments
 (0)