Python: Resolve all meth = obj.meth; meth() TODOs

It would probably have been easier to do this as the _first_ thing...
but that's too late now 😓
This commit is contained in:
Rasmus Wriedt Larsen
2021-07-21 13:25:53 +02:00
parent 6f63c03558
commit be1cad864b
9 changed files with 81 additions and 190 deletions

View File

@@ -201,24 +201,17 @@
" */",
" private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {",
" override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {",
" // Methods",
" //",
" // TODO: When we have tools that make it easy, model these properly to handle",
" // `meth = obj.meth; meth()`. Until then, we'll use this more syntactic approach",
" // (since it allows us to at least capture the most common cases).",
" // normal (non-async) methods",
" nodeFrom = instance() and",
" exists(DataFlow::AttrRead attr | attr.getObject() = nodeFrom |",
" // normal (non-async) methods",
" attr.getAttributeName() in [\"TODO\"] and",
" nodeTo.(DataFlow::CallCfgNode).getFunction() = attr",
" or",
" // async methods",
" exists(Await await, DataFlow::CallCfgNode call |",
" attr.getAttributeName() in [\"TODO\"] and",
" call.getFunction() = attr and",
" await.getValue() = call.asExpr() and",
" nodeTo.asExpr() = await",
" )",
" nodeTo.(DataFlow::MethodCallNode).calls(nodeFrom, [\"TODO\"])",
" or",
" // async methods",
" exists(DataFlow::MethodCallNode call, Await await |",
" nodeTo.asExpr() = await and",
" nodeFrom = instance()",
" |",
" await.getValue() = any(DataFlow::Node awaitable | call.flowsTo(awaitable)).asExpr() and",
" call.calls(nodeFrom, [\"TODO\"])",
" )",
" or",
" // Attributes",

View File

@@ -359,27 +359,21 @@ module AiohttpWebModel {
*/
private class AiohttpStreamReaderAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// Methods
//
// TODO: When we have tools that make it easy, model these properly to handle
// `meth = obj.meth; meth()`. Until then, we'll use this more syntactic approach
// (since it allows us to at least capture the most common cases).
nodeFrom = StreamReader::instance() and
exists(DataFlow::AttrRead attr | attr.getObject() = nodeFrom |
// normal methods
attr.getAttributeName() in ["read_nowait"] and
nodeTo.(DataFlow::CallCfgNode).getFunction() = attr
or
// async methods
exists(Await await, DataFlow::CallCfgNode call |
attr.getAttributeName() in [
"read", "readany", "readexactly", "readline", "readchunk", "iter_chunked",
"iter_any", "iter_chunks"
] and
call.getFunction() = attr and
await.getValue() = call.asExpr() and
nodeTo.asExpr() = await
)
// normal (non-async) methods
nodeFrom = instance() and
nodeTo.(DataFlow::MethodCallNode).calls(nodeFrom, ["read_nowait"])
or
// async methods
exists(DataFlow::MethodCallNode call, Await await |
nodeTo.asExpr() = await and
nodeFrom = instance()
|
await.getValue() = any(DataFlow::Node awaitable | call.flowsTo(awaitable)).asExpr() and
call.calls(nodeFrom,
[
"read", "readany", "readexactly", "readline", "readchunk", "iter_chunked", "iter_any",
"iter_chunks"
])
)
}
}
@@ -438,24 +432,17 @@ module AiohttpWebModel {
*/
private class AiohttpRequestAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// Methods
//
// TODO: When we have tools that make it easy, model these properly to handle
// `meth = obj.meth; meth()`. Until then, we'll use this more syntactic approach
// (since it allows us to at least capture the most common cases).
// normal (non-async) methods
nodeFrom = Request::instance() and
exists(DataFlow::AttrRead attr | attr.getObject() = nodeFrom |
// normal methods
attr.getAttributeName() in ["clone", "get_extra_info"] and
nodeTo.(DataFlow::CallCfgNode).getFunction() = attr
or
// async methods
exists(Await await, DataFlow::CallCfgNode call |
attr.getAttributeName() in ["read", "text", "json", "multipart", "post"] and
call.getFunction() = attr and
await.getValue() = call.asExpr() and
nodeTo.asExpr() = await
)
nodeTo.(DataFlow::MethodCallNode).calls(nodeFrom, ["clone", "get_extra_info"])
or
// async methods
exists(DataFlow::MethodCallNode call, Await await |
nodeTo.asExpr() = await and
nodeFrom = Request::instance()
|
await.getValue() = any(DataFlow::Node awaitable | call.flowsTo(awaitable)).asExpr() and
call.calls(nodeFrom, ["read", "text", "json", "multipart", "post"])
)
or
// Attributes

View File

@@ -348,17 +348,11 @@ private module Django {
nodeTo = call
)
or
// Methods
//
// TODO: When we have tools that make it easy, model these properly to handle
// `meth = obj.meth; meth()`. Until then, we'll use this more syntactic approach
// (since it allows us to at least capture the most common cases).
// normal (non-async) methods
nodeFrom = instance() and
exists(DataFlow::AttrRead attr | attr.getObject() = nodeFrom |
// methods (non-async)
attr.getAttributeName() in ["getlist", "lists", "popitem", "dict", "urlencode"] and
nodeTo.(DataFlow::CallCfgNode).getFunction() = attr
)
nodeTo
.(DataFlow::MethodCallNode)
.calls(nodeFrom, ["getlist", "lists", "popitem", "dict", "urlencode"])
}
}
}
@@ -2044,18 +2038,11 @@ private module PrivateDjango {
private class DjangoHttpRequestAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// Methods
//
// TODO: When we have tools that make it easy, model these properly to handle
// `meth = obj.meth; meth()`. Until then, we'll use this more syntactic approach
// (since it allows us to at least capture the most common cases).
// normal (non-async) methods
nodeFrom = django::http::request::HttpRequest::instance() and
exists(DataFlow::AttrRead attr | attr.getObject() = nodeFrom |
attr.getAttributeName() in [
"get_full_path", "get_full_path_info", "read", "readline", "readlines"
] and
nodeTo.(DataFlow::CallCfgNode).getFunction() = attr
)
nodeTo
.(DataFlow::MethodCallNode)
.calls(nodeFrom, ["get_full_path", "get_full_path_info", "read", "readline", "readlines"])
or
// special handling of the `build_absolute_uri` method, see
// https://docs.djangoproject.com/en/3.0/ref/request-response/#django.http.HttpRequest.build_absolute_uri

View File

@@ -71,17 +71,9 @@ module Multidict {
nodeTo = call
)
or
// Methods
//
// TODO: When we have tools that make it easy, model these properly to handle
// `meth = obj.meth; meth()`. Until then, we'll use this more syntactic approach
// (since it allows us to at least capture the most common cases).
// normal (non-async) methods
nodeFrom = instance() and
exists(DataFlow::AttrRead attr | attr.getObject() = nodeFrom |
// methods (non-async)
attr.getAttributeName() in ["getone", "getall"] and
nodeTo.(DataFlow::CallCfgNode).getFunction() = attr
)
nodeTo.(DataFlow::MethodCallNode).calls(nodeFrom, ["getone", "getall"])
}
}
}

