1
1
import java
2
- import FlowUtils
3
- import semmle.code.java.dataflow.FlowSources
4
2
import semmle.code.java.dataflow.TaintTracking
3
+ private import semmle.code.java.dataflow.ExternalFlow
5
4
6
5
/**
7
- * A taint-tracking configuration for unsafe user input
8
- * that is used to construct and evaluate a JEXL expression.
9
- * It supports both JEXL 2 and 3.
6
+ * A sink for Expresssion Language injection vulnerabilities via Jexl,
7
+ * i.e. method calls that run evaluation of a JEXL expression.
10
8
*/
11
- class JexlInjectionConfig extends TaintTracking:: Configuration {
12
- JexlInjectionConfig ( ) { this = "JexlInjectionConfig" }
9
+ abstract class JexlEvaluationSink extends DataFlow:: ExprNode { }
13
10
14
- override predicate isSource ( DataFlow:: Node source ) { source instanceof RemoteFlowSource }
15
-
16
- override predicate isSink ( DataFlow:: Node sink ) { sink instanceof JexlEvaluationSink }
11
+ /** Default sink for JXEL injection vulnerabilities. */
12
+ private class DefaultJexlEvaluationSink extends JexlEvaluationSink {
13
+ DefaultJexlEvaluationSink ( ) { sinkNode ( this , "jexl" ) }
14
+ }
17
15
18
- override predicate isAdditionalTaintStep ( DataFlow:: Node fromNode , DataFlow:: Node toNode ) {
19
- any ( TaintPropagatingJexlMethodCall c ) .taintFlow ( fromNode , toNode ) or
20
- hasGetterFlow ( fromNode , toNode )
16
+ private class DefaultJexlInjectionSinkModel extends SinkModelCsv {
17
+ override predicate row ( string row ) {
18
+ row =
19
+ [
20
+ // JEXL2
21
+ "org.apache.commons.jexl2;JexlEngine;false;getProperty;(JexlContext,Object,String);;Argument[2];jexl" ,
22
+ "org.apache.commons.jexl2;JexlEngine;false;getProperty;(Object,String);;Argument[1];jexl" ,
23
+ "org.apache.commons.jexl2;JexlEngine;false;setProperty;(JexlContext,Object,String,Object);;Argument[2];jexl" ,
24
+ "org.apache.commons.jexl2;JexlEngine;false;setProperty;(Object,String,Object);;Argument[1];jexl" ,
25
+ "org.apache.commons.jexl2;Expression;false;evaluate;;;Argument[-1];jexl" ,
26
+ "org.apache.commons.jexl2;Expression;false;callable;;;Argument[-1];jexl" ,
27
+ "org.apache.commons.jexl2;JexlExpression;false;evaluate;;;Argument[-1];jexl" ,
28
+ "org.apache.commons.jexl2;JexlExpression;false;callable;;;Argument[-1];jexl" ,
29
+ "org.apache.commons.jexl2;Script;false;execute;;;Argument[-1];jexl" ,
30
+ "org.apache.commons.jexl2;Script;false;callable;;;Argument[-1];jexl" ,
31
+ "org.apache.commons.jexl2;JexlScript;false;execute;;;Argument[-1];jexl" ,
32
+ "org.apache.commons.jexl2;JexlScript;false;callable;;;Argument[-1];jexl" ,
33
+ "org.apache.commons.jexl2;JxltEngine$Expression;false;evaluate;;;Argument[-1];jexl" ,
34
+ "org.apache.commons.jexl2;JxltEngine$Expression;false;prepare;;;Argument[-1];jexl" ,
35
+ "org.apache.commons.jexl2;JxltEngine$Template;false;evaluate;;;Argument[-1];jexl" ,
36
+ "org.apache.commons.jexl2;UnifiedJEXL$Expression;false;evaluate;;;Argument[-1];jexl" ,
37
+ "org.apache.commons.jexl2;UnifiedJEXL$Expression;false;prepare;;;Argument[-1];jexl" ,
38
+ "org.apache.commons.jexl2;UnifiedJEXL$Template;false;evaluate;;;Argument[-1];jexl" ,
39
+ // JEXL3
40
+ "org.apache.commons.jexl3;JexlEngine;false;getProperty;(JexlContext,Object,String);;Argument[2];jexl" ,
41
+ "org.apache.commons.jexl3;JexlEngine;false;getProperty;(Object,String);;Argument[1];jexl" ,
42
+ "org.apache.commons.jexl3;JexlEngine;false;setProperty;(JexlContext,Object,String);;Argument[2];jexl" ,
43
+ "org.apache.commons.jexl3;JexlEngine;false;setProperty;(Object,String,Object);;Argument[1];jexl" ,
44
+ "org.apache.commons.jexl3;Expression;false;evaluate;;;Argument[-1];jexl" ,
45
+ "org.apache.commons.jexl3;Expression;false;callable;;;Argument[-1];jexl" ,
46
+ "org.apache.commons.jexl3;JexlExpression;false;evaluate;;;Argument[-1];jexl" ,
47
+ "org.apache.commons.jexl3;JexlExpression;false;callable;;;Argument[-1];jexl" ,
48
+ "org.apache.commons.jexl3;Script;false;execute;;;Argument[-1];jexl" ,
49
+ "org.apache.commons.jexl3;Script;false;callable;;;Argument[-1];jexl" ,
50
+ "org.apache.commons.jexl3;JexlScript;false;execute;;;Argument[-1];jexl" ,
51
+ "org.apache.commons.jexl3;JexlScript;false;callable;;;Argument[-1];jexl" ,
52
+ "org.apache.commons.jexl3;JxltEngine$Expression;false;evaluate;;;Argument[-1];jexl" ,
53
+ "org.apache.commons.jexl3;JxltEngine$Expression;false;prepare;;;Argument[-1];jexl" ,
54
+ "org.apache.commons.jexl3;JxltEngine$Template;false;evaluate;;;Argument[-1];jexl" ,
55
+ "org.apache.commons.jexl3;UnifiedJEXL$Expression;false;evaluate;;;Argument[-1];jexl" ,
56
+ "org.apache.commons.jexl3;UnifiedJEXL$Expression;false;prepare;;;Argument[-1];jexl" ,
57
+ "org.apache.commons.jexl3;UnifiedJEXL$Template;false;evaluate;;;Argument[-1];jexl"
58
+ ]
21
59
}
22
60
}
23
61
24
62
/**
25
- * A sink for Expresssion Language injection vulnerabilities via Jexl,
26
- * i.e. method calls that run evaluation of a JEXL expression.
63
+ * A unit class for adding additional taint steps.
27
64
*
28
- * Creating a `Callable` from a tainted JEXL expression or script is considered as a sink
29
- * although the tainted expression is not executed at this point.
30
- * Here we assume that it will get executed at some point,
31
- * maybe stored in an object field and then reached by a different flow.
65
+ * Extend this class to add additional taint steps that should apply to the `JexlInjectionFlowConfig`.
32
66
*/
33
- private class JexlEvaluationSink extends DataFlow:: ExprNode {
34
- JexlEvaluationSink ( ) {
35
- exists ( MethodAccess ma , Method m , Expr taintFrom |
36
- ma .getMethod ( ) = m and taintFrom = this .asExpr ( )
37
- |
38
- m instanceof DirectJexlEvaluationMethod and ma .getQualifier ( ) = taintFrom
39
- or
40
- m instanceof CreateJexlCallableMethod and ma .getQualifier ( ) = taintFrom
41
- or
42
- m instanceof JexlEngineGetSetPropertyMethod and
43
- taintFrom .getType ( ) instanceof TypeString and
44
- ma .getAnArgument ( ) = taintFrom
45
- )
67
+ abstract class JexlInjectionAdditionalTaintStep extends Unit {
68
+ /**
69
+ * Holds if the step from `node1` to `node2` should be considered a taint
70
+ * step for the `JexlInjectionConfig` configuration.
71
+ */
72
+ abstract predicate step ( DataFlow:: Node node1 , DataFlow:: Node node2 ) ;
73
+ }
74
+
75
+ /** A set of additional taint steps to consider when taint tracking JXEL related data flows. */
76
+ private class DefaultJexlInjectionAdditionalTaintStep extends JexlInjectionAdditionalTaintStep {
77
+ override predicate step ( DataFlow:: Node node1 , DataFlow:: Node node2 ) {
78
+ createJexlScriptStep ( node1 , node2 ) or
79
+ createJexlExpressionStep ( node1 , node2 ) or
80
+ createJexlTemplateStep ( node1 , node2 )
46
81
}
47
82
}
48
83
49
84
/**
50
- * Defines method calls that propagate tainted data via one of the methods
51
- * from JEXL library .
85
+ * Holds if `n1` to `n2` is a dataflow step that creates a JEXL script using an unsafe engine
86
+ * i.e. `tainted.createScript(jexlExpr)` .
52
87
*/
53
- private class TaintPropagatingJexlMethodCall extends MethodAccess {
54
- Expr taintFromExpr ;
55
-
56
- TaintPropagatingJexlMethodCall ( ) {
57
- exists ( Method m , RefType taintType |
58
- this .getMethod ( ) = m and
59
- taintType = taintFromExpr .getType ( )
60
- |
61
- isUnsafeEngine ( this .getQualifier ( ) ) and
62
- (
63
- m instanceof CreateJexlScriptMethod and
64
- taintFromExpr = this .getArgument ( 0 ) and
65
- taintType instanceof TypeString
66
- or
67
- m instanceof CreateJexlExpressionMethod and
68
- taintFromExpr = this .getAnArgument ( ) and
69
- taintType instanceof TypeString
70
- or
71
- m instanceof CreateJexlTemplateMethod and
72
- ( taintType instanceof TypeString or taintType instanceof Reader ) and
73
- taintFromExpr = this .getArgument ( [ 0 , 1 ] )
74
- )
75
- )
76
- }
88
+ private predicate createJexlScriptStep ( DataFlow:: Node n1 , DataFlow:: Node n2 ) {
89
+ exists ( MethodAccess ma , Method m | m = ma .getMethod ( ) and n2 .asExpr ( ) = ma |
90
+ isUnsafeEngine ( ma .getQualifier ( ) ) and
91
+ m instanceof CreateJexlScriptMethod and
92
+ n1 .asExpr ( ) = ma .getArgument ( 0 ) and
93
+ n1 .asExpr ( ) .getType ( ) instanceof TypeString
94
+ )
95
+ }
77
96
78
- /**
79
- * Holds if `fromNode` to `toNode` is a dataflow step that propagates
80
- * tainted data.
81
- */
82
- predicate taintFlow ( DataFlow:: Node fromNode , DataFlow:: Node toNode ) {
83
- fromNode .asExpr ( ) = taintFromExpr and toNode .asExpr ( ) = this
84
- }
97
+ /**
98
+ * Holds if `n1` to `n2` is a dataflow step that creates a JEXL expression using an unsafe engine
99
+ * i.e. `tainted.createExpression(jexlExpr)`.
100
+ */
101
+ private predicate createJexlExpressionStep ( DataFlow:: Node n1 , DataFlow:: Node n2 ) {
102
+ exists ( MethodAccess ma , Method m | m = ma .getMethod ( ) and n2 .asExpr ( ) = ma |
103
+ isUnsafeEngine ( ma .getQualifier ( ) ) and
104
+ m instanceof CreateJexlExpressionMethod and
105
+ n1 .asExpr ( ) = ma .getAnArgument ( ) and
106
+ n1 .asExpr ( ) .getType ( ) instanceof TypeString
107
+ )
108
+ }
109
+
110
+ /**
111
+ * Holds if `n1` to `n2` is a dataflow step that creates a JEXL template using an unsafe engine
112
+ * i.e. `tainted.createTemplate(jexlExpr)`.
113
+ */
114
+ private predicate createJexlTemplateStep ( DataFlow:: Node n1 , DataFlow:: Node n2 ) {
115
+ exists ( MethodAccess ma , Method m , RefType taintType |
116
+ m = ma .getMethod ( ) and n2 .asExpr ( ) = ma and taintType = n1 .asExpr ( ) .getType ( )
117
+ |
118
+ isUnsafeEngine ( ma .getQualifier ( ) ) and
119
+ m instanceof CreateJexlTemplateMethod and
120
+ n1 .asExpr ( ) = ma .getArgument ( [ 0 , 1 ] ) and
121
+ ( taintType instanceof TypeString or taintType instanceof Reader )
122
+ )
85
123
}
86
124
87
125
/**
@@ -92,7 +130,7 @@ private predicate isUnsafeEngine(Expr expr) {
92
130
}
93
131
94
132
/**
95
- * A configuration for a tracking sandboxed JEXL engines.
133
+ * A configuration for tracking sandboxed JEXL engines.
96
134
*/
97
135
private class SandboxedJexlFlowConfig extends DataFlow2:: Configuration {
98
136
SandboxedJexlFlowConfig ( ) { this = "JexlInjection::SandboxedJexlFlowConfig" }
0 commit comments