Include more scenarios and update qldoc

This commit is contained in:
luchua-bc
2021-05-05 14:21:52 +00:00
parent 852bcfb5c7
commit d664aa6d6a
20 changed files with 633 additions and 55 deletions

View File

@@ -4,7 +4,7 @@
<qhelp>
<overview>
<p>The JavaScript Engine API has been available since the release of Java 6, which allows
<p>The Java Scripting API has been available since the release of Java 6, which allows
applications to interact with scripts written in languages such as JavaScript. It serves
as an embedded scripting engine inside Java applications which allows Java-to-JavaScript
interoperability and provides a seamless integration between the two languages. If an
@@ -13,11 +13,11 @@
</overview>
<recommendation>
<p>In general, including user input in a JavaScript Engine expression should be avoided.
<p>In general, including user input in a Java Script Engine expression should be avoided.
If user input must be included in the expression, it should be then evaluated in a safe
context that doesn't allow arbitrary code invocation. Use "Cloudbees Rhino Sandbox" or
sandboxing with SecurityManager or use <a href="https://www.graalvm.org/">graalvm</a>
instead.</p>
context that doesn't allow arbitrary code invocation. Use "Cloudbees Rhino Sandbox" or
sandboxing with SecurityManager, which will be deprecated in a future release, or use
<a href="https://www.graalvm.org/">GraalVM</a> instead.</p>
</recommendation>
<example>
@@ -36,6 +36,9 @@
<li>
CERT coding standard: <a href="https://wiki.sei.cmu.edu/confluence/display/java/IDS52-J.+Prevent+code+injection">ScriptEngine code injection</a>
</li>
<li>
GraalVM: <a href="https://www.graalvm.org/reference-manual/js/NashornMigrationGuide/#secure-by-default">Secure by Default</a>
</li>
<li>
Mozilla Rhino: <a href="https://github.com/mozilla/rhino">Rhino: JavaScript in Java</a>
</li>
@@ -43,7 +46,7 @@ CERT coding standard: <a href="https://wiki.sei.cmu.edu/confluence/display/java/
Rhino Sandbox: <a href="https://github.com/javadelight/delight-rhino-sandbox">A sandbox to execute JavaScript code with Rhino in Java</a>
</li>
<li>
GuardRails: <a href="https://docs.guardrails.io/docs/en/vulnerabilities/java/insecure_use_of_dangerous_function">Code Injection</a>
GuardRails: <a href="https://docs.guardrails.io/docs/en/vulnerabilities/java/insecure_use_of_dangerous_function#code-injection">Code Injection</a>
</li>
</references>
</qhelp>

View File

