@@ -5,6 +5,8 @@ private import codeql.ruby.DataFlow
5
5
private import codeql.ruby.Concepts
6
6
private import codeql.ruby.AST
7
7
private import codeql.ruby.dataflow.FlowSummary
8
+ private import codeql.ruby.dataflow.internal.DataFlowPrivate as DataFlowPrivate
9
+ private import codeql.ruby.dataflow.SSA
8
10
9
11
/** Provides modeling for the Sinatra library. */
10
12
module Sinatra {
@@ -30,10 +32,9 @@ module Sinatra {
30
32
}
31
33
32
34
private class Params extends DataFlow:: CallNode , Http:: Server:: RequestInputAccess:: Range {
33
- private Route route ;
34
-
35
35
Params ( ) {
36
- this .asExpr ( ) .getExpr ( ) .getEnclosingCallable ( ) = route .getBody ( ) .asCallableAstNode ( ) and
36
+ this .asExpr ( ) .getExpr ( ) .getEnclosingCallable ( ) =
37
+ [ any ( Route r ) .getBody ( ) , any ( Filter f ) .getBody ( ) ] .asCallableAstNode ( ) and
37
38
this .getMethodName ( ) = "params"
38
39
}
39
40
@@ -140,6 +141,9 @@ module Sinatra {
140
141
141
142
Filter ( ) { this = app .getAModuleLevelCall ( [ "before" , "after" ] ) }
142
143
144
+ /** Gets the app which this filter belongs to. */
145
+ App getApp ( ) { result = app }
146
+
143
147
/**
144
148
* Gets the pattern which constrains this route, if any. In the example below, the pattern is `/protected/*`.
145
149
* Patterns are typically given as strings, and are interpreted by the `mustermann` gem (they are not regular expressions).
@@ -169,4 +173,50 @@ module Sinatra {
169
173
class AfterFilter extends Filter {
170
174
AfterFilter ( ) { this .getMethodName ( ) = "after" }
171
175
}
176
+
177
+ /**
178
+ * A class defining additional jump steps arising from filters.
179
+ */
180
+ class FilterJumpStep extends DataFlowPrivate:: AdditionalJumpStep {
181
+ /**
182
+ * Holds if data can flow from `pred` to `succ` via a callback chain.
183
+ */
184
+ override predicate step ( DataFlow:: Node pred , DataFlow:: Node succ ) {
185
+ exists ( BeforeFilter filter , Route route |
186
+ // the filter and route belong to the same app
187
+ filter .getApp ( ) = route .getApp ( ) and
188
+ // the filter applies to all routes
189
+ not filter .hasPattern ( ) and
190
+ selfPostUpdate ( pred , filter .getApp ( ) , filter .getBody ( ) .asExpr ( ) .getExpr ( ) ) and
191
+ blockCapturedSelfParameterNode ( succ , route .getBody ( ) .asExpr ( ) .getExpr ( ) )
192
+ )
193
+ }
194
+
195
+ /**
196
+ * Holds if `n` is a post-update node for the `self` parameter of `app` in block `b`.
197
+ *
198
+ * In this example, `n` is the post-update node for `@foo = 1`.
199
+ * ```rb
200
+ * class MyApp < Sinatra::Base
201
+ * before do
202
+ * @foo = 1
203
+ * end
204
+ * end
205
+ * ```
206
+ */
207
+ private predicate selfPostUpdate ( DataFlow:: PostUpdateNode n , App app , Block b ) {
208
+ n .getPreUpdateNode ( ) .asExpr ( ) .getExpr ( ) =
209
+ any ( SelfVariableAccess self |
210
+ pragma [ only_bind_into ] ( b ) = self .getEnclosingCallable ( ) and
211
+ self .getVariable ( ) .getDeclaringScope ( ) = app .getADeclaration ( )
212
+ )
213
+ }
214
+ }
215
+
216
+ private predicate blockCapturedSelfParameterNode ( DataFlow:: Node n , Block b ) {
217
+ exists ( Ssa:: CapturedSelfDefinition d |
218
+ n .( DataFlowPrivate:: SsaDefinitionExtNode ) .getDefinitionExt ( ) = d and
219
+ d .getBasicBlock ( ) .getScope ( ) = b
220
+ )
221
+ }
172
222
}
0 commit comments