@@ -49,6 +49,15 @@ private class StaplerCsrfUnprotectedMethod extends CsrfUnprotectedMethod instanc
49
49
}
50
50
}
51
51
52
+ /** A method that appears to change application state based on its name. */
53
+ private class NameStateChangeMethod extends Method {
54
+ NameStateChangeMethod ( ) {
55
+ this .getName ( )
56
+ .regexpMatch ( "(^|\\w+(?=[A-Z]))((?i)post|put|patch|delete|remove|create|add|update|edit|(un|)publish|fill|move|transfer|log(out|in)|access|connect(|ion)|register|submit)($|(?![a-z])\\w+)" ) and
57
+ not this .getName ( ) .regexpMatch ( "^(get|show|view|list|query|find)(?![a-z])\\w*" )
58
+ }
59
+ }
60
+
52
61
/** A method that updates a database. */
53
62
abstract class DatabaseUpdateMethod extends Method { }
54
63
@@ -163,20 +172,46 @@ module CallGraph {
163
172
164
173
import CallGraph
165
174
166
- /** Holds if `src` is an unprotected request handler that reaches a state-changing `sink`. */
167
- predicate unprotectedStateChange ( CallPathNode src , CallPathNode sink , CallPathNode sinkPred ) {
168
- src .asMethod ( ) instanceof CsrfUnprotectedMethod and
169
- sink .asMethod ( ) instanceof DatabaseUpdateMethod and
170
- sinkPred .getASuccessor ( ) = sink and
171
- src .getASuccessor + ( ) = sinkPred and
172
- if
173
- sink .asMethod ( ) instanceof SqlInjectionMethod and
174
- sink .asMethod ( ) .hasName ( "execute" )
175
- then
176
- exists ( SqlExecuteFlow:: PathNode executeSrc , SqlExecuteFlow:: PathNode executeSink |
177
- SqlExecuteFlow:: flowPath ( executeSrc , executeSink )
178
- |
179
- sinkPred .asCall ( ) = executeSink .getNode ( ) .asExpr ( ) .( Argument ) .getCall ( )
180
- )
181
- else any ( )
175
+ /**
176
+ * Holds if `src` is an unprotected request handler that reaches a
177
+ * `sink` that updates a database.
178
+ */
179
+ predicate unprotectedDatabaseUpdate ( CallPathNode sourceMethod , CallPathNode sinkMethodCall ) {
180
+ sourceMethod .asMethod ( ) instanceof CsrfUnprotectedMethod and
181
+ exists ( CallPathNode sinkMethod |
182
+ sinkMethod .asMethod ( ) instanceof DatabaseUpdateMethod and
183
+ sinkMethodCall .getASuccessor ( ) = sinkMethod and
184
+ sourceMethod .getASuccessor + ( ) = sinkMethodCall and
185
+ if
186
+ sinkMethod .asMethod ( ) instanceof SqlInjectionMethod and
187
+ sinkMethod .asMethod ( ) .hasName ( "execute" )
188
+ then
189
+ exists ( SqlExecuteFlow:: PathNode executeSrc , SqlExecuteFlow:: PathNode executeSink |
190
+ SqlExecuteFlow:: flowPath ( executeSrc , executeSink )
191
+ |
192
+ sinkMethodCall .asCall ( ) = executeSink .getNode ( ) .asExpr ( ) .( Argument ) .getCall ( )
193
+ )
194
+ else any ( )
195
+ )
196
+ }
197
+
198
+ /**
199
+ * Holds if `src` is an unprotected request handler that appears to
200
+ * change application state based on its name.
201
+ */
202
+ private predicate unprotectedHeuristicStateChange ( CallPathNode sourceMethod , CallPathNode sinkMethod ) {
203
+ sourceMethod .asMethod ( ) instanceof CsrfUnprotectedMethod and
204
+ sinkMethod .asMethod ( ) instanceof NameStateChangeMethod and
205
+ sinkMethod = sourceMethod and
206
+ // exclude any alerts that update a database
207
+ not unprotectedDatabaseUpdate ( sourceMethod , _)
208
+ }
209
+
210
+ /**
211
+ * Holds if `src` is an unprotected request handler that may
212
+ * change an application's state.
213
+ */
214
+ predicate unprotectedStateChange ( CallPathNode source , CallPathNode sink ) {
215
+ unprotectedDatabaseUpdate ( source , sink ) or
216
+ unprotectedHeuristicStateChange ( source , sink )
182
217
}
0 commit comments