View File

@@ -101,17 +101,11 @@ module Stdlib {
*/
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// Methods
//
// TODO: When we have tools that make it easy, model these properly to handle
// `meth = obj.meth; meth()`. Until then, we'll use this more syntactic approach
// (since it allows us to at least capture the most common cases).
// normal (non-async) methods
nodeFrom = instance() and
exists(DataFlow::AttrRead attr | attr.getObject() = nodeFrom |
// normal (non-async) methods
attr.getAttributeName() in ["get_all", "as_bytes", "as_string", "keys"] and
nodeTo.(DataFlow::CallCfgNode).getFunction() = attr
)
nodeTo
.(DataFlow::MethodCallNode)
.calls(nodeFrom, ["get_all", "as_bytes", "as_string", "keys"])
}
}
}
@@ -149,17 +143,9 @@ module Stdlib {
*/
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// Methods
//
// TODO: When we have tools that make it easy, model these properly to handle
// `meth = obj.meth; meth()`. Until then, we'll use this more syntactic approach
// (since it allows us to at least capture the most common cases).
// normal (non-async) methods
nodeFrom = instance() and
exists(DataFlow::AttrRead attr | attr.getObject() = nodeFrom |
// normal (non-async) methods
attr.getAttributeName() in ["output", "js_output"] and
nodeTo.(DataFlow::CallCfgNode).getFunction() = attr
)
nodeTo.(DataFlow::MethodCallNode).calls(nodeFrom, ["output", "js_output"])
or
// Attributes
nodeFrom = instance() and

View File

@@ -50,17 +50,9 @@ private module Tornado {
*/
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// Methods
//
// TODO: When we have tools that make it easy, model these properly to handle
// `meth = obj.meth; meth()`. Until then, we'll use this more syntactic approach
// (since it allows us to at least capture the most common cases).
// normal (non-async) methods
nodeFrom = instance() and
exists(DataFlow::AttrRead attr | attr.getObject() = nodeFrom |
// normal (non-async) methods
attr.getAttributeName() in ["get_list", "get_all"] and
nodeTo.(DataFlow::CallCfgNode).getFunction() = attr
)
nodeTo.(DataFlow::MethodCallNode).calls(nodeFrom, ["get_list", "get_all"])
}
}
}

View File

