@@ -7,19 +7,40 @@ private import codeql.ruby.DataFlow
77private import codeql.ruby.typetracking.TypeTracker
88private import Response:: Private as RP
99
10- /** A method node for a method named `call`. */
11- private class CallMethodNode extends DataFlow:: MethodNode {
12- CallMethodNode ( ) { this .getMethodName ( ) = "call" }
10+ /**
11+ * A callable node that takes a single argument and, if it has a method name,
12+ * is called "call".
13+ */
14+ private class PotentialCallNode extends DataFlow:: CallableNode {
15+ PotentialCallNode ( ) {
16+ this .getNumberOfParameters ( ) = 1 and
17+ (
18+ this .( DataFlow:: MethodNode ) .getMethodName ( ) = "call" or
19+ not this instanceof DataFlow:: MethodNode
20+ )
21+ }
22+ }
23+
24+ /**
25+ * A callable node that looks like it implements the rack specification.
26+ */
27+ private class CallNode extends PotentialCallNode {
28+ private RP:: PotentialResponseNode resp ;
29+
30+ CallNode ( ) { resp = trackRackResponse ( this ) }
31+
32+ /** Gets the response returned from a request to this application. */
33+ RP:: PotentialResponseNode getResponse ( ) { result = resp }
1334}
1435
15- private DataFlow:: LocalSourceNode trackRackResponse ( TypeBackTracker t , CallMethodNode call ) {
36+ private DataFlow:: LocalSourceNode trackRackResponse ( TypeBackTracker t , DataFlow :: CallableNode call ) {
1637 t .start ( ) and
1738 result = call .getAReturnNode ( ) .getALocalSource ( )
1839 or
1940 exists ( TypeBackTracker t2 | result = trackRackResponse ( t2 , call ) .backtrack ( t2 , t ) )
2041}
2142
22- private RP:: PotentialResponseNode trackRackResponse ( CallMethodNode call ) {
43+ private RP:: PotentialResponseNode trackRackResponse ( DataFlow :: CallableNode call ) {
2344 result = trackRackResponse ( TypeBackTracker:: end ( ) , call )
2445}
2546
@@ -28,12 +49,13 @@ private RP::PotentialResponseNode trackRackResponse(CallMethodNode call) {
2849 */
2950module App {
3051 /**
52+ * DEPRECATED: Use `App` instead.
3153 * A class that may be a rack application.
3254 * This is a class that has a `call` method that takes a single argument
3355 * (traditionally called `env`) and returns a rack-compatible response.
3456 */
35- class AppCandidate extends DataFlow:: ClassNode {
36- private CallMethodNode call ;
57+ deprecated class AppCandidate extends DataFlow:: ClassNode {
58+ private CallNode call ;
3759 private RP:: PotentialResponseNode resp ;
3860
3961 AppCandidate ( ) {
@@ -50,4 +72,61 @@ module App {
5072 /** Gets the response returned from a request to this application. */
5173 RP:: PotentialResponseNode getResponse ( ) { result = resp }
5274 }
75+
76+ private newtype TApp =
77+ TClassApp ( DataFlow:: ClassNode cn , CallNode call ) or
78+ TAnonymousApp ( CallNode call )
79+
80+ /**
81+ * A rack application. This is either some object that responds to `call`
82+ * taking a single argument and returns a rack response, or a lambda or
83+ * proc that takes a single `env` argument and returns a rack response.
84+ */
85+ abstract class App extends TApp {
86+ string toString ( ) { result = "Rack application" }
87+
88+ abstract CallNode getCall ( ) ;
89+
90+ RP:: PotentialResponseNode getResponse ( ) { result = this .getCall ( ) .getResponse ( ) }
91+
92+ DataFlow:: ParameterNode getEnv ( ) { result = this .getCall ( ) .getParameter ( 0 ) }
93+ }
94+
95+ /**
96+ * A rack application using a `DataFlow::ClassNode`. The class has either
97+ * an instance method or a singleton method named "call" which takes a
98+ * single `env` argument and returns a rack response.
99+ */
100+ private class ClassApp extends TApp , App {
101+ private DataFlow:: ClassNode cn ;
102+ private CallNode call ;
103+
104+ ClassApp ( ) {
105+ this = TClassApp ( cn , call ) and
106+ call = [ cn .getInstanceMethod ( "call" ) , cn .getSingletonMethod ( "call" ) ]
107+ }
108+
109+ override string toString ( ) { result = "Rack application: " + cn .toString ( ) }
110+
111+ override CallNode getCall ( ) { result = call }
112+ }
113+
114+ /**
115+ * A rack application that is either a lambda or a proc, which takes a
116+ * single `env` argument and returns a rack response.
117+ */
118+ private class AnonymousApp extends TApp , App {
119+ private CallNode call ;
120+
121+ AnonymousApp ( ) {
122+ this = TAnonymousApp ( call ) and
123+ not exists ( DataFlow:: ClassNode cn |
124+ call = [ cn .getInstanceMethod ( _) , cn .getSingletonMethod ( _) ]
125+ )
126+ }
127+
128+ override string toString ( ) { result = "Rack application: " + call .toString ( ) }
129+
130+ override CallNode getCall ( ) { result = call }
131+ }
53132}
0 commit comments