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

Skip to content

Commit 6a48420

Browse files
committed
Python: Basic support for falcon framework; routing and requests.
1 parent 742c1d0 commit 6a48420

11 files changed

Lines changed: 177 additions & 9 deletions

File tree

change-notes/1.20/analysis-python.md

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

4040
* Added support for the `dill` pickle library.
4141
* Added support for the `bottle` web framework.
42+
* Added support for the `falcon` web API framework.
4243
* Added support for the `turbogears` web framework.
4344

4445

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/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 am 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 item in this sequence */
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/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
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+
class FalconRoute extends ControlFlowNode {
20+
21+
FalconRoute() {
22+
api_route(this, _, _)
23+
}
24+
25+
string getUrl() {
26+
exists(StrConst url |
27+
api_route(this, url.getAFlowNode(), _) and
28+
result = url.getText()
29+
)
30+
}
31+
32+
ClassObject getResourceClass() {
33+
api_route(this, _, result)
34+
}
35+
36+
FalconHandlerFunction getHandlerFunction() {
37+
result = this.getResourceClass().lookupAttribute(_).(FunctionObject).getFunction()
38+
}
39+
40+
FalconHandlerFunction getHandlerFunction(string method) {
41+
result = this.getResourceClass().lookupAttribute("on_" + method).(FunctionObject).getFunction()
42+
}
43+
44+
}
45+
46+
class FalconHandlerFunction extends Function {
47+
48+
string method;
49+
50+
FalconHandlerFunction() {
51+
exists(ClassObject resource |
52+
resource.lookupAttribute("on_" + method).(FunctionObject).getFunction() = this
53+
)
54+
}
55+
56+
string getMethod() {
57+
result = method.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: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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+
result instanceof ExternalStringKind and
18+
(
19+
name = "uri" or name = "url" or
20+
name = "forwarded_uri" or
21+
name = "relative_uri" or
22+
name = "query_string"
23+
)
24+
or
25+
result instanceof ExternalStringDictKind and
26+
(
27+
name = "cookies" or name = "params"
28+
)
29+
or
30+
name = "stream" and result instanceof ExternalFileObject
31+
}
32+
33+
override TaintKind getTaintOfMethodResult(string name) {
34+
name = "get_param" and result instanceof ExternalStringKind
35+
or
36+
name = "get_param_as_json" and result instanceof ExternalJsonKind
37+
or
38+
name = "get_param_as_list" and result instanceof ExternalStringSequenceKind
39+
}
40+
}
41+
42+
class FalconRequestParameter extends TaintSource {
43+
44+
FalconRequestParameter() {
45+
exists(FalconHandlerFunction f |
46+
f.getRequest() = this.(ControlFlowNode).getNode()
47+
)
48+
}
49+
50+
override predicate isSourceOf(TaintKind k) {
51+
k instanceof FalconRequest
52+
}
53+
54+
}
55+
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
fail
1+
| /hello | get | test.py:9:5:9:32 | Function on_get |
2+
| /hello | post | test.py:12:5:12:33 | Function on_post |

python/ql/test/library-tests/web/falcon/Routing.ql

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import python
22

3-
import semmle.python.web.bottle.General
3+
import semmle.python.web.falcon.General
44

5-
from BottleRoute route
5+
from FalconRoute route, string method
6+
7+
select route.getUrl(), method, route.getHandlerFunction(method)
68

7-
select route.getUrl(), route.getFunction()
Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,14 @@
1-
fail
1+
| test.py:9 | req | falcon.request |
2+
| test.py:10 | Attribute | file[externally controlled string] |
3+
| test.py:10 | Attribute() | externally controlled string |
4+
| test.py:10 | req | falcon.request |
5+
| test.py:11 | Attribute() | externally controlled string |
6+
| test.py:11 | Attribute() | json[externally controlled string] |
7+
| test.py:11 | raw_json | externally controlled string |
8+
| test.py:13 | Dict | {externally controlled string} |
9+
| test.py:13 | Dict | {json[externally controlled string]} |
10+
| test.py:15 | result | externally controlled string |
11+
| test.py:15 | result | json[externally controlled string] |
12+
| test.py:17 | result | {externally controlled string} |
13+
| test.py:17 | result | {json[externally controlled string]} |
14+
| test.py:19 | req | falcon.request |

python/ql/test/library-tests/web/falcon/Taint.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ import semmle.python.security.strings.Untrusted
88

99

1010
from TaintedNode node
11-
11+
where node.getLocation().getFile().getName().matches("%falcon/test.py")
1212
select node.getLocation().toString(), node.getNode().getNode().toString(), node.getTaintKind()
1313

0 commit comments

Comments
 (0)