@@ -130,20 +130,15 @@ private module Twisted {
*/
private class TwistedRequestAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// Methods
//
// TODO: When we have tools that make it easy, model these properly to handle
// `meth = obj.meth; meth()`. Until then, we'll use this more syntactic approach
// (since it allows us to at least capture the most common cases).
// normal (non-async) methods
nodeFrom = Request::instance() and
exists(DataFlow::AttrRead attr | attr.getObject() = nodeFrom |
// normal (non-async) methods
attr.getAttributeName() in [
"getCookie", "getHeader", "getAllHeaders", "getUser", "getPassword", "getHost",
"getRequestHostname"
] and
nodeTo.(DataFlow::CallCfgNode).getFunction() = attr
)
nodeTo
.(DataFlow::MethodCallNode)
.calls(nodeFrom,
[
"getCookie", "getHeader", "getAllHeaders", "getUser", "getPassword", "getHost",
"getRequestHostname"
])
or
// Attributes
nodeFrom = Request::instance() and
@@ -198,17 +193,8 @@ private module Twisted {
*
* See https://twistedmatrix.com/documents/21.2.0/api/twisted.web.server.Request.html#write
*/
class TwistedRequestWriteCall extends HTTP::Server::HttpResponse::Range, DataFlow::CallCfgNode {
TwistedRequestWriteCall() {
// TODO: When we have tools that make it easy, model these properly to handle
// `meth = obj.meth; meth()`. Until then, we'll use this more syntactic approach
// (since it allows us to at least capture the most common cases).
exists(DataFlow::AttrRead read |
this.getFunction() = read and
read.getObject() = Request::instance() and
read.getAttributeName() = "write"
)
}
class TwistedRequestWriteCall extends HTTP::Server::HttpResponse::Range, DataFlow::MethodCallNode {
TwistedRequestWriteCall() { this.calls(Request::instance(), "write") }
override DataFlow::Node getBody() {
result.asCfgNode() in [node.getArg(0), node.getArgByName("data")]
@@ -225,17 +211,8 @@ private module Twisted {
* See https://twistedmatrix.com/documents/21.2.0/api/twisted.web.http.Request.html#redirect
*/
class TwistedRequestRedirectCall extends HTTP::Server::HttpRedirectResponse::Range,
DataFlow::CallCfgNode {
TwistedRequestRedirectCall() {
// TODO: When we have tools that make it easy, model these properly to handle
// `meth = obj.meth; meth()`. Until then, we'll use this more syntactic approach
// (since it allows us to at least capture the most common cases).
exists(DataFlow::AttrRead read |
this.getFunction() = read and
read.getObject() = Request::instance() and
read.getAttributeName() = "redirect"
)
}
DataFlow::MethodCallNode {
TwistedRequestRedirectCall() { this.calls(Request::instance(), "redirect") }
override DataFlow::Node getBody() { none() }

View File

@@ -154,17 +154,11 @@ module Werkzeug {
*/
class HeadersAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// Methods
//
// TODO: When we have tools that make it easy, model these properly to handle
// `meth = obj.meth; meth()`. Until then, we'll use this more syntactic approach
// (since it allows us to at least capture the most common cases).
// normal (non-async) methods
nodeFrom = instance() and
exists(DataFlow::AttrRead attr | attr.getObject() = nodeFrom |
// methods (non-async)
attr.getAttributeName() in ["getlist", "get_all", "popitem", "to_wsgi_list"] and
nodeTo.(DataFlow::CallCfgNode).getFunction() = attr
)
nodeTo
.(DataFlow::MethodCallNode)
.calls(nodeFrom, ["getlist", "get_all", "popitem", "to_wsgi_list"])
}
}
}

View File

@@ -63,40 +63,23 @@ module Yarl {
nodeTo = call
)
or
// Methods
//
// TODO: When we have tools that make it easy, model these properly to handle
// `meth = obj.meth; meth()`. Until then, we'll use this more syntactic approach
// (since it allows us to at least capture the most common cases).
exists(DataFlow::AttrRead attr |
// methods (that replaces part of URL, taken as only arguments)
attr.getAttributeName() in [
// normal (non-async) methods
nodeFrom = instance() and
nodeTo.(DataFlow::MethodCallNode).calls(nodeFrom, ["human_repr"])
or
// methods that give an altered URL. taint both from object, and form argument
// (to result of call)
exists(DataFlow::MethodCallNode call |
call.calls(instance(),
[
"with_scheme", "with_user", "with_password", "with_host", "with_port", "with_path",
"with_query", "with_query", "update_query", "update_query", "with_fragment",
"with_name",
// join is a bit different, but is still correct to add here :+1:
"join"
] and
(
// obj -> obj.meth()
nodeFrom = instance() and
attr.getObject() = nodeFrom and
nodeTo.(DataFlow::CallCfgNode).getFunction() = attr
or
// argument of obj.meth() -> obj.meth()
attr.getObject() = instance() and
nodeTo.(DataFlow::CallCfgNode).getFunction() = attr and
nodeFrom in [
nodeTo.(DataFlow::CallCfgNode).getArg(_),
nodeTo.(DataFlow::CallCfgNode).getArgByName(_)
]
)
or
// other methods
nodeFrom = instance() and
attr.getObject() = nodeFrom and
attr.getAttributeName() in ["human_repr"] and
nodeTo.(DataFlow::CallCfgNode).getFunction() = attr
]) and
nodeTo = call and
nodeFrom in [call.getObject(), call.getArg(_), call.getArgByName(_)]
)
or
// Attributes