Python: Model certificate disabling in httpx

This commit is contained in:
Rasmus Wriedt Larsen
2022-06-08 16:46:08 +02:00
parent 1a2a4232a8
commit 049e87201c
2 changed files with 37 additions and 9 deletions

View File

@@ -23,7 +23,7 @@ private module HttpxModel {
*
* See https://www.python-httpx.org/api/
*/
private class RequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode {
private class RequestCall extends HTTP::Client::Request::Range, API::CallNode {
string methodName;
RequestCall() {
@@ -44,8 +44,11 @@ private module HttpxModel {
override predicate disablesCertificateValidation(
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
) {
// TODO: Look into disabling certificate validation
none()
disablingNode = this.getKeywordParameter("verify").getARhs() and
argumentOrigin = this.getKeywordParameter("verify").getAValueReachingRhs() and
// unlike `requests`, httpx treats `None` as turning off verify (and not as the default)
argumentOrigin.asExpr().(ImmutableLiteral).booleanValue() = false
// TODO: Handling of insecure SSLContext passed to verify argument
}
}
@@ -60,16 +63,13 @@ private module HttpxModel {
result = API::moduleImport("httpx").getMember(["Client", "AsyncClient"])
}
/** Get a reference to an `httpx.Client` or `httpx.AsyncClient` instance. */
private API::Node instance() { result = classRef().getReturn() }
/** A method call on a Client that sends off a request */
private class OutgoingRequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode {
string methodName;
OutgoingRequestCall() {
methodName in [HTTP::httpVerbLower(), "request", "stream"] and
this = instance().getMember(methodName).getACall()
this = classRef().getReturn().getMember(methodName).getACall()
}
override DataFlow::Node getAUrlPart() {
@@ -85,8 +85,16 @@ private module HttpxModel {
override predicate disablesCertificateValidation(
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
) {
// TODO: Look into disabling certificate validation
none()
exists(API::CallNode constructor |
constructor = classRef().getACall() and
this = constructor.getReturn().getMember(methodName).getACall()
|
disablingNode = constructor.getKeywordParameter("verify").getARhs() and
argumentOrigin = constructor.getKeywordParameter("verify").getAValueReachingRhs() and
// unlike `requests`, httpx treats `None` as turning off verify (and not as the default)
argumentOrigin.asExpr().(ImmutableLiteral).booleanValue() = false
// TODO: Handling of insecure SSLContext passed to verify argument
)
}
}
}

View File

@@ -1,4 +1,5 @@
import httpx
import ssl
httpx.get("url") # $ clientRequestUrlPart="url"
httpx.post("url") # $ clientRequestUrlPart="url"
@@ -23,3 +24,22 @@ async def async_test():
response = await client.options("url") # $ clientRequestUrlPart="url"
response = await client.request("method", url="url") # $ clientRequestUrlPart="url"
response = await client.stream("method", url="url") # $ clientRequestUrlPart="url"
# ==============================================================================
# Disabling certificate validation
# ==============================================================================
httpx.get("url", verify=False) # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
httpx.get("url", verify=0) # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
httpx.get("url", verify=None) # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
# A manually constructed SSLContext does not have safe defaults, so is effectively the
# same as turning off SSL validation
context = ssl.SSLContext()
assert context.check_hostname == False
assert context.verify_mode == ssl.VerifyMode.CERT_NONE
httpx.get("url", verify=context) # $ clientRequestUrlPart="url" MISSING: clientRequestCertValidationDisabled
client = httpx.Client(verify=False)
client.get("url") # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled