@@ -5,6 +5,8 @@ private import codeql.ruby.DataFlow
55private import codeql.ruby.Concepts
66private import codeql.ruby.AST
77private import codeql.ruby.dataflow.FlowSummary
8+ private import codeql.ruby.dataflow.internal.DataFlowPrivate as DataFlowPrivate
9+ private import codeql.ruby.dataflow.SSA
810
911/** Provides modeling for the Sinatra library. */
1012module Sinatra {
@@ -30,10 +32,9 @@ module Sinatra {
3032 }
3133
3234 private class Params extends DataFlow:: CallNode , Http:: Server:: RequestInputAccess:: Range {
33- private Route route ;
34-
3535 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
3738 this .getMethodName ( ) = "params"
3839 }
3940
@@ -140,6 +141,9 @@ module Sinatra {
140141
141142 Filter ( ) { this = app .getAModuleLevelCall ( [ "before" , "after" ] ) }
142143
144+ /** Gets the app which this filter belongs to. */
145+ App getApp ( ) { result = app }
146+
143147 /**
144148 * Gets the pattern which constrains this route, if any. In the example below, the pattern is `/protected/*`.
145149 * Patterns are typically given as strings, and are interpreted by the `mustermann` gem (they are not regular expressions).
@@ -169,4 +173,50 @@ module Sinatra {
169173 class AfterFilter extends Filter {
170174 AfterFilter ( ) { this .getMethodName ( ) = "after" }
171175 }
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+ }
172222}
0 commit comments