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,62 @@ 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+ this .hasName ( [ "getProgram" , "getMethodCallSyntax" ] )
2128 }
2229}
2330
24- /** The context class `org.mozilla.javascript.Context` of Rhino JavaScript Engine. */
31+ /** The context class `org.mozilla.javascript.Context` of Rhino Java Script Engine. */
2532class RhinoContext extends RefType {
2633 RhinoContext ( ) { this .hasQualifiedName ( "org.mozilla.javascript" , "Context" ) }
2734}
2835
29- /**
30- * A method that evaluates a Rhino expression.
31- */
36+ /** A method that evaluates a Rhino expression with `org.mozilla.javascript.Context`. */
3237class RhinoEvaluateExpressionMethod extends Method {
3338 RhinoEvaluateExpressionMethod ( ) {
3439 this .getDeclaringType ( ) .getAnAncestor * ( ) instanceof RhinoContext and
35- (
36- hasName ( "evaluateString" ) or
37- hasName ( "evaluateReader" )
38- )
40+ this .hasName ( [
41+ "evaluateString" , "evaluateReader" , "compileFunction" , "compileReader" , "compileString"
42+ ] )
3943 }
4044}
4145
46+ /**
47+ * A method that compiles a Rhino expression with
48+ * `org.mozilla.javascript.optimizer.ClassCompiler`.
49+ */
50+ class RhinoCompileClassMethod extends Method {
51+ RhinoCompileClassMethod ( ) {
52+ this .getDeclaringType ( )
53+ .getASupertype * ( )
54+ .hasQualifiedName ( "org.mozilla.javascript.optimizer" , "ClassCompiler" ) and
55+ this .hasName ( "compileToClassFiles" )
56+ }
57+ }
58+
59+ /**
60+ * A method that defines a Java class from a Rhino expression with
61+ * `org.mozilla.javascript.GeneratedClassLoader`.
62+ */
63+ class RhinoDefineClassMethod extends Method {
64+ RhinoDefineClassMethod ( ) {
65+ this .getDeclaringType ( )
66+ .getASupertype * ( )
67+ .hasQualifiedName ( "org.mozilla.javascript" , "GeneratedClassLoader" ) and
68+ this .hasName ( "defineClass" )
69+ }
70+ }
71+
72+ /** Holds if `ma` is a method access of `ScriptEngineMethod`. */
4273predicate scriptEngine ( MethodAccess ma , Expr sink ) {
4374 exists ( Method m | m = ma .getMethod ( ) |
4475 m instanceof ScriptEngineMethod and
@@ -47,11 +78,17 @@ predicate scriptEngine(MethodAccess ma, Expr sink) {
4778}
4879
4980/**
50- * Holds if `ma` has Rhino code injection vulnerabilities .
81+ * Holds if a Rhino expression evaluation method has the code injection vulnerability .
5182 */
5283predicate evaluateRhinoExpression ( MethodAccess ma , Expr sink ) {
5384 exists ( RhinoEvaluateExpressionMethod m | m = ma .getMethod ( ) |
54- sink = ma .getArgument ( 1 ) and // The second argument is the JavaScript or Java input
85+ (
86+ sink = ma .getArgument ( 1 ) and // The second argument is the JavaScript or Java input
87+ not ma .getMethod ( ) .getName ( ) = "compileReader"
88+ or
89+ sink = ma .getArgument ( 0 ) and // The first argument is the input reader
90+ ma .getMethod ( ) .getName ( ) = "compileReader"
91+ ) and
5592 not exists ( MethodAccess ca |
5693 (
5794 ca .getMethod ( ) .hasName ( "initSafeStandardObjects" ) // safe mode
@@ -63,15 +100,34 @@ predicate evaluateRhinoExpression(MethodAccess ma, Expr sink) {
63100 )
64101}
65102
103+ /**
104+ * Holds if a Rhino expression compilation method has the code injection vulnerability.
105+ */
106+ predicate compileScript ( MethodAccess ma , Expr sink ) {
107+ exists ( RhinoCompileClassMethod m | m = ma .getMethod ( ) | sink = ma .getArgument ( 0 ) )
108+ }
109+
110+ /**
111+ * Holds if a Rhino class loading method has the code injection vulnerability.
112+ */
113+ predicate defineClass ( MethodAccess ma , Expr sink ) {
114+ exists ( RhinoDefineClassMethod m | m = ma .getMethod ( ) | sink = ma .getArgument ( 1 ) )
115+ }
116+
117+ /** A sink of script injection. */
66118class ScriptInjectionSink extends DataFlow:: ExprNode {
67119 ScriptInjectionSink ( ) {
68120 scriptEngine ( _, this .getExpr ( ) ) or
69- evaluateRhinoExpression ( _, this .getExpr ( ) )
121+ evaluateRhinoExpression ( _, this .getExpr ( ) ) or
122+ compileScript ( _, this .getExpr ( ) ) or
123+ defineClass ( _, this .getExpr ( ) )
70124 }
71125
72126 MethodAccess getMethodAccess ( ) {
73127 scriptEngine ( result , this .getExpr ( ) ) or
74- evaluateRhinoExpression ( result , this .getExpr ( ) )
128+ evaluateRhinoExpression ( result , this .getExpr ( ) ) or
129+ compileScript ( result , this .getExpr ( ) ) or
130+ defineClass ( result , this .getExpr ( ) )
75131 }
76132}
77133
@@ -90,4 +146,4 @@ class ScriptInjectionConfiguration extends TaintTracking::Configuration {
90146from DataFlow:: PathNode source , DataFlow:: PathNode sink , ScriptInjectionConfiguration conf
91147where conf .hasFlowPath ( source , sink )
92148select sink .getNode ( ) .( ScriptInjectionSink ) .getMethodAccess ( ) , source , sink ,
93- "JavaScript Engine evaluate $@." , source .getNode ( ) , "user input"
149+ "Java Script Engine evaluate $@." , source .getNode ( ) , "user input"
0 commit comments