mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Python: Provide internal InstanceTaintStepsHelper
I realized that if you ever wanted to the way taint-steps works again, you would have to go to all the 117 places it has been implemented, and change EVERY ONE OF THEM :( so trying to solve that problem here. Not super happy with the name, but that was just the best I could come up with :D
This commit is contained in:
54
python/.vscode/ql.code-snippets
vendored
54
python/.vscode/ql.code-snippets
vendored
@@ -199,31 +199,51 @@
|
|||||||
" /**",
|
" /**",
|
||||||
" * Taint propagation for `${TM_SELECTED_TEXT}`.",
|
" * Taint propagation for `${TM_SELECTED_TEXT}`.",
|
||||||
" */",
|
" */",
|
||||||
|
" private class InstanceTaintSteps extends InstanceTaintStepsHelper {",
|
||||||
|
" InstanceTaintSteps() { this = \"${TM_SELECTED_TEXT}\" }",
|
||||||
|
" ",
|
||||||
|
" override DataFlow::Node getInstance() { result = instance() }",
|
||||||
|
" ",
|
||||||
|
" override string getAttributeName() { none() }",
|
||||||
|
" ",
|
||||||
|
" override string getMethodName() { none() }",
|
||||||
|
" ",
|
||||||
|
" override string getAsyncMethodName() { none() }",
|
||||||
|
" }",
|
||||||
|
"",
|
||||||
|
" /**",
|
||||||
|
" * Extra taint propagation for `${TM_SELECTED_TEXT}`, not covered by `InstanceTaintSteps`.",
|
||||||
|
" */",
|
||||||
" private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {",
|
" private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {",
|
||||||
" override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {",
|
" override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {",
|
||||||
" // normal (non-async) methods",
|
" // TODO",
|
||||||
" nodeFrom = instance() and",
|
" none()",
|
||||||
" 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",
|
|
||||||
" nodeFrom = instance() and",
|
|
||||||
" nodeTo.(DataFlow::AttrRead).getObject() = nodeFrom and",
|
|
||||||
" nodeTo.(DataFlow::AttrRead).getAttributeName() in [\"TODO\"]",
|
|
||||||
" }",
|
" }",
|
||||||
" }",
|
" }",
|
||||||
"}",
|
"}",
|
||||||
],
|
],
|
||||||
"description": "Type tracking class (select full class path before inserting)",
|
"description": "Type tracking class (select full class path before inserting)",
|
||||||
},
|
},
|
||||||
|
"foo": {
|
||||||
|
"scope": "ql",
|
||||||
|
"prefix": "foo",
|
||||||
|
"body": [
|
||||||
|
" /**",
|
||||||
|
" * Taint propagation for `$1`.",
|
||||||
|
" */",
|
||||||
|
" private class InstanceTaintSteps extends InstanceTaintStepsHelper {",
|
||||||
|
" InstanceTaintSteps() { this = \"$1\" }",
|
||||||
|
"",
|
||||||
|
" override DataFlow::Node getInstance() { result = instance() }",
|
||||||
|
"",
|
||||||
|
" override string getAttributeName() { none() }",
|
||||||
|
"",
|
||||||
|
" override string getMethodName() { none() }",
|
||||||
|
"",
|
||||||
|
" override string getAsyncMethodName() { none() }",
|
||||||
|
" }",
|
||||||
|
],
|
||||||
|
},
|
||||||
"API graph .getMember chain": {
|
"API graph .getMember chain": {
|
||||||
"scope": "ql",
|
"scope": "ql",
|
||||||
"prefix": "api graph .getMember chain",
|
"prefix": "api graph .getMember chain",
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ private import semmle.python.frameworks.internal.PoorMansFunctionResolution
|
|||||||
private import semmle.python.frameworks.internal.SelfRefMixin
|
private import semmle.python.frameworks.internal.SelfRefMixin
|
||||||
private import semmle.python.frameworks.Multidict
|
private import semmle.python.frameworks.Multidict
|
||||||
private import semmle.python.frameworks.Yarl
|
private import semmle.python.frameworks.Yarl
|
||||||
|
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* INTERNAL: Do not use.
|
* INTERNAL: Do not use.
|
||||||
@@ -296,33 +297,25 @@ module AiohttpWebModel {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Taint propagation for `aiohttp.web.Request`.
|
* Taint propagation for `aiohttp.web.Request`.
|
||||||
*
|
|
||||||
* See https://docs.aiohttp.org/en/stable/web_reference.html#request-and-base-request
|
|
||||||
*/
|
*/
|
||||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
InstanceTaintSteps() { this = "aiohttp.web.Request" }
|
||||||
// normal (non-async) methods
|
|
||||||
nodeFrom = Request::instance() and
|
override DataFlow::Node getInstance() { result = instance() }
|
||||||
nodeTo.(DataFlow::MethodCallNode).calls(nodeFrom, ["clone", "get_extra_info"])
|
|
||||||
or
|
override string getAttributeName() {
|
||||||
// async methods
|
result in [
|
||||||
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
|
|
||||||
nodeFrom = Request::instance() and
|
|
||||||
nodeTo.(DataFlow::AttrRead).getObject() = nodeFrom and
|
|
||||||
nodeTo.(DataFlow::AttrRead).getAttributeName() in [
|
|
||||||
"url", "rel_url", "forwarded", "host", "remote", "path", "path_qs", "raw_path", "query",
|
"url", "rel_url", "forwarded", "host", "remote", "path", "path_qs", "raw_path", "query",
|
||||||
"headers", "transport", "cookies", "content", "_payload", "content_type", "charset",
|
"headers", "transport", "cookies", "content", "_payload", "content_type", "charset",
|
||||||
"http_range", "if_modified_since", "if_unmodified_since", "if_range", "match_info"
|
"http_range", "if_modified_since", "if_unmodified_since", "if_range", "match_info"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override string getMethodName() { result in ["clone", "get_extra_info"] }
|
||||||
|
|
||||||
|
override string getAsyncMethodName() {
|
||||||
|
result in ["read", "text", "json", "multipart", "post"]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An attribute read on an `aiohttp.web.Request` that is a `MultiDictProxy` instance. */
|
/** An attribute read on an `aiohttp.web.Request` that is a `MultiDictProxy` instance. */
|
||||||
@@ -424,24 +417,20 @@ module AiohttpWebModel {
|
|||||||
/**
|
/**
|
||||||
* Taint propagation for `aiohttp.StreamReader`.
|
* Taint propagation for `aiohttp.StreamReader`.
|
||||||
*/
|
*/
|
||||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
InstanceTaintSteps() { this = "aiohttp.StreamReader" }
|
||||||
// normal (non-async) methods
|
|
||||||
nodeFrom = instance() and
|
override DataFlow::Node getInstance() { result = instance() }
|
||||||
nodeTo.(DataFlow::MethodCallNode).calls(nodeFrom, ["read_nowait"])
|
|
||||||
or
|
override string getAttributeName() { none() }
|
||||||
// async methods
|
|
||||||
exists(DataFlow::MethodCallNode call, Await await |
|
override string getMethodName() { result in ["read_nowait"] }
|
||||||
nodeTo.asExpr() = await and
|
|
||||||
nodeFrom = instance()
|
override string getAsyncMethodName() {
|
||||||
|
|
result in [
|
||||||
await.getValue() = any(DataFlow::Node awaitable | call.flowsTo(awaitable)).asExpr() and
|
"read", "readany", "readexactly", "readline", "readchunk", "iter_chunked", "iter_any",
|
||||||
call.calls(nodeFrom,
|
"iter_chunks"
|
||||||
[
|
]
|
||||||
"read", "readany", "readexactly", "readline", "readchunk", "iter_chunked", "iter_any",
|
|
||||||
"iter_chunks"
|
|
||||||
])
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ private import semmle.python.frameworks.Stdlib
|
|||||||
private import semmle.python.regex
|
private import semmle.python.regex
|
||||||
private import semmle.python.frameworks.internal.PoorMansFunctionResolution
|
private import semmle.python.frameworks.internal.PoorMansFunctionResolution
|
||||||
private import semmle.python.frameworks.internal.SelfRefMixin
|
private import semmle.python.frameworks.internal.SelfRefMixin
|
||||||
|
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides models for the `django` PyPI package.
|
* Provides models for the `django` PyPI package.
|
||||||
@@ -340,6 +341,23 @@ private module Django {
|
|||||||
/**
|
/**
|
||||||
* Taint propagation for `django.utils.datastructures.MultiValueDict`.
|
* Taint propagation for `django.utils.datastructures.MultiValueDict`.
|
||||||
*/
|
*/
|
||||||
|
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||||
|
InstanceTaintSteps() { this = "django.utils.datastructures.MultiValueDict" }
|
||||||
|
|
||||||
|
override DataFlow::Node getInstance() { result = instance() }
|
||||||
|
|
||||||
|
override string getAttributeName() { none() }
|
||||||
|
|
||||||
|
override string getMethodName() {
|
||||||
|
result in ["getlist", "lists", "popitem", "dict", "urlencode"]
|
||||||
|
}
|
||||||
|
|
||||||
|
override string getAsyncMethodName() { none() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extra taint propagation for `django.utils.datastructures.MultiValueDict`, not covered by `InstanceTaintSteps`.
|
||||||
|
*/
|
||||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||||
// class instantiation
|
// class instantiation
|
||||||
@@ -347,12 +365,6 @@ private module Django {
|
|||||||
nodeFrom = call.getArg(0) and
|
nodeFrom = call.getArg(0) and
|
||||||
nodeTo = call
|
nodeTo = call
|
||||||
)
|
)
|
||||||
or
|
|
||||||
// normal (non-async) methods
|
|
||||||
nodeFrom = instance() and
|
|
||||||
nodeTo
|
|
||||||
.(DataFlow::MethodCallNode)
|
|
||||||
.calls(nodeFrom, ["getlist", "lists", "popitem", "dict", "urlencode"])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -388,15 +400,20 @@ private module Django {
|
|||||||
/**
|
/**
|
||||||
* Taint propagation for `django.core.files.uploadedfile.UploadedFile`.
|
* Taint propagation for `django.core.files.uploadedfile.UploadedFile`.
|
||||||
*/
|
*/
|
||||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
InstanceTaintSteps() { this = "django.core.files.uploadedfile.UploadedFile" }
|
||||||
// Attributes
|
|
||||||
nodeFrom = instance() and
|
override DataFlow::Node getInstance() { result = instance() }
|
||||||
nodeTo.(DataFlow::AttrRead).getObject() = nodeFrom and
|
|
||||||
nodeTo.(DataFlow::AttrRead).getAttributeName() in [
|
override string getAttributeName() {
|
||||||
|
result in [
|
||||||
"content_type", "content_type_extra", "content_type_extra", "charset", "name", "file"
|
"content_type", "content_type_extra", "content_type_extra", "charset", "name", "file"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override string getMethodName() { none() }
|
||||||
|
|
||||||
|
override string getAsyncMethodName() { none() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A file-like object instance that originates from a `UploadedFile`. */
|
/** A file-like object instance that originates from a `UploadedFile`. */
|
||||||
@@ -436,13 +453,16 @@ private module Django {
|
|||||||
/**
|
/**
|
||||||
* Taint propagation for `django.urls.ResolverMatch`.
|
* Taint propagation for `django.urls.ResolverMatch`.
|
||||||
*/
|
*/
|
||||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
InstanceTaintSteps() { this = "django.urls.ResolverMatch" }
|
||||||
// Attributes
|
|
||||||
nodeFrom = instance() and
|
override DataFlow::Node getInstance() { result = instance() }
|
||||||
nodeTo.(DataFlow::AttrRead).getObject() = nodeFrom and
|
|
||||||
nodeTo.(DataFlow::AttrRead).getAttributeName() in ["args", "kwargs"]
|
override string getAttributeName() { result in ["args", "kwargs"] }
|
||||||
}
|
|
||||||
|
override string getMethodName() { none() }
|
||||||
|
|
||||||
|
override string getAsyncMethodName() { none() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -747,15 +767,43 @@ private module PrivateDjango {
|
|||||||
/**
|
/**
|
||||||
* Taint propagation for `django.http.request.HttpRequest`.
|
* Taint propagation for `django.http.request.HttpRequest`.
|
||||||
*/
|
*/
|
||||||
|
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||||
|
InstanceTaintSteps() { this = "django.http.request.HttpRequest" }
|
||||||
|
|
||||||
|
override DataFlow::Node getInstance() { result = instance() }
|
||||||
|
|
||||||
|
override string getAttributeName() {
|
||||||
|
result in [
|
||||||
|
// str / bytes
|
||||||
|
"body", "path", "path_info", "method", "encoding", "content_type",
|
||||||
|
// django.http.QueryDict
|
||||||
|
"GET", "POST",
|
||||||
|
// dict[str, str]
|
||||||
|
"content_params", "COOKIES",
|
||||||
|
// dict[str, Any]
|
||||||
|
"META",
|
||||||
|
// HttpHeaders (case insensitive dict-like)
|
||||||
|
"headers",
|
||||||
|
// MultiValueDict[str, UploadedFile]
|
||||||
|
"FILES",
|
||||||
|
// django.urls.ResolverMatch
|
||||||
|
"resolver_match"
|
||||||
|
]
|
||||||
|
// TODO: Handle that a HttpRequest is iterable
|
||||||
|
}
|
||||||
|
|
||||||
|
override string getMethodName() {
|
||||||
|
result in ["get_full_path", "get_full_path_info", "read", "readline", "readlines"]
|
||||||
|
}
|
||||||
|
|
||||||
|
override string getAsyncMethodName() { none() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extra taint propagation for `django.http.request.HttpRequest`, not covered by `InstanceTaintSteps`.
|
||||||
|
*/
|
||||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||||
// normal (non-async) methods
|
|
||||||
nodeFrom = django::http::request::HttpRequest::instance() and
|
|
||||||
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
|
// 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
|
// https://docs.djangoproject.com/en/3.0/ref/request-response/#django.http.HttpRequest.build_absolute_uri
|
||||||
exists(DataFlow::AttrRead attr, DataFlow::CallCfgNode call, DataFlow::Node instance |
|
exists(DataFlow::AttrRead attr, DataFlow::CallCfgNode call, DataFlow::Node instance |
|
||||||
@@ -775,27 +823,6 @@ private module PrivateDjango {
|
|||||||
nodeFrom = call.getArgByName("location")
|
nodeFrom = call.getArgByName("location")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
or
|
|
||||||
// Attributes
|
|
||||||
nodeFrom = django::http::request::HttpRequest::instance() and
|
|
||||||
nodeTo.(DataFlow::AttrRead).getObject() = nodeFrom and
|
|
||||||
nodeTo.(DataFlow::AttrRead).getAttributeName() in [
|
|
||||||
// str / bytes
|
|
||||||
"body", "path", "path_info", "method", "encoding", "content_type",
|
|
||||||
// django.http.QueryDict
|
|
||||||
"GET", "POST",
|
|
||||||
// dict[str, str]
|
|
||||||
"content_params", "COOKIES",
|
|
||||||
// dict[str, Any]
|
|
||||||
"META",
|
|
||||||
// HttpHeaders (case insensitive dict-like)
|
|
||||||
"headers",
|
|
||||||
// MultiValueDict[str, UploadedFile]
|
|
||||||
"FILES",
|
|
||||||
// django.urls.ResolverMatch
|
|
||||||
"resolver_match"
|
|
||||||
]
|
|
||||||
// TODO: Handle that a HttpRequest is iterable
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ private import semmle.python.dataflow.new.TaintTracking
|
|||||||
private import semmle.python.Concepts
|
private import semmle.python.Concepts
|
||||||
private import semmle.python.frameworks.Werkzeug
|
private import semmle.python.frameworks.Werkzeug
|
||||||
private import semmle.python.ApiGraphs
|
private import semmle.python.ApiGraphs
|
||||||
|
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides models for the `flask` PyPI package.
|
* Provides models for the `flask` PyPI package.
|
||||||
@@ -341,56 +342,56 @@ module Flask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Taint propagation for a flask request.
|
* Taint propagation for `flask.Request`.
|
||||||
*
|
*
|
||||||
* See https://flask.palletsprojects.com/en/1.1.x/api/#flask.Request
|
* See https://flask.palletsprojects.com/en/1.1.x/api/#flask.Request
|
||||||
*/
|
*/
|
||||||
private class FlaskRequestAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
InstanceTaintSteps() { this = "flask.Request" }
|
||||||
// normal (non-async) methods
|
|
||||||
nodeFrom = request().getAUse() and
|
override DataFlow::Node getInstance() { result = request().getAUse() }
|
||||||
nodeTo.(DataFlow::MethodCallNode).calls(nodeFrom, ["get_data", "get_json"])
|
|
||||||
or
|
override string getAttributeName() {
|
||||||
// Attributes
|
result in [
|
||||||
nodeFrom = request().getAUse() and
|
// str
|
||||||
exists(DataFlow::AttrRead read | nodeTo = read and read.getObject() = nodeFrom |
|
"path", "full_path", "base_url", "url", "access_control_request_method",
|
||||||
read.getAttributeName() in [
|
"content_encoding", "content_md5", "content_type", "data", "method", "mimetype", "origin",
|
||||||
// str
|
"query_string", "referrer", "remote_addr", "remote_user", "user_agent",
|
||||||
"path", "full_path", "base_url", "url", "access_control_request_method",
|
// dict
|
||||||
"content_encoding", "content_md5", "content_type", "data", "method", "mimetype",
|
"environ", "cookies", "mimetype_params", "view_args",
|
||||||
"origin", "query_string", "referrer", "remote_addr", "remote_user", "user_agent",
|
// json
|
||||||
// dict
|
"json",
|
||||||
"environ", "cookies", "mimetype_params", "view_args",
|
// List[str]
|
||||||
// json
|
"access_route",
|
||||||
"json",
|
// file-like
|
||||||
// List[str]
|
"stream", "input_stream",
|
||||||
"access_route",
|
// MultiDict[str, str]
|
||||||
// file-like
|
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.MultiDict
|
||||||
"stream", "input_stream",
|
"args", "values", "form",
|
||||||
// MultiDict[str, str]
|
// MultiDict[str, FileStorage]
|
||||||
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.MultiDict
|
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.FileStorage
|
||||||
"args", "values", "form",
|
// TODO: FileStorage needs extra taint steps
|
||||||
// MultiDict[str, FileStorage]
|
"files",
|
||||||
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.FileStorage
|
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.HeaderSet
|
||||||
// TODO: FileStorage needs extra taint steps
|
"access_control_request_headers", "pragma",
|
||||||
"files",
|
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Accept
|
||||||
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.HeaderSet
|
// TODO: Kinda badly modeled for now -- has type List[Tuple[value, quality]], and some extra methods
|
||||||
"access_control_request_headers", "pragma",
|
"accept_charsets", "accept_encodings", "accept_languages", "accept_mimetypes",
|
||||||
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Accept
|
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Authorization
|
||||||
// TODO: Kinda badly modeled for now -- has type List[Tuple[value, quality]], and some extra methods
|
// TODO: dict subclass with extra attributes like `username` and `password`
|
||||||
"accept_charsets", "accept_encodings", "accept_languages", "accept_mimetypes",
|
"authorization",
|
||||||
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Authorization
|
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.RequestCacheControl
|
||||||
// TODO: dict subclass with extra attributes like `username` and `password`
|
// TODO: has attributes like `no_cache`, and `to_header` method (actually, many of these models do)
|
||||||
"authorization",
|
"cache_control",
|
||||||
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.RequestCacheControl
|
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Headers
|
||||||
// TODO: has attributes like `no_cache`, and `to_header` method (actually, many of these models do)
|
// TODO: dict-like with wsgiref.headers.Header compatibility methods
|
||||||
"cache_control",
|
"headers"
|
||||||
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Headers
|
]
|
||||||
// TODO: dict-like with wsgiref.headers.Header compatibility methods
|
|
||||||
"headers"
|
|
||||||
]
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override string getMethodName() { result in ["get_data", "get_json"] }
|
||||||
|
|
||||||
|
override string getAsyncMethodName() { none() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private class RequestAttrMultiDict extends Werkzeug::MultiDict::InstanceSource {
|
private class RequestAttrMultiDict extends Werkzeug::MultiDict::InstanceSource {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ private import semmle.python.dataflow.new.DataFlow
|
|||||||
private import semmle.python.dataflow.new.TaintTracking
|
private import semmle.python.dataflow.new.TaintTracking
|
||||||
private import semmle.python.Concepts
|
private import semmle.python.Concepts
|
||||||
private import semmle.python.ApiGraphs
|
private import semmle.python.ApiGraphs
|
||||||
|
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides models for the `MarkupSafe` PyPI package.
|
* Provides models for the `MarkupSafe` PyPI package.
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ private import semmle.python.dataflow.new.DataFlow
|
|||||||
private import semmle.python.dataflow.new.TaintTracking
|
private import semmle.python.dataflow.new.TaintTracking
|
||||||
private import semmle.python.Concepts
|
private import semmle.python.Concepts
|
||||||
private import semmle.python.ApiGraphs
|
private import semmle.python.ApiGraphs
|
||||||
|
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* INTERNAL: Do not use.
|
* INTERNAL: Do not use.
|
||||||
@@ -60,8 +61,21 @@ module Multidict {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Taint propagation for `multidict.MultiDictProxy`.
|
* Taint propagation for `multidict.MultiDictProxy`.
|
||||||
*
|
*/
|
||||||
* See https://multidict.readthedocs.io/en/stable/multidict.html#multidictproxy
|
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||||
|
InstanceTaintSteps() { this = "multidict.MultiDictProxy" }
|
||||||
|
|
||||||
|
override DataFlow::Node getInstance() { result = instance() }
|
||||||
|
|
||||||
|
override string getAttributeName() { none() }
|
||||||
|
|
||||||
|
override string getMethodName() { result in ["getone", "getall"] }
|
||||||
|
|
||||||
|
override string getAsyncMethodName() { none() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extra taint propagation for `multidict.MultiDictProxy`, not covered by `InstanceTaintSteps`.
|
||||||
*/
|
*/
|
||||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||||
@@ -70,10 +84,6 @@ module Multidict {
|
|||||||
nodeFrom = call.getArg(0) and
|
nodeFrom = call.getArg(0) and
|
||||||
nodeTo = call
|
nodeTo = call
|
||||||
)
|
)
|
||||||
or
|
|
||||||
// normal (non-async) methods
|
|
||||||
nodeFrom = instance() and
|
|
||||||
nodeTo.(DataFlow::MethodCallNode).calls(nodeFrom, ["getone", "getall"])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ private import semmle.python.dataflow.new.RemoteFlowSources
|
|||||||
private import semmle.python.Concepts
|
private import semmle.python.Concepts
|
||||||
private import semmle.python.ApiGraphs
|
private import semmle.python.ApiGraphs
|
||||||
private import semmle.python.frameworks.PEP249
|
private import semmle.python.frameworks.PEP249
|
||||||
|
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
|
||||||
|
|
||||||
/** Provides models for the Python standard library. */
|
/** Provides models for the Python standard library. */
|
||||||
module Stdlib {
|
module Stdlib {
|
||||||
@@ -47,12 +48,23 @@ module Stdlib {
|
|||||||
/**
|
/**
|
||||||
* Taint propagation for file-like objects.
|
* Taint propagation for file-like objects.
|
||||||
*/
|
*/
|
||||||
|
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||||
|
InstanceTaintSteps() { this = "<file-like object>" }
|
||||||
|
|
||||||
|
override DataFlow::Node getInstance() { result = instance() }
|
||||||
|
|
||||||
|
override string getAttributeName() { none() }
|
||||||
|
|
||||||
|
override string getMethodName() { result in ["read", "readline", "readlines"] }
|
||||||
|
|
||||||
|
override string getAsyncMethodName() { none() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extra taint propagation for file-like objects, not covered by `InstanceTaintSteps`.",
|
||||||
|
*/
|
||||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||||
// result of method call is tainted
|
|
||||||
nodeFrom = instance() and
|
|
||||||
nodeTo.(DataFlow::MethodCallNode).calls(nodeFrom, ["read", "readline", "readlines"])
|
|
||||||
or
|
|
||||||
// taint-propagation back to instance from `foo.write(tainted_data)`
|
// taint-propagation back to instance from `foo.write(tainted_data)`
|
||||||
exists(DataFlow::AttrRead write, DataFlow::CallCfgNode call, DataFlow::Node instance_ |
|
exists(DataFlow::AttrRead write, DataFlow::CallCfgNode call, DataFlow::Node instance_ |
|
||||||
instance_ = instance() and
|
instance_ = instance() and
|
||||||
@@ -99,14 +111,16 @@ module Stdlib {
|
|||||||
/**
|
/**
|
||||||
* Taint propagation for `http.client.HTTPMessage`.
|
* Taint propagation for `http.client.HTTPMessage`.
|
||||||
*/
|
*/
|
||||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
InstanceTaintSteps() { this = "http.client.HTTPMessage" }
|
||||||
// normal (non-async) methods
|
|
||||||
nodeFrom = instance() and
|
override DataFlow::Node getInstance() { result = instance() }
|
||||||
nodeTo
|
|
||||||
.(DataFlow::MethodCallNode)
|
override string getAttributeName() { none() }
|
||||||
.calls(nodeFrom, ["get_all", "as_bytes", "as_string", "keys"])
|
|
||||||
}
|
override string getMethodName() { result in ["get_all", "as_bytes", "as_string", "keys"] }
|
||||||
|
|
||||||
|
override string getAsyncMethodName() { none() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,17 +155,16 @@ module Stdlib {
|
|||||||
/**
|
/**
|
||||||
* Taint propagation for `http.cookies.Morsel`.
|
* Taint propagation for `http.cookies.Morsel`.
|
||||||
*/
|
*/
|
||||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
InstanceTaintSteps() { this = "http.cookies.Morsel" }
|
||||||
// normal (non-async) methods
|
|
||||||
nodeFrom = instance() and
|
override DataFlow::Node getInstance() { result = instance() }
|
||||||
nodeTo.(DataFlow::MethodCallNode).calls(nodeFrom, ["output", "js_output"])
|
|
||||||
or
|
override string getAttributeName() { result in ["key", "value", "coded_value"] }
|
||||||
// Attributes
|
|
||||||
nodeFrom = instance() and
|
override string getMethodName() { result in ["output", "js_output"] }
|
||||||
nodeTo.(DataFlow::AttrRead).getObject() = nodeFrom and
|
|
||||||
nodeTo.(DataFlow::AttrRead).getAttributeName() in ["key", "value", "coded_value"]
|
override string getAsyncMethodName() { none() }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ private import semmle.python.Concepts
|
|||||||
private import semmle.python.ApiGraphs
|
private import semmle.python.ApiGraphs
|
||||||
private import semmle.python.regex
|
private import semmle.python.regex
|
||||||
private import semmle.python.frameworks.Stdlib
|
private import semmle.python.frameworks.Stdlib
|
||||||
|
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides models for the `tornado` PyPI package.
|
* Provides models for the `tornado` PyPI package.
|
||||||
@@ -48,12 +49,16 @@ private module Tornado {
|
|||||||
/**
|
/**
|
||||||
* Taint propagation for `tornado.httputil.HTTPHeaders`.
|
* Taint propagation for `tornado.httputil.HTTPHeaders`.
|
||||||
*/
|
*/
|
||||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
InstanceTaintSteps() { this = "tornado.httputil.HTTPHeaders" }
|
||||||
// normal (non-async) methods
|
|
||||||
nodeFrom = instance() and
|
override DataFlow::Node getInstance() { result = instance() }
|
||||||
nodeTo.(DataFlow::MethodCallNode).calls(nodeFrom, ["get_list", "get_all"])
|
|
||||||
}
|
override string getAttributeName() { none() }
|
||||||
|
|
||||||
|
override string getMethodName() { result in ["get_list", "get_all"] }
|
||||||
|
|
||||||
|
override string getAsyncMethodName() { none() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,31 +167,33 @@ private module Tornado {
|
|||||||
/** Gets a reference to the `write` method. */
|
/** Gets a reference to the `write` method. */
|
||||||
DataFlow::Node writeMethod() { writeMethod(DataFlow::TypeTracker::end()).flowsTo(result) }
|
DataFlow::Node writeMethod() { writeMethod(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||||
|
|
||||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
/**
|
||||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
* Taint propagation for `tornado.web.RequestHandler`.
|
||||||
// normal (non-async) methods
|
*/
|
||||||
nodeFrom = instance() and
|
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||||
nodeTo
|
InstanceTaintSteps() { this = "tornado.web.RequestHandler" }
|
||||||
.(DataFlow::MethodCallNode)
|
|
||||||
.calls(nodeFrom,
|
override DataFlow::Node getInstance() { result = instance() }
|
||||||
[
|
|
||||||
"get_argument", "get_body_argument", "get_query_argument", "get_arguments",
|
override string getAttributeName() {
|
||||||
"get_body_arguments", "get_query_arguments"
|
result in [
|
||||||
])
|
// List[str]
|
||||||
or
|
"path_args",
|
||||||
// Attributes
|
// Dict[str, str]
|
||||||
nodeFrom = instance() and
|
"path_kwargs",
|
||||||
exists(DataFlow::AttrRead read | nodeTo = read and read.getObject() = nodeFrom |
|
// tornado.httputil.HTTPServerRequest
|
||||||
read.getAttributeName() in [
|
"request"
|
||||||
// List[str]
|
]
|
||||||
"path_args",
|
|
||||||
// Dict[str, str]
|
|
||||||
"path_kwargs",
|
|
||||||
// tornado.httputil.HTTPServerRequest
|
|
||||||
"request"
|
|
||||||
]
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override string getMethodName() {
|
||||||
|
result in [
|
||||||
|
"get_argument", "get_body_argument", "get_query_argument", "get_arguments",
|
||||||
|
"get_body_arguments", "get_query_arguments"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
override string getAsyncMethodName() { none() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private class RequestAttrAccess extends tornado::httputil::HttpServerRequest::InstanceSource {
|
private class RequestAttrAccess extends tornado::httputil::HttpServerRequest::InstanceSource {
|
||||||
@@ -290,27 +297,30 @@ private module Tornado {
|
|||||||
/** Gets a reference to an instance of `tornado.httputil.HttpServerRequest`. */
|
/** Gets a reference to an instance of `tornado.httputil.HttpServerRequest`. */
|
||||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||||
|
|
||||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
/**
|
||||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
* Taint propagation for `tornado.httputil.HttpServerRequest`.
|
||||||
// normal (non-async) methods
|
*/
|
||||||
nodeFrom = instance() and
|
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||||
nodeTo.(DataFlow::MethodCallNode).calls(nodeFrom, ["full_url"])
|
InstanceTaintSteps() { this = "tornado.httputil.HttpServerRequest" }
|
||||||
or
|
|
||||||
// Attributes
|
override DataFlow::Node getInstance() { result = instance() }
|
||||||
nodeFrom = instance() and
|
|
||||||
exists(DataFlow::AttrRead read | nodeTo = read and read.getObject() = nodeFrom |
|
override string getAttributeName() {
|
||||||
read.getAttributeName() in [
|
result in [
|
||||||
// str / bytes
|
// str / bytes
|
||||||
"uri", "path", "query", "remote_ip", "body",
|
"uri", "path", "query", "remote_ip", "body",
|
||||||
// Dict[str, List[bytes]]
|
// Dict[str, List[bytes]]
|
||||||
"arguments", "query_arguments", "body_arguments",
|
"arguments", "query_arguments", "body_arguments",
|
||||||
// dict-like, https://www.tornadoweb.org/en/stable/httputil.html#tornado.httputil.HTTPHeaders
|
// dict-like, https://www.tornadoweb.org/en/stable/httputil.html#tornado.httputil.HTTPHeaders
|
||||||
"headers",
|
"headers",
|
||||||
// Dict[str, http.cookies.Morsel]
|
// Dict[str, http.cookies.Morsel]
|
||||||
"cookies"
|
"cookies"
|
||||||
]
|
]
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override string getMethodName() { result in ["full_url"] }
|
||||||
|
|
||||||
|
override string getAsyncMethodName() { none() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An `HTTPHeaders` instance that originates from a Tornado request. */
|
/** An `HTTPHeaders` instance that originates from a Tornado request. */
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ private import semmle.python.dataflow.new.RemoteFlowSources
|
|||||||
private import semmle.python.dataflow.new.TaintTracking
|
private import semmle.python.dataflow.new.TaintTracking
|
||||||
private import semmle.python.Concepts
|
private import semmle.python.Concepts
|
||||||
private import semmle.python.ApiGraphs
|
private import semmle.python.ApiGraphs
|
||||||
|
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides models for the `twisted` PyPI package.
|
* Provides models for the `twisted` PyPI package.
|
||||||
@@ -114,26 +115,26 @@ private module Twisted {
|
|||||||
/**
|
/**
|
||||||
* Taint propagation for `twisted.web.server.Request`.
|
* Taint propagation for `twisted.web.server.Request`.
|
||||||
*/
|
*/
|
||||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
InstanceTaintSteps() { this = "twisted.web.server.Request" }
|
||||||
// normal (non-async) methods
|
|
||||||
nodeFrom = instance() and
|
override DataFlow::Node getInstance() { result = instance() }
|
||||||
nodeTo
|
|
||||||
.(DataFlow::MethodCallNode)
|
override string getAttributeName() {
|
||||||
.calls(nodeFrom,
|
result in [
|
||||||
[
|
|
||||||
"getCookie", "getHeader", "getAllHeaders", "getUser", "getPassword", "getHost",
|
|
||||||
"getRequestHostname"
|
|
||||||
])
|
|
||||||
or
|
|
||||||
// Attributes
|
|
||||||
nodeFrom = instance() and
|
|
||||||
nodeTo.(DataFlow::AttrRead).getObject() = nodeFrom and
|
|
||||||
nodeTo.(DataFlow::AttrRead).getAttributeName() in [
|
|
||||||
"uri", "path", "prepath", "postpath", "content", "args", "received_cookies",
|
"uri", "path", "prepath", "postpath", "content", "args", "received_cookies",
|
||||||
"requestHeaders", "user", "password", "host"
|
"requestHeaders", "user", "password", "host"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override string getMethodName() {
|
||||||
|
result in [
|
||||||
|
"getCookie", "getHeader", "getAllHeaders", "getUser", "getPassword", "getHost",
|
||||||
|
"getRequestHostname"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
override string getAsyncMethodName() { none() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ private import semmle.python.dataflow.new.TaintTracking
|
|||||||
private import semmle.python.ApiGraphs
|
private import semmle.python.ApiGraphs
|
||||||
private import semmle.python.frameworks.Stdlib
|
private import semmle.python.frameworks.Stdlib
|
||||||
private import semmle.python.Concepts
|
private import semmle.python.Concepts
|
||||||
|
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides models for the `Werkzeug` PyPI package.
|
* Provides models for the `Werkzeug` PyPI package.
|
||||||
@@ -47,11 +48,19 @@ module Werkzeug {
|
|||||||
/** Gets a reference to an instance of `werkzeug.datastructures.MultiDict`. */
|
/** Gets a reference to an instance of `werkzeug.datastructures.MultiDict`. */
|
||||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||||
|
|
||||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
/**
|
||||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
* Taint propagation for `werkzeug.datastructures.MultiDict`.
|
||||||
nodeFrom = instance() and
|
*/
|
||||||
nodeTo.(DataFlow::MethodCallNode).calls(nodeFrom, "getlist")
|
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||||
}
|
InstanceTaintSteps() { this = "werkzeug.datastructures.MultiDict" }
|
||||||
|
|
||||||
|
override DataFlow::Node getInstance() { result = instance() }
|
||||||
|
|
||||||
|
override string getAttributeName() { none() }
|
||||||
|
|
||||||
|
override string getMethodName() { result in ["getlist"] }
|
||||||
|
|
||||||
|
override string getAsyncMethodName() { none() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,23 +96,30 @@ module Werkzeug {
|
|||||||
/** Gets a reference to an instance of `werkzeug.datastructures.FileStorage`. */
|
/** Gets a reference to an instance of `werkzeug.datastructures.FileStorage`. */
|
||||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||||
|
|
||||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
/**
|
||||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
* Taint propagation for `werkzeug.datastructures.FileStorage`.
|
||||||
nodeFrom = instance() and
|
*/
|
||||||
exists(DataFlow::AttrRead read | nodeTo = read |
|
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||||
read.getAttributeName() in [
|
InstanceTaintSteps() { this = "werkzeug.datastructures.FileStorage" }
|
||||||
// str
|
|
||||||
"filename", "name", "content_type", "mimetype",
|
override DataFlow::Node getInstance() { result = instance() }
|
||||||
// file-like
|
|
||||||
"stream",
|
override string getAttributeName() {
|
||||||
// TODO: werkzeug.datastructures.Headers
|
result in [
|
||||||
"headers",
|
// str
|
||||||
// dict[str, str]
|
"filename", "name", "content_type", "mimetype",
|
||||||
"mimetype_params"
|
// file-like
|
||||||
] and
|
"stream",
|
||||||
read.getObject() = nodeFrom
|
// TODO: werkzeug.datastructures.Headers
|
||||||
)
|
"headers",
|
||||||
|
// dict[str, str]
|
||||||
|
"mimetype_params"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override string getMethodName() { none() }
|
||||||
|
|
||||||
|
override string getAsyncMethodName() { none() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A file-like object instance that originates from a `FileStorage`. */
|
/** A file-like object instance that originates from a `FileStorage`. */
|
||||||
@@ -152,14 +168,18 @@ module Werkzeug {
|
|||||||
/**
|
/**
|
||||||
* Taint propagation for `werkzeug.datastructures.Headers`.
|
* Taint propagation for `werkzeug.datastructures.Headers`.
|
||||||
*/
|
*/
|
||||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
InstanceTaintSteps() { this = "werkzeug.datastructures.Headers" }
|
||||||
// normal (non-async) methods
|
|
||||||
nodeFrom = instance() and
|
override DataFlow::Node getInstance() { result = instance() }
|
||||||
nodeTo
|
|
||||||
.(DataFlow::MethodCallNode)
|
override string getAttributeName() { none() }
|
||||||
.calls(nodeFrom, ["getlist", "get_all", "popitem", "to_wsgi_list"])
|
|
||||||
|
override string getMethodName() {
|
||||||
|
result in ["getlist", "get_all", "popitem", "to_wsgi_list"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override string getAsyncMethodName() { none() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,16 +214,21 @@ module Werkzeug {
|
|||||||
/**
|
/**
|
||||||
* Taint propagation for `werkzeug.datastructures.Authorization`.
|
* Taint propagation for `werkzeug.datastructures.Authorization`.
|
||||||
*/
|
*/
|
||||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
InstanceTaintSteps() { this = "werkzeug.datastructures.Authorization" }
|
||||||
// Attributes
|
|
||||||
nodeFrom = instance() and
|
override DataFlow::Node getInstance() { result = instance() }
|
||||||
nodeTo.(DataFlow::AttrRead).getObject() = nodeFrom and
|
|
||||||
nodeTo.(DataFlow::AttrRead).getAttributeName() in [
|
override string getAttributeName() {
|
||||||
|
result in [
|
||||||
"username", "password", "realm", "nonce", "uri", "nc", "cnonce", "response", "opaque",
|
"username", "password", "realm", "nonce", "uri", "nc", "cnonce", "response", "opaque",
|
||||||
"qop"
|
"qop"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override string getMethodName() { none() }
|
||||||
|
|
||||||
|
override string getAsyncMethodName() { none() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ private import semmle.python.dataflow.new.TaintTracking
|
|||||||
private import semmle.python.Concepts
|
private import semmle.python.Concepts
|
||||||
private import semmle.python.ApiGraphs
|
private import semmle.python.ApiGraphs
|
||||||
private import semmle.python.frameworks.Multidict
|
private import semmle.python.frameworks.Multidict
|
||||||
|
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* INTERNAL: Do not use.
|
* INTERNAL: Do not use.
|
||||||
@@ -52,8 +53,28 @@ module Yarl {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Taint propagation for `yarl.URL`.
|
* Taint propagation for `yarl.URL`.
|
||||||
*
|
*/
|
||||||
* See https://yarl.readthedocs.io/en/stable/api.html#yarl.URL
|
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||||
|
InstanceTaintSteps() { this = "yarl.URL" }
|
||||||
|
|
||||||
|
override DataFlow::Node getInstance() { result = instance() }
|
||||||
|
|
||||||
|
override string getAttributeName() {
|
||||||
|
result in [
|
||||||
|
"user", "raw_user", "password", "raw_password", "host", "raw_host", "port",
|
||||||
|
"explicit_port", "authority", "raw_authority", "path", "raw_path", "path_qs",
|
||||||
|
"raw_path_qs", "query_string", "raw_query_string", "fragment", "raw_fragment", "parts",
|
||||||
|
"raw_parts", "name", "raw_name", "query"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
override string getMethodName() { result in ["human_repr"] }
|
||||||
|
|
||||||
|
override string getAsyncMethodName() { none() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extra taint propagation for `yarl.URL`, not covered by `InstanceTaintSteps`.
|
||||||
*/
|
*/
|
||||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||||
@@ -63,10 +84,6 @@ module Yarl {
|
|||||||
nodeTo = call
|
nodeTo = call
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// 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
|
// methods that give an altered URL. taint both from object, and form argument
|
||||||
// (to result of call)
|
// (to result of call)
|
||||||
exists(DataFlow::MethodCallNode call |
|
exists(DataFlow::MethodCallNode call |
|
||||||
@@ -81,16 +98,6 @@ module Yarl {
|
|||||||
nodeTo = call and
|
nodeTo = call and
|
||||||
nodeFrom in [call.getObject(), call.getArg(_), call.getArgByName(_)]
|
nodeFrom in [call.getObject(), call.getArg(_), call.getArgByName(_)]
|
||||||
)
|
)
|
||||||
or
|
|
||||||
// Attributes
|
|
||||||
nodeFrom = instance() and
|
|
||||||
nodeTo.(DataFlow::AttrRead).getObject() = nodeFrom and
|
|
||||||
nodeTo.(DataFlow::AttrRead).getAttributeName() in [
|
|
||||||
"user", "raw_user", "password", "raw_password", "host", "raw_host", "port",
|
|
||||||
"explicit_port", "authority", "raw_authority", "path", "raw_path", "path_qs",
|
|
||||||
"raw_path_qs", "query_string", "raw_query_string", "fragment", "raw_fragment", "parts",
|
|
||||||
"raw_parts", "name", "raw_name", "query"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* INTERNAL: Do no use.
|
||||||
|
*
|
||||||
|
* Provides helper class for defining additional taint step.
|
||||||
|
*/
|
||||||
|
|
||||||
|
private import python
|
||||||
|
private import semmle.python.dataflow.new.DataFlow
|
||||||
|
private import semmle.python.dataflow.new.TaintTracking
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper class for defining additional taint steps.
|
||||||
|
*/
|
||||||
|
bindingset[this]
|
||||||
|
abstract class InstanceTaintStepsHelper extends string {
|
||||||
|
/** Gets an instance that the additional taint steps should be applied to. */
|
||||||
|
abstract DataFlow::Node getInstance();
|
||||||
|
|
||||||
|
/** Gets the name of an attribute that should be tainted. */
|
||||||
|
abstract string getAttributeName();
|
||||||
|
|
||||||
|
/** Gets the name of a method, whose results should be tainted. */
|
||||||
|
abstract string getMethodName();
|
||||||
|
|
||||||
|
/** Gets the name of an async method, whose results should be tainted. */
|
||||||
|
abstract string getAsyncMethodName();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class InstanceAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||||
|
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||||
|
exists(InstanceTaintStepsHelper helper |
|
||||||
|
// normal (non-async) methods
|
||||||
|
nodeFrom = helper.getInstance() and
|
||||||
|
nodeTo.(DataFlow::MethodCallNode).calls(nodeFrom, helper.getMethodName())
|
||||||
|
or
|
||||||
|
// async methods
|
||||||
|
exists(DataFlow::MethodCallNode call, Await await |
|
||||||
|
nodeTo.asExpr() = await and
|
||||||
|
nodeFrom = helper.getInstance()
|
||||||
|
|
|
||||||
|
await.getValue() = any(DataFlow::Node awaitable | call.flowsTo(awaitable)).asExpr() and
|
||||||
|
call.calls(nodeFrom, helper.getAsyncMethodName())
|
||||||
|
)
|
||||||
|
or
|
||||||
|
// Attributes
|
||||||
|
nodeFrom = helper.getInstance() and
|
||||||
|
nodeTo.(DataFlow::AttrRead).accesses(nodeFrom, helper.getAttributeName())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user