Python: Expand Tornado tests and add annotations

I should probably have split this up into 2 commits, so sorry that didn't happen :|
This commit is contained in:
Rasmus Wriedt Larsen
2020-12-21 15:29:01 +01:00
parent b4f3399534
commit 9cd8a862a0
10 changed files with 314 additions and 13 deletions

View File

@@ -0,0 +1,12 @@
import python
import experimental.meta.ConceptsTest
//
// class DedicatedResponseTest extends HttpServerHttpResponseTest {
// DedicatedResponseTest() { file.getShortName() = "response_test.py" }
// }
//
// class OtherResponseTest extends HttpServerHttpResponseTest {
// OtherResponseTest() { not this instanceof DedicatedResponseTest }
//
// override string getARelevantTag() { result = "HttpResponse" }
// }

View File

@@ -0,0 +1,3 @@
From Tornado 6.0 Python 3.5+ is [required](https://www.tornadoweb.org/en/stable/index.html#installation)
https://www.tornadoweb.org/en/stable/guide/structure.html#handling-request-input

View File

@@ -0,0 +1,41 @@
| taint_test.py:6 | fail | get | name |
| taint_test.py:6 | fail | get | number |
| taint_test.py:7 | ok | get | foo |
| taint_test.py:11 | fail | get | self.get_argument(..) |
| taint_test.py:12 | fail | get | self.get_arguments(..) |
| taint_test.py:13 | fail | get | self.get_arguments(..)[0] |
| taint_test.py:15 | fail | get | self.get_body_argument(..) |
| taint_test.py:16 | fail | get | self.get_body_arguments(..) |
| taint_test.py:17 | fail | get | self.get_body_arguments(..)[0] |
| taint_test.py:19 | fail | get | self.get_query_argument(..) |
| taint_test.py:20 | fail | get | self.get_query_arguments(..) |
| taint_test.py:21 | fail | get | self.get_query_arguments(..)[0] |
| taint_test.py:23 | fail | get | self.path_args |
| taint_test.py:24 | fail | get | self.path_args[0] |
| taint_test.py:26 | fail | get | self.path_kwargs |
| taint_test.py:27 | fail | get | self.path_kwargs["name"] |
| taint_test.py:34 | fail | get | request |
| taint_test.py:36 | fail | get | request.uri |
| taint_test.py:37 | fail | get | request.path |
| taint_test.py:38 | fail | get | request.query |
| taint_test.py:39 | fail | get | request.full_url() |
| taint_test.py:41 | fail | get | request.remote_ip |
| taint_test.py:43 | fail | get | request.body |
| taint_test.py:45 | fail | get | request.arguments |
| taint_test.py:46 | fail | get | request.arguments["name"] |
| taint_test.py:47 | fail | get | request.arguments["name"][0] |
| taint_test.py:49 | fail | get | request.query_arguments |
| taint_test.py:50 | fail | get | request.query_arguments["name"] |
| taint_test.py:51 | fail | get | request.query_arguments["name"][0] |
| taint_test.py:53 | fail | get | request.body_arguments |
| taint_test.py:54 | fail | get | request.body_arguments["name"] |
| taint_test.py:55 | fail | get | request.body_arguments["name"][0] |
| taint_test.py:58 | fail | get | request.headers |
| taint_test.py:59 | fail | get | request.headers["header-name"] |
| taint_test.py:60 | fail | get | request.headers.get_list(..) |
| taint_test.py:61 | fail | get | request.headers.get_all() |
| taint_test.py:62 | fail | get | ListComp |
| taint_test.py:65 | fail | get | request.cookies |
| taint_test.py:66 | fail | get | request.cookies["cookie-name"] |
| taint_test.py:67 | fail | get | request.cookies["cookie-name"].key |
| taint_test.py:68 | fail | get | request.cookies["cookie-name"].value |

View File

@@ -0,0 +1,6 @@
import experimental.dataflow.tainttracking.TestTaintLib
import semmle.python.dataflow.new.RemoteFlowSources
class RemoteFlowTestTaintConfiguration extends TestTaintTrackingConfiguration {
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
}

View File

@@ -2,26 +2,26 @@ import tornado.web
class BasicHandler(tornado.web.RequestHandler):
def get(self):
def get(self): # $ MISSING: requestHandler
self.write("BasicHandler " + self.get_argument("xss"))
def post(self):
def post(self): # $ MISSING: requestHandler
self.write("BasicHandler (POST)")
class DeepInheritance(BasicHandler):
def get(self):
def get(self): # $ MISSING: requestHandler
self.write("DeepInheritance" + self.get_argument("also_xss"))
class FormHandler(tornado.web.RequestHandler):
def post(self):
def post(self): # $ MISSING: requestHandler
name = self.get_body_argument("name")
self.write(name)
class RedirectHandler(tornado.web.RequestHandler):
def get(self):
def get(self): # $ MISSING: requestHandler
req = self.request
h = req.headers
url = h["url"]
@@ -29,7 +29,7 @@ class RedirectHandler(tornado.web.RequestHandler):
class BaseReverseInheritance(tornado.web.RequestHandler):
def get(self):
def get(self): # $ MISSING: requestHandler
self.write("hello from BaseReverseInheritance")
@@ -38,13 +38,16 @@ class ReverseInheritance(BaseReverseInheritance):
def make_app():
return tornado.web.Application([
(r"/basic", BasicHandler),
(r"/deep", DeepInheritance),
(r"/form", FormHandler),
(r"/redirect", RedirectHandler),
(r"/reverse-inheritance", ReverseInheritance),
])
return tornado.web.Application(
[
(r"/basic", BasicHandler), # $ MISSING: routeSetup="/basic"
(r"/deep", DeepInheritance), # $ MISSING: routeSetup="/deep"
(r"/form", FormHandler), # $ MISSING: routeSetup="/form"
(r"/redirect", RedirectHandler), # $ MISSING: routeSetup="/redirect"
(r"/reverse-inheritance", ReverseInheritance), # $ MISSING: routeSetup="/reverse-inheritance"
],
debug=True,
)
if __name__ == "__main__":

View File

@@ -0,0 +1 @@
semmle-extractor-options: --max-import-depth=1 --lang=3

View File

@@ -0,0 +1,35 @@
import tornado.web
class ResponseWriting(tornado.web.RequestHandler):
def get(self, type_): # $ MISSING: requestHandler routedParameter=type_
if type_ == "str":
self.write("foo")
elif type_ == "bytes":
self.write(b"foo")
elif type_ == "dict":
# Content-type will be set to `application/json`
self.write({"foo": 42})
else:
raise Exception("Bad type {} {}".format(type_, type(type_)))
def make_app():
return tornado.web.Application(
[
(r"/ResponseWriting/(str|bytes|dict)", ResponseWriting), # $ MISSING: routeSetup="/ResponseWriting/(str|bytes|dict)"
],
debug=True,
)
if __name__ == "__main__":
import tornado.ioloop
app = make_app()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
# http://localhost:8888/ResponseWriting/str
# http://localhost:8888/ResponseWriting/bytes
# http://localhost:8888/ResponseWriting/dict

View File

@@ -0,0 +1,110 @@
import tornado.web
import tornado.routing
class FooHandler(tornado.web.RequestHandler):
def get(self, x, y=None, not_used=None): # $ MISSING: requestHandler routedParameter=x routedParameter=y
self.write("FooHandler {} {}".format(x, y))
class BarHandler(tornado.web.RequestHandler):
def get(self, x, y=None, not_used=None): # $ MISSING: requestHandler routedParameter=x routedParameter=y
self.write("BarHandler {} {}".format(x, y))
class BazHandler(tornado.web.RequestHandler):
def get(self, x, y=None, not_used=None): # $ MISSING: requestHandler routedParameter=x routedParameter=y
self.write("BazHandler {} {}".format(x, y))
class KwArgs(tornado.web.RequestHandler):
def get(self, *, x, y=None, not_used=None): # $ MISSING: requestHandler routedParameter=x routedParameter=y
self.write("KwArgs {} {}".format(x, y))
class OnlyLocalhost(tornado.web.RequestHandler):
def get(self): # $ MISSING: requestHandler
self.write("OnlyLocalhost")
class One(tornado.web.RequestHandler):
def get(self): # $ MISSING: requestHandler
self.write("One")
class Two(tornado.web.RequestHandler):
def get(self): # $ MISSING: requestHandler
self.write("Two")
class Three(tornado.web.RequestHandler):
def get(self): # $ MISSING: requestHandler
self.write("Three")
class AddedLater(tornado.web.RequestHandler):
def get(self, x, y=None, not_used=None): # $ MISSING: requestHandler routedParameter=x routedParameter=y
self.write("AddedLater {} {}".format(x, y))
class PossiblyNotRouted(tornado.web.RequestHandler):
# Even if our analysis can't find a route-setup for this class, we should still
# consider it to be a handle incoming HTTP requests
def get(self): # $ MISSING: requestHandler
self.write("NotRouted")
def make_app():
# see https://www.tornadoweb.org/en/stable/routing.html for even more examples
app = tornado.web.Application(
[
(r"/foo/([0-9]+)/([0-9]+)?", FooHandler), # $ MISSING: routeSetup="/foo/([0-9]+)/([0-9]+)?"
tornado.web.URLSpec(r"/bar/([0-9]+)/([0-9]+)?", BarHandler), # $ MISSING: routeSetup="/bar/([0-9]+)/([0-9]+)?"
# Very verbose way to write same as FooHandler
tornado.routing.Rule(tornado.routing.PathMatches(r"/baz/([0-9]+)/([0-9]+)?"), BazHandler), # $ MISSING: routeSetup="/baz/([0-9]+)/([0-9]+)?"
(r"/kw-args/(?P<x>[0-9]+)/(?P<y>[0-9]+)?", KwArgs), # $ MISSING: routeSetup="/kw-args/(?P<x>[0-9]+)/(?P<y>[0-9]+)?"
# You can do nesting
(r"/(one|two|three)", [
(r"/one", One), # $ MISSING: routeSetup="/one"
(r"/two", Two), # $ MISSING: routeSetup="/two"
(r"/three", Three) # $ MISSING: routeSetup="/three"
]),
# which is _one_ recommended way to ensure known host is used
(tornado.routing.HostMatches(r"(localhost|127\.0\.0\.1)"), [
("/only-localhost", OnlyLocalhost) # $ MISSING: routeSetup="/only-localhost"
]),
],
debug=True,
)
app.add_handlers(r".*", [(r"/added-later/([0-9]+)/([0-9]+)?", AddedLater)]) # $ MISSING: routeSetup="/added-later/([0-9]+)/([0-9]+)?"
return app
if __name__ == "__main__":
import tornado.ioloop
app = make_app()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
# http://localhost:8888/foo/42/
# http://localhost:8888/foo/42/1337
# http://localhost:8888/bar/42/
# http://localhost:8888/bar/42/1337
# http://localhost:8888/baz/42/
# http://localhost:8888/baz/42/1337
# http://localhost:8888/kw-args/42/
# http://localhost:8888/kw-args/42/1337
# http://localhost:8888/only-localhost
# http://localhost:8888/one
# http://localhost:8888/two
# http://localhost:8888/three
# http://localhost:8888/added-later

View File

@@ -0,0 +1,90 @@
import tornado.web
class TaintTest(tornado.web.RequestHandler):
def get(self, name = "World!", number="0", foo="foo"): # $ MISSING: requestHandler routedParameter=name routedParameter=number
ensure_tainted(name, number)
ensure_not_tainted(foo)
ensure_tainted(
# see https://www.tornadoweb.org/en/stable/web.html#input
self.get_argument("name"),
self.get_arguments("name"),
self.get_arguments("name")[0],
self.get_body_argument("name"),
self.get_body_arguments("name"),
self.get_body_arguments("name")[0],
self.get_query_argument("name"),
self.get_query_arguments("name"),
self.get_query_arguments("name")[0],
self.path_args,
self.path_args[0],
self.path_kwargs,
self.path_kwargs["name"],
)
request = self.request
ensure_tainted(
# see https://www.tornadoweb.org/en/stable/httputil.html#tornado.httputil.HTTPServerRequest
request,
request.uri,
request.path,
request.query,
request.full_url(),
request.remote_ip,
request.body,
request.arguments,
request.arguments["name"],
request.arguments["name"][0],
request.query_arguments,
request.query_arguments["name"],
request.query_arguments["name"][0],
request.body_arguments,
request.body_arguments["name"],
request.body_arguments["name"][0],
# dict-like, see https://www.tornadoweb.org/en/stable/httputil.html#tornado.httputil.HTTPHeaders
request.headers,
request.headers["header-name"],
request.headers.get_list("header-name"),
request.headers.get_all(),
[(k, v) for (k, v) in request.headers.get_all()],
# Dict[str, http.cookies.Morsel]
request.cookies,
request.cookies["cookie-name"],
request.cookies["cookie-name"].key,
request.cookies["cookie-name"].value,
)
def make_app():
return tornado.web.Application(
[
(r"/test_taint/([^/]+)/([0-9]+)", TaintTest), # $ MISSING: routeSetup="/test_taint/([^/]+)/([0-9]+)"
],
debug=True,
)
if __name__ == "__main__":
import tornado.ioloop
app = make_app()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
# http://localhost:8888/ResponseWriting/str
# http://localhost:8888/ResponseWriting/bytes
# http://localhost:8888/ResponseWriting/dict