From 921b8e80a2d328c34553a082df1008e8729f7144 Mon Sep 17 00:00:00 2001
From: haby0
Date: Wed, 26 May 2021 08:58:29 +0800
Subject: [PATCH 1/9] Jshell Injection
---
.../Security/CWE/CWE-094/JShellInjection.java | 26 ++++++++++++++++
.../CWE/CWE-094/JShellInjection.qhelp | 31 +++++++++++++++++++
.../Security/CWE/CWE-094/JShellInjection.ql | 29 +++++++++++++++++
.../Security/CWE/CWE-094/JShellInjection.qll | 28 +++++++++++++++++
.../security/CWE-094/JShellInjection.expected | 11 +++++++
.../security/CWE-094/JShellInjection.java | 26 ++++++++++++++++
.../security/CWE-094/JShellInjection.qlref | 1 +
7 files changed, 152 insertions(+)
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.java
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.qhelp
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.ql
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.qll
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/JShellInjection.expected
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/JShellInjection.java
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/JShellInjection.qlref
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.java b/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.java
new file mode 100644
index 00000000000..8d1257697b9
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.java
@@ -0,0 +1,26 @@
+import javax.servlet.http.HttpServletRequest;
+import jdk.jshell.JShell;
+import jdk.jshell.SourceCodeAnalysis;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+
+@Controller
+public class JShellInjection {
+
+ @GetMapping(value = "bad1")
+ public void bad1(HttpServletRequest request) {
+ String input = request.getParameter("code");
+ JShell jShell = JShell.builder().build();
+ // BAD: allow execution of arbitrary Java code
+ jShell.eval(input);
+ }
+
+ @GetMapping(value = "bad2")
+ public void bad2(HttpServletRequest request) {
+ String input = request.getParameter("code");
+ JShell jShell = JShell.builder().build();
+ SourceCodeAnalysis sourceCodeAnalysis = jShell.sourceCodeAnalysis();
+ // BAD: allow execution of arbitrary Java code
+ sourceCodeAnalysis.wrappers(input);
+ }
+}
\ No newline at end of file
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.qhelp
new file mode 100644
index 00000000000..cae528f5ef1
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.qhelp
@@ -0,0 +1,31 @@
+
+
+
+
+The Java Shell tool (JShell) is an interactive tool for learning the Java programming
+language and prototyping Java code. JShell is a Read-Evaluate-Print Loop (REPL), which
+evaluates declarations, statements, and expressions as they are entered and immediately
+shows the results. If an expression is built using attacker-controlled data and then evaluated,
+it may allow the attacker to run arbitrary code.
+
+
+
+It is generally recommended to avoid using untrusted input in a JShell expression.
+If it is not possible,JShell expressions should be run in a sandbox that allows accessing only
+explicitly allowed classes.
+
+
+
+The following example calls JShell.eval(...) or SourceCodeAnalysis.wrappers(...)
+to execute untrusted data.
+
+
+
+
+
+Java 9 jshell tutorial: JShell introduction
+
+
+
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.ql
new file mode 100644
index 00000000000..2f776500a84
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.ql
@@ -0,0 +1,29 @@
+/**
+ * @name JShell injection
+ * @description Evaluation of a user-controlled JShell expression
+ * may lead to arbitrary code execution.
+ * @kind path-problem
+ * @problem.severity error
+ * @precision high
+ * @id java/jshell-injection
+ * @tags security
+ * external/cwe-094
+ */
+
+import java
+import JShellInjection
+import semmle.code.java.dataflow.FlowSources
+import DataFlow::PathGraph
+
+class JShellInjectionConfiguration extends TaintTracking::Configuration {
+ JShellInjectionConfiguration() { this = "JShellInjectionConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
+
+ override predicate isSink(DataFlow::Node sink) { sink instanceof JShellInjectionSink }
+}
+
+from DataFlow::PathNode source, DataFlow::PathNode sink, JShellInjectionConfiguration conf
+where conf.hasFlowPath(source, sink)
+select sink.getNode(), source, sink, "JShell injection from $@.", source.getNode(),
+ "this user input"
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.qll b/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.qll
new file mode 100644
index 00000000000..20c394f6bd8
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.qll
@@ -0,0 +1,28 @@
+import java
+import semmle.code.java.dataflow.FlowSources
+
+/** A sink for JShell expression injection vulnerabilities. */
+class JShellInjectionSink extends DataFlow::Node {
+ JShellInjectionSink() {
+ this.asExpr() = any(JShellEvalCall jsec).getArgument(0) or
+ this.asExpr() = any(SourceCodeAnalysisWrappersCall scawc).getArgument(0)
+ }
+}
+
+/** A call to `JShell.eval`. */
+class JShellEvalCall extends MethodAccess {
+ JShellEvalCall() {
+ this.getMethod().hasName("eval") and
+ this.getMethod().getDeclaringType().hasQualifiedName("jdk.jshell", "JShell") and
+ this.getMethod().getNumberOfParameters() = 1
+ }
+}
+
+/** A call to `SourceCodeAnalysis.wrappers`. */
+class SourceCodeAnalysisWrappersCall extends MethodAccess {
+ SourceCodeAnalysisWrappersCall() {
+ this.getMethod().hasName("wrappers") and
+ this.getMethod().getDeclaringType().hasQualifiedName("jdk.jshell", "SourceCodeAnalysis") and
+ this.getMethod().getNumberOfParameters() = 1
+ }
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JShellInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-094/JShellInjection.expected
new file mode 100644
index 00000000000..6c7f32fc94d
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-094/JShellInjection.expected
@@ -0,0 +1,11 @@
+edges
+| JShellInjection.java:12:18:12:45 | getParameter(...) : String | JShellInjection.java:15:15:15:19 | input |
+| JShellInjection.java:20:18:20:45 | getParameter(...) : String | JShellInjection.java:24:31:24:35 | input |
+nodes
+| JShellInjection.java:12:18:12:45 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JShellInjection.java:15:15:15:19 | input | semmle.label | input |
+| JShellInjection.java:20:18:20:45 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JShellInjection.java:24:31:24:35 | input | semmle.label | input |
+#select
+| JShellInjection.java:15:15:15:19 | input | JShellInjection.java:12:18:12:45 | getParameter(...) : String | JShellInjection.java:15:15:15:19 | input | JShell injection from $@. | JShellInjection.java:12:18:12:45 | getParameter(...) | this user input |
+| JShellInjection.java:24:31:24:35 | input | JShellInjection.java:20:18:20:45 | getParameter(...) : String | JShellInjection.java:24:31:24:35 | input | JShell injection from $@. | JShellInjection.java:20:18:20:45 | getParameter(...) | this user input |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JShellInjection.java b/java/ql/test/experimental/query-tests/security/CWE-094/JShellInjection.java
new file mode 100644
index 00000000000..8d1257697b9
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-094/JShellInjection.java
@@ -0,0 +1,26 @@
+import javax.servlet.http.HttpServletRequest;
+import jdk.jshell.JShell;
+import jdk.jshell.SourceCodeAnalysis;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+
+@Controller
+public class JShellInjection {
+
+ @GetMapping(value = "bad1")
+ public void bad1(HttpServletRequest request) {
+ String input = request.getParameter("code");
+ JShell jShell = JShell.builder().build();
+ // BAD: allow execution of arbitrary Java code
+ jShell.eval(input);
+ }
+
+ @GetMapping(value = "bad2")
+ public void bad2(HttpServletRequest request) {
+ String input = request.getParameter("code");
+ JShell jShell = JShell.builder().build();
+ SourceCodeAnalysis sourceCodeAnalysis = jShell.sourceCodeAnalysis();
+ // BAD: allow execution of arbitrary Java code
+ sourceCodeAnalysis.wrappers(input);
+ }
+}
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JShellInjection.qlref b/java/ql/test/experimental/query-tests/security/CWE-094/JShellInjection.qlref
new file mode 100644
index 00000000000..d2128dd2058
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-094/JShellInjection.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-094/JShellInjection.ql
\ No newline at end of file
From ed0aabef46b4b3f44122d6499e64342f4da5030d Mon Sep 17 00:00:00 2001
From: haby0
Date: Wed, 26 May 2021 17:48:10 +0800
Subject: [PATCH 2/9] add isAdditionalTaintStep
---
.../Security/CWE/CWE-094/JShellInjection.java | 18 ++++++++++++++++--
.../Security/CWE/CWE-094/JShellInjection.ql | 13 +++++++++++++
.../Security/CWE/CWE-094/JShellInjection.qll | 18 +++++++++++++++++-
.../security/CWE-094/JShellInjection.expected | 4 ++++
.../security/CWE-094/JShellInjection.java | 18 ++++++++++++++++--
5 files changed, 66 insertions(+), 5 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.java b/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.java
index 8d1257697b9..115030087ff 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.java
+++ b/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.java
@@ -11,7 +11,7 @@ public class JShellInjection {
public void bad1(HttpServletRequest request) {
String input = request.getParameter("code");
JShell jShell = JShell.builder().build();
- // BAD: allow execution of arbitrary Java code
+ // BAD: allow execution of arbitrary Java code
jShell.eval(input);
}
@@ -20,7 +20,21 @@ public class JShellInjection {
String input = request.getParameter("code");
JShell jShell = JShell.builder().build();
SourceCodeAnalysis sourceCodeAnalysis = jShell.sourceCodeAnalysis();
- // BAD: allow execution of arbitrary Java code
+ // BAD: allow execution of arbitrary Java code
sourceCodeAnalysis.wrappers(input);
}
+
+ @GetMapping(value = "bad3")
+ public void bad3(HttpServletRequest request) {
+ String input = request.getParameter("code");
+ JShell jShell = JShell.builder().build();
+ SourceCodeAnalysis.CompletionInfo info;
+ SourceCodeAnalysis sca = jShell.sourceCodeAnalysis();
+ for (info = sca.analyzeCompletion(input);
+ info.completeness().isComplete();
+ info = sca.analyzeCompletion(info.remaining())) {
+ // BAD: allow execution of arbitrary Java code
+ jShell.eval(info.source());
+ }
+ }
}
\ No newline at end of file
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.ql
index 2f776500a84..8fc84ab0d97 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.ql
@@ -21,6 +21,19 @@ class JShellInjectionConfiguration extends TaintTracking::Configuration {
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) { sink instanceof JShellInjectionSink }
+
+ override predicate isAdditionalTaintStep(DataFlow::Node prod, DataFlow::Node succ) {
+ exists(MethodAccess ma |
+ ma.getMethod().hasName("analyzeCompletion") and
+ ma.getMethod().getNumberOfParameters() = 1 and
+ ma.getMethod()
+ .getDeclaringType()
+ .getASupertype*()
+ .hasQualifiedName("jdk.jshell", "SourceCodeAnalysis") and
+ ma.getArgument(0) = prod.asExpr() and
+ ma = succ.asExpr()
+ )
+ }
}
from DataFlow::PathNode source, DataFlow::PathNode sink, JShellInjectionConfiguration conf
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.qll b/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.qll
index 20c394f6bd8..410e5acffa8 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.qll
+++ b/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.qll
@@ -4,8 +4,24 @@ import semmle.code.java.dataflow.FlowSources
/** A sink for JShell expression injection vulnerabilities. */
class JShellInjectionSink extends DataFlow::Node {
JShellInjectionSink() {
- this.asExpr() = any(JShellEvalCall jsec).getArgument(0) or
+ this.asExpr() = any(JShellEvalCall jsec).getArgument(0)
+ or
this.asExpr() = any(SourceCodeAnalysisWrappersCall scawc).getArgument(0)
+ or
+ exists(MethodAccess ma |
+ ma.getMethod().hasName("source") and
+ ma.getMethod().getNumberOfParameters() = 0 and
+ ma.getMethod()
+ .getDeclaringType()
+ .getASupertype*()
+ .hasQualifiedName("jdk.jshell", "SourceCodeAnalysis$CompletionInfo") and
+ ma.getQualifier() = this.asExpr() and
+ (
+ ma = any(JShellEvalCall jsec).getArgument(0)
+ or
+ ma = any(SourceCodeAnalysisWrappersCall scawc).getArgument(0)
+ )
+ )
}
}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JShellInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-094/JShellInjection.expected
index 6c7f32fc94d..a6b227de39e 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-094/JShellInjection.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-094/JShellInjection.expected
@@ -1,11 +1,15 @@
edges
| JShellInjection.java:12:18:12:45 | getParameter(...) : String | JShellInjection.java:15:15:15:19 | input |
| JShellInjection.java:20:18:20:45 | getParameter(...) : String | JShellInjection.java:24:31:24:35 | input |
+| JShellInjection.java:29:18:29:45 | getParameter(...) : String | JShellInjection.java:37:16:37:19 | info |
nodes
| JShellInjection.java:12:18:12:45 | getParameter(...) : String | semmle.label | getParameter(...) : String |
| JShellInjection.java:15:15:15:19 | input | semmle.label | input |
| JShellInjection.java:20:18:20:45 | getParameter(...) : String | semmle.label | getParameter(...) : String |
| JShellInjection.java:24:31:24:35 | input | semmle.label | input |
+| JShellInjection.java:29:18:29:45 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JShellInjection.java:37:16:37:19 | info | semmle.label | info |
#select
| JShellInjection.java:15:15:15:19 | input | JShellInjection.java:12:18:12:45 | getParameter(...) : String | JShellInjection.java:15:15:15:19 | input | JShell injection from $@. | JShellInjection.java:12:18:12:45 | getParameter(...) | this user input |
| JShellInjection.java:24:31:24:35 | input | JShellInjection.java:20:18:20:45 | getParameter(...) : String | JShellInjection.java:24:31:24:35 | input | JShell injection from $@. | JShellInjection.java:20:18:20:45 | getParameter(...) | this user input |
+| JShellInjection.java:37:16:37:19 | info | JShellInjection.java:29:18:29:45 | getParameter(...) : String | JShellInjection.java:37:16:37:19 | info | JShell injection from $@. | JShellInjection.java:29:18:29:45 | getParameter(...) | this user input |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JShellInjection.java b/java/ql/test/experimental/query-tests/security/CWE-094/JShellInjection.java
index 8d1257697b9..115030087ff 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-094/JShellInjection.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-094/JShellInjection.java
@@ -11,7 +11,7 @@ public class JShellInjection {
public void bad1(HttpServletRequest request) {
String input = request.getParameter("code");
JShell jShell = JShell.builder().build();
- // BAD: allow execution of arbitrary Java code
+ // BAD: allow execution of arbitrary Java code
jShell.eval(input);
}
@@ -20,7 +20,21 @@ public class JShellInjection {
String input = request.getParameter("code");
JShell jShell = JShell.builder().build();
SourceCodeAnalysis sourceCodeAnalysis = jShell.sourceCodeAnalysis();
- // BAD: allow execution of arbitrary Java code
+ // BAD: allow execution of arbitrary Java code
sourceCodeAnalysis.wrappers(input);
}
+
+ @GetMapping(value = "bad3")
+ public void bad3(HttpServletRequest request) {
+ String input = request.getParameter("code");
+ JShell jShell = JShell.builder().build();
+ SourceCodeAnalysis.CompletionInfo info;
+ SourceCodeAnalysis sca = jShell.sourceCodeAnalysis();
+ for (info = sca.analyzeCompletion(input);
+ info.completeness().isComplete();
+ info = sca.analyzeCompletion(info.remaining())) {
+ // BAD: allow execution of arbitrary Java code
+ jShell.eval(info.source());
+ }
+ }
}
\ No newline at end of file
From 3a2a99e28957d76a4f1969fce50afec3452b6d29 Mon Sep 17 00:00:00 2001
From: haby0
Date: Wed, 2 Jun 2021 09:18:22 +0800
Subject: [PATCH 3/9] Fix 1
---
.../experimental/Security/CWE/CWE-094/JShellInjection.qhelp | 4 ++--
.../src/experimental/Security/CWE/CWE-094/JShellInjection.ql | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.qhelp
index cae528f5ef1..44a95f9d399 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.qhelp
+++ b/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.qhelp
@@ -13,7 +13,7 @@ it may allow the attacker to run arbitrary code.
It is generally recommended to avoid using untrusted input in a JShell expression.
-If it is not possible,JShell expressions should be run in a sandbox that allows accessing only
+If it is not possible, JShell expressions should be run in a sandbox that allows accessing only
explicitly allowed classes.
@@ -25,7 +25,7 @@ to execute untrusted data.
-Java 9 jshell tutorial: JShell introduction
+Introduction to JShell: Java Shell User’s Guide
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.ql
index 8fc84ab0d97..2754d58be54 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.ql
@@ -22,7 +22,7 @@ class JShellInjectionConfiguration extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) { sink instanceof JShellInjectionSink }
- override predicate isAdditionalTaintStep(DataFlow::Node prod, DataFlow::Node succ) {
+ override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(MethodAccess ma |
ma.getMethod().hasName("analyzeCompletion") and
ma.getMethod().getNumberOfParameters() = 1 and
@@ -30,7 +30,7 @@ class JShellInjectionConfiguration extends TaintTracking::Configuration {
.getDeclaringType()
.getASupertype*()
.hasQualifiedName("jdk.jshell", "SourceCodeAnalysis") and
- ma.getArgument(0) = prod.asExpr() and
+ ma.getArgument(0) = pred.asExpr() and
ma = succ.asExpr()
)
}
From bfe0d40987954c704f07aa7ff4c4789e538cdd2b Mon Sep 17 00:00:00 2001
From: haby0
Date: Wed, 16 Jun 2021 13:50:53 +0800
Subject: [PATCH 4/9] using isAdditionalTaintStep
---
.../Security/CWE/CWE-094/JShellInjection.ql | 16 +++----
.../Security/CWE/CWE-094/JShellInjection.qll | 43 +++++++++++--------
2 files changed, 33 insertions(+), 26 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.ql
index 2754d58be54..9b50d500c58 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.ql
@@ -12,6 +12,7 @@
import java
import JShellInjection
+import semmle.code.java.dataflow.DataFlow2
import semmle.code.java.dataflow.FlowSources
import DataFlow::PathGraph
@@ -23,15 +24,12 @@ class JShellInjectionConfiguration extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) { sink instanceof JShellInjectionSink }
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
- exists(MethodAccess ma |
- ma.getMethod().hasName("analyzeCompletion") and
- ma.getMethod().getNumberOfParameters() = 1 and
- ma.getMethod()
- .getDeclaringType()
- .getASupertype*()
- .hasQualifiedName("jdk.jshell", "SourceCodeAnalysis") and
- ma.getArgument(0) = pred.asExpr() and
- ma = succ.asExpr()
+ exists(
+ SourceCodeAnalysisAnalyzeCompletionCall scaacc, CompletionInfoSourceOrRemainingCall cisorc
+ |
+ scaacc.getArgument(0) = pred.asExpr() and
+ cisorc = succ.asExpr() and
+ DataFlow2::localExprFlow(scaacc, cisorc.getQualifier())
)
}
}
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.qll b/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.qll
index 410e5acffa8..894cd03ce67 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.qll
+++ b/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.qll
@@ -7,26 +7,11 @@ class JShellInjectionSink extends DataFlow::Node {
this.asExpr() = any(JShellEvalCall jsec).getArgument(0)
or
this.asExpr() = any(SourceCodeAnalysisWrappersCall scawc).getArgument(0)
- or
- exists(MethodAccess ma |
- ma.getMethod().hasName("source") and
- ma.getMethod().getNumberOfParameters() = 0 and
- ma.getMethod()
- .getDeclaringType()
- .getASupertype*()
- .hasQualifiedName("jdk.jshell", "SourceCodeAnalysis$CompletionInfo") and
- ma.getQualifier() = this.asExpr() and
- (
- ma = any(JShellEvalCall jsec).getArgument(0)
- or
- ma = any(SourceCodeAnalysisWrappersCall scawc).getArgument(0)
- )
- )
}
}
/** A call to `JShell.eval`. */
-class JShellEvalCall extends MethodAccess {
+private class JShellEvalCall extends MethodAccess {
JShellEvalCall() {
this.getMethod().hasName("eval") and
this.getMethod().getDeclaringType().hasQualifiedName("jdk.jshell", "JShell") and
@@ -35,10 +20,34 @@ class JShellEvalCall extends MethodAccess {
}
/** A call to `SourceCodeAnalysis.wrappers`. */
-class SourceCodeAnalysisWrappersCall extends MethodAccess {
+private class SourceCodeAnalysisWrappersCall extends MethodAccess {
SourceCodeAnalysisWrappersCall() {
this.getMethod().hasName("wrappers") and
this.getMethod().getDeclaringType().hasQualifiedName("jdk.jshell", "SourceCodeAnalysis") and
this.getMethod().getNumberOfParameters() = 1
}
}
+
+/** A call to `SourceCodeAnalysis.analyzeCompletion`. */
+class SourceCodeAnalysisAnalyzeCompletionCall extends MethodAccess {
+ SourceCodeAnalysisAnalyzeCompletionCall() {
+ this.getMethod().hasName("analyzeCompletion") and
+ this.getMethod()
+ .getDeclaringType()
+ .getASupertype*()
+ .hasQualifiedName("jdk.jshell", "SourceCodeAnalysis") and
+ this.getMethod().getNumberOfParameters() = 1
+ }
+}
+
+/** A call to `CompletionInfo.source` or `CompletionInfo.remaining`. */
+class CompletionInfoSourceOrRemainingCall extends MethodAccess {
+ CompletionInfoSourceOrRemainingCall() {
+ this.getMethod().getName() in ["source", "remaining"] and
+ this.getMethod()
+ .getDeclaringType()
+ .getASupertype*()
+ .hasQualifiedName("jdk.jshell", "SourceCodeAnalysis$CompletionInfo") and
+ this.getMethod().getNumberOfParameters() = 0
+ }
+}
From a71757f0f4fbf43c3d0c36d54ebe13cd54e54e1d Mon Sep 17 00:00:00 2001
From: haby0
Date: Thu, 3 Jun 2021 16:04:29 +0800
Subject: [PATCH 5/9] Update
java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.qhelp
Co-authored-by: Marcono1234
---
.../src/experimental/Security/CWE/CWE-094/JShellInjection.qhelp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.qhelp
index 44a95f9d399..05457c8fd82 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.qhelp
+++ b/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.qhelp
@@ -25,7 +25,7 @@ to execute untrusted data.
-Introduction to JShell: Java Shell User’s Guide
+Java Shell User’s Guide: Introduction to JShell
From 2b77f7d1bc73d7616d507c9348a47a017f667959 Mon Sep 17 00:00:00 2001
From: haby0
Date: Thu, 17 Jun 2021 09:12:06 +0800
Subject: [PATCH 6/9] Modify isAdditionalTaintStep
---
.../Security/CWE/CWE-094/JShellInjection.ql | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.ql
index 9b50d500c58..62dbdcba670 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.ql
@@ -12,7 +12,6 @@
import java
import JShellInjection
-import semmle.code.java.dataflow.DataFlow2
import semmle.code.java.dataflow.FlowSources
import DataFlow::PathGraph
@@ -24,12 +23,12 @@ class JShellInjectionConfiguration extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) { sink instanceof JShellInjectionSink }
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
- exists(
- SourceCodeAnalysisAnalyzeCompletionCall scaacc, CompletionInfoSourceOrRemainingCall cisorc
- |
- scaacc.getArgument(0) = pred.asExpr() and
- cisorc = succ.asExpr() and
- DataFlow2::localExprFlow(scaacc, cisorc.getQualifier())
+ exists(SourceCodeAnalysisAnalyzeCompletionCall scaacc |
+ scaacc.getArgument(0) = pred.asExpr() and scaacc = succ.asExpr()
+ )
+ or
+ exists(CompletionInfoSourceOrRemainingCall cisorc |
+ cisorc.getQualifier() = pred.asExpr() and cisorc = succ.asExpr()
)
}
}
From dca737190b8914846a533d35e7c3d0d4387eb650 Mon Sep 17 00:00:00 2001
From: haby0
Date: Thu, 17 Jun 2021 17:20:07 +0800
Subject: [PATCH 7/9] Modify JShellInjection.expected
---
.../query-tests/security/CWE-094/JShellInjection.expected | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JShellInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-094/JShellInjection.expected
index a6b227de39e..43f86caaf6d 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-094/JShellInjection.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-094/JShellInjection.expected
@@ -1,15 +1,15 @@
edges
| JShellInjection.java:12:18:12:45 | getParameter(...) : String | JShellInjection.java:15:15:15:19 | input |
| JShellInjection.java:20:18:20:45 | getParameter(...) : String | JShellInjection.java:24:31:24:35 | input |
-| JShellInjection.java:29:18:29:45 | getParameter(...) : String | JShellInjection.java:37:16:37:19 | info |
+| JShellInjection.java:29:18:29:45 | getParameter(...) : String | JShellInjection.java:37:16:37:28 | source(...) |
nodes
| JShellInjection.java:12:18:12:45 | getParameter(...) : String | semmle.label | getParameter(...) : String |
| JShellInjection.java:15:15:15:19 | input | semmle.label | input |
| JShellInjection.java:20:18:20:45 | getParameter(...) : String | semmle.label | getParameter(...) : String |
| JShellInjection.java:24:31:24:35 | input | semmle.label | input |
| JShellInjection.java:29:18:29:45 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JShellInjection.java:37:16:37:19 | info | semmle.label | info |
+| JShellInjection.java:37:16:37:28 | source(...) | semmle.label | source(...) |
#select
| JShellInjection.java:15:15:15:19 | input | JShellInjection.java:12:18:12:45 | getParameter(...) : String | JShellInjection.java:15:15:15:19 | input | JShell injection from $@. | JShellInjection.java:12:18:12:45 | getParameter(...) | this user input |
| JShellInjection.java:24:31:24:35 | input | JShellInjection.java:20:18:20:45 | getParameter(...) : String | JShellInjection.java:24:31:24:35 | input | JShell injection from $@. | JShellInjection.java:20:18:20:45 | getParameter(...) | this user input |
-| JShellInjection.java:37:16:37:19 | info | JShellInjection.java:29:18:29:45 | getParameter(...) : String | JShellInjection.java:37:16:37:19 | info | JShell injection from $@. | JShellInjection.java:29:18:29:45 | getParameter(...) | this user input |
+| JShellInjection.java:37:16:37:28 | source(...) | JShellInjection.java:29:18:29:45 | getParameter(...) : String | JShellInjection.java:37:16:37:28 | source(...) | JShell injection from $@. | JShellInjection.java:29:18:29:45 | getParameter(...) | this user input |
From 1750efad2a88de88b32789a8a4430e8c352b59f1 Mon Sep 17 00:00:00 2001
From: haby0
Date: Fri, 18 Jun 2021 21:46:48 +0800
Subject: [PATCH 8/9] fix
---
.../query-tests/security/CWE-094/options | 2 +-
.../test/stubs/jshell/jdk/jshell/JShell.java | 37 ++++++
.../test/stubs/jshell/jdk/jshell/Snippet.java | 31 +++++
.../stubs/jshell/jdk/jshell/SnippetEvent.java | 5 +
.../jshell/jdk/jshell/SourceCodeAnalysis.java | 111 ++++++++++++++++++
5 files changed, 185 insertions(+), 1 deletion(-)
create mode 100644 java/ql/test/stubs/jshell/jdk/jshell/JShell.java
create mode 100644 java/ql/test/stubs/jshell/jdk/jshell/Snippet.java
create mode 100644 java/ql/test/stubs/jshell/jdk/jshell/SnippetEvent.java
create mode 100644 java/ql/test/stubs/jshell/jdk/jshell/SourceCodeAnalysis.java
diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/options b/java/ql/test/experimental/query-tests/security/CWE-094/options
index 1fc637be50f..22891626ba4 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-094/options
+++ b/java/ql/test/experimental/query-tests/security/CWE-094/options
@@ -1 +1 @@
-//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../../stubs/mvel2-2.4.7:${testdir}/../../../../stubs/jsr223-api:${testdir}/../../../../stubs/scriptengine:${testdir}/../../../../stubs/java-ee-el:${testdir}/../../../../stubs/juel-2.2:${testdir}/../../../stubs/groovy-all-3.0.7:${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/jython-2.7.2:${testdir}/../../../../experimental/stubs/rhino-1.7.13:${testdir}/../../../../stubs/bsh-2.0b5
\ 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:${testdir}/../../../../stubs/scriptengine:${testdir}/../../../../stubs/java-ee-el:${testdir}/../../../../stubs/juel-2.2:${testdir}/../../../stubs/groovy-all-3.0.7:${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/jython-2.7.2:${testdir}/../../../../experimental/stubs/rhino-1.7.13:${testdir}/../../../../stubs/bsh-2.0b5:${testdir}/../../../../stubs/jshell
\ No newline at end of file
diff --git a/java/ql/test/stubs/jshell/jdk/jshell/JShell.java b/java/ql/test/stubs/jshell/jdk/jshell/JShell.java
new file mode 100644
index 00000000000..fe49d13cd6e
--- /dev/null
+++ b/java/ql/test/stubs/jshell/jdk/jshell/JShell.java
@@ -0,0 +1,37 @@
+package jdk.jshell;
+
+import java.util.List;
+import java.lang.IllegalStateException;
+
+public class JShell implements AutoCloseable {
+
+ JShell(Builder b) throws IllegalStateException { }
+
+ public static class Builder {
+
+ Builder() { }
+
+ public JShell build() throws IllegalStateException {
+ return null;
+ }
+ }
+
+ public static JShell create() throws IllegalStateException {
+ return null;
+ }
+
+ public static Builder builder() {
+ return null;
+ }
+
+ public SourceCodeAnalysis sourceCodeAnalysis() {
+ return null;
+ }
+
+ public List eval(String input) throws IllegalStateException {
+ return null;
+ }
+
+ @Override
+ public void close() { }
+}
diff --git a/java/ql/test/stubs/jshell/jdk/jshell/Snippet.java b/java/ql/test/stubs/jshell/jdk/jshell/Snippet.java
new file mode 100644
index 00000000000..38da9b6cc35
--- /dev/null
+++ b/java/ql/test/stubs/jshell/jdk/jshell/Snippet.java
@@ -0,0 +1,31 @@
+package jdk.jshell;
+
+public abstract class Snippet {
+
+ public enum Kind {
+
+ IMPORT(true),
+
+ TYPE_DECL(true),
+
+ METHOD(true),
+
+ VAR(true),
+
+ EXPRESSION(false),
+
+ STATEMENT(false),
+
+ ERRONEOUS(false);
+
+ private final boolean isPersistent;
+
+ Kind(boolean isPersistent) {
+ this.isPersistent = isPersistent;
+ }
+
+ public boolean isPersistent() {
+ return false;
+ }
+ }
+}
diff --git a/java/ql/test/stubs/jshell/jdk/jshell/SnippetEvent.java b/java/ql/test/stubs/jshell/jdk/jshell/SnippetEvent.java
new file mode 100644
index 00000000000..9425a278a6c
--- /dev/null
+++ b/java/ql/test/stubs/jshell/jdk/jshell/SnippetEvent.java
@@ -0,0 +1,5 @@
+package jdk.jshell;
+
+public class SnippetEvent {
+
+}
diff --git a/java/ql/test/stubs/jshell/jdk/jshell/SourceCodeAnalysis.java b/java/ql/test/stubs/jshell/jdk/jshell/SourceCodeAnalysis.java
new file mode 100644
index 00000000000..7f629a46cbd
--- /dev/null
+++ b/java/ql/test/stubs/jshell/jdk/jshell/SourceCodeAnalysis.java
@@ -0,0 +1,111 @@
+package jdk.jshell;
+
+import java.util.Collection;
+import java.util.List;
+
+public abstract class SourceCodeAnalysis {
+
+ public abstract CompletionInfo analyzeCompletion(String input);
+
+ public abstract List completionSuggestions(String input, int cursor, int[] anchor);
+
+ public abstract List documentation(String input, int cursor, boolean computeJavadoc);
+
+ public abstract String analyzeType(String code, int cursor);
+
+ public abstract QualifiedNames listQualifiedNames(String code, int cursor);
+
+ public abstract SnippetWrapper wrapper(Snippet snippet);
+
+ public abstract List wrappers(String input);
+
+ public abstract Collection dependents(Snippet snippet);
+
+ SourceCodeAnalysis() {}
+
+ public interface CompletionInfo {
+
+ Completeness completeness();
+
+ String remaining();
+
+ String source();
+ }
+
+ public enum Completeness {
+
+ COMPLETE(true),
+
+ COMPLETE_WITH_SEMI(true),
+
+ DEFINITELY_INCOMPLETE(false),
+
+ CONSIDERED_INCOMPLETE(false),
+
+ EMPTY(false),
+
+ UNKNOWN(true);
+
+ private final boolean isComplete;
+
+ Completeness(boolean isComplete) {
+ this.isComplete = isComplete;
+ }
+
+ public boolean isComplete() {
+ return isComplete;
+ }
+ }
+
+ public interface Suggestion {
+
+ String continuation();
+
+ boolean matchesType();
+ }
+
+ public interface Documentation {
+
+ String signature();
+
+ String javadoc();
+ }
+
+ public static final class QualifiedNames {
+
+
+ QualifiedNames(List names, int simpleNameLength, boolean upToDate, boolean resolvable) { }
+
+ public List getNames() {
+ return null;
+ }
+
+ public int getSimpleNameLength() {
+ return 1;
+ }
+
+ public boolean isUpToDate() {
+ return false;
+ }
+
+ public boolean isResolvable() {
+ return false;
+ }
+
+ }
+
+ public interface SnippetWrapper {
+
+ String source();
+
+ String wrapped();
+
+ String fullClassName();
+
+ Snippet.Kind kind();
+
+ int sourceToWrappedPosition(int pos);
+
+ int wrappedToSourcePosition(int pos);
+ }
+}
From 3cf71c50b84b29efd0053e24c73aa3f70675d02a Mon Sep 17 00:00:00 2001
From: haby0
Date: Thu, 24 Jun 2021 19:24:38 +0800
Subject: [PATCH 9/9] Mobile stubs
---
java/ql/test/experimental/query-tests/security/CWE-094/options | 2 +-
.../test/{ => experimental}/stubs/jshell/jdk/jshell/JShell.java | 0
.../{ => experimental}/stubs/jshell/jdk/jshell/Snippet.java | 0
.../stubs/jshell/jdk/jshell/SnippetEvent.java | 0
.../stubs/jshell/jdk/jshell/SourceCodeAnalysis.java | 0
5 files changed, 1 insertion(+), 1 deletion(-)
rename java/ql/test/{ => experimental}/stubs/jshell/jdk/jshell/JShell.java (100%)
rename java/ql/test/{ => experimental}/stubs/jshell/jdk/jshell/Snippet.java (100%)
rename java/ql/test/{ => experimental}/stubs/jshell/jdk/jshell/SnippetEvent.java (100%)
rename java/ql/test/{ => experimental}/stubs/jshell/jdk/jshell/SourceCodeAnalysis.java (100%)
diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/options b/java/ql/test/experimental/query-tests/security/CWE-094/options
index 22891626ba4..d54ac82b606 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-094/options
+++ b/java/ql/test/experimental/query-tests/security/CWE-094/options
@@ -1 +1 @@
-//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../../stubs/mvel2-2.4.7:${testdir}/../../../../stubs/jsr223-api:${testdir}/../../../../stubs/scriptengine:${testdir}/../../../../stubs/java-ee-el:${testdir}/../../../../stubs/juel-2.2:${testdir}/../../../stubs/groovy-all-3.0.7:${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/jython-2.7.2:${testdir}/../../../../experimental/stubs/rhino-1.7.13:${testdir}/../../../../stubs/bsh-2.0b5:${testdir}/../../../../stubs/jshell
\ 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:${testdir}/../../../../stubs/scriptengine:${testdir}/../../../../stubs/java-ee-el:${testdir}/../../../../stubs/juel-2.2:${testdir}/../../../stubs/groovy-all-3.0.7:${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/jython-2.7.2:${testdir}/../../../../experimental/stubs/rhino-1.7.13:${testdir}/../../../../stubs/bsh-2.0b5:${testdir}/../../../../experimental/stubs/jshell
\ No newline at end of file
diff --git a/java/ql/test/stubs/jshell/jdk/jshell/JShell.java b/java/ql/test/experimental/stubs/jshell/jdk/jshell/JShell.java
similarity index 100%
rename from java/ql/test/stubs/jshell/jdk/jshell/JShell.java
rename to java/ql/test/experimental/stubs/jshell/jdk/jshell/JShell.java
diff --git a/java/ql/test/stubs/jshell/jdk/jshell/Snippet.java b/java/ql/test/experimental/stubs/jshell/jdk/jshell/Snippet.java
similarity index 100%
rename from java/ql/test/stubs/jshell/jdk/jshell/Snippet.java
rename to java/ql/test/experimental/stubs/jshell/jdk/jshell/Snippet.java
diff --git a/java/ql/test/stubs/jshell/jdk/jshell/SnippetEvent.java b/java/ql/test/experimental/stubs/jshell/jdk/jshell/SnippetEvent.java
similarity index 100%
rename from java/ql/test/stubs/jshell/jdk/jshell/SnippetEvent.java
rename to java/ql/test/experimental/stubs/jshell/jdk/jshell/SnippetEvent.java
diff --git a/java/ql/test/stubs/jshell/jdk/jshell/SourceCodeAnalysis.java b/java/ql/test/experimental/stubs/jshell/jdk/jshell/SourceCodeAnalysis.java
similarity index 100%
rename from java/ql/test/stubs/jshell/jdk/jshell/SourceCodeAnalysis.java
rename to java/ql/test/experimental/stubs/jshell/jdk/jshell/SourceCodeAnalysis.java