Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b8b4216

Browse files
authored
Merge pull request #979 from markshannon/python-falcon
Python: Add support for falcon web API framework.
2 parents 5478e0d + 1444b39 commit b8b4216

24 files changed

Lines changed: 375 additions & 16 deletions

File tree

change-notes/1.20/analysis-python.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ The API has been improved to declutter the global namespace and improve discover
4040

4141
* Added support for the `dill` pickle library.
4242
* Added support for the `bottle` web framework.
43+
* Added support for the `falcon` web API framework.
4344
* Added support for the `turbogears` web framework.
4445

4546

change-notes/1.20/support/python-frameworks.csv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
Name, Category
22
Bottle, Web framework
33
Django, Web application framework
4+
Falcon, Web API framework
45
Flask, Microframework
56
Pyramid, Web application framework
67
Tornado, Web application framework and asynchronous networking library

python/ql/src/semmle/python/security/TaintTracking.qll

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ abstract class CollectionKind extends TaintKind {
167167
/* Prevent any collection kinds more than 2 deep */
168168
not this.charAt(2) = "[" and not this.charAt(2) = "{"
169169
}
170+
170171
}
171172

172173
/** A taint kind representing a flat collections of kinds.
@@ -193,7 +194,7 @@ class SequenceKind extends CollectionKind {
193194
tonode.(BinaryExprNode).getAnOperand() = fromnode
194195
)
195196
or
196-
result = this and copy_call(fromnode, tonode)
197+
result = this and TaintFlowImplementation::copyCall(fromnode, tonode)
197198
or
198199
exists(BinaryExprNode mod |
199200
mod = tonode and
@@ -236,20 +237,6 @@ private predicate slice(ControlFlowNode fromnode, SubscriptNode tonode) {
236237
)
237238
}
238239

239-
/* A call that returns a copy (or similar) of the argument */
240-
private predicate copy_call(ControlFlowNode fromnode, CallNode tonode) {
241-
tonode.getFunction().(AttrNode).getObject("copy") = fromnode
242-
or
243-
exists(ModuleObject copy, string name |
244-
name = "copy" or name = "deepcopy" |
245-
copy.attr(name).(FunctionObject).getACall() = tonode and
246-
tonode.getArg(0) = fromnode
247-
)
248-
or
249-
tonode.getFunction().refersTo(Object::builtin("reversed")) and
250-
tonode.getArg(0) = fromnode
251-
}
252-
253240
/** A taint kind representing a mapping of objects to kinds.
254241
* Typically a dict, but can include other mappings.
255242
*/
@@ -272,7 +259,7 @@ class DictKind extends CollectionKind {
272259
result = valueKind and
273260
tonode.(CallNode).getFunction().(AttrNode).getObject("get") = fromnode
274261
or
275-
result = this and copy_call(fromnode, tonode)
262+
result = this and TaintFlowImplementation::copyCall(fromnode, tonode)
276263
or
277264
result = this and
278265
tonode.(CallNode).getFunction().refersTo(theDictType()) and
@@ -1263,6 +1250,20 @@ library module TaintFlowImplementation {
12631250
context = fromnode.getContext()
12641251
}
12651252

1253+
/* A call that returns a copy (or similar) of the argument */
1254+
predicate copyCall(ControlFlowNode fromnode, CallNode tonode) {
1255+
tonode.getFunction().(AttrNode).getObject("copy") = fromnode
1256+
or
1257+
exists(ModuleObject copy, string name |
1258+
name = "copy" or name = "deepcopy" |
1259+
copy.attr(name).(FunctionObject).getACall() = tonode and
1260+
tonode.getArg(0) = fromnode
1261+
)
1262+
or
1263+
tonode.getFunction().refersTo(Object::builtin("reversed")) and
1264+
tonode.getArg(0) = fromnode
1265+
}
1266+
12661267
}
12671268

12681269
/* Helper predicate for tainted_with */

