Skip to content

Commit e027d51

Browse files
authored
Merge pull request github#6037 from atorralba/atorralba/promote-spel-injection
Java: Promote SpEL Injection query from experimental
2 parents 92ffd8c + 6d9a88d commit e027d51

File tree

23 files changed

+603
-310
lines changed

23 files changed

+603
-310
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm,codescanning
2+
* The query "Expression language injection (Spring)" (`java/spel-expression-injection`) has been promoted from experimental to the main query pack. Its results will now appear by default. This query was originally [submitted as an experimental query by @artem-smotrakov](https://github.com/github/codeql/pull/3291).
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: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/** Provides classes to reason about SpEL injection attacks. */
2+
3+
import java
4+
private import semmle.code.java.dataflow.DataFlow
5+
private import semmle.code.java.dataflow.ExternalFlow
6+
private 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+
/**
12+
* A unit class for adding additional taint steps.
13+
*
14+
* Extend this class to add additional taint steps that should apply to the `SpELInjectionConfig`.
15+
*/
16+
class SpelExpressionInjectionAdditionalTaintStep extends Unit {
17+
/**
18+
* Holds if the step from `node1` to `node2` should be considered a taint
19+
* step for the `SpELInjectionConfig` configuration.
20+
*/
21+
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
22+
}
23+
24+
/** A set of additional taint steps to consider when taint tracking SpEL related data flows. */
25+
private class DefaultSpelExpressionInjectionAdditionalTaintStep extends SpelExpressionInjectionAdditionalTaintStep {
26+
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
27+
expressionParsingStep(node1, node2)
28+
}
29+
}
30+
31+
/**
32+
* Holds if `node1` to `node2` is a dataflow step that parses a SpEL expression,
33+
* by calling `parser.parseExpression(tainted)`.
34+
*/
35+
private predicate expressionParsingStep(DataFlow::Node node1, DataFlow::Node node2) {
36+
exists(MethodAccess ma, Method m | ma.getMethod() = m |
37+
m.getDeclaringType().getASupertype*() instanceof ExpressionParser and
38+
m.hasName(["parseExpression", "parseRaw"]) and
39+
ma.getAnArgument() = node1.asExpr() and
40+
node2.asExpr() = ma
41+
)
42+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/** Provides taint tracking and dataflow configurations to be used in SpEL injection queries. */
2+
3+
import java
4+
private import semmle.code.java.dataflow.FlowSources
5+
private import semmle.code.java.dataflow.TaintTracking
6+
private import semmle.code.java.frameworks.spring.SpringExpression
7+
private import semmle.code.java.security.SpelInjection
8+
9+
/**
10+
* A taint-tracking configuration for unsafe user input
11+
* that is used to construct and evaluate a SpEL expression.
12+
*/
13+
class SpelInjectionConfig extends TaintTracking::Configuration {
14+
SpelInjectionConfig() { this = "SpelInjectionConfig" }
15+
16+
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
17+
18+
override predicate isSink(DataFlow::Node sink) { sink instanceof SpelExpressionEvaluationSink }
19+
20+
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
21+
any(SpelExpressionInjectionAdditionalTaintStep c).step(node1, node2)
22+
}
23+
}
24+
25+
/** Default sink for SpEL injection vulnerabilities. */
26+
private class DefaultSpelExpressionEvaluationSink extends SpelExpressionEvaluationSink {
27+
DefaultSpelExpressionEvaluationSink() {
28+
exists(MethodAccess ma |
29+
ma.getMethod() instanceof ExpressionEvaluationMethod and
30+
ma.getQualifier() = this.asExpr() and
31+
not exists(SafeEvaluationContextFlowConfig config |
32+
config.hasFlowTo(DataFlow::exprNode(ma.getArgument(0)))
33+
)
34+
)
35+
}
36+
}
37+
38+
/**
39+
* A configuration for safe evaluation context that may be used in expression evaluation.
40+
*/
41+
private class SafeEvaluationContextFlowConfig extends DataFlow2::Configuration {
42+
SafeEvaluationContextFlowConfig() { this = "SpelInjection::SafeEvaluationContextFlowConfig" }
43+
44+
override predicate isSource(DataFlow::Node source) { source instanceof SafeContextSource }
45+
46+
override predicate isSink(DataFlow::Node sink) {
47+
exists(MethodAccess ma |
48+
ma.getMethod() instanceof ExpressionEvaluationMethod and
49+
ma.getArgument(0) = sink.asExpr()
50+
)
51+
}
52+
53+
override int fieldFlowBranchLimit() { result = 0 }
54+
}
55+
56+
/**
57+
* A `ContextSource` that is safe from SpEL injection.
58+
*/
59+
private class SafeContextSource extends DataFlow::ExprNode {
60+
SafeContextSource() {
61+
isSimpleEvaluationContextConstructorCall(getExpr()) or
62+
isSimpleEvaluationContextBuilderCall(getExpr())
63+
}
64+
}
65+
66+
/**
67+
* Holds if `expr` constructs `SimpleEvaluationContext`.
68+
*/
69+
private predicate isSimpleEvaluationContextConstructorCall(Expr expr) {
70+
exists(ConstructorCall cc |
71+
cc.getConstructedType() instanceof SimpleEvaluationContext and
72+
cc = expr
73+
)
74+
}
75+
76+
/**
77+
* Holds if `expr` builds `SimpleEvaluationContext` via `SimpleEvaluationContext.Builder`,
78+
* for instance, `SimpleEvaluationContext.forReadWriteDataBinding().build()`.
79+
*/
80+
private predicate isSimpleEvaluationContextBuilderCall(Expr expr) {
81+
exists(MethodAccess ma, Method m | ma.getMethod() = m |
82+
m.getDeclaringType() instanceof SimpleEvaluationContextBuilder and
83+
m.hasName("build") and
84+
ma = expr
85+
)
86+
}

