@@ -4,9 +4,9 @@ import java
4
4
private import semmle.code.java.frameworks.spring.SpringController
5
5
private import semmle.code.java.frameworks.MyBatis
6
6
private import semmle.code.java.frameworks.Jdbc
7
- private import semmle.code.java.dataflow.DataFlow
8
7
private import semmle.code.java.dataflow.ExternalFlow
9
8
private import semmle.code.java.dispatch.VirtualDispatch
9
+ private import semmle.code.java.dataflow.TaintTracking
10
10
11
11
/** A method that is not protected from CSRF by default. */
12
12
abstract class CsrfUnprotectedMethod extends Method { }
@@ -66,10 +66,9 @@ private class PreparedStatementDatabaseUpdateMethod extends DatabaseUpdateMethod
66
66
}
67
67
}
68
68
69
- /** A method that updates a SQL database. */
70
- private class SqlDatabaseUpdateMethod extends DatabaseUpdateMethod {
71
- SqlDatabaseUpdateMethod ( ) {
72
- // TODO: constrain to only insert/update/delete for `execute%` methods; need to track the sql expression into the execute call.
69
+ /** A method found via the sql-injection models which may update a SQL database. */
70
+ private class SqlInjectionMethod extends DatabaseUpdateMethod {
71
+ SqlInjectionMethod ( ) {
73
72
exists ( DataFlow:: Node n | this = n .asExpr ( ) .( Argument ) .getCall ( ) .getCallee ( ) |
74
73
sinkNode ( n , "sql-injection" ) and
75
74
// do not include `executeQuery` since it is typically used with a select statement
@@ -81,12 +80,33 @@ private class SqlDatabaseUpdateMethod extends DatabaseUpdateMethod {
81
80
}
82
81
}
83
82
83
+ /**
84
+ * A taint-tracking configuration for reasoning about SQL queries that update a database.
85
+ */
86
+ module SqlExecuteConfig implements DataFlow:: ConfigSig {
87
+ predicate isSource ( DataFlow:: Node source ) {
88
+ exists ( StringLiteral sl | source .asExpr ( ) = sl |
89
+ sl .getValue ( ) .regexpMatch ( "^(?i)(insert|update|delete).*" )
90
+ )
91
+ }
92
+
93
+ predicate isSink ( DataFlow:: Node sink ) {
94
+ exists ( Method m | m = sink .asExpr ( ) .( Argument ) .getCall ( ) .getCallee ( ) |
95
+ m instanceof SqlInjectionMethod and
96
+ m .hasName ( "execute" )
97
+ )
98
+ }
99
+ }
100
+
101
+ /** Tracks flow from SQL queries that update a database to the argument of an execute method call. */
102
+ module SqlExecuteFlow = TaintTracking:: Global< SqlExecuteConfig > ;
103
+
84
104
module CallGraph {
85
- newtype TPathNode =
105
+ newtype TCallPathNode =
86
106
TMethod ( Method m ) or
87
107
TCall ( Call c )
88
108
89
- class PathNode extends TPathNode {
109
+ class CallPathNode extends TCallPathNode {
90
110
Method asMethod ( ) { this = TMethod ( result ) }
91
111
92
112
Call asCall ( ) { this = TCall ( result ) }
@@ -97,16 +117,16 @@ module CallGraph {
97
117
result = this .asCall ( ) .toString ( )
98
118
}
99
119
100
- private PathNode getACallee ( ) {
120
+ private CallPathNode getACallee ( ) {
101
121
[ viableCallable ( this .asCall ( ) ) , this .asCall ( ) .getCallee ( ) ] = result .asMethod ( )
102
122
}
103
123
104
- PathNode getASuccessor ( ) {
124
+ CallPathNode getASuccessor ( ) {
105
125
this .asMethod ( ) = result .asCall ( ) .getEnclosingCallable ( )
106
126
or
107
127
result = this .getACallee ( ) and
108
128
(
109
- exists ( PathNode p |
129
+ exists ( CallPathNode p |
110
130
p = this .getACallee ( ) and
111
131
p .asMethod ( ) instanceof DatabaseUpdateMethod
112
132
)
@@ -122,15 +142,25 @@ module CallGraph {
122
142
}
123
143
}
124
144
125
- predicate edges ( PathNode pred , PathNode succ ) { pred .getASuccessor ( ) = succ }
145
+ predicate edges ( CallPathNode pred , CallPathNode succ ) { pred .getASuccessor ( ) = succ }
126
146
}
127
147
128
148
import CallGraph
129
149
130
150
/** Holds if `src` is an unprotected request handler that reaches a state-changing `sink`. */
131
- predicate unprotectedStateChange ( PathNode src , PathNode sink , PathNode sinkPred ) {
151
+ predicate unprotectedStateChange ( CallPathNode src , CallPathNode sink , CallPathNode sinkPred ) {
132
152
src .asMethod ( ) instanceof CsrfUnprotectedMethod and
133
153
sink .asMethod ( ) instanceof DatabaseUpdateMethod and
134
154
sinkPred .getASuccessor ( ) = sink and
135
- src .getASuccessor + ( ) = sinkPred
155
+ src .getASuccessor + ( ) = sinkPred and
156
+ if
157
+ sink .asMethod ( ) instanceof SqlInjectionMethod and
158
+ sink .asMethod ( ) .hasName ( "execute" )
159
+ then
160
+ exists ( SqlExecuteFlow:: PathNode executeSrc , SqlExecuteFlow:: PathNode executeSink |
161
+ SqlExecuteFlow:: flowPath ( executeSrc , executeSink )
162
+ |
163
+ sinkPred .asCall ( ) = executeSink .getNode ( ) .asExpr ( ) .( Argument ) .getCall ( )
164
+ )
165
+ else any ( )
136
166
}
0 commit comments