@@ -1,7 +1,7 @@
/**
* @name Injection in JavaScript Engine
* @name Injection in Java Script Engine
* @description Evaluation of a user-controlled malicious JavaScript or Java expression in
* JavaScript Engine may lead to remote code execution.
* Java Script Engine may lead to remote code execution.
* @kind path-problem
* @problem.severity error
* @precision high
@@ -14,31 +14,62 @@ import java
import semmle.code.java.dataflow.FlowSources
import DataFlow::PathGraph
/** A method of ScriptEngine that allows code injection. */
class ScriptEngineMethod extends Method {
ScriptEngineMethod() {
this.getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "ScriptEngine") and
this.hasName("eval")
or
this.getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "Compilable") and
this.hasName("compile")
or
this.getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "ScriptEngineFactory") and
this.hasName(["getProgram", "getMethodCallSyntax"])
}
}
/** The context class `org.mozilla.javascript.Context` of Rhino JavaScript Engine. */
/** The context class `org.mozilla.javascript.Context` of Rhino Java Script Engine. */
class RhinoContext extends RefType {
RhinoContext() { this.hasQualifiedName("org.mozilla.javascript", "Context") }
}
/**
* A method that evaluates a Rhino expression.
*/
/** A method that evaluates a Rhino expression with `org.mozilla.javascript.Context`. */
class RhinoEvaluateExpressionMethod extends Method {
RhinoEvaluateExpressionMethod() {
this.getDeclaringType().getAnAncestor*() instanceof RhinoContext and
(
hasName("evaluateString") or
hasName("evaluateReader")
)
this.hasName([
"evaluateString", "evaluateReader", "compileFunction", "compileReader", "compileString"
])
}
}
/**
* A method that compiles a Rhino expression with
* `org.mozilla.javascript.optimizer.ClassCompiler`.
*/
class RhinoCompileClassMethod extends Method {
RhinoCompileClassMethod() {
this.getDeclaringType()
.getASupertype*()
.hasQualifiedName("org.mozilla.javascript.optimizer", "ClassCompiler") and
this.hasName("compileToClassFiles")
}
}
/**
* A method that defines a Java class from a Rhino expression with
* `org.mozilla.javascript.GeneratedClassLoader`.
*/
class RhinoDefineClassMethod extends Method {
RhinoDefineClassMethod() {
this.getDeclaringType()
.getASupertype*()
.hasQualifiedName("org.mozilla.javascript", "GeneratedClassLoader") and
this.hasName("defineClass")
}
}
/** Holds if `ma` is a method access of `ScriptEngineMethod`. */
predicate scriptEngine(MethodAccess ma, Expr sink) {
exists(Method m | m = ma.getMethod() |
m instanceof ScriptEngineMethod and
@@ -47,11 +78,17 @@ predicate scriptEngine(MethodAccess ma, Expr sink) {
}
/**
* Holds if `ma` has Rhino code injection vulnerabilities.
* Holds if a Rhino expression evaluation method has the code injection vulnerability.
*/
predicate evaluateRhinoExpression(MethodAccess ma, Expr sink) {
exists(RhinoEvaluateExpressionMethod m | m = ma.getMethod() |
sink = ma.getArgument(1) and // The second argument is the JavaScript or Java input
(
sink = ma.getArgument(1) and // The second argument is the JavaScript or Java input
not ma.getMethod().getName() = "compileReader"
or
sink = ma.getArgument(0) and // The first argument is the input reader
ma.getMethod().getName() = "compileReader"
) and
not exists(MethodAccess ca |
(
ca.getMethod().hasName("initSafeStandardObjects") // safe mode
@@ -63,15 +100,34 @@ predicate evaluateRhinoExpression(MethodAccess ma, Expr sink) {
)
}
/**
* Holds if a Rhino expression compilation method has the code injection vulnerability.
*/
predicate compileScript(MethodAccess ma, Expr sink) {
exists(RhinoCompileClassMethod m | m = ma.getMethod() | sink = ma.getArgument(0))
}
/**
* Holds if a Rhino class loading method has the code injection vulnerability.
*/
predicate defineClass(MethodAccess ma, Expr sink) {
exists(RhinoDefineClassMethod m | m = ma.getMethod() | sink = ma.getArgument(1))
}
/** A sink of script injection. */
class ScriptInjectionSink extends DataFlow::ExprNode {
ScriptInjectionSink() {
scriptEngine(_, this.getExpr()) or
evaluateRhinoExpression(_, this.getExpr())
evaluateRhinoExpression(_, this.getExpr()) or
compileScript(_, this.getExpr()) or
defineClass(_, this.getExpr())
}
MethodAccess getMethodAccess() {
scriptEngine(result, this.getExpr()) or
evaluateRhinoExpression(result, this.getExpr())
evaluateRhinoExpression(result, this.getExpr()) or
compileScript(result, this.getExpr()) or
defineClass(result, this.getExpr())
}
}
@@ -90,4 +146,4 @@ class ScriptInjectionConfiguration extends TaintTracking::Configuration {
from DataFlow::PathNode source, DataFlow::PathNode sink, ScriptInjectionConfiguration conf
where conf.hasFlowPath(source, sink)
select sink.getNode().(ScriptInjectionSink).getMethodAccess(), source, sink,
"JavaScript Engine evaluate $@.", source.getNode(), "user input"
"Java Script Engine evaluate $@.", source.getNode(), "user input"