@@ -19,49 +19,62 @@ private import semmle.python.frameworks.Stdlib
1919 * See https://docs.pylonsproject.org/projects/pyramid/.
2020 */
2121module Pyramid {
22- // TODO: qldoc
22+ /** Provides models for pyramid View callables. */
2323 module View {
2424 /**
2525 * A callable that could be used as a pyramid view callable.
2626 */
2727 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 ( ) {
2932 this .getPositionalParameterCount ( ) = 1 and
30- this .getArgName ( 0 ) = "request"
33+ result = this .getArg ( 0 )
3134 or
3235 this .getPositionalParameterCount ( ) = 2 and
33- this .getArgName ( 0 ) = "context" and
34- this .getArgName ( 1 ) = "request"
36+ result = this .getArg ( 1 )
3537 }
36-
37- /** Gets the `request` parameter of this view callable. */
38- Parameter getRequestParameter ( ) { result = this .getArgByName ( "request" ) }
3938 }
4039
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 {
4442 override string getFramework ( ) { result = "Pyramid" }
4543 }
4644
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 ( )
5556 }
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 ( ) }
5663 }
5764
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 )
6169 }
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 ( ) }
6274 }
6375 }
6476
77+ /** Provides models for `pyramid.config.Configurator` */
6578 module Configurator {
6679 /** Gets a reference to the class `pyramid.config.Configurator`. */
6780 API:: Node classRef ( ) {
@@ -79,14 +92,21 @@ module Pyramid {
7992 /** Gets a reference to an instance of `pyramid.config.Configurator`. */
8093 DataFlow:: Node instance ( ) { instance ( DataFlow:: TypeTracker:: end ( ) ) .flowsTo ( result ) }
8194
95+ /** Gets a call to the `add_view` method of an instance of `pyramid.config.Configurator`. */
8296 class AddViewCall extends DataFlow:: MethodCallNode {
8397 AddViewCall ( ) { this .calls ( instance ( ) , "add_view" ) }
8498
8599 DataFlow:: Node getViewArg ( ) { result = [ this .getArg ( 0 ) , this .getArgByName ( "view" ) ] }
86100 }
87101 }
88102
103+ /** Provides modelling for pyramid requests. */
89104 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+ */
90110 abstract class InstanceSource extends DataFlow:: LocalSourceNode { }
91111
92112 /** Gets a reference to an instance of `pyramid.request.Request`. */
@@ -100,10 +120,14 @@ module Pyramid {
100120 /** Gets a reference to an instance of `pyramid.request.Request`. */
101121 DataFlow:: Node instance ( ) { instance ( DataFlow:: TypeTracker:: end ( ) ) .flowsTo ( result ) }
102122
103- private class RequestParameter extends InstanceSource , DataFlow:: ParameterNode {
123+ private class RequestParameter extends InstanceSource , RemoteFlowSource:: Range instanceof DataFlow:: ParameterNode
124+ {
104125 RequestParameter ( ) { this .getParameter ( ) = any ( View:: ViewCallable vc ) .getRequestParameter ( ) }
126+
127+ override string getSourceType ( ) { result = "Pyramid request parameter" }
105128 }
106129
130+ /** Taint steps for request instances. */
107131 private class InstanceTaintSteps extends InstanceTaintStepsHelper {
108132 InstanceTaintSteps ( ) { this = "pyramid.request.Request" }
109133
@@ -115,9 +139,9 @@ module Pyramid {
115139 "as_bytes" , "authorization" , "body" , "body_file" , "body_file_raw" , "body_file_seekable" ,
116140 "cache_control" , "client_addr" , "content_type" , "cookies" , "domain" , "headers" , "host" ,
117141 "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"
121145 ]
122146 }
123147
@@ -128,10 +152,12 @@ module Pyramid {
128152 override string getAsyncMethodName ( ) { none ( ) }
129153 }
130154
155+ /** A call to a method of a `request` that copies the request. */
131156 private class RequestCopyCall extends InstanceSource , DataFlow:: MethodCallNode {
132157 RequestCopyCall ( ) { this .calls ( instance ( ) , [ "copy" , "copy_get" ] ) }
133158 }
134159
160+ /** A member of a request that is a file-like object. */
135161 private class RequestBodyFileLike extends Stdlib:: FileLikeObject:: InstanceSource instanceof DataFlow:: AttrRead
136162 {
137163 RequestBodyFileLike ( ) {
@@ -141,7 +167,9 @@ module Pyramid {
141167 }
142168 }
143169
170+ /** Provides modelling for pyramid responses. */
144171 module Response {
172+ /** A response returned by a view callable. */
145173 private class PyramidReturnResponse extends Http:: Server:: HttpResponse:: Range {
146174 PyramidReturnResponse ( ) {
147175 this .asCfgNode ( ) = any ( View:: ViewCallable vc ) .getAReturnValueFlowNode ( ) and
@@ -155,27 +183,39 @@ module Pyramid {
155183 override string getMimetypeDefault ( ) { result = "text/html" }
156184 }
157185
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 * ( )
160190 }
161191
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+ */
162201 abstract class InstanceSource extends DataFlow:: LocalSourceNode ,
163202 Http:: Server:: HttpResponse:: Range
164203 { }
165204
166- /** Gets a reference to an instance of `pyramid.request.Request `. */
205+ /** Gets a reference to an instance of `pyramid.response.Response `. */
167206 private DataFlow:: TypeTrackingNode instance ( DataFlow:: TypeTracker t ) {
168207 t .start ( ) and
169208 result instanceof InstanceSource
170209 or
171210 exists ( DataFlow:: TypeTracker t2 | result = instance ( t2 ) .track ( t2 , t ) )
172211 }
173212
174- /** Gets a reference to an instance of `pyramid.request.Request `. */
213+ /** Gets a reference to an instance of `pyramid.response.Response `. */
175214 DataFlow:: Node instance ( ) { instance ( DataFlow:: TypeTracker:: end ( ) ) .flowsTo ( result ) }
176215
216+ /** An instantiation of the class `pyramid.response.Response` or a subclass. */
177217 private class ClassInstantiation extends InstanceSource , DataFlow:: CallCfgNode {
178- ClassInstantiation ( ) { this = classRef ( ) .getACall ( ) }
218+ ClassInstantiation ( ) { this = subclassRef ( ) .getACall ( ) }
179219
180220 override DataFlow:: Node getBody ( ) { result = [ this .getArg ( 0 ) , this .getArgByName ( "body" ) ] }
181221
@@ -186,6 +226,7 @@ module Pyramid {
186226 override string getMimetypeDefault ( ) { result = "text/html" }
187227 }
188228
229+ /** A write to a field that sets the body of a response. */
189230 private class ResponseBodySet extends Http:: Server:: HttpResponse:: Range instanceof DataFlow:: AttrWrite
190231 {
191232 string attrName ;
@@ -207,6 +248,7 @@ module Pyramid {
207248 }
208249 }
209250
251+ /** A use of the `response` attribute of a `Request`. */
210252 private class RequestResponseAttr extends InstanceSource instanceof DataFlow:: AttrRead {
211253 RequestResponseAttr ( ) {
212254 this .getObject ( ) = Request:: instance ( ) and this .getAttributeName ( ) = "response"
@@ -219,6 +261,7 @@ module Pyramid {
219261 override string getMimetypeDefault ( ) { result = "text/html" }
220262 }
221263
264+ /** A call to `response.set_cookie`. */
222265 private class SetCookieCall extends Http:: Server:: CookieWrite:: Range , DataFlow:: MethodCallNode {
223266 SetCookieCall ( ) { this .calls ( instance ( ) , "set_cookie" ) }
224267
@@ -231,4 +274,33 @@ module Pyramid {
231274 }
232275 }
233276 }
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+ }
234306}
0 commit comments