java/ql/src/experimental/Security/CWE/CWE-094/SpelInjection.qhelp renamed to java/ql/src/Security/CWE/CWE-094/SpelInjection.qhelp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<overview>
55
<p>
66
The Spring Expression Language (SpEL) is a powerful expression language
7-
provided by Spring Framework. The language offers many features
7+
provided by the Spring Framework. The language offers many features
88
including invocation of methods available in the JVM.
99
If a SpEL expression is built using attacker-controlled data,
1010
and then evaluated in a powerful context,
@@ -31,7 +31,7 @@ that doesn't allow arbitrary method invocation.
3131
<example>
3232
<p>
3333
The following example uses untrusted data to build a SpEL expression
34-
and then runs it in the default powerfull context.
34+
and then runs it in the default powerful context.
3535
</p>
3636
<sample src="UnsafeSpelExpressionEvaluation.java" />
3737

@@ -53,4 +53,4 @@ However, it's recommended to avoid using untrusted input in SpEL expressions.
5353
<a href="https://owasp.org/www-community/vulnerabilities/Expression_Language_Injection">Expression Language Injection</a>.
5454
</li>
5555
</references>
56-
</qhelp>
56+
</qhelp>

java/ql/src/experimental/Security/CWE/CWE-094/SpelInjection.ql renamed to java/ql/src/Security/CWE/CWE-094/SpelInjection.ql

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@
1111
*/
1212

1313
import java
14-
import SpelInjectionLib
14+
import semmle.code.java.security.SpelInjectionQuery
15+
import semmle.code.java.dataflow.DataFlow
1516
import DataFlow::PathGraph
1617

17-
from DataFlow::PathNode source, DataFlow::PathNode sink, ExpressionInjectionConfig conf
18+
from DataFlow::PathNode source, DataFlow::PathNode sink, SpelInjectionConfig conf
1819
where conf.hasFlowPath(source, sink)
1920
select sink.getNode(), source, sink, "SpEL injection from $@.", source.getNode(), "this user input"

java/ql/src/experimental/Security/CWE/CWE-094/SpelInjectionLib.qll

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

0 commit comments

Comments
 (0)