mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
Python: Basic support for falcon framework; routing and requests.
This commit is contained in:
@@ -39,6 +39,7 @@ The API has been improved to declutter the global namespace and improve discover
|
||||
|
||||
* Added support for the `dill` pickle library.
|
||||
* Added support for the `bottle` web framework.
|
||||
* Added support for the `falcon` web API framework.
|
||||
* Added support for the `turbogears` web framework.
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
Name, Category
|
||||
Bottle, Web framework
|
||||
Django, Web application framework
|
||||
Falcon, Web API framework
|
||||
Flask, Microframework
|
||||
Pyramid, Web application framework
|
||||
Tornado, Web application framework and asynchronous networking library
|
||||
|
||||
|
@@ -96,3 +96,23 @@ private predicate json_load(ControlFlowNode fromnode, CallNode tonode) {
|
||||
)
|
||||
}
|
||||
|
||||
/** A kind of "taint", representing am open file-like object from an external source. */
|
||||
class ExternalFileObject extends TaintKind {
|
||||
|
||||
ExternalFileObject() {
|
||||
this = "file[" + any(ExternalStringKind key) + "]"
|
||||
}
|
||||
|
||||
|
||||
/** Gets the taint kind for item in this sequence */
|
||||
TaintKind getValue() {
|
||||
this = "file[" + result + "]"
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
name = "read" and result = this.getValue()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -5,3 +5,4 @@ import semmle.python.web.pyramid.Request
|
||||
import semmle.python.web.twisted.Request
|
||||
import semmle.python.web.bottle.Request
|
||||
import semmle.python.web.turbogears.Request
|
||||
import semmle.python.web.falcon.Request
|
||||
|
||||
68
python/ql/src/semmle/python/web/falcon/General.qll
Normal file
68
python/ql/src/semmle/python/web/falcon/General.qll
Normal file
@@ -0,0 +1,68 @@
|
||||
import python
|
||||
import semmle.python.web.Http
|
||||
|
||||
|
||||
/** The falcon API class */
|
||||
ClassObject theFalconAPIClass() {
|
||||
result = ModuleObject::named("falcon").getAttribute("API")
|
||||
}
|
||||
|
||||
|
||||
/** Holds if `route` is routed to `resource`
|
||||
*/
|
||||
private predicate api_route(CallNode route_call, ControlFlowNode route, ClassObject resource) {
|
||||
route_call.getFunction().(AttrNode).getObject("add_route").refersTo(_, theFalconAPIClass(), _) and
|
||||
route_call.getArg(0) = route and
|
||||
route_call.getArg(1).refersTo(_, resource, _)
|
||||
}
|
||||
|
||||
class FalconRoute extends ControlFlowNode {
|
||||
|
||||
FalconRoute() {
|
||||
api_route(this, _, _)
|
||||
}
|
||||
|
||||
string getUrl() {
|
||||
exists(StrConst url |
|
||||
api_route(this, url.getAFlowNode(), _) and
|
||||
result = url.getText()
|
||||
)
|
||||
}
|
||||
|
||||
ClassObject getResourceClass() {
|
||||
api_route(this, _, result)
|
||||
}
|
||||
|
||||
FalconHandlerFunction getHandlerFunction() {
|
||||
result = this.getResourceClass().lookupAttribute(_).(FunctionObject).getFunction()
|
||||
}
|
||||
|
||||
FalconHandlerFunction getHandlerFunction(string method) {
|
||||
result = this.getResourceClass().lookupAttribute("on_" + method).(FunctionObject).getFunction()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FalconHandlerFunction extends Function {
|
||||
|
||||
string method;
|
||||
|
||||
FalconHandlerFunction() {
|
||||
exists(ClassObject resource |
|
||||
resource.lookupAttribute("on_" + method).(FunctionObject).getFunction() = this
|
||||
)
|
||||
}
|
||||
|
||||
string getMethod() {
|
||||
result = method.toUpperCase()
|
||||
}
|
||||
|
||||
Parameter getRequest() {
|
||||
result = this.getArg(1)
|
||||
}
|
||||
|
||||
Parameter getResponse() {
|
||||
result = this.getArg(2)
|
||||
}
|
||||
|
||||
}
|
||||
55
python/ql/src/semmle/python/web/falcon/Request.qll
Normal file
55
python/ql/src/semmle/python/web/falcon/Request.qll
Normal file
@@ -0,0 +1,55 @@
|
||||
import python
|
||||
|
||||
import semmle.python.security.TaintTracking
|
||||
import semmle.python.web.Http
|
||||
import semmle.python.web.falcon.General
|
||||
import semmle.python.security.strings.External
|
||||
|
||||
/** https://falcon.readthedocs.io/en/stable/api/request_and_response.html */
|
||||
class FalconRequest extends TaintKind {
|
||||
|
||||
FalconRequest() {
|
||||
this = "falcon.request"
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
// name = "env" and result instanceof WsgiEnvironment
|
||||
result instanceof ExternalStringKind and
|
||||
(
|
||||
name = "uri" or name = "url" or
|
||||
name = "forwarded_uri" or
|
||||
name = "relative_uri" or
|
||||
name = "query_string"
|
||||
)
|
||||
or
|
||||
result instanceof ExternalStringDictKind and
|
||||
(
|
||||
name = "cookies" or name = "params"
|
||||
)
|
||||
or
|
||||
name = "stream" and result instanceof ExternalFileObject
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
name = "get_param" and result instanceof ExternalStringKind
|
||||
or
|
||||
name = "get_param_as_json" and result instanceof ExternalJsonKind
|
||||
or
|
||||
name = "get_param_as_list" and result instanceof ExternalStringSequenceKind
|
||||
}
|
||||
}
|
||||
|
||||
class FalconRequestParameter extends TaintSource {
|
||||
|
||||
FalconRequestParameter() {
|
||||
exists(FalconHandlerFunction f |
|
||||
f.getRequest() = this.(ControlFlowNode).getNode()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind k) {
|
||||
k instanceof FalconRequest
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
fail
|
||||
| /hello | get | test.py:9:5:9:32 | Function on_get |
|
||||
| /hello | post | test.py:12:5:12:33 | Function on_post |
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import python
|
||||
|
||||
import semmle.python.web.bottle.General
|
||||
import semmle.python.web.falcon.General
|
||||
|
||||
from BottleRoute route
|
||||
from FalconRoute route, string method
|
||||
|
||||
select route.getUrl(), method, route.getHandlerFunction(method)
|
||||
|
||||
select route.getUrl(), route.getFunction()
|
||||
|
||||
@@ -1 +1,14 @@
|
||||
fail
|
||||
| test.py:9 | req | falcon.request |
|
||||
| test.py:10 | Attribute | file[externally controlled string] |
|
||||
| test.py:10 | Attribute() | externally controlled string |
|
||||
| test.py:10 | req | falcon.request |
|
||||
| test.py:11 | Attribute() | externally controlled string |
|
||||
| test.py:11 | Attribute() | json[externally controlled string] |
|
||||
| test.py:11 | raw_json | externally controlled string |
|
||||
| test.py:13 | Dict | {externally controlled string} |
|
||||
| test.py:13 | Dict | {json[externally controlled string]} |
|
||||
| test.py:15 | result | externally controlled string |
|
||||
| test.py:15 | result | json[externally controlled string] |
|
||||
| test.py:17 | result | {externally controlled string} |
|
||||
| test.py:17 | result | {json[externally controlled string]} |
|
||||
| test.py:19 | req | falcon.request |
|
||||
|
||||
@@ -8,6 +8,6 @@ import semmle.python.security.strings.Untrusted
|
||||
|
||||
|
||||
from TaintedNode node
|
||||
|
||||
where node.getLocation().getFile().getName().matches("%falcon/test.py")
|
||||
select node.getLocation().toString(), node.getNode().getNode().toString(), node.getTaintKind()
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
|
||||
import json
|
||||
|
||||
from falcon import API
|
||||
|
||||
@@ -7,10 +7,17 @@ app = API()
|
||||
class Handler(object):
|
||||
|
||||
def on_get(self, req, resp):
|
||||
...
|
||||
raw_json = req.stream.read()
|
||||
result = json.loads(raw_json)
|
||||
resp.status = 200
|
||||
result = {
|
||||
'status': 'success',
|
||||
'data': result
|
||||
}
|
||||
resp.body = json.dumps(result)
|
||||
|
||||
def on_post(self, req, resp):
|
||||
...
|
||||
pass
|
||||
|
||||
app.add_route('/hello', Handler())
|
||||
|
||||
|
||||
Reference in New Issue
Block a user