mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
Merge pull request #978 from markshannon/python-turbogears
Python: Add support for turbogears; requests and responses.
This commit is contained in:
@@ -174,6 +174,14 @@ class Function extends Function_, Scope, AstNode {
|
||||
Scope.super.contains(inner)
|
||||
}
|
||||
|
||||
/** Gets a control flow node for a return value of this function */
|
||||
ControlFlowNode getAReturnValueFlowNode() {
|
||||
exists(Return ret |
|
||||
ret.getScope() = this and
|
||||
ret.getValue() = result.getNode()
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A def statement. Note that FunctionDef extends Assign as a function definition binds the newly created function */
|
||||
|
||||
@@ -115,3 +115,14 @@ private predicate os_path_join(ControlFlowNode fromnode, CallNode tonode) {
|
||||
tonode = path_join.getACall() and tonode.getAnArg() = fromnode
|
||||
)
|
||||
}
|
||||
|
||||
/** A kind of "taint", representing a dictionary mapping str->"taint" */
|
||||
class StringDictKind extends DictKind {
|
||||
|
||||
StringDictKind() {
|
||||
this.getValue() instanceof StringKind
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -160,10 +160,7 @@ class PyFunctionObject extends FunctionObject {
|
||||
|
||||
/** Gets a control flow node corresponding to the value of a return statement */
|
||||
ControlFlowNode getAReturnedNode() {
|
||||
exists(Return ret |
|
||||
ret.getScope() = this.getFunction() and
|
||||
result.getNode() = ret.getValue()
|
||||
)
|
||||
result = this.getFunction().getAReturnValueFlowNode()
|
||||
}
|
||||
|
||||
override string descriptiveString() {
|
||||
|
||||
@@ -4,3 +4,4 @@ import semmle.python.web.tornado.Request
|
||||
import semmle.python.web.pyramid.Request
|
||||
import semmle.python.web.twisted.Request
|
||||
import semmle.python.web.bottle.Request
|
||||
import semmle.python.web.turbogears.Request
|
||||
|
||||
@@ -4,3 +4,4 @@ import semmle.python.web.pyramid.Response
|
||||
import semmle.python.web.tornado.Response
|
||||
import semmle.python.web.twisted.Response
|
||||
import semmle.python.web.bottle.Response
|
||||
import semmle.python.web.turbogears.Response
|
||||
|
||||
33
python/ql/src/semmle/python/web/turbogears/Request.qll
Normal file
33
python/ql/src/semmle/python/web/turbogears/Request.qll
Normal file
@@ -0,0 +1,33 @@
|
||||
import python
|
||||
import semmle.python.security.strings.Untrusted
|
||||
|
||||
import TurboGears
|
||||
|
||||
private class ValidatedMethodParameter extends Parameter {
|
||||
|
||||
ValidatedMethodParameter() {
|
||||
exists(string name, TurboGearsControllerMethod method |
|
||||
method.getArgByName(name) = this and
|
||||
method.getValidationDict().getItem(_).(KeyValuePair).getKey().(StrConst).getText() = name
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class UnvalidatedControllerMethodParameter extends TaintSource {
|
||||
|
||||
UnvalidatedControllerMethodParameter() {
|
||||
exists(Parameter p |
|
||||
any(TurboGearsControllerMethod m | not m.getName() = "onerror").getAnArg() = p and
|
||||
not p instanceof ValidatedMethodParameter and
|
||||
not p.isSelf() and
|
||||
p.(Name).getAFlowNode() = this
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) {
|
||||
kind instanceof UntrustedStringKind
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
38
python/ql/src/semmle/python/web/turbogears/Response.qll
Normal file
38
python/ql/src/semmle/python/web/turbogears/Response.qll
Normal file
@@ -0,0 +1,38 @@
|
||||
import python
|
||||
|
||||
import semmle.python.security.TaintTracking
|
||||
import semmle.python.security.strings.Basic
|
||||
|
||||
import TurboGears
|
||||
|
||||
|
||||
|
||||
class ControllerMethodReturnValue extends TaintSink {
|
||||
|
||||
ControllerMethodReturnValue() {
|
||||
exists(TurboGearsControllerMethod m |
|
||||
m.getAReturnValueFlowNode() = this and
|
||||
not m.isTemplated()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) {
|
||||
kind instanceof StringKind
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ControllerMethodTemplatedReturnValue extends TaintSink {
|
||||
|
||||
ControllerMethodTemplatedReturnValue() {
|
||||
exists(TurboGearsControllerMethod m |
|
||||
m.getAReturnValueFlowNode() = this and
|
||||
m.isTemplated()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) {
|
||||
kind instanceof StringDictKind
|
||||
}
|
||||
|
||||
}
|
||||
55
python/ql/src/semmle/python/web/turbogears/TurboGears.qll
Normal file
55
python/ql/src/semmle/python/web/turbogears/TurboGears.qll
Normal file
@@ -0,0 +1,55 @@
|
||||
import python
|
||||
|
||||
import semmle.python.security.TaintTracking
|
||||
|
||||
private ClassObject theTurboGearsControllerClass() {
|
||||
result = ModuleObject::named("tg").getAttribute("TGController")
|
||||
}
|
||||
|
||||
|
||||
ClassObject aTurboGearsControllerClass() {
|
||||
result.getASuperType() = theTurboGearsControllerClass()
|
||||
}
|
||||
|
||||
|
||||
class TurboGearsControllerMethod extends Function {
|
||||
|
||||
ControlFlowNode decorator;
|
||||
|
||||
TurboGearsControllerMethod() {
|
||||
aTurboGearsControllerClass().getPyClass() = this.getScope() and
|
||||
decorator = this.getADecorator().getAFlowNode() and
|
||||
/* Is decorated with @expose() or @expose(path) */
|
||||
(
|
||||
decorator.(CallNode).getFunction().(NameNode).getId() = "expose"
|
||||
or
|
||||
decorator.refersTo(_, ModuleObject::named("tg").getAttribute("expose"), _)
|
||||
)
|
||||
}
|
||||
|
||||
private ControlFlowNode templateName() {
|
||||
result = decorator.(CallNode).getArg(0)
|
||||
}
|
||||
|
||||
predicate isTemplated() {
|
||||
exists(templateName())
|
||||
}
|
||||
|
||||
string getTemplateName() {
|
||||
exists(StringObject str |
|
||||
templateName().refersTo(str) and
|
||||
result = str.getText()
|
||||
)
|
||||
}
|
||||
|
||||
Dict getValidationDict() {
|
||||
exists(Call call, Object dict |
|
||||
call = this.getADecorator() and
|
||||
call.getFunc().(Name).getId() = "validate" and
|
||||
call.getArg(0).refersTo(dict) and
|
||||
result = dict.getOrigin()
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
| test.py:7:5:7:32 | Function onerror |
|
||||
| test.py:13:5:13:50 | Function ok_validated |
|
||||
| test.py:18:5:18:57 | Function partially_validated |
|
||||
| test.py:22:5:22:51 | Function not_validated |
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
|
||||
import python
|
||||
|
||||
import semmle.python.web.turbogears.TurboGears
|
||||
|
||||
from TurboGearsControllerMethod m
|
||||
select m
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
| test.py:8 | BinaryExpr | externally controlled string |
|
||||
| test.py:14 | BinaryExpr | externally controlled string |
|
||||
| test.py:19 | BinaryExpr | externally controlled string |
|
||||
| test.py:23 | BinaryExpr | externally controlled string |
|
||||
10
python/ql/test/library-tests/web/turbogears/Sinks.ql
Normal file
10
python/ql/test/library-tests/web/turbogears/Sinks.ql
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
import python
|
||||
|
||||
import semmle.python.web.HttpRequest
|
||||
import semmle.python.web.HttpResponse
|
||||
import semmle.python.security.strings.Untrusted
|
||||
|
||||
from TaintSink sink, TaintKind kind
|
||||
where sink.sinks(kind)
|
||||
select sink.getLocation().toString(), sink.(ControlFlowNode).getNode().toString(), kind
|
||||
@@ -0,0 +1,3 @@
|
||||
| test.py:18 | b | externally controlled string |
|
||||
| test.py:22 | a | externally controlled string |
|
||||
| test.py:22 | b | externally controlled string |
|
||||
10
python/ql/test/library-tests/web/turbogears/Sources.ql
Normal file
10
python/ql/test/library-tests/web/turbogears/Sources.ql
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
import python
|
||||
|
||||
import semmle.python.web.HttpRequest
|
||||
import semmle.python.security.strings.Untrusted
|
||||
|
||||
|
||||
from TaintSource src, TaintKind kind
|
||||
where src.isSourceOf(kind)
|
||||
select src.getLocation().toString(), src.(ControlFlowNode).getNode().toString(), kind
|
||||
12
python/ql/test/library-tests/web/turbogears/Taint.expected
Normal file
12
python/ql/test/library-tests/web/turbogears/Taint.expected
Normal file
@@ -0,0 +1,12 @@
|
||||
| test.py:18 | b | externally controlled string |
|
||||
| test.py:19 | BinaryExpr | [externally controlled string] |
|
||||
| test.py:19 | BinaryExpr | externally controlled string |
|
||||
| test.py:19 | Tuple | [externally controlled string] |
|
||||
| test.py:19 | b | externally controlled string |
|
||||
| test.py:22 | a | externally controlled string |
|
||||
| test.py:22 | b | externally controlled string |
|
||||
| test.py:23 | BinaryExpr | [externally controlled string] |
|
||||
| test.py:23 | BinaryExpr | externally controlled string |
|
||||
| test.py:23 | Tuple | [externally controlled string] |
|
||||
| test.py:23 | a | externally controlled string |
|
||||
| test.py:23 | b | externally controlled string |
|
||||
13
python/ql/test/library-tests/web/turbogears/Taint.ql
Normal file
13
python/ql/test/library-tests/web/turbogears/Taint.ql
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
import python
|
||||
|
||||
|
||||
import semmle.python.web.HttpRequest
|
||||
import semmle.python.web.HttpResponse
|
||||
import semmle.python.security.strings.Untrusted
|
||||
|
||||
|
||||
from TaintedNode node
|
||||
|
||||
select node.getLocation().toString(), node.getNode().getNode().toString(), node.getTaintKind()
|
||||
|
||||
2
python/ql/test/library-tests/web/turbogears/options
Normal file
2
python/ql/test/library-tests/web/turbogears/options
Normal file
@@ -0,0 +1,2 @@
|
||||
semmle-extractor-options: --max-import-depth=3 --lang=3 -p ../../../query-tests/Security/lib/
|
||||
optimize: true
|
||||
23
python/ql/test/library-tests/web/turbogears/test.py
Normal file
23
python/ql/test/library-tests/web/turbogears/test.py
Normal file
@@ -0,0 +1,23 @@
|
||||
|
||||
from tg import request, validate, expose, TGController
|
||||
from formencode import validators
|
||||
|
||||
class RootController(TGController):
|
||||
@expose()
|
||||
def onerror(self, **kwargs):
|
||||
return 'An error occurred: %s' % request.validation['errors']
|
||||
|
||||
@expose()
|
||||
@validate({"a": validators.Int(not_empty=True), "b": validators.Email},
|
||||
error_handler=onerror)
|
||||
def ok_validated(self, a=None, b=None, *args):
|
||||
return 'Values: %s, %s, %s' % (a, b, args)
|
||||
|
||||
@expose()
|
||||
@validate({"a": validators.Int(not_empty=True)})
|
||||
def partially_validated(self, a=None, b=None, *args):
|
||||
return 'Values: %s, %s, %s' % (a, b, args)
|
||||
|
||||
@expose()
|
||||
def not_validated(self, a=None, b=None, *args):
|
||||
return 'Values: %s, %s, %s' % (a, b, args)
|
||||
30
python/ql/test/query-tests/Security/lib/tg.py
Normal file
30
python/ql/test/query-tests/Security/lib/tg.py
Normal file
@@ -0,0 +1,30 @@
|
||||
|
||||
def validate(validators):
|
||||
def validator(func):
|
||||
return func
|
||||
|
||||
def expose(*args):
|
||||
if cond:
|
||||
return args[0]
|
||||
def with_template(func):
|
||||
func
|
||||
return with_template
|
||||
|
||||
class TGController(object):
|
||||
pass
|
||||
|
||||
class TurboGearsContextMember(object):
|
||||
"""Member of the TurboGears request context.
|
||||
Provides access to turbogears context members
|
||||
like request, response, template context and so on
|
||||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
self.__dict__['name'] = name
|
||||
|
||||
def _current_obj(self):
|
||||
return getattr(context, self.name)
|
||||
|
||||
|
||||
request = TurboGearsContextMember(name="request")
|
||||
response = TurboGearsContextMember(name="response")
|
||||
Reference in New Issue
Block a user