diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/MvelInjectionLib.qll b/java/ql/src/experimental/Security/CWE/CWE-094/MvelInjectionLib.qll index be457d9534e..04833b8537e 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-094/MvelInjectionLib.qll +++ b/java/ql/src/experimental/Security/CWE/CWE-094/MvelInjectionLib.qll @@ -36,6 +36,11 @@ class MvelEvaluationSink extends DataFlow::ExprNode { m instanceof ExecutableStatementEvaluationMethod and (ma = asExpr() or ma.getQualifier() = asExpr()) ) + or + exists(MethodAccess ma, Method m | m = ma.getMethod() | + m instanceof CompiledExpressionEvaluationMethod and + (ma = asExpr() or ma.getQualifier() = asExpr()) + ) } } @@ -118,6 +123,16 @@ class ExecutableStatementEvaluationMethod extends Method { } } +/** + * Methods in `CompiledExpression` that trigger evaluating a MVEL expression. + */ +class CompiledExpressionEvaluationMethod extends Method { + CompiledExpressionEvaluationMethod() { + getDeclaringType() instanceof CompiledExpression and + hasName("getDirectValue") + } +} + class MVEL extends RefType { MVEL() { hasQualifiedName("org.mvel2", "MVEL") } } @@ -129,3 +144,7 @@ class ExpressionCompiler extends RefType { class ExecutableStatement extends RefType { ExecutableStatement() { hasQualifiedName("org.mvel2.compiler", "ExecutableStatement") } } + +class CompiledExpression extends RefType { + CompiledExpression() { hasQualifiedName("org.mvel2.compiler", "CompiledExpression") } +} diff --git a/java/ql/test/experimental/Security/CWE/CWE-094/MvelInjection.expected b/java/ql/test/experimental/Security/CWE/CWE-094/MvelInjection.expected index 837cee1e3a1..399ab3fe6ac 100644 --- a/java/ql/test/experimental/Security/CWE/CWE-094/MvelInjection.expected +++ b/java/ql/test/experimental/Security/CWE/CWE-094/MvelInjection.expected @@ -1,15 +1,19 @@ edges -| MvelInjection.java:13:27:13:49 | getInputStream(...) : InputStream | MvelInjection.java:17:17:17:21 | input | -| MvelInjection.java:22:27:22:49 | getInputStream(...) : InputStream | MvelInjection.java:27:30:27:39 | expression | -| MvelInjection.java:32:27:32:49 | getInputStream(...) : InputStream | MvelInjection.java:38:7:38:15 | statement | +| MvelInjection.java:14:27:14:49 | getInputStream(...) : InputStream | MvelInjection.java:18:17:18:21 | input | +| MvelInjection.java:23:27:23:49 | getInputStream(...) : InputStream | MvelInjection.java:28:30:28:39 | expression | +| MvelInjection.java:33:27:33:49 | getInputStream(...) : InputStream | MvelInjection.java:39:7:39:15 | statement | +| MvelInjection.java:44:27:44:49 | getInputStream(...) : InputStream | MvelInjection.java:50:7:50:16 | expression | nodes -| MvelInjection.java:13:27:13:49 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream | -| MvelInjection.java:17:17:17:21 | input | semmle.label | input | -| MvelInjection.java:22:27:22:49 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream | -| MvelInjection.java:27:30:27:39 | expression | semmle.label | expression | -| MvelInjection.java:32:27:32:49 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream | -| MvelInjection.java:38:7:38:15 | statement | semmle.label | statement | +| MvelInjection.java:14:27:14:49 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream | +| MvelInjection.java:18:17:18:21 | input | semmle.label | input | +| MvelInjection.java:23:27:23:49 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream | +| MvelInjection.java:28:30:28:39 | expression | semmle.label | expression | +| MvelInjection.java:33:27:33:49 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream | +| MvelInjection.java:39:7:39:15 | statement | semmle.label | statement | +| MvelInjection.java:44:27:44:49 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream | +| MvelInjection.java:50:7:50:16 | expression | semmle.label | expression | #select -| MvelInjection.java:17:17:17:21 | input | MvelInjection.java:13:27:13:49 | getInputStream(...) : InputStream | MvelInjection.java:17:17:17:21 | input | MVEL injection from $@. | MvelInjection.java:13:27:13:49 | getInputStream(...) | this user input | -| MvelInjection.java:27:30:27:39 | expression | MvelInjection.java:22:27:22:49 | getInputStream(...) : InputStream | MvelInjection.java:27:30:27:39 | expression | MVEL injection from $@. | MvelInjection.java:22:27:22:49 | getInputStream(...) | this user input | -| MvelInjection.java:38:7:38:15 | statement | MvelInjection.java:32:27:32:49 | getInputStream(...) : InputStream | MvelInjection.java:38:7:38:15 | statement | MVEL injection from $@. | MvelInjection.java:32:27:32:49 | getInputStream(...) | this user input | \ No newline at end of file +| MvelInjection.java:18:17:18:21 | input | MvelInjection.java:14:27:14:49 | getInputStream(...) : InputStream | MvelInjection.java:18:17:18:21 | input | MVEL injection from $@. | MvelInjection.java:14:27:14:49 | getInputStream(...) | this user input | +| MvelInjection.java:28:30:28:39 | expression | MvelInjection.java:23:27:23:49 | getInputStream(...) : InputStream | MvelInjection.java:28:30:28:39 | expression | MVEL injection from $@. | MvelInjection.java:23:27:23:49 | getInputStream(...) | this user input | +| MvelInjection.java:39:7:39:15 | statement | MvelInjection.java:33:27:33:49 | getInputStream(...) : InputStream | MvelInjection.java:39:7:39:15 | statement | MVEL injection from $@. | MvelInjection.java:33:27:33:49 | getInputStream(...) | this user input | +| MvelInjection.java:50:7:50:16 | expression | MvelInjection.java:44:27:44:49 | getInputStream(...) : InputStream | MvelInjection.java:50:7:50:16 | expression | MVEL injection from $@. | MvelInjection.java:44:27:44: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 index 6c5ceb957b6..c5199b272c4 100644 --- a/java/ql/test/experimental/Security/CWE/CWE-094/MvelInjection.java +++ b/java/ql/test/experimental/Security/CWE/CWE-094/MvelInjection.java @@ -3,6 +3,7 @@ import java.io.InputStream; import java.io.Serializable; import java.net.Socket; import org.mvel2.MVEL; +import org.mvel2.compiler.CompiledExpression; import org.mvel2.compiler.ExecutableStatement; import org.mvel2.compiler.ExpressionCompiler; import org.mvel2.integration.impl.ImmutableDefaultFactory; @@ -38,4 +39,15 @@ public class MvelInjection { statement.getValue(new Object(), new ImmutableDefaultFactory()); } } + + public static void testWithCompiledExpressionGetDirectValue(Socket socket) throws IOException { + try (InputStream in = socket.getInputStream()) { + byte[] bytes = new byte[1024]; + int n = in.read(bytes); + String input = new String(bytes, 0, n); + ExpressionCompiler compiler = new ExpressionCompiler(input); + CompiledExpression expression = compiler.compile(); + expression.getDirectValue(new Object(), new ImmutableDefaultFactory()); + } + } } 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 00000000000..234900b0fa3 --- /dev/null +++ b/java/ql/test/stubs/mvel2-2.4.7/org/mvel2/compiler/CompiledExpression.java @@ -0,0 +1,8 @@ +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; } +} \ 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 index cdd510035fe..1e15b8e6678 100644 --- 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 @@ -3,5 +3,5 @@ package org.mvel2.compiler; import org.mvel2.integration.VariableResolverFactory; public interface ExecutableStatement { - public Object getValue(Object staticContext, VariableResolverFactory factory); + 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 index 204bd5e283f..3f22268b891 100644 --- 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 @@ -2,5 +2,5 @@ package org.mvel2.compiler; public class ExpressionCompiler { public ExpressionCompiler(String expression) {} - public ExecutableStatement compile() { return null; } + public CompiledExpression compile() { return null; } } \ No newline at end of file