Skip to content

Commit 70081b6

Browse files
committed
Refactor MvelInjection.qll
1 parent 46faf68 commit 70081b6

File tree

4 files changed

+236
-235
lines changed

4 files changed

+236
-235
lines changed

java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ private module Frameworks {
9898
private import semmle.code.java.security.InformationLeak
9999
private import semmle.code.java.security.JexlInjectionSinkModels
100100
private import semmle.code.java.security.LdapInjection
101-
private import semmle.code.java.security.MvelInjectionSinkModels
101+
private import semmle.code.java.security.MvelInjection
102102
private import semmle.code.java.security.XPath
103103
private import semmle.code.java.frameworks.android.SQLite
104104
private import semmle.code.java.frameworks.Jdbc
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
/** Provides classes to reason about MVEL injection attacks. */
2+
3+
import java
4+
import semmle.code.java.dataflow.DataFlow
5+
import semmle.code.java.dataflow.ExternalFlow
6+
7+
/** A data flow sink for unvalidated user input that is used to construct MVEL expressions. */
8+
abstract class MvelEvaluationSink extends DataFlow::Node { }
9+
10+
/** A sanitizer that prevents MVEL injection attacks. */
11+
abstract class MvelInjectionSanitizer extends DataFlow::Node { }
12+
13+
/**
14+
* A unit class for adding additional taint steps.
15+
*
16+
* Extend this class to add additional taint steps that should apply to the `MvelInjectionFlowConfig`.
17+
*/
18+
class MvelInjectionAdditionalTaintStep extends Unit {
19+
/**
20+
* Holds if the step from `node1` to `node2` should be considered a taint
21+
* step for the `MvelInjectionFlowConfig` configuration.
22+
*/
23+
abstract predicate step(DataFlow::Node n1, DataFlow::Node n2);
24+
}
25+
26+
/** Default sink for MVEL injection vulnerabilities. */
27+
private class DefaultMvelEvaluationSink extends MvelEvaluationSink {
28+
DefaultMvelEvaluationSink() { sinkNode(this, "mvel") }
29+
}
30+
31+
private class DefaulMvelEvaluationSinkModel extends SinkModelCsv {
32+
override predicate row(string row) {
33+
row =
34+
[
35+
"javax.script;CompiledScript;false;eval;;;Argument[-1];mvel",
36+
"org.mvel2;MVEL;false;eval;;;Argument[0];mvel",
37+
"org.mvel2;MVEL;false;executeExpression;;;Argument[0];mvel",
38+
"org.mvel2;MVEL;false;evalToBoolean;;;Argument[0];mvel",
39+
"org.mvel2;MVEL;false;evalToString;;;Argument[0];mvel",
40+
"org.mvel2;MVEL;false;executeAllExpression;;;Argument[0];mvel",
41+
"org.mvel2;MVEL;false;executeSetExpression;;;Argument[0];mvel",
42+
"org.mvel2;MVELRuntime;false;execute;;;Argument[1];mvel",
43+
"org.mvel2.templates;TemplateRuntime;false;eval;;;Argument[0];mvel",
44+
"org.mvel2.templates;TemplateRuntime;false;execute;;;Argument[0];mvel",
45+
"org.mvel2.jsr223;MvelScriptEngine;false;eval;;;Argument[0];mvel",
46+
"org.mvel2.jsr223;MvelScriptEngine;false;evaluate;;;Argument[0];mvel",
47+
"org.mvel2.jsr223;MvelCompiledScript;false;eval;;;Argument[-1];mvel",
48+
"org.mvel2.compiler;ExecutableStatement;false;getValue;;;Argument[-1];mvel",
49+
"org.mvel2.compiler;CompiledExpression;false;getDirectValue;;;Argument[-1];mvel",
50+
"org.mvel2.compiler;CompiledAccExpression;false;getValue;;;Argument[-1];mvel",
51+
"org.mvel2.compiler;Accessor;false;getValue;;;Argument[-1];mvel"
52+
]
53+
}
54+
}
55+
56+
/** A default sanitizer that considers numeric and boolean typed data safe for building MVEL expressions */
57+
private class DefaultMvelInjectionSanitizer extends MvelInjectionSanitizer {
58+
DefaultMvelInjectionSanitizer() {
59+
this.getType() instanceof NumericType or this.getType() instanceof BooleanType
60+
}
61+
}
62+
63+
/** A set of additional taint steps to consider when taint tracking MVEL related data flows. */
64+
private class DefaultMvelInjectionAdditionalTaintStep extends MvelInjectionAdditionalTaintStep {
65+
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
66+
expressionCompilationStep(node1, node2) or
67+
createExpressionCompilerStep(node1, node2) or
68+
expressionCompilerCompileStep(node1, node2) or
69+
createCompiledAccExpressionStep(node1, node2) or
70+
scriptCompileStep(node1, node2) or
71+
createMvelCompiledScriptStep(node1, node2) or
72+
templateCompileStep(node1, node2) or
73+
createTemplateCompilerStep(node1, node2)
74+
}
75+
}
76+
77+
/**
78+
* Holds if `node1` to `node2` is a dataflow step that compiles a MVEL expression
79+
* by callilng `MVEL.compileExpression(tainted)`.
80+
*/
81+
private predicate expressionCompilationStep(DataFlow::Node node1, DataFlow::Node node2) {
82+
exists(StaticMethodAccess ma, Method m | ma.getMethod() = m |
83+
m.getDeclaringType() instanceof MVEL and
84+
m.hasName("compileExpression") and
85+
ma.getAnArgument() = node1.asExpr() and
86+
node2.asExpr() = ma
87+
)
88+
}
89+
90+
/**
91+
* Holds if `node1` to `node2` is a dataflow step that creates `ExpressionCompiler`
92+
* by calling `new ExpressionCompiler(tainted)`.
93+
*/
94+
private predicate createExpressionCompilerStep(DataFlow::Node node1, DataFlow::Node node2) {
95+
exists(ConstructorCall cc |
96+
cc.getConstructedType() instanceof ExpressionCompiler and
97+
cc = node2.asExpr() and
98+
cc.getArgument(0) = node1.asExpr()
99+
)
100+
}
101+
102+
/**
103+
* Holds if `node1` to `node2` is a dataflow step creates `CompiledAccExpression`
104+
* by calling `new CompiledAccExpression(tainted, ...)`.
105+
*/
106+
private predicate createCompiledAccExpressionStep(DataFlow::Node node1, DataFlow::Node node2) {
107+
exists(ConstructorCall cc |
108+
cc.getConstructedType() instanceof CompiledAccExpression and
109+
cc = node2.asExpr() and
110+
cc.getArgument(0) = node1.asExpr()
111+
)
112+
}
113+
114+
/**
115+
* Holds if `node1` to `node2` is a dataflow step that compiles a MVEL expression
116+
* by calling `ExpressionCompiler.compile()`.
117+
*/
118+
private predicate expressionCompilerCompileStep(DataFlow::Node node1, DataFlow::Node node2) {
119+
exists(MethodAccess ma, Method m | ma.getMethod() = m |
120+
m.getDeclaringType() instanceof ExpressionCompiler and
121+
m.hasName("compile") and
122+
ma = node2.asExpr() and
123+
ma.getQualifier() = node1.asExpr()
124+
)
125+
}
126+
127+
/**
128+
* Holds if `node1` to `node2` is a dataflow step that compiles a script via `MvelScriptEngine`
129+
* by calling `engine.compile(tainted)` or `engine.compiledScript(tainted)`.
130+
*/
131+
private predicate scriptCompileStep(DataFlow::Node node1, DataFlow::Node node2) {
132+
exists(MethodAccess ma, Method m | ma.getMethod() = m |
133+
m instanceof MvelScriptEngineCompilationMethod and
134+
ma = node2.asExpr() and
135+
ma.getArgument(0) = node1.asExpr()
136+
)
137+
}
138+
139+
/**
140+
* Holds if `node1` to `node2` is a dataflow step that creates `MvelCompiledScript`
141+
* by calling `new MvelCompiledScript(engine, tainted)`.
142+
*/
143+
private predicate createMvelCompiledScriptStep(DataFlow::Node node1, DataFlow::Node node2) {
144+
exists(ConstructorCall cc |
145+
cc.getConstructedType() instanceof MvelCompiledScript and
146+
cc = node2.asExpr() and
147+
cc.getArgument(1) = node1.asExpr()
148+
)
149+
}
150+
151+
/**
152+
* Holds if `node1` to `node2` is a dataflow step creates `TemplateCompiler`
153+
* by calling `new TemplateCompiler(tainted)`.
154+
*/
155+
private predicate createTemplateCompilerStep(DataFlow::Node node1, DataFlow::Node node2) {
156+
exists(ConstructorCall cc |
157+
cc.getConstructedType() instanceof TemplateCompiler and
158+
cc = node2.asExpr() and
159+
cc.getArgument(0) = node1.asExpr()
160+
)
161+
}
162+
163+
/**
164+
* Holds if `node1` to `node2` is a dataflow step that compiles a script via `TemplateCompiler`
165+
* by calling `compiler.compile()` or `TemplateCompiler.compileTemplate(tainted)`.
166+
*/
167+
private predicate templateCompileStep(DataFlow::Node node1, DataFlow::Node node2) {
168+
exists(MethodAccess ma, Method m | ma.getMethod() = m |
169+
m instanceof TemplateCompilerCompileMethod and
170+
ma.getQualifier() = node1.asExpr() and
171+
ma = node2.asExpr()
172+
)
173+
or
174+
exists(StaticMethodAccess ma, Method m | ma.getMethod() = m |
175+
m instanceof TemplateCompilerCompileTemplateMethod and
176+
ma = node2.asExpr() and
177+
ma.getArgument(0) = node1.asExpr()
178+
)
179+
}
180+
181+
/**
182+
* Methods in `MvelScriptEngine` that compile a MVEL expression.
183+
*/
184+
private class MvelScriptEngineCompilationMethod extends Method {
185+
MvelScriptEngineCompilationMethod() {
186+
getDeclaringType() instanceof MvelScriptEngine and
187+
(hasName("compile") or hasName("compiledScript"))
188+
}
189+
}
190+
191+
/**
192+
* `TemplateCompiler.compile()` method that compiles a MVEL template.
193+
*/
194+
private class TemplateCompilerCompileMethod extends Method {
195+
TemplateCompilerCompileMethod() {
196+
getDeclaringType() instanceof TemplateCompiler and
197+
hasName("compile")
198+
}
199+
}
200+
201+
/**
202+
* `TemplateCompiler.compileTemplate(tainted)` static method that compiles a MVEL template.
203+
*/
204+
private class TemplateCompilerCompileTemplateMethod extends Method {
205+
TemplateCompilerCompileTemplateMethod() {
206+
getDeclaringType() instanceof TemplateCompiler and
207+
hasName("compileTemplate")
208+
}
209+
}
210+
211+
private class MVEL extends RefType {
212+
MVEL() { hasQualifiedName("org.mvel2", "MVEL") }
213+
}
214+
215+
private class ExpressionCompiler extends RefType {
216+
ExpressionCompiler() { hasQualifiedName("org.mvel2.compiler", "ExpressionCompiler") }
217+
}
218+
219+
private class CompiledAccExpression extends RefType {
220+
CompiledAccExpression() { hasQualifiedName("org.mvel2.compiler", "CompiledAccExpression") }
221+
}
222+
223+
private class MvelScriptEngine extends RefType {
224+
MvelScriptEngine() { hasQualifiedName("org.mvel2.jsr223", "MvelScriptEngine") }
225+
}
226+
227+
private class MvelCompiledScript extends RefType {
228+
MvelCompiledScript() { hasQualifiedName("org.mvel2.jsr223", "MvelCompiledScript") }
229+
}
230+
231+
private class TemplateCompiler extends RefType {
232+
TemplateCompiler() { hasQualifiedName("org.mvel2.templates", "TemplateCompiler") }
233+
}

0 commit comments

Comments
 (0)