Python: Add modeling of http.client.HTTPResponse

This commit is contained in:
Rasmus Wriedt Larsen
2021-12-15 20:40:03 +01:00
parent a5bae30d81
commit 6f81685f48
2 changed files with 233 additions and 40 deletions

View File

@@ -2091,6 +2091,187 @@ private module StdlibPrivate {
}
}
// ---------------------------------------------------------------------------
// http.client (Python 3)
// httplib (Python 2)
// ---------------------------------------------------------------------------
/**
* Provides models for the `http.client.HTTPConnection` and `HTTPSConnection` classes
*
* See
* - https://docs.python.org/3.10/library/http.client.html#http.client.HTTPConnection
* - https://docs.python.org/3.10/library/http.client.html#http.client.HTTPSConnection
* - https://docs.python.org/2.7/library/httplib.html#httplib.HTTPConnection
* - https://docs.python.org/2.7/library/httplib.html#httplib.HTTPSConnection
*/
module HTTPConnection {
/** Gets a reference to the `http.client.HTTPConnection` class. */
private API::Node classRef() {
exists(string className | className in ["HTTPConnection", "HTTPSConnection"] |
// Python 3
result = API::moduleImport("http").getMember("client").getMember(className)
or
// Python 2
result = API::moduleImport("httplib").getMember(className)
or
result =
API::moduleImport("six").getMember("moves").getMember("http_client").getMember(className)
)
}
/**
* A source of instances of `http.client.HTTPConnection`, extend this class to model new instances.
*
* This can include instantiations of the class, return values from function
* calls, or a special parameter that will be set when functions are called by an external
* library.
*
* Use the predicate `HTTPConnection::instance()` to get references to instances of `http.client.HTTPConnection`.
*/
abstract class InstanceSource extends DataFlow::LocalSourceNode {
/** Gets the argument that specified the host, if any. */
abstract DataFlow::Node getHostArgument();
}
/** A direct instantiation of `http.client.HTTPConnection`. */
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
ClassInstantiation() { this = classRef().getACall() }
override DataFlow::Node getHostArgument() {
result in [this.getArg(0), this.getArgByName("host")]
}
}
/**
* Gets a reference to an instance of `http.client.HTTPConnection`,
* that was instantiated with host argument `hostArg`.
*/
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t, DataFlow::Node hostArg) {
t.start() and
hostArg = result.(InstanceSource).getHostArgument()
or
exists(DataFlow::TypeTracker t2 | result = instance(t2, hostArg).track(t2, t))
}
/**
* Gets a reference to an instance of `http.client.HTTPConnection`,
* that was instantiated with host argument `hostArg`.
*/
DataFlow::Node instance(DataFlow::Node hostArg) {
instance(DataFlow::TypeTracker::end(), hostArg).flowsTo(result)
}
/** A method call on a HTTPConnection that sends off a request */
private class RequestCall extends HTTP::Client::Request::Range, DataFlow::MethodCallNode {
RequestCall() { this.calls(instance(_), ["request", "_send_request", "putrequest"]) }
override DataFlow::Node getResponse() {
// TODO: this does not seem like the right abstraction, to allow for nice path-explanations
//
// For nice path-explanation, we would like either
// 1: tainting instance
// 1a. host on object creation -> obj
// 1b. url on request call -> obj
// 2. obj -> obj.getresponse()
//
// For now, that's really all we use the `getResponse` predicate for.
result.(HttpConnectionGetResponseCall).getObject().getALocalSource() =
this.getObject().getALocalSource()
}
override DataFlow::Node getUrl() {
result in [this.getArg(1), this.getArgByName("url")]
or
this.getObject() = instance(result)
}
override string getFramework() { result = "http.client.HTTP[S]Connection" }
override predicate disablesCertificateValidation(
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
) {
// TODO: Proper alerting of insecure verification settings on SSLContext.
// Because that is not restricted to HTTP[S]Connection usage, we need something
// more general, and I would like to tackle that in future PR.
none()
}
}
/** A call to the `getresponse` method. */
private class HttpConnectionGetResponseCall extends DataFlow::MethodCallNode,
HTTPResponse::InstanceSource {
HttpConnectionGetResponseCall() { this.calls(instance(_), "getresponse") }
}
}
/**
* Provides models for the `http.client.HTTPResponse` class
*
* See
* - https://docs.python.org/3.10/library/http.client.html#httpresponse-objects
* - https://docs.python.org/3/library/http.client.html#http.client.HTTPResponse.
*/
module HTTPResponse {
/** Gets a reference to the `http.client.HTTPResponse` class. */
private API::Node classRef() {
result = API::moduleImport("http").getMember("client").getMember("HTTPResponse")
}
/**
* A source of instances of `http.client.HTTPResponse`, extend this class to model new instances.
*
* A `http.client.HTTPResponse` is itself a file-like object.
*
* This can include instantiations of the class, return values from function
* calls, or a special parameter that will be set when functions are called by an external
* library.
*
* Use the predicate `HTTPResponse::instance()` to get references to instances of `http.client.HTTPResponse`.
*/
abstract class InstanceSource extends Stdlib::FileLikeObject::InstanceSource,
DataFlow::LocalSourceNode { }
/** A direct instantiation of `http.client.HTTPResponse`. */
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
ClassInstantiation() { this = classRef().getACall() }
}
/** Gets a reference to an instance of `http.client.HTTPResponse`. */
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
t.start() and
result instanceof InstanceSource
or
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
}
/** Gets a reference to an instance of `http.client.HTTPResponse`. */
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
/**
* Taint propagation for `http.client.HTTPResponse`.
*/
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
InstanceTaintSteps() { this = "http.client.HTTPResponse" }
override DataFlow::Node getInstance() { result = instance() }
override string getAttributeName() { result in ["headers", "msg", "reason", "url"] }
override string getMethodName() { result in ["getheader", "getheaders", "info", "geturl",] }
override string getAsyncMethodName() { none() }
}
/** An attribute read that is a HTTPMessage instance. */
private class HTTPMessageInstances extends Stdlib::HTTPMessage::InstanceSource {
HTTPMessageInstances() {
this.(DataFlow::AttrRead).accesses(instance(), ["headers", "msg"])
or
this.(DataFlow::MethodCallNode).calls(instance(), "info")
}
}
}
// ---------------------------------------------------------------------------
// sqlite3
// ---------------------------------------------------------------------------