@@ -19,49 +19,62 @@ private import semmle.python.frameworks.Stdlib
19
19
* See https://docs.pylonsproject.org/projects/pyramid/.
20
20
*/
21
21
module Pyramid {
22
- // TODO: qldoc
22
+ /** Provides models for pyramid View callables. */
23
23
module View {
24
24
/**
25
25
* A callable that could be used as a pyramid view callable.
26
26
*/
27
27
private class PotentialViewCallable extends Function {
28
- PotentialViewCallable ( ) {
28
+ PotentialViewCallable ( ) { this .getPositionalParameterCount ( ) in [ 1 , 2 ] }
29
+
30
+ /** Gets the `request` parameter of this view callable. */
31
+ Parameter getRequestParameter ( ) {
29
32
this .getPositionalParameterCount ( ) = 1 and
30
- this .getArgName ( 0 ) = "request"
33
+ result = this .getArg ( 0 )
31
34
or
32
35
this .getPositionalParameterCount ( ) = 2 and
33
- this .getArgName ( 0 ) = "context" and
34
- this .getArgName ( 1 ) = "request"
36
+ result = this .getArg ( 1 )
35
37
}
36
-
37
- /** Gets the `request` parameter of this view callable. */
38
- Parameter getRequestParameter ( ) { result = this .getArgByName ( "request" ) }
39
38
}
40
39
41
- abstract class ViewCallable extends PotentialViewCallable , Http:: Server:: RequestHandler:: Range {
42
- override Parameter getARoutedParameter ( ) { result = this .getRequestParameter ( ) }
43
-
40
+ /** A dataflow node that sets up a route on a server using the Pyramid framework. */
41
+ abstract private class PyramidRouteSetup extends Http:: Server:: RouteSetup:: Range {
44
42
override string getFramework ( ) { result = "Pyramid" }
45
43
}
46
44
47
- private class ViewCallableFromDecorator extends ViewCallable {
48
- ViewCallableFromDecorator ( ) {
49
- this .getADecorator ( ) =
50
- API:: moduleImport ( "pyramid" )
51
- .getMember ( "view" )
52
- .getMember ( "view_config" )
53
- .getACall ( )
54
- .asExpr ( )
45
+ /**
46
+ * A Pyramid view callable, that handles incoming requests.
47
+ */
48
+ class ViewCallable extends PotentialViewCallable {
49
+ ViewCallable ( ) { this = any ( PyramidRouteSetup rs ) .getARequestHandler ( ) }
50
+ }
51
+
52
+ /** A pyramid route setup using the `pyramid.view.view_config` decorator. */
53
+ private class DecoratorSetup extends PyramidRouteSetup {
54
+ DecoratorSetup ( ) {
55
+ this = API:: moduleImport ( "pyramid" ) .getMember ( "view" ) .getMember ( "view_config" ) .getACall ( )
55
56
}
57
+
58
+ override Function getARequestHandler ( ) { result .getADecorator ( ) = this .asExpr ( ) }
59
+
60
+ override DataFlow:: Node getUrlPatternArg ( ) { none ( ) } // there is a `route_name` arg, but that does not contain the url pattern
61
+
62
+ override Parameter getARoutedParameter ( ) { none ( ) }
56
63
}
57
64
58
- private class ViewCallableFromConfigurator extends ViewCallable {
59
- ViewCallableFromConfigurator ( ) {
60
- any ( Configurator:: AddViewCall c ) .getViewArg ( ) = poorMansFunctionTracker ( this )
65
+ /** A pyramid route setup using a call to `pyramid.config.Configurator.add_view`. */
66
+ private class ConfiguratorSetup extends PyramidRouteSetup instanceof Configurator:: AddViewCall {
67
+ override Function getARequestHandler ( ) {
68
+ this .( Configurator:: AddViewCall ) .getViewArg ( ) = poorMansFunctionTracker ( result )
61
69
}
70
+
71
+ override DataFlow:: Node getUrlPatternArg ( ) { none ( ) } // there is a `route_name` arg, but that does not contain the url pattern
72
+
73
+ override Parameter getARoutedParameter ( ) { none ( ) }
62
74
}
63
75
}
64
76
77
+ /** Provides models for `pyramid.config.Configurator` */
65
78
module Configurator {
66
79
/** Gets a reference to the class `pyramid.config.Configurator`. */
67
80
API:: Node classRef ( ) {
@@ -79,14 +92,21 @@ module Pyramid {
79
92
/** Gets a reference to an instance of `pyramid.config.Configurator`. */
80
93
DataFlow:: Node instance ( ) { instance ( DataFlow:: TypeTracker:: end ( ) ) .flowsTo ( result ) }
81
94
95
+ /** Gets a call to the `add_view` method of an instance of `pyramid.config.Configurator`. */
82
96
class AddViewCall extends DataFlow:: MethodCallNode {
83
97
AddViewCall ( ) { this .calls ( instance ( ) , "add_view" ) }
84
98
85
99
DataFlow:: Node getViewArg ( ) { result = [ this .getArg ( 0 ) , this .getArgByName ( "view" ) ] }
86
100
}
87
101
}
88
102
103
+ /** Provides modelling for pyramid requests. */
89
104
module Request {
105
+ /**
106
+ * A source of instances of `pyramid.request.Request`, extend this class to model new instances.
107
+ *
108
+ * Use the predicate `Request::instance()` to get references to instances of `pyramid.request.Request`.
109
+ */
90
110
abstract class InstanceSource extends DataFlow:: LocalSourceNode { }
91
111
92
112
/** Gets a reference to an instance of `pyramid.request.Request`. */
@@ -100,10 +120,14 @@ module Pyramid {
100
120
/** Gets a reference to an instance of `pyramid.request.Request`. */
101
121
DataFlow:: Node instance ( ) { instance ( DataFlow:: TypeTracker:: end ( ) ) .flowsTo ( result ) }
102
122
103
- private class RequestParameter extends InstanceSource , DataFlow:: ParameterNode {
123
+ private class RequestParameter extends InstanceSource , RemoteFlowSource:: Range instanceof DataFlow:: ParameterNode
124
+ {
104
125
RequestParameter ( ) { this .getParameter ( ) = any ( View:: ViewCallable vc ) .getRequestParameter ( ) }
126
+
127
+ override string getSourceType ( ) { result = "Pyramid request parameter" }
105
128
}
106
129
130
+ /** Taint steps for request instances. */
107
131
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
108
132
InstanceTaintSteps ( ) { this = "pyramid.request.Request" }
109
133
@@ -115,9 +139,9 @@ module Pyramid {
115
139
"as_bytes" , "authorization" , "body" , "body_file" , "body_file_raw" , "body_file_seekable" ,
116
140
"cache_control" , "client_addr" , "content_type" , "cookies" , "domain" , "headers" , "host" ,
117
141
"host_port" , "host_url" , "GET" , "if_match" , "if_none_match" , "if_range" ,
118
- "if_none_match" , "json" , "json_body" , "params " , "path " , "path_info " , "path_qs " ,
119
- "path_url" , "POST" , "pragma" , "query_string" , "range" , "referer" , "referrer" , "text ",
120
- "url" , "urlargs" , "urlvars" , "user_agent"
142
+ "if_none_match" , "json" , "json_body" , "matchdict " , "params " , "path " , "path_info " ,
143
+ "path_qs" , " path_url", "POST" , "pragma" , "query_string" , "range" , "referer" , "referrer" ,
144
+ "text" , " url", "urlargs" , "urlvars" , "user_agent"
121
145
]
122
146
}
123
147
@@ -128,10 +152,12 @@ module Pyramid {
128
152
override string getAsyncMethodName ( ) { none ( ) }
129
153
}
130
154
155
+ /** A call to a method of a `request` that copies the request. */
131
156
private class RequestCopyCall extends InstanceSource , DataFlow:: MethodCallNode {
132
157
RequestCopyCall ( ) { this .calls ( instance ( ) , [ "copy" , "copy_get" ] ) }
133
158
}
134
159
160
+ /** A member of a request that is a file-like object. */
135
161
private class RequestBodyFileLike extends Stdlib:: FileLikeObject:: InstanceSource instanceof DataFlow:: AttrRead
136
162
{
137
163
RequestBodyFileLike ( ) {
@@ -141,7 +167,9 @@ module Pyramid {
141
167
}
142
168
}
143
169
170
+ /** Provides modelling for pyramid responses. */
144
171
module Response {
172
+ /** A response returned by a view callable. */
145
173
private class PyramidReturnResponse extends Http:: Server:: HttpResponse:: Range {
146
174
PyramidReturnResponse ( ) {
147
175
this .asCfgNode ( ) = any ( View:: ViewCallable vc ) .getAReturnValueFlowNode ( ) and
@@ -155,27 +183,39 @@ module Pyramid {
155
183
override string getMimetypeDefault ( ) { result = "text/html" }
156
184
}
157
185
158
- private API:: Node classRef ( ) {
159
- result = API:: moduleImport ( "pyramid" ) .getMember ( "response" ) .getMember ( "Response" )
186
+ /** Gets a reference to the class `pyramid.response.Response` or a subclass. */
187
+ API:: Node subclassRef ( ) {
188
+ result = API:: moduleImport ( "pyramid" ) .getMember ( "response" ) .getMember ( "Response" ) or
189
+ result = ModelOutput:: getATypeNode ( "pyramid.response.Response~Subclass" ) .getASubclass * ( )
160
190
}
161
191
192
+ /**
193
+ * A source of instances of `pyramid.response.Response`, extend this class to model new instances.
194
+ *
195
+ * This can include instantiations of the class, return values from function
196
+ * calls, or a special parameter that will be set when functions are called by an external
197
+ * library.
198
+ *
199
+ * Use the predicate `Response::instance()` to get references to instances of `pyramid.response.Response`.
200
+ */
162
201
abstract class InstanceSource extends DataFlow:: LocalSourceNode ,
163
202
Http:: Server:: HttpResponse:: Range
164
203
{ }
165
204
166
- /** Gets a reference to an instance of `pyramid.request.Request `. */
205
+ /** Gets a reference to an instance of `pyramid.response.Response `. */
167
206
private DataFlow:: TypeTrackingNode instance ( DataFlow:: TypeTracker t ) {
168
207
t .start ( ) and
169
208
result instanceof InstanceSource
170
209
or
171
210
exists ( DataFlow:: TypeTracker t2 | result = instance ( t2 ) .track ( t2 , t ) )
172
211
}
173
212
174
- /** Gets a reference to an instance of `pyramid.request.Request `. */
213
+ /** Gets a reference to an instance of `pyramid.response.Response `. */
175
214
DataFlow:: Node instance ( ) { instance ( DataFlow:: TypeTracker:: end ( ) ) .flowsTo ( result ) }
176
215
216
+ /** An instantiation of the class `pyramid.response.Response` or a subclass. */
177
217
private class ClassInstantiation extends InstanceSource , DataFlow:: CallCfgNode {
178
- ClassInstantiation ( ) { this = classRef ( ) .getACall ( ) }
218
+ ClassInstantiation ( ) { this = subclassRef ( ) .getACall ( ) }
179
219
180
220
override DataFlow:: Node getBody ( ) { result = [ this .getArg ( 0 ) , this .getArgByName ( "body" ) ] }
181
221
@@ -186,6 +226,7 @@ module Pyramid {
186
226
override string getMimetypeDefault ( ) { result = "text/html" }
187
227
}
188
228
229
+ /** A write to a field that sets the body of a response. */
189
230
private class ResponseBodySet extends Http:: Server:: HttpResponse:: Range instanceof DataFlow:: AttrWrite
190
231
{
191
232
string attrName ;
@@ -207,6 +248,7 @@ module Pyramid {
207
248
}
208
249
}
209
250
251
+ /** A use of the `response` attribute of a `Request`. */
210
252
private class RequestResponseAttr extends InstanceSource instanceof DataFlow:: AttrRead {
211
253
RequestResponseAttr ( ) {
212
254
this .getObject ( ) = Request:: instance ( ) and this .getAttributeName ( ) = "response"
@@ -219,6 +261,7 @@ module Pyramid {
219
261
override string getMimetypeDefault ( ) { result = "text/html" }
220
262
}
221
263
264
+ /** A call to `response.set_cookie`. */
222
265
private class SetCookieCall extends Http:: Server:: CookieWrite:: Range , DataFlow:: MethodCallNode {
223
266
SetCookieCall ( ) { this .calls ( instance ( ) , "set_cookie" ) }
224
267
@@ -231,4 +274,33 @@ module Pyramid {
231
274
}
232
275
}
233
276
}
277
+
278
+ /** Provides models for pyramid http redirects. */
279
+ module Redirect {
280
+ /** Gets a reference to a subclass of `pyramid.httpexceptions._HTTPMove`, which each each exception class representing an HTTP redirect response is a subclass of. */
281
+ API:: Node subclassRef ( ) {
282
+ result =
283
+ API:: moduleImport ( "pyramid" )
284
+ .getMember ( "httpexceptions" )
285
+ .getMember ( "_HTTPMove" )
286
+ .getASubclass * ( ) or
287
+ result =
288
+ ModelOutput:: getATypeNode ( "pyramid.httpexceptions._HTTPMove~Subclass" ) .getASubclass * ( )
289
+ }
290
+
291
+ /** Gets a call to a pyramid HTTP exception class that represents an HTTP redirect response. */
292
+ class PyramidRedirect extends Http:: Server:: HttpRedirectResponse:: Range , DataFlow:: CallCfgNode {
293
+ PyramidRedirect ( ) { this = subclassRef ( ) .getACall ( ) }
294
+
295
+ override DataFlow:: Node getRedirectLocation ( ) {
296
+ result = [ this .getArg ( 0 ) , this .getArgByName ( "location" ) ]
297
+ }
298
+
299
+ override DataFlow:: Node getBody ( ) { none ( ) }
300
+
301
+ override DataFlow:: Node getMimetypeOrContentTypeArg ( ) { none ( ) }
302
+
303
+ override string getMimetypeDefault ( ) { result = "text/html" }
304
+ }
305
+ }
234
306
}
0 commit comments