python/ql/src/semmle/python/security/strings/External.qll

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,23 @@ private predicate json_load(ControlFlowNode fromnode, CallNode tonode) {
9696
)
9797
}
9898

99+
/** A kind of "taint", representing an open file-like object from an external source. */
100+
class ExternalFileObject extends TaintKind {
101+
102+
ExternalFileObject() {
103+
this = "file[" + any(ExternalStringKind key) + "]"
104+
}
105+
106+
107+
/** Gets the taint kind for the contents of this file */
108+
TaintKind getValue() {
109+
this = "file[" + result + "]"
110+
}
111+
112+
override TaintKind getTaintOfMethodResult(string name) {
113+
name = "read" and result = this.getValue()
114+
}
115+
116+
}
117+
118+

python/ql/src/semmle/python/web/Http.qll

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,37 @@ string httpVerb() {
2323
string httpVerbLower() {
2424
result = httpVerb().toLowerCase()
2525
}
26+
27+
/** Taint kind representing the WSGI environment.
28+
* As specified in PEP 3333. https://www.python.org/dev/peps/pep-3333/#environ-variables
29+
*/
30+
class WsgiEnvironment extends TaintKind {
31+
32+
WsgiEnvironment() { this = "wsgi.environment" }
33+
34+
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
35+
result = this and TaintFlowImplementation::copyCall(fromnode, tonode)
36+
or
37+
result = this and
38+
tonode.(CallNode).getFunction().refersTo(theDictType()) and
39+
tonode.(CallNode).getArg(0) = fromnode
40+
or
41+
exists(StringObject key, string text |
42+
tonode.(CallNode).getFunction().(AttrNode).getObject("get") = fromnode and
43+
tonode.(CallNode).getArg(0).refersTo(key)
44+
or
45+
tonode.(SubscriptNode).getValue() = fromnode and tonode.isLoad() and
46+
tonode.(SubscriptNode).getIndex().refersTo(key)
47+
|
48+
text = key.getText() and result instanceof ExternalStringKind and
49+
(
50+
text = "QUERY_STRING" or
51+
text = "PATH_INFO" or
52+
text.prefix(5) = "HTTP_"
53+
)
54+
)
55+
}
56+
57+
}
58+
59+

python/ql/src/semmle/python/web/HttpRequest.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ import semmle.python.web.pyramid.Request
55
import semmle.python.web.twisted.Request
66
import semmle.python.web.bottle.Request
77
import semmle.python.web.turbogears.Request
8+
import semmle.python.web.falcon.Request

