diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/MvelInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-094/MvelInjection.qhelp new file mode 100644 index 000000000000..d68d298b5f5a --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/MvelInjection.qhelp @@ -0,0 +1,38 @@ + + + + +

+MVEL is an expression language based on Java-syntax. +The language offers many features +including invocation of methods available in the JVM. +If a MVEL expression is built using attacker-controlled data, +and then evaluated, then it may allow the attacker to run arbitrary code. +

+
+ + +

+Including user input in a MVEL expression should be avoided. +

+
+ + +

+The following example uses untrusted data to build a MVEL expression +and then runs it in the default powerfull context. +

+ +
+ + +
  • + MVEL Documentation: + Language Guide for 2.0. +
  • +
  • + OWASP: + Expression Language Injection. +
  • +
    +
    \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/MvelInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/MvelInjection.ql new file mode 100644 index 000000000000..d32c33c343c3 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/MvelInjection.ql @@ -0,0 +1,19 @@ +/** + * @name Expression language injection (MVEL) + * @description Evaluation of a user-controlled MVEL expression + * may lead to remote code execution. + * @kind path-problem + * @problem.severity error + * @precision high + * @id java/mvel-expression-injection + * @tags security + * external/cwe/cwe-094 + */ + +import java +import MvelInjectionLib +import DataFlow::PathGraph + +from DataFlow::PathNode source, DataFlow::PathNode sink, MvelInjectionConfig conf +where conf.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "MVEL injection from $@.", source.getNode(), "this user input" diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/MvelInjectionLib.qll b/java/ql/src/experimental/Security/CWE/CWE-094/MvelInjectionLib.qll new file mode 100644 index 000000000000..a6cf891330f0 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/MvelInjectionLib.qll @@ -0,0 +1,367 @@ +import java +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.dataflow.TaintTracking + +/** + * A taint-tracking configuration for unsafe user input + * that is used to construct and evaluate a MVEL expression. + */ +class MvelInjectionConfig extends TaintTracking::Configuration { + MvelInjectionConfig() { this = "MvelInjectionConfig" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof MvelEvaluationSink } + + override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { + expressionCompilationStep(node1, node2) or + createExpressionCompilerStep(node1, node2) or + expressionCompilerCompileStep(node1, node2) or + createCompiledAccExpressionStep(node1, node2) or + scriptCompileStep(node1, node2) or + createMvelCompiledScriptStep(node1, node2) or + templateCompileStep(node1, node2) or + createTemplateCompilerStep(node1, node2) + } +} + +/** + * A sink for EL injection vulnerabilities via MVEL, + * i.e. methods that run evaluation of a MVEL expression. + */ +class MvelEvaluationSink extends DataFlow::ExprNode { + MvelEvaluationSink() { + exists(StaticMethodAccess ma, Method m | m = ma.getMethod() | + ( + m instanceof MvelEvalMethod or + m instanceof TemplateRuntimeEvaluationMethod + ) and + ma.getArgument(0) = asExpr() + ) + or + exists(MethodAccess ma, Method m | m = ma.getMethod() | + m instanceof MvelScriptEngineEvaluationMethod and + ma.getArgument(0) = asExpr() + ) + or + exists(MethodAccess ma, Method m | m = ma.getMethod() | + ( + m instanceof ExecutableStatementEvaluationMethod or + m instanceof CompiledExpressionEvaluationMethod or + m instanceof CompiledAccExpressionEvaluationMethod or + m instanceof AccessorEvaluationMethod or + m instanceof CompiledScriptEvaluationMethod or + m instanceof MvelCompiledScriptEvaluationMethod + ) and + ma.getQualifier() = asExpr() + ) + or + exists(StaticMethodAccess ma, Method m | m = ma.getMethod() | + m instanceof MvelRuntimeEvaluationMethod and + ma.getArgument(1) = asExpr() + ) + } +} + +/** + * Holds if `node1` to `node2` is a dataflow step that compiles a MVEL expression + * by callilng `MVEL.compileExpression(tainted)`. + */ +predicate expressionCompilationStep(DataFlow::Node node1, DataFlow::Node node2) { + exists(StaticMethodAccess ma, Method m | ma.getMethod() = m | + m.getDeclaringType() instanceof MVEL and + m.hasName("compileExpression") and + ma.getAnArgument() = node1.asExpr() and + node2.asExpr() = ma + ) +} + +/** + * Holds if `node1` to `node2` is a dataflow step creates `ExpressionCompiler`, + * i.e. `new ExpressionCompiler(tainted)`. + */ +predicate createExpressionCompilerStep(DataFlow::Node node1, DataFlow::Node node2) { + exists(ConstructorCall cc | + cc.getConstructedType() instanceof ExpressionCompiler and + cc = node2.asExpr() and + cc.getArgument(0) = node1.asExpr() + ) +} + +/** + * Holds if `node1` to `node2` is a dataflow step creates `CompiledAccExpression`, + * i.e. `new CompiledAccExpression(tainted, ...)`. + */ +predicate createCompiledAccExpressionStep(DataFlow::Node node1, DataFlow::Node node2) { + exists(ConstructorCall cc | + cc.getConstructedType() instanceof CompiledAccExpression and + cc = node2.asExpr() and + cc.getArgument(0) = node1.asExpr() + ) +} + +/** + * Holds if `node1` to `node2` is a dataflow step that compiles a MVEL expression + * by calling `ExpressionCompiler.compile()`. + */ +predicate expressionCompilerCompileStep(DataFlow::Node node1, DataFlow::Node node2) { + exists(MethodAccess ma, Method m | ma.getMethod() = m | + m.getDeclaringType() instanceof ExpressionCompiler and + m.hasName("compile") and + ma = node2.asExpr() and + ma.getQualifier() = node1.asExpr() + ) +} + +/** + * Holds if `node1` to `node2` is a dataflow step that compiles a script via `MvelScriptEngine`, + * i.e. `engine.compile(tainted)` or `engine.compiledScript(tainted)`. + */ +predicate scriptCompileStep(DataFlow::Node node1, DataFlow::Node node2) { + exists(MethodAccess ma, Method m | ma.getMethod() = m | + m instanceof MvelScriptEngineCompilationMethod and + ma = node2.asExpr() and + ma.getArgument(0) = node1.asExpr() + ) +} + +/** + * Holds if `node1` to `node2` is a dataflow step creates `MvelCompiledScript`, + * i.e. `new MvelCompiledScript(engine, tainted)`. + */ +predicate createMvelCompiledScriptStep(DataFlow::Node node1, DataFlow::Node node2) { + exists(ConstructorCall cc | + cc.getConstructedType() instanceof MvelCompiledScript and + cc = node2.asExpr() and + cc.getArgument(1) = node1.asExpr() + ) +} + +/** + * Holds if `node1` to `node2` is a dataflow step creates `TemplateCompiler`, + * i.e. `new TemplateCompiler(tainted)`. + */ +predicate createTemplateCompilerStep(DataFlow::Node node1, DataFlow::Node node2) { + exists(ConstructorCall cc | + cc.getConstructedType() instanceof TemplateCompiler and + cc = node2.asExpr() and + cc.getArgument(0) = node1.asExpr() + ) +} + +/** + * Holds if `node1` to `node2` is a dataflow step that compiles a script via `TemplateCompiler`, + * i.e. `compiler.compile()` or `TemplateCompiler.compileTemplate(tainted)`. + */ +predicate templateCompileStep(DataFlow::Node node1, DataFlow::Node node2) { + exists(MethodAccess ma, Method m | ma.getMethod() = m | + m instanceof TemplateCompilerCompileMethod and + ma.getQualifier() = node1.asExpr() and + ma = node2.asExpr() + ) + or + exists(StaticMethodAccess ma, Method m | ma.getMethod() = m | + m instanceof TemplateCompilerCompileTemplateMethod and + ma = node2.asExpr() and + ma.getArgument(0) = node1.asExpr() + ) +} + +/** + * Methods in the MVEL class that evaluate a MVEL expression. + */ +class MvelEvalMethod extends Method { + MvelEvalMethod() { + getDeclaringType() instanceof MVEL and + ( + hasName("eval") or + hasName("executeExpression") or + hasName("evalToBoolean") or + hasName("evalToString") or + hasName("executeAllExpression") or + hasName("executeSetExpression") + ) + } +} + +/** + * Methods in `MVEL` class that compile a MVEL expression. + */ +class MvelCompileExpressionMethod extends Method { + MvelCompileExpressionMethod() { + getDeclaringType() instanceof MVEL and + ( + hasName("compileExpression") or + hasName("compileGetExpression") or + hasName("compileSetExpression") + ) + } +} + +/** + * Methods in `ExecutableStatement` that evaluate a MVEL expression. + */ +class ExecutableStatementEvaluationMethod extends Method { + ExecutableStatementEvaluationMethod() { + getDeclaringType() instanceof ExecutableStatement and + hasName("getValue") + } +} + +/** + * Methods in `CompiledExpression` that evaluate a MVEL expression. + */ +class CompiledExpressionEvaluationMethod extends Method { + CompiledExpressionEvaluationMethod() { + getDeclaringType() instanceof CompiledExpression and + hasName("getDirectValue") + } +} + +/** + * Methods in `CompiledAccExpression` that evaluate a MVEL expression. + */ +class CompiledAccExpressionEvaluationMethod extends Method { + CompiledAccExpressionEvaluationMethod() { + getDeclaringType() instanceof CompiledAccExpression and + hasName("getValue") + } +} + +/** + * Methods in `Accessor` that evaluate a MVEL expression. + */ +class AccessorEvaluationMethod extends Method { + AccessorEvaluationMethod() { + getDeclaringType() instanceof Accessor and + hasName("getValue") + } +} + +/** + * Methods in `MvelScriptEngine` that evaluate a MVEL expression. + */ +class MvelScriptEngineEvaluationMethod extends Method { + MvelScriptEngineEvaluationMethod() { + getDeclaringType() instanceof MvelScriptEngine and + (hasName("eval") or hasName("evaluate")) + } +} + +/** + * Methods in `MvelScriptEngine` that compile a MVEL expression. + */ +class MvelScriptEngineCompilationMethod extends Method { + MvelScriptEngineCompilationMethod() { + getDeclaringType() instanceof MvelScriptEngine and + (hasName("compile") or hasName("compiledScript")) + } +} + +/** + * Methods in `CompiledScript` that evaluate a MVEL expression. + */ +class CompiledScriptEvaluationMethod extends Method { + CompiledScriptEvaluationMethod() { + getDeclaringType() instanceof CompiledScript and + hasName("eval") + } +} + +/** + * Methods in `TemplateRuntime` that evaluate a MVEL template. + */ +class TemplateRuntimeEvaluationMethod extends Method { + TemplateRuntimeEvaluationMethod() { + getDeclaringType() instanceof TemplateRuntime and + (hasName("eval") or hasName("execute")) + } +} + +/** + * `TemplateCompiler.compile()` method compiles a MVEL template. + */ +class TemplateCompilerCompileMethod extends Method { + TemplateCompilerCompileMethod() { + getDeclaringType() instanceof TemplateCompiler and + hasName("compile") + } +} + +/** + * `TemplateCompiler.compileTemplate(tainted)` static method compiles a MVEL template. + */ +class TemplateCompilerCompileTemplateMethod extends Method { + TemplateCompilerCompileTemplateMethod() { + getDeclaringType() instanceof TemplateCompiler and + hasName("compileTemplate") + } +} + +/** + * Methods in `MvelCompiledScript` that evaluate a MVEL expression. + */ +class MvelCompiledScriptEvaluationMethod extends Method { + MvelCompiledScriptEvaluationMethod() { + getDeclaringType() instanceof MvelCompiledScript and + hasName("eval") + } +} + +/** + * Methods in `MVELRuntime` that evaluate a MVEL expression. + */ +class MvelRuntimeEvaluationMethod extends Method { + MvelRuntimeEvaluationMethod() { + getDeclaringType() instanceof MVELRuntime and + hasName("execute") + } +} + +class MVEL extends RefType { + MVEL() { hasQualifiedName("org.mvel2", "MVEL") } +} + +class ExpressionCompiler extends RefType { + ExpressionCompiler() { hasQualifiedName("org.mvel2.compiler", "ExpressionCompiler") } +} + +class ExecutableStatement extends RefType { + ExecutableStatement() { hasQualifiedName("org.mvel2.compiler", "ExecutableStatement") } +} + +class CompiledExpression extends RefType { + CompiledExpression() { hasQualifiedName("org.mvel2.compiler", "CompiledExpression") } +} + +class CompiledAccExpression extends RefType { + CompiledAccExpression() { hasQualifiedName("org.mvel2.compiler", "CompiledAccExpression") } +} + +class Accessor extends RefType { + Accessor() { hasQualifiedName("org.mvel2.compiler", "Accessor") } +} + +class CompiledScript extends RefType { + CompiledScript() { hasQualifiedName("javax.script", "CompiledScript") } +} + +class MvelScriptEngine extends RefType { + MvelScriptEngine() { hasQualifiedName("org.mvel2.jsr223", "MvelScriptEngine") } +} + +class MvelCompiledScript extends RefType { + MvelCompiledScript() { hasQualifiedName("org.mvel2.jsr223", "MvelCompiledScript") } +} + +class TemplateRuntime extends RefType { + TemplateRuntime() { hasQualifiedName("org.mvel2.templates", "TemplateRuntime") } +} + +class TemplateCompiler extends RefType { + TemplateCompiler() { hasQualifiedName("org.mvel2.templates", "TemplateCompiler") } +} + +class MVELRuntime extends RefType { + MVELRuntime() { hasQualifiedName("org.mvel2", "MVELRuntime") } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/UnsafeMvelExpressionEvaluation.java b/java/ql/src/experimental/Security/CWE/CWE-094/UnsafeMvelExpressionEvaluation.java new file mode 100644 index 000000000000..4942bee79f6d --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/UnsafeMvelExpressionEvaluation.java @@ -0,0 +1,8 @@ +public void evaluate(Socket socket) throws IOException { + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(socket.getInputStream()))) { + + String expression = reader.readLine(); + MVEL.eval(expression); + } +} \ No newline at end of file diff --git a/java/ql/test/experimental/Security/CWE/CWE-094/MvelInjection.expected b/java/ql/test/experimental/Security/CWE/CWE-094/MvelInjection.expected new file mode 100644 index 000000000000..b2325fa78a42 --- /dev/null +++ b/java/ql/test/experimental/Security/CWE/CWE-094/MvelInjection.expected @@ -0,0 +1,63 @@ +edges +| MvelInjection.java:29:54:29:65 | read(...) : String | MvelInjection.java:30:28:30:37 | expression | +| MvelInjection.java:34:58:34:69 | read(...) : String | MvelInjection.java:36:5:36:13 | statement | +| MvelInjection.java:34:58:34:69 | read(...) : String | MvelInjection.java:37:5:37:13 | statement | +| MvelInjection.java:41:58:41:69 | read(...) : String | MvelInjection.java:43:5:43:14 | expression | +| MvelInjection.java:48:7:48:18 | read(...) : String | MvelInjection.java:49:5:49:14 | expression | +| MvelInjection.java:53:20:53:31 | read(...) : String | MvelInjection.java:57:5:57:18 | compiledScript | +| MvelInjection.java:53:20:53:31 | read(...) : String | MvelInjection.java:60:21:60:26 | script | +| MvelInjection.java:65:58:65:69 | read(...) : String | MvelInjection.java:68:5:68:10 | script | +| MvelInjection.java:77:40:77:51 | read(...) : String | MvelInjection.java:77:7:77:52 | compileTemplate(...) | +| MvelInjection.java:81:54:81:65 | read(...) : String | MvelInjection.java:82:29:82:46 | compile(...) | +| MvelInjection.java:86:58:86:69 | read(...) : String | MvelInjection.java:88:32:88:41 | expression | +| MvelInjection.java:92:27:92:49 | getInputStream(...) : InputStream | MvelInjection.java:95:14:95:36 | new String(...) : String | +| MvelInjection.java:95:14:95:36 | new String(...) : String | MvelInjection.java:25:15:25:26 | read(...) | +| MvelInjection.java:95:14:95:36 | new String(...) : String | MvelInjection.java:29:54:29:65 | read(...) : String | +| MvelInjection.java:95:14:95:36 | new String(...) : String | MvelInjection.java:34:58:34:69 | read(...) : String | +| MvelInjection.java:95:14:95:36 | new String(...) : String | MvelInjection.java:41:58:41:69 | read(...) : String | +| MvelInjection.java:95:14:95:36 | new String(...) : String | MvelInjection.java:48:7:48:18 | read(...) : String | +| MvelInjection.java:95:14:95:36 | new String(...) : String | MvelInjection.java:53:20:53:31 | read(...) : String | +| MvelInjection.java:95:14:95:36 | new String(...) : String | MvelInjection.java:65:58:65:69 | read(...) : String | +| MvelInjection.java:95:14:95:36 | new String(...) : String | MvelInjection.java:72:26:72:37 | read(...) | +| MvelInjection.java:95:14:95:36 | new String(...) : String | MvelInjection.java:77:40:77:51 | read(...) : String | +| MvelInjection.java:95:14:95:36 | new String(...) : String | MvelInjection.java:81:54:81:65 | read(...) : String | +| MvelInjection.java:95:14:95:36 | new String(...) : String | MvelInjection.java:86:58:86:69 | read(...) : String | +nodes +| MvelInjection.java:25:15:25:26 | read(...) | semmle.label | read(...) | +| MvelInjection.java:29:54:29:65 | read(...) : String | semmle.label | read(...) : String | +| MvelInjection.java:30:28:30:37 | expression | semmle.label | expression | +| MvelInjection.java:34:58:34:69 | read(...) : String | semmle.label | read(...) : String | +| MvelInjection.java:36:5:36:13 | statement | semmle.label | statement | +| MvelInjection.java:37:5:37:13 | statement | semmle.label | statement | +| MvelInjection.java:41:58:41:69 | read(...) : String | semmle.label | read(...) : String | +| MvelInjection.java:43:5:43:14 | expression | semmle.label | expression | +| MvelInjection.java:48:7:48:18 | read(...) : String | semmle.label | read(...) : String | +| MvelInjection.java:49:5:49:14 | expression | semmle.label | expression | +| MvelInjection.java:53:20:53:31 | read(...) : String | semmle.label | read(...) : String | +| MvelInjection.java:57:5:57:18 | compiledScript | semmle.label | compiledScript | +| MvelInjection.java:60:21:60:26 | script | semmle.label | script | +| MvelInjection.java:65:58:65:69 | read(...) : String | semmle.label | read(...) : String | +| MvelInjection.java:68:5:68:10 | script | semmle.label | script | +| MvelInjection.java:72:26:72:37 | read(...) | semmle.label | read(...) | +| MvelInjection.java:77:7:77:52 | compileTemplate(...) | semmle.label | compileTemplate(...) | +| MvelInjection.java:77:40:77:51 | read(...) : String | semmle.label | read(...) : String | +| MvelInjection.java:81:54:81:65 | read(...) : String | semmle.label | read(...) : String | +| MvelInjection.java:82:29:82:46 | compile(...) | semmle.label | compile(...) | +| MvelInjection.java:86:58:86:69 | read(...) : String | semmle.label | read(...) : String | +| MvelInjection.java:88:32:88:41 | expression | semmle.label | expression | +| MvelInjection.java:92:27:92:49 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream | +| MvelInjection.java:95:14:95:36 | new String(...) : String | semmle.label | new String(...) : String | +#select +| MvelInjection.java:25:15:25:26 | read(...) | MvelInjection.java:92:27:92:49 | getInputStream(...) : InputStream | MvelInjection.java:25:15:25:26 | read(...) | MVEL injection from $@. | MvelInjection.java:92:27:92:49 | getInputStream(...) | this user input | +| MvelInjection.java:30:28:30:37 | expression | MvelInjection.java:92:27:92:49 | getInputStream(...) : InputStream | MvelInjection.java:30:28:30:37 | expression | MVEL injection from $@. | MvelInjection.java:92:27:92:49 | getInputStream(...) | this user input | +| MvelInjection.java:36:5:36:13 | statement | MvelInjection.java:92:27:92:49 | getInputStream(...) : InputStream | MvelInjection.java:36:5:36:13 | statement | MVEL injection from $@. | MvelInjection.java:92:27:92:49 | getInputStream(...) | this user input | +| MvelInjection.java:37:5:37:13 | statement | MvelInjection.java:92:27:92:49 | getInputStream(...) : InputStream | MvelInjection.java:37:5:37:13 | statement | MVEL injection from $@. | MvelInjection.java:92:27:92:49 | getInputStream(...) | this user input | +| MvelInjection.java:43:5:43:14 | expression | MvelInjection.java:92:27:92:49 | getInputStream(...) : InputStream | MvelInjection.java:43:5:43:14 | expression | MVEL injection from $@. | MvelInjection.java:92:27:92:49 | getInputStream(...) | this user input | +| MvelInjection.java:49:5:49:14 | expression | MvelInjection.java:92:27:92:49 | getInputStream(...) : InputStream | MvelInjection.java:49:5:49:14 | expression | MVEL injection from $@. | MvelInjection.java:92:27:92:49 | getInputStream(...) | this user input | +| MvelInjection.java:57:5:57:18 | compiledScript | MvelInjection.java:92:27:92:49 | getInputStream(...) : InputStream | MvelInjection.java:57:5:57:18 | compiledScript | MVEL injection from $@. | MvelInjection.java:92:27:92:49 | getInputStream(...) | this user input | +| MvelInjection.java:60:21:60:26 | script | MvelInjection.java:92:27:92:49 | getInputStream(...) : InputStream | MvelInjection.java:60:21:60:26 | script | MVEL injection from $@. | MvelInjection.java:92:27:92:49 | getInputStream(...) | this user input | +| MvelInjection.java:68:5:68:10 | script | MvelInjection.java:92:27:92:49 | getInputStream(...) : InputStream | MvelInjection.java:68:5:68:10 | script | MVEL injection from $@. | MvelInjection.java:92:27:92:49 | getInputStream(...) | this user input | +| MvelInjection.java:72:26:72:37 | read(...) | MvelInjection.java:92:27:92:49 | getInputStream(...) : InputStream | MvelInjection.java:72:26:72:37 | read(...) | MVEL injection from $@. | MvelInjection.java:92:27:92:49 | getInputStream(...) | this user input | +| MvelInjection.java:77:7:77:52 | compileTemplate(...) | MvelInjection.java:92:27:92:49 | getInputStream(...) : InputStream | MvelInjection.java:77:7:77:52 | compileTemplate(...) | MVEL injection from $@. | MvelInjection.java:92:27:92:49 | getInputStream(...) | this user input | +| MvelInjection.java:82:29:82:46 | compile(...) | MvelInjection.java:92:27:92:49 | getInputStream(...) : InputStream | MvelInjection.java:82:29:82:46 | compile(...) | MVEL injection from $@. | MvelInjection.java:92:27:92:49 | getInputStream(...) | this user input | +| MvelInjection.java:88:32:88:41 | expression | MvelInjection.java:92:27:92:49 | getInputStream(...) : InputStream | MvelInjection.java:88:32:88:41 | expression | MVEL injection from $@. | MvelInjection.java:92:27:92:49 | getInputStream(...) | this user input | diff --git a/java/ql/test/experimental/Security/CWE/CWE-094/MvelInjection.java b/java/ql/test/experimental/Security/CWE/CWE-094/MvelInjection.java new file mode 100644 index 000000000000..88f9b861caee --- /dev/null +++ b/java/ql/test/experimental/Security/CWE/CWE-094/MvelInjection.java @@ -0,0 +1,98 @@ +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.net.Socket; +import java.util.HashMap; +import javax.script.CompiledScript; +import javax.script.SimpleScriptContext; +import org.mvel2.MVEL; +import org.mvel2.MVELRuntime; +import org.mvel2.ParserContext; +import org.mvel2.compiler.CompiledAccExpression; +import org.mvel2.compiler.CompiledExpression; +import org.mvel2.compiler.ExecutableStatement; +import org.mvel2.compiler.ExpressionCompiler; +import org.mvel2.integration.impl.ImmutableDefaultFactory; +import org.mvel2.jsr223.MvelCompiledScript; +import org.mvel2.jsr223.MvelScriptEngine; +import org.mvel2.templates.CompiledTemplate; +import org.mvel2.templates.TemplateCompiler; +import org.mvel2.templates.TemplateRuntime; + +public class MvelInjection { + + public static void testWithMvelEval(Socket socket) throws IOException { + MVEL.eval(read(socket)); + } + + public static void testWithMvelCompileAndExecute(Socket socket) throws IOException { + Serializable expression = MVEL.compileExpression(read(socket)); + MVEL.executeExpression(expression); + } + + public static void testWithExpressionCompiler(Socket socket) throws IOException { + ExpressionCompiler compiler = new ExpressionCompiler(read(socket)); + ExecutableStatement statement = compiler.compile(); + statement.getValue(new Object(), new ImmutableDefaultFactory()); + statement.getValue(new Object(), new Object(), new ImmutableDefaultFactory()); + } + + public static void testWithCompiledExpressionGetDirectValue(Socket socket) throws IOException { + ExpressionCompiler compiler = new ExpressionCompiler(read(socket)); + CompiledExpression expression = compiler.compile(); + expression.getDirectValue(new Object(), new ImmutableDefaultFactory()); + } + + public static void testCompiledAccExpressionGetValue(Socket socket) throws IOException { + CompiledAccExpression expression = new CompiledAccExpression( + read(socket).toCharArray(), Object.class, new ParserContext()); + expression.getValue(new Object(), new ImmutableDefaultFactory()); + } + + public static void testMvelScriptEngineCompileAndEvaluate(Socket socket) throws Exception { + String input = read(socket); + + MvelScriptEngine engine = new MvelScriptEngine(); + CompiledScript compiledScript = engine.compile(input); + compiledScript.eval(); + + Serializable script = engine.compiledScript(input); + engine.evaluate(script, new SimpleScriptContext()); + } + + public static void testMvelCompiledScriptCompileAndEvaluate(Socket socket) throws Exception { + MvelScriptEngine engine = new MvelScriptEngine(); + ExpressionCompiler compiler = new ExpressionCompiler(read(socket)); + ExecutableStatement statement = compiler.compile(); + MvelCompiledScript script = new MvelCompiledScript(engine, statement); + script.eval(new SimpleScriptContext()); + } + + public static void testTemplateRuntimeEval(Socket socket) throws Exception { + TemplateRuntime.eval(read(socket), new HashMap()); + } + + public static void testTemplateRuntimeCompileTemplateAndExecute(Socket socket) throws Exception { + TemplateRuntime.execute( + TemplateCompiler.compileTemplate(read(socket)), new HashMap()); + } + + public static void testTemplateRuntimeCompileAndExecute(Socket socket) throws Exception { + TemplateCompiler compiler = new TemplateCompiler(read(socket)); + TemplateRuntime.execute(compiler.compile(), new HashMap()); + } + + public static void testMvelRuntimeExecute(Socket socket) throws Exception { + ExpressionCompiler compiler = new ExpressionCompiler(read(socket)); + CompiledExpression expression = compiler.compile(); + MVELRuntime.execute(false, expression, new Object(), new ImmutableDefaultFactory()); + } + + public static String read(Socket socket) throws IOException { + try (InputStream is = socket.getInputStream()) { + byte[] bytes = new byte[1024]; + int n = is.read(bytes); + return new String(bytes, 0, n); + } + } +} diff --git a/java/ql/test/experimental/Security/CWE/CWE-094/MvelInjection.qlref b/java/ql/test/experimental/Security/CWE/CWE-094/MvelInjection.qlref new file mode 100644 index 000000000000..13d7cbd22950 --- /dev/null +++ b/java/ql/test/experimental/Security/CWE/CWE-094/MvelInjection.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-094/MvelInjection.ql \ No newline at end of file diff --git a/java/ql/test/experimental/Security/CWE/CWE-094/options b/java/ql/test/experimental/Security/CWE/CWE-094/options index 31b8e3f69351..51fae354cec3 100644 --- a/java/ql/test/experimental/Security/CWE/CWE-094/options +++ b/java/ql/test/experimental/Security/CWE/CWE-094/options @@ -1 +1 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3 \ No newline at end of file +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../../stubs/mvel2-2.4.7:${testdir}/../../../../stubs/jsr223-api diff --git a/java/ql/test/stubs/jsr223-api/javax/script/CompiledScript.java b/java/ql/test/stubs/jsr223-api/javax/script/CompiledScript.java new file mode 100644 index 000000000000..dba6c9f88d24 --- /dev/null +++ b/java/ql/test/stubs/jsr223-api/javax/script/CompiledScript.java @@ -0,0 +1,5 @@ +package javax.script; + +public class CompiledScript { + public Object eval() throws ScriptException { return null; } +} \ No newline at end of file diff --git a/java/ql/test/stubs/jsr223-api/javax/script/ScriptContext.java b/java/ql/test/stubs/jsr223-api/javax/script/ScriptContext.java new file mode 100644 index 000000000000..1e587a0a9bb5 --- /dev/null +++ b/java/ql/test/stubs/jsr223-api/javax/script/ScriptContext.java @@ -0,0 +1,3 @@ +package javax.script; + +public interface ScriptContext {} \ No newline at end of file diff --git a/java/ql/test/stubs/jsr223-api/javax/script/ScriptException.java b/java/ql/test/stubs/jsr223-api/javax/script/ScriptException.java new file mode 100644 index 000000000000..65d124925c9d --- /dev/null +++ b/java/ql/test/stubs/jsr223-api/javax/script/ScriptException.java @@ -0,0 +1,3 @@ +package javax.script; + +public class ScriptException extends Exception {} diff --git a/java/ql/test/stubs/jsr223-api/javax/script/SimpleScriptContext.java b/java/ql/test/stubs/jsr223-api/javax/script/SimpleScriptContext.java new file mode 100644 index 000000000000..b292cb8b9a6b --- /dev/null +++ b/java/ql/test/stubs/jsr223-api/javax/script/SimpleScriptContext.java @@ -0,0 +1,3 @@ +package javax.script; + +public class SimpleScriptContext implements ScriptContext {} \ No newline at end of file diff --git a/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/MVEL.java b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/MVEL.java new file mode 100644 index 000000000000..3d0c39e724ff --- /dev/null +++ b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/MVEL.java @@ -0,0 +1,9 @@ +package org.mvel2; + +import java.io.Serializable; + +public class MVEL { + public static Object eval(String expression) { return null; } + public static Serializable compileExpression(String expression) { return null; } + public static Object executeExpression(Object compiledExpression) { return null; } +} \ No newline at end of file diff --git a/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/MVELRuntime.java b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/MVELRuntime.java new file mode 100644 index 000000000000..30fef49f0b88 --- /dev/null +++ b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/MVELRuntime.java @@ -0,0 +1,8 @@ +package org.mvel2; + +import org.mvel2.compiler.CompiledExpression; +import org.mvel2.integration.VariableResolverFactory; + +public class MVELRuntime { + public static Object execute(boolean debugger, CompiledExpression expression, Object ctx, VariableResolverFactory variableFactory) { return null; } +} \ No newline at end of file diff --git a/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/ParserContext.java b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/ParserContext.java new file mode 100644 index 000000000000..ce160eec2288 --- /dev/null +++ b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/ParserContext.java @@ -0,0 +1,3 @@ +package org.mvel2; + +public class ParserContext {} \ No newline at end of file diff --git a/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/compiler/Accessor.java b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/compiler/Accessor.java new file mode 100644 index 000000000000..5c01ab6366da --- /dev/null +++ b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/compiler/Accessor.java @@ -0,0 +1,7 @@ +package org.mvel2.compiler; + +import org.mvel2.integration.VariableResolverFactory; + +public interface Accessor { + public Object getValue(Object ctx, Object elCtx, VariableResolverFactory factory); +} \ No newline at end of file diff --git a/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/compiler/CompiledAccExpression.java b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/compiler/CompiledAccExpression.java new file mode 100644 index 000000000000..88630888e67e --- /dev/null +++ b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/compiler/CompiledAccExpression.java @@ -0,0 +1,10 @@ +package org.mvel2.compiler; + +import org.mvel2.ParserContext; +import org.mvel2.integration.VariableResolverFactory; + +public class CompiledAccExpression implements ExecutableStatement { + public CompiledAccExpression(char[] expression, Class ingressType, ParserContext context) {} + public Object getValue(Object staticContext, VariableResolverFactory factory) { return null; } + public Object getValue(Object ctx, Object elCtx, VariableResolverFactory variableFactory) { return null; } +} diff --git a/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/compiler/CompiledExpression.java b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/compiler/CompiledExpression.java new file mode 100644 index 000000000000..b8a887b4f0db --- /dev/null +++ b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/compiler/CompiledExpression.java @@ -0,0 +1,9 @@ +package org.mvel2.compiler; + +import org.mvel2.integration.VariableResolverFactory; + +public class CompiledExpression implements ExecutableStatement { + public Object getDirectValue(Object staticContext, VariableResolverFactory factory) { return null; } + public Object getValue(Object staticContext, VariableResolverFactory factory) { return null; } + public Object getValue(Object ctx, Object elCtx, VariableResolverFactory factory) { return null; } +} \ No newline at end of file diff --git a/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/compiler/ExecutableStatement.java b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/compiler/ExecutableStatement.java new file mode 100644 index 000000000000..8aed7035aa67 --- /dev/null +++ b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/compiler/ExecutableStatement.java @@ -0,0 +1,8 @@ +package org.mvel2.compiler; + +import java.io.Serializable; +import org.mvel2.integration.VariableResolverFactory; + +public interface ExecutableStatement extends Accessor, Serializable { + public Object getValue(Object staticContext, VariableResolverFactory factory); +} \ No newline at end of file diff --git a/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/compiler/ExpressionCompiler.java b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/compiler/ExpressionCompiler.java new file mode 100644 index 000000000000..3f22268b891b --- /dev/null +++ b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/compiler/ExpressionCompiler.java @@ -0,0 +1,6 @@ +package org.mvel2.compiler; + +public class ExpressionCompiler { + public ExpressionCompiler(String expression) {} + public CompiledExpression compile() { return null; } +} \ No newline at end of file diff --git a/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/integration/VariableResolverFactory.java b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/integration/VariableResolverFactory.java new file mode 100644 index 000000000000..1fb643374b68 --- /dev/null +++ b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/integration/VariableResolverFactory.java @@ -0,0 +1,5 @@ +package org.mvel2.integration; + +import java.io.Serializable; + +public interface VariableResolverFactory extends Serializable {} \ No newline at end of file diff --git a/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/integration/impl/ImmutableDefaultFactory.java b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/integration/impl/ImmutableDefaultFactory.java new file mode 100644 index 000000000000..97463a9e9d3d --- /dev/null +++ b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/integration/impl/ImmutableDefaultFactory.java @@ -0,0 +1,5 @@ +package org.mvel2.integration.impl; + +import org.mvel2.integration.VariableResolverFactory; + +public class ImmutableDefaultFactory implements VariableResolverFactory {} \ No newline at end of file diff --git a/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/jsr223/MvelCompiledScript.java b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/jsr223/MvelCompiledScript.java new file mode 100644 index 000000000000..a4be37ada323 --- /dev/null +++ b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/jsr223/MvelCompiledScript.java @@ -0,0 +1,11 @@ +package org.mvel2.jsr223; + +import java.io.Serializable; +import javax.script.CompiledScript; +import javax.script.ScriptContext; +import javax.script.ScriptException; + +public class MvelCompiledScript extends CompiledScript { + public MvelCompiledScript(MvelScriptEngine engine, Serializable compiledScript) {} + public Object eval(ScriptContext context) throws ScriptException { return null; } +} \ No newline at end of file diff --git a/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/jsr223/MvelScriptEngine.java b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/jsr223/MvelScriptEngine.java new file mode 100644 index 000000000000..6769a6198a4e --- /dev/null +++ b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/jsr223/MvelScriptEngine.java @@ -0,0 +1,12 @@ +package org.mvel2.jsr223; + +import java.io.Serializable; +import javax.script.CompiledScript; +import javax.script.ScriptContext; +import javax.script.ScriptException; + +public class MvelScriptEngine { + public CompiledScript compile(String script) throws ScriptException { return null; } + public Serializable compiledScript(String script) throws ScriptException { return null; } + public Object evaluate(Serializable expression, ScriptContext context) throws ScriptException { return null; } +} \ No newline at end of file diff --git a/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/templates/CompiledTemplate.java b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/templates/CompiledTemplate.java new file mode 100644 index 000000000000..d0152d28318e --- /dev/null +++ b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/templates/CompiledTemplate.java @@ -0,0 +1,3 @@ +package org.mvel2.templates; + +public class CompiledTemplate {} \ No newline at end of file diff --git a/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/templates/TemplateCompiler.java b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/templates/TemplateCompiler.java new file mode 100644 index 000000000000..333e4164bcd4 --- /dev/null +++ b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/templates/TemplateCompiler.java @@ -0,0 +1,7 @@ +package org.mvel2.templates; + +public class TemplateCompiler { + public TemplateCompiler(String template) {} + public static CompiledTemplate compileTemplate(String template) { return null; } + public CompiledTemplate compile() { return null; } +} \ No newline at end of file diff --git a/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/templates/TemplateRuntime.java b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/templates/TemplateRuntime.java new file mode 100644 index 000000000000..4fec9fa4e30b --- /dev/null +++ b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/templates/TemplateRuntime.java @@ -0,0 +1,8 @@ +package org.mvel2.templates; + +import java.util.Map; + +public class TemplateRuntime { + public static Object eval(String template, Map vars) { return null; } + public static Object execute(CompiledTemplate compiled, Map vars) { return null; } +} \ No newline at end of file