mirror of
https://github.com/github/codeql.git
synced 2026-05-02 04:05:14 +02:00
Merge pull request #979 from markshannon/python-falcon
Python: Add support for falcon web API framework.
This commit is contained in:
@@ -167,6 +167,7 @@ abstract class CollectionKind extends TaintKind {
|
||||
/* Prevent any collection kinds more than 2 deep */
|
||||
not this.charAt(2) = "[" and not this.charAt(2) = "{"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A taint kind representing a flat collections of kinds.
|
||||
@@ -193,7 +194,7 @@ class SequenceKind extends CollectionKind {
|
||||
tonode.(BinaryExprNode).getAnOperand() = fromnode
|
||||
)
|
||||
or
|
||||
result = this and copy_call(fromnode, tonode)
|
||||
result = this and TaintFlowImplementation::copyCall(fromnode, tonode)
|
||||
or
|
||||
exists(BinaryExprNode mod |
|
||||
mod = tonode and
|
||||
@@ -236,20 +237,6 @@ private predicate slice(ControlFlowNode fromnode, SubscriptNode tonode) {
|
||||
)
|
||||
}
|
||||
|
||||
/* A call that returns a copy (or similar) of the argument */
|
||||
private predicate copy_call(ControlFlowNode fromnode, CallNode tonode) {
|
||||
tonode.getFunction().(AttrNode).getObject("copy") = fromnode
|
||||
or
|
||||
exists(ModuleObject copy, string name |
|
||||
name = "copy" or name = "deepcopy" |
|
||||
copy.attr(name).(FunctionObject).getACall() = tonode and
|
||||
tonode.getArg(0) = fromnode
|
||||
)
|
||||
or
|
||||
tonode.getFunction().refersTo(Object::builtin("reversed")) and
|
||||
tonode.getArg(0) = fromnode
|
||||
}
|
||||
|
||||
/** A taint kind representing a mapping of objects to kinds.
|
||||
* Typically a dict, but can include other mappings.
|
||||
*/
|
||||
@@ -272,7 +259,7 @@ class DictKind extends CollectionKind {
|
||||
result = valueKind and
|
||||
tonode.(CallNode).getFunction().(AttrNode).getObject("get") = fromnode
|
||||
or
|
||||
result = this and copy_call(fromnode, tonode)
|
||||
result = this and TaintFlowImplementation::copyCall(fromnode, tonode)
|
||||
or
|
||||
result = this and
|
||||
tonode.(CallNode).getFunction().refersTo(theDictType()) and
|
||||
@@ -1263,6 +1250,20 @@ library module TaintFlowImplementation {
|
||||
context = fromnode.getContext()
|
||||
}
|
||||
|
||||
/* A call that returns a copy (or similar) of the argument */
|
||||
predicate copyCall(ControlFlowNode fromnode, CallNode tonode) {
|
||||
tonode.getFunction().(AttrNode).getObject("copy") = fromnode
|
||||
or
|
||||
exists(ModuleObject copy, string name |
|
||||
name = "copy" or name = "deepcopy" |
|
||||
copy.attr(name).(FunctionObject).getACall() = tonode and
|
||||
tonode.getArg(0) = fromnode
|
||||
)
|
||||
or
|
||||
tonode.getFunction().refersTo(Object::builtin("reversed")) and
|
||||
tonode.getArg(0) = fromnode
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Helper predicate for tainted_with */
|
||||
|
||||
@@ -96,3 +96,23 @@ private predicate json_load(ControlFlowNode fromnode, CallNode tonode) {
|
||||
)
|
||||
}
|
||||
|
||||
/** A kind of "taint", representing an open file-like object from an external source. */
|
||||
class ExternalFileObject extends TaintKind {
|
||||
|
||||
ExternalFileObject() {
|
||||
this = "file[" + any(ExternalStringKind key) + "]"
|
||||
}
|
||||
|
||||
|
||||
/** Gets the taint kind for the contents of this file */
|
||||
TaintKind getValue() {
|
||||
this = "file[" + result + "]"
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
name = "read" and result = this.getValue()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -23,3 +23,37 @@ string httpVerb() {
|
||||
string httpVerbLower() {
|
||||
result = httpVerb().toLowerCase()
|
||||
}
|
||||
|
||||
/** Taint kind representing the WSGI environment.
|
||||
* As specified in PEP 3333. https://www.python.org/dev/peps/pep-3333/#environ-variables
|
||||
*/
|
||||
class WsgiEnvironment extends TaintKind {
|
||||
|
||||
WsgiEnvironment() { this = "wsgi.environment" }
|
||||
|
||||
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
result = this and TaintFlowImplementation::copyCall(fromnode, tonode)
|
||||
or
|
||||
result = this and
|
||||
tonode.(CallNode).getFunction().refersTo(theDictType()) and
|
||||
tonode.(CallNode).getArg(0) = fromnode
|
||||
or
|
||||
exists(StringObject key, string text |
|
||||
tonode.(CallNode).getFunction().(AttrNode).getObject("get") = fromnode and
|
||||
tonode.(CallNode).getArg(0).refersTo(key)
|
||||
or
|
||||
tonode.(SubscriptNode).getValue() = fromnode and tonode.isLoad() and
|
||||
tonode.(SubscriptNode).getIndex().refersTo(key)
|
||||
|
|
||||
text = key.getText() and result instanceof ExternalStringKind and
|
||||
(
|
||||
text = "QUERY_STRING" or
|
||||
text = "PATH_INFO" or
|
||||
text.prefix(5) = "HTTP_"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -5,3 +5,4 @@ import semmle.python.web.tornado.Response
|
||||
import semmle.python.web.twisted.Response
|
||||
import semmle.python.web.bottle.Response
|
||||
import semmle.python.web.turbogears.Response
|
||||
import semmle.python.web.falcon.Response
|
||||
|
||||
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, _)
|
||||
}
|
||||
|
||||
private predicate route(FalconRoute route, Function target, string funcname) {
|
||||
route.getResourceClass().lookupAttribute("on_" + funcname).(FunctionObject).getFunction() = target
|
||||
}
|
||||
|
||||
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(string method) {
|
||||
route(this, result, method)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FalconHandlerFunction extends Function {
|
||||
|
||||
FalconHandlerFunction() {
|
||||
route(_, this, _)
|
||||
}
|
||||
|
||||
private string methodName() {
|
||||
route(_, this, result)
|
||||
}
|
||||
|
||||
string getMethod() {
|
||||
result = this.methodName().toUpperCase()
|
||||
}
|
||||
|
||||
Parameter getRequest() {
|
||||
result = this.getArg(1)
|
||||
}
|
||||
|
||||
Parameter getResponse() {
|
||||
result = this.getArg(2)
|
||||
}
|
||||
|
||||
}
|
||||
56
python/ql/src/semmle/python/web/falcon/Request.qll
Normal file
56
python/ql/src/semmle/python/web/falcon/Request.qll
Normal file
@@ -0,0 +1,56 @@
|
||||
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
|
||||
or
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
48
python/ql/src/semmle/python/web/falcon/Response.qll
Normal file
48
python/ql/src/semmle/python/web/falcon/Response.qll
Normal file
@@ -0,0 +1,48 @@
|
||||
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 FalconResponse extends TaintKind {
|
||||
|
||||
FalconResponse() {
|
||||
this = "falcon.response"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FalconResponseParameter extends TaintSource {
|
||||
|
||||
FalconResponseParameter() {
|
||||
exists(FalconHandlerFunction f |
|
||||
f.getResponse() = this.(ControlFlowNode).getNode()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind k) {
|
||||
k instanceof FalconResponse
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FalconResponseBodySink extends TaintSink {
|
||||
|
||||
FalconResponseBodySink() {
|
||||
exists(AttrNode attr |
|
||||
any(FalconResponse f).taints(attr.getObject("body")) |
|
||||
attr.(DefinitionNode).getValue() = this
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) {
|
||||
kind instanceof StringKind
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user