python/ql/src/semmle/python/web/HttpResponse.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ import semmle.python.web.tornado.Response
55
import semmle.python.web.twisted.Response
66
import semmle.python.web.bottle.Response
77
import semmle.python.web.turbogears.Response
8+
import semmle.python.web.falcon.Response
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import python
2+
import semmle.python.web.Http
3+
4+
5+
/** The falcon API class */
6+
ClassObject theFalconAPIClass() {
7+
result = ModuleObject::named("falcon").getAttribute("API")
8+
}
9+
10+
11+
/** Holds if `route` is routed to `resource`
12+
*/
13+
private predicate api_route(CallNode route_call, ControlFlowNode route, ClassObject resource) {
14+
route_call.getFunction().(AttrNode).getObject("add_route").refersTo(_, theFalconAPIClass(), _) and
15+
route_call.getArg(0) = route and
16+
route_call.getArg(1).refersTo(_, resource, _)
17+
}
18+
19+
private predicate route(FalconRoute route, Function target, string funcname) {
20+
route.getResourceClass().lookupAttribute("on_" + funcname).(FunctionObject).getFunction() = target
21+
}
22+
23+
class FalconRoute extends ControlFlowNode {
24+
25+
FalconRoute() {
26+
api_route(this, _, _)
27+
}
28+
29+
string getUrl() {
30+
exists(StrConst url |
31+
api_route(this, url.getAFlowNode(), _) and
32+
result = url.getText()
33+
)
34+
}
35+
36+
ClassObject getResourceClass() {
37+
api_route(this, _, result)
38+
}
39+
40+
FalconHandlerFunction getHandlerFunction(string method) {
41+
route(this, result, method)
42+
}
43+
44+
}
45+
46+
class FalconHandlerFunction extends Function {
47+
48+
FalconHandlerFunction() {
49+
route(_, this, _)
50+
}
51+
52+
private string methodName() {
53+
route(_, this, result)
54+
}
55+
56+
string getMethod() {
57+
result = this.methodName().toUpperCase()
58+
}
59+
60+
Parameter getRequest() {
61+
result = this.getArg(1)
62+
}
63+
64+
Parameter getResponse() {
65+
result = this.getArg(2)
66+
}
67+
68+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import python
2+
3+
import semmle.python.security.TaintTracking
4+
import semmle.python.web.Http
5+
import semmle.python.web.falcon.General
6+
import semmle.python.security.strings.External
7+
8+
/** https://falcon.readthedocs.io/en/stable/api/request_and_response.html */
9+
class FalconRequest extends TaintKind {
10+
11+
FalconRequest() {
12+
this = "falcon.request"
13+
}
14+
15+
override TaintKind getTaintOfAttribute(string name) {
16+
name = "env" and result instanceof WsgiEnvironment
17+
or
18+
result instanceof ExternalStringKind and
19+
(
20+
name = "uri" or name = "url" or
21+
name = "forwarded_uri" or
22+
name = "relative_uri" or
23+
name = "query_string"
24+
)
25+
or
26+
result instanceof ExternalStringDictKind and
27+
(
28+
name = "cookies" or name = "params"
29+
)
30+
or
31+
name = "stream" and result instanceof ExternalFileObject
32+
}
33+
34+
override TaintKind getTaintOfMethodResult(string name) {
35+
name = "get_param" and result instanceof ExternalStringKind
36+
or
37+
name = "get_param_as_json" and result instanceof ExternalJsonKind
38+
or
39+
name = "get_param_as_list" and result instanceof ExternalStringSequenceKind
40+
}
41+
}
42+
43+
class FalconRequestParameter extends TaintSource {
44+
45+
FalconRequestParameter() {
46+
exists(FalconHandlerFunction f |
47+
f.getRequest() = this.(ControlFlowNode).getNode()
48+
)
49+
}
50+
51+
override predicate isSourceOf(TaintKind k) {
52+
k instanceof FalconRequest
53+
}
54+
55+
}
56+
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import python
2+
3+
4+
import semmle.python.security.TaintTracking
5+
import semmle.python.web.Http
6+
import semmle.python.web.falcon.General
7+
import semmle.python.security.strings.External
8+
9+
10+
/** https://falcon.readthedocs.io/en/stable/api/request_and_response.html */
11+
class FalconResponse extends TaintKind {
12+
13+
FalconResponse() {
14+
this = "falcon.response"
15+
}
16+
17+
}
18+
19+
class FalconResponseParameter extends TaintSource {
20+
21+
FalconResponseParameter() {
22+
exists(FalconHandlerFunction f |
23+
f.getResponse() = this.(ControlFlowNode).getNode()
24+
)
25+
}
26+
27+
override predicate isSourceOf(TaintKind k) {
28+
k instanceof FalconResponse
29+
}
30+
31+
}
32+
33+
class FalconResponseBodySink extends TaintSink {
34+
35+
FalconResponseBodySink() {
36+
exists(AttrNode attr |
37+
any(FalconResponse f).taints(attr.getObject("body")) |
38+
attr.(DefinitionNode).getValue() = this
39+
)
40+
}
41+
42+
override predicate sinks(TaintKind kind) {
43+
kind instanceof StringKind
44+
}
45+
46+
}
47+
48+

0 commit comments

Comments
 (0)