11/**
2- * @name Injection in JavaScript Engine
2+ * @name Injection in Java Script Engine
33 * @description Evaluation of a user-controlled malicious JavaScript or Java expression in
4- * JavaScript Engine may lead to remote code execution.
4+ * Java Script Engine may lead to remote code execution.
55 * @kind path-problem
66 * @problem.severity error
77 * @precision high
@@ -14,31 +14,65 @@ import java
1414import semmle.code.java.dataflow.FlowSources
1515import DataFlow:: PathGraph
1616
17+ /** A method of ScriptEngine that allows code injection. */
1718class ScriptEngineMethod extends Method {
1819 ScriptEngineMethod ( ) {
1920 this .getDeclaringType ( ) .getASupertype * ( ) .hasQualifiedName ( "javax.script" , "ScriptEngine" ) and
2021 this .hasName ( "eval" )
22+ or
23+ this .getDeclaringType ( ) .getASupertype * ( ) .hasQualifiedName ( "javax.script" , "Compilable" ) and
24+ this .hasName ( "compile" )
25+ or
26+ this .getDeclaringType ( ) .getASupertype * ( ) .hasQualifiedName ( "javax.script" , "ScriptEngineFactory" ) and
27+ (
28+ this .hasName ( "getProgram" ) or
29+ this .hasName ( "getMethodCallSyntax" )
30+ )
2131 }
2232}
2333
24- /** The context class `org.mozilla.javascript.Context` of Rhino JavaScript Engine. */
34+ /** The context class `org.mozilla.javascript.Context` of Rhino Java Script Engine. */
2535class RhinoContext extends RefType {
2636 RhinoContext ( ) { this .hasQualifiedName ( "org.mozilla.javascript" , "Context" ) }
2737}
2838
29- /**
30- * A method that evaluates a Rhino expression.
31- */
39+ /** A method that evaluates a Rhino expression with `org.mozilla.javascript.Context`. */
3240class RhinoEvaluateExpressionMethod extends Method {
3341 RhinoEvaluateExpressionMethod ( ) {
3442 this .getDeclaringType ( ) .getAnAncestor * ( ) instanceof RhinoContext and
35- (
36- hasName ( "evaluateString" ) or
37- hasName ( "evaluateReader" )
38- )
43+ this .hasName ( [
44+ "evaluateString" , "evaluateReader" , "compileFunction" , "compileReader" , "compileString"
45+ ] )
3946 }
4047}
4148
49+ /**
50+ * A method that compiles a Rhino expression with
51+ * `org.mozilla.javascript.optimizer.ClassCompiler`.
52+ */
53+ class RhinoCompileClassMethod extends Method {
54+ RhinoCompileClassMethod ( ) {
55+ this .getDeclaringType ( )
56+ .getASupertype * ( )
57+ .hasQualifiedName ( "org.mozilla.javascript.optimizer" , "ClassCompiler" ) and
58+ this .hasName ( "compileToClassFiles" )
59+ }
60+ }
61+
62+ /**
63+ * A method that defines a Java class from a Rhino expression with
64+ * `org.mozilla.javascript.GeneratedClassLoader`.
65+ */
66+ class RhinoDefineClassMethod extends Method {
67+ RhinoDefineClassMethod ( ) {
68+ this .getDeclaringType ( )
69+ .getASupertype * ( )
70+ .hasQualifiedName ( "org.mozilla.javascript" , "GeneratedClassLoader" ) and
71+ this .hasName ( "defineClass" )
72+ }
73+ }
74+
75+ /** Holds if `ma` is a method access of `ScriptEngineMethod`. */
4276predicate scriptEngine ( MethodAccess ma , Expr sink ) {
4377 exists ( Method m | m = ma .getMethod ( ) |
4478 m instanceof ScriptEngineMethod and
@@ -47,11 +81,17 @@ predicate scriptEngine(MethodAccess ma, Expr sink) {
4781}
4882
4983/**
50- * Holds if `ma` has Rhino code injection vulnerabilities .
84+ * Holds if a Rhino expression evaluation method has the code injection vulnerability .
5185 */
5286predicate evaluateRhinoExpression ( MethodAccess ma , Expr sink ) {
5387 exists ( RhinoEvaluateExpressionMethod m | m = ma .getMethod ( ) |
54- sink = ma .getArgument ( 1 ) and // The second argument is the JavaScript or Java input
88+ (
89+ sink = ma .getArgument ( 1 ) and // The second argument is the JavaScript or Java input
90+ not ma .getMethod ( ) .getName ( ) = "compileReader"
91+ or
92+ sink = ma .getArgument ( 0 ) and // The first argument is the input reader
93+ ma .getMethod ( ) .getName ( ) = "compileReader"
94+ ) and
5595 not exists ( MethodAccess ca |
5696 (
5797 ca .getMethod ( ) .hasName ( "initSafeStandardObjects" ) // safe mode
@@ -63,15 +103,34 @@ predicate evaluateRhinoExpression(MethodAccess ma, Expr sink) {
63103 )
64104}
65105
106+ /**
107+ * Holds if a Rhino expression compilation method has the code injection vulnerability.
108+ */
109+ predicate compileScript ( MethodAccess ma , Expr sink ) {
110+ exists ( RhinoCompileClassMethod m | m = ma .getMethod ( ) | sink = ma .getArgument ( 0 ) )
111+ }
112+
113+ /**
114+ * Holds if a Rhino class loading method has the code injection vulnerability.
115+ */
116+ predicate defineClass ( MethodAccess ma , Expr sink ) {
117+ exists ( RhinoDefineClassMethod m | m = ma .getMethod ( ) | sink = ma .getArgument ( 1 ) )
118+ }
119+
120+ /** A sink of script injection. */
66121class ScriptInjectionSink extends DataFlow:: ExprNode {
67122 ScriptInjectionSink ( ) {
68123 scriptEngine ( _, this .getExpr ( ) ) or
69- evaluateRhinoExpression ( _, this .getExpr ( ) )
124+ evaluateRhinoExpression ( _, this .getExpr ( ) ) or
125+ compileScript ( _, this .getExpr ( ) ) or
126+ defineClass ( _, this .getExpr ( ) )
70127 }
71128
72129 MethodAccess getMethodAccess ( ) {
73130 scriptEngine ( result , this .getExpr ( ) ) or
74- evaluateRhinoExpression ( result , this .getExpr ( ) )
131+ evaluateRhinoExpression ( result , this .getExpr ( ) ) or
132+ compileScript ( result , this .getExpr ( ) ) or
133+ defineClass ( result , this .getExpr ( ) )
75134 }
76135}
77136
@@ -90,4 +149,4 @@ class ScriptInjectionConfiguration extends TaintTracking::Configuration {
90149from DataFlow:: PathNode source , DataFlow:: PathNode sink , ScriptInjectionConfiguration conf
91150where conf .hasFlowPath ( source , sink )
92151select sink .getNode ( ) .( ScriptInjectionSink ) .getMethodAccess ( ) , source , sink ,
93- "JavaScript Engine evaluate $@." , source .getNode ( ) , "user input"
152+ "Java Script Engine evaluate $@." , source .getNode ( ) , "user input"
0 commit comments