Python: Tornado cleanup using API graphs

I wasn't able to roll out API graphs as widely in Tornado as I had
hoped, since we're lacking the "def" part. This means most of the
`InstanceSource` machinery will have to stay.
This commit is contained in:
Taus
2021-04-13 11:43:00 +00:00
committed by GitHub
parent f341d5010d
commit 98d936d8b3

View File

@@ -8,6 +8,7 @@ private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.dataflow.new.TaintTracking
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
private import semmle.python.regex
/**
@@ -19,54 +20,13 @@ private module Tornado {
// tornado
// ---------------------------------------------------------------------------
/** Gets a reference to the `tornado` module. */
private DataFlow::Node tornado(DataFlow::TypeTracker t) {
t.start() and
result = DataFlow::importNode("tornado")
or
exists(DataFlow::TypeTracker t2 | result = tornado(t2).track(t2, t))
}
/** Gets a reference to the `tornado` module. */
DataFlow::Node tornado() { result = tornado(DataFlow::TypeTracker::end()) }
API::Node tornado() { result = API::moduleImport("tornado") }
/**
* Gets a reference to the attribute `attr_name` of the `tornado` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node tornado_attr(DataFlow::TypeTracker t, string attr_name) {
attr_name in ["web", "httputil"] and
(
t.start() and
result = DataFlow::importNode("tornado" + "." + attr_name)
or
t.startInAttr(attr_name) and
result = tornado()
)
or
// Due to bad performance when using normal setup with `tornado_attr(t2, attr_name).track(t2, t)`
// we have inlined that code and forced a join
exists(DataFlow::TypeTracker t2 |
exists(DataFlow::StepSummary summary |
tornado_attr_first_join(t2, attr_name, result, summary) and
t = t2.append(summary)
)
)
}
pragma[nomagic]
private predicate tornado_attr_first_join(
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res, DataFlow::StepSummary summary
) {
DataFlow::StepSummary::step(tornado_attr(t2, attr_name), res, summary)
}
/**
* Gets a reference to the attribute `attr_name` of the `tornado` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node tornado_attr(string attr_name) {
result = tornado_attr(DataFlow::TypeTracker::end(), attr_name)
}
private API::Node tornado_attr(string attr_name) { result = tornado().getMember(attr_name) }
/** Provides models for the `tornado` module. */
module tornado {
@@ -74,7 +34,7 @@ private module Tornado {
// tornado.web
// -------------------------------------------------------------------------
/** Gets a reference to the `tornado.web` module. */
DataFlow::Node web() { result = tornado_attr("web") }
API::Node web() { result = tornado_attr("web") }
/** Provides models for the `tornado.web` module */
module web {
@@ -82,41 +42,7 @@ private module Tornado {
* Gets a reference to the attribute `attr_name` of the `tornado.web` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node web_attr(DataFlow::TypeTracker t, string attr_name) {
attr_name in ["RequestHandler", "Application"] and
(
t.start() and
result = DataFlow::importNode("tornado.web" + "." + attr_name)
or
t.startInAttr(attr_name) and
result = web()
)
or
// Due to bad performance when using normal setup with `web_attr(t2, attr_name).track(t2, t)`
// we have inlined that code and forced a join
exists(DataFlow::TypeTracker t2 |
exists(DataFlow::StepSummary summary |
web_attr_first_join(t2, attr_name, result, summary) and
t = t2.append(summary)
)
)
}
pragma[nomagic]
private predicate web_attr_first_join(
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
DataFlow::StepSummary summary
) {
DataFlow::StepSummary::step(web_attr(t2, attr_name), res, summary)
}
/**
* Gets a reference to the attribute `attr_name` of the `tornado.web` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node web_attr(string attr_name) {
result = web_attr(DataFlow::TypeTracker::end(), attr_name)
}
private API::Node web_attr(string attr_name) { result = web().getMember(attr_name) }
/**
* Provides models for the `tornado.web.RequestHandler` class and subclasses.
@@ -125,22 +51,11 @@ private module Tornado {
*/
module RequestHandler {
/** Gets a reference to the `tornado.web.RequestHandler` class or any subclass. */
private DataFlow::Node subclassRef(DataFlow::TypeTracker t) {
t.start() and
result = web_attr("RequestHandler")
or
// subclasses in project code
result.asExpr().(ClassExpr).getABase() = subclassRef(t.continue()).asExpr()
or
exists(DataFlow::TypeTracker t2 | result = subclassRef(t2).track(t2, t))
}
/** Gets a reference to the `tornado.web.RequestHandler` class or any subclass. */
DataFlow::Node subclassRef() { result = subclassRef(DataFlow::TypeTracker::end()) }
API::Node subclassRef() { result = web_attr("RequestHandler").getASubclass*() }
/** A RequestHandler class (most likely in project code). */
class RequestHandlerClass extends Class {
RequestHandlerClass() { this.getParent() = subclassRef().asExpr() }
RequestHandlerClass() { this.getParent() = subclassRef().getAUse().asExpr() }
/** Gets a function that could handle incoming requests, if any. */
Function getARequestHandler() {
@@ -151,7 +66,7 @@ private module Tornado {
}
/** Gets a reference to this class. */
private DataFlow::Node getARef(DataFlow::TypeTracker t) {
private DataFlow::LocalSourceNode getARef(DataFlow::TypeTracker t) {
t.start() and
result.asExpr().(ClassExpr) = this.getParent()
or
@@ -159,7 +74,7 @@ private module Tornado {
}
/** Gets a reference to this class. */
DataFlow::Node getARef() { result = this.getARef(DataFlow::TypeTracker::end()) }
DataFlow::Node getARef() { this.getARef(DataFlow::TypeTracker::end()).flowsTo(result) }
}
/**
@@ -184,7 +99,7 @@ private module Tornado {
}
/** Gets a reference to an instance of the `tornado.web.RequestHandler` class or any subclass. */
private DataFlow::Node instance(DataFlow::TypeTracker t) {
private DataFlow::LocalSourceNode instance(DataFlow::TypeTracker t) {
t.start() and
result instanceof InstanceSource
or
@@ -192,10 +107,10 @@ private module Tornado {
}
/** Gets a reference to an instance of the `tornado.web.RequestHandler` class or any subclass. */
DataFlow::Node instance() { result = instance(DataFlow::TypeTracker::end()) }
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
/** Gets a reference to one of the methods `get_argument`, `get_body_argument`, `get_query_argument`. */
private DataFlow::Node argumentMethod(DataFlow::TypeTracker t) {
private DataFlow::LocalSourceNode argumentMethod(DataFlow::TypeTracker t) {
t.startInAttr(["get_argument", "get_body_argument", "get_query_argument"]) and
result = instance()
or
@@ -203,10 +118,12 @@ private module Tornado {
}
/** Gets a reference to one of the methods `get_argument`, `get_body_argument`, `get_query_argument`. */
DataFlow::Node argumentMethod() { result = argumentMethod(DataFlow::TypeTracker::end()) }
DataFlow::Node argumentMethod() {
argumentMethod(DataFlow::TypeTracker::end()).flowsTo(result)
}
/** Gets a reference to one of the methods `get_arguments`, `get_body_arguments`, `get_query_arguments`. */
private DataFlow::Node argumentsMethod(DataFlow::TypeTracker t) {
private DataFlow::LocalSourceNode argumentsMethod(DataFlow::TypeTracker t) {
t.startInAttr(["get_arguments", "get_body_arguments", "get_query_arguments"]) and
result = instance()
or
@@ -217,7 +134,7 @@ private module Tornado {
DataFlow::Node argumentsMethod() { result = argumentsMethod(DataFlow::TypeTracker::end()) }
/** Gets a reference the `redirect` method. */
private DataFlow::Node redirectMethod(DataFlow::TypeTracker t) {
private DataFlow::LocalSourceNode redirectMethod(DataFlow::TypeTracker t) {
t.startInAttr("redirect") and
result = instance()
or
@@ -225,10 +142,12 @@ private module Tornado {
}
/** Gets a reference the `redirect` method. */
DataFlow::Node redirectMethod() { result = redirectMethod(DataFlow::TypeTracker::end()) }
DataFlow::Node redirectMethod() {
redirectMethod(DataFlow::TypeTracker::end()).flowsTo(result)
}
/** Gets a reference to the `write` method. */
private DataFlow::Node writeMethod(DataFlow::TypeTracker t) {
private DataFlow::LocalSourceNode writeMethod(DataFlow::TypeTracker t) {
t.startInAttr("write") and
result = instance()
or
@@ -236,7 +155,7 @@ private module Tornado {
}
/** Gets a reference to the `write` method. */
DataFlow::Node writeMethod() { result = writeMethod(DataFlow::TypeTracker::end()) }
DataFlow::Node writeMethod() { writeMethod(DataFlow::TypeTracker::end()).flowsTo(result) }
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
@@ -279,15 +198,7 @@ private module Tornado {
*/
module Application {
/** Gets a reference to the `tornado.web.Application` class. */
private DataFlow::Node classRef(DataFlow::TypeTracker t) {
t.start() and
result = web_attr("Application")
or
exists(DataFlow::TypeTracker t2 | result = classRef(t2).track(t2, t))
}
/** Gets a reference to the `tornado.web.Application` class. */
DataFlow::Node classRef() { result = classRef(DataFlow::TypeTracker::end()) }
API::Node classRef() { result = web_attr("Application") }
/**
* A source of instances of `tornado.web.Application`, extend this class to model new instances.
@@ -304,11 +215,11 @@ private module Tornado {
class ClassInstantiation extends InstanceSource, DataFlow::CfgNode {
override CallNode node;
ClassInstantiation() { node.getFunction() = classRef().asCfgNode() }
ClassInstantiation() { this = classRef().getACall() }
}
/** Gets a reference to an instance of `tornado.web.Application`. */
private DataFlow::Node instance(DataFlow::TypeTracker t) {
private DataFlow::LocalSourceNode instance(DataFlow::TypeTracker t) {
t.start() and
result instanceof InstanceSource
or
@@ -316,10 +227,10 @@ private module Tornado {
}
/** Gets a reference to an instance of `tornado.web.Application`. */
DataFlow::Node instance() { result = instance(DataFlow::TypeTracker::end()) }
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
/** Gets a reference to the `add_handlers` method. */
private DataFlow::Node add_handlers(DataFlow::TypeTracker t) {
private DataFlow::LocalSourceNode add_handlers(DataFlow::TypeTracker t) {
t.startInAttr("add_handlers") and
result = instance()
or
@@ -327,7 +238,7 @@ private module Tornado {
}
/** Gets a reference to the `add_handlers` method. */
DataFlow::Node add_handlers() { result = add_handlers(DataFlow::TypeTracker::end()) }
DataFlow::Node add_handlers() { add_handlers(DataFlow::TypeTracker::end()).flowsTo(result) }
}
}
@@ -335,7 +246,7 @@ private module Tornado {
// tornado.httputil
// -------------------------------------------------------------------------
/** Gets a reference to the `tornado.httputil` module. */
DataFlow::Node httputil() { result = tornado_attr("httputil") }
API::Node httputil() { result = tornado_attr("httputil") }
/** Provides models for the `tornado.httputil` module */
module httputil {
@@ -343,41 +254,7 @@ private module Tornado {
* Gets a reference to the attribute `attr_name` of the `tornado.httputil` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node httputil_attr(DataFlow::TypeTracker t, string attr_name) {
attr_name in ["HTTPServerRequest"] and
(
t.start() and
result = DataFlow::importNode("tornado.httputil" + "." + attr_name)
or
t.startInAttr(attr_name) and
result = httputil()
)
or
// Due to bad performance when using normal setup with `httputil_attr(t2, attr_name).track(t2, t)`
// we have inlined that code and forced a join
exists(DataFlow::TypeTracker t2 |
exists(DataFlow::StepSummary summary |
httputil_attr_first_join(t2, attr_name, result, summary) and
t = t2.append(summary)
)
)
}
pragma[nomagic]
private predicate httputil_attr_first_join(
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
DataFlow::StepSummary summary
) {
DataFlow::StepSummary::step(httputil_attr(t2, attr_name), res, summary)
}
/**
* Gets a reference to the attribute `attr_name` of the `tornado.httputil` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node httputil_attr(string attr_name) {
result = httputil_attr(DataFlow::TypeTracker::end(), attr_name)
}
private API::Node httputil_attr(string attr_name) { result = httputil().getMember(attr_name) }
/**
* Provides models for the `tornado.httputil.HttpServerRequest` class
@@ -386,15 +263,7 @@ private module Tornado {
*/
module HttpServerRequest {
/** Gets a reference to the `tornado.httputil.HttpServerRequest` class. */
private DataFlow::Node classRef(DataFlow::TypeTracker t) {
t.start() and
result = httputil_attr("HttpServerRequest")
or
exists(DataFlow::TypeTracker t2 | result = classRef(t2).track(t2, t))
}
/** Gets a reference to the `tornado.httputil.HttpServerRequest` class. */
DataFlow::Node classRef() { result = classRef(DataFlow::TypeTracker::end()) }
API::Node classRef() { result = httputil_attr("HttpServerRequest") }
/**
* A source of instances of `tornado.httputil.HttpServerRequest`, extend this class to model new instances.
@@ -411,7 +280,7 @@ private module Tornado {
private class ClassInstantiation extends InstanceSource, DataFlow::CfgNode {
override CallNode node;
ClassInstantiation() { node.getFunction() = classRef().asCfgNode() }
ClassInstantiation() { this = classRef().getACall() }
}
/** Gets a reference to an instance of `tornado.httputil.HttpServerRequest`. */