Python: Expand azure blob modeling

Now we can differentiate between the classes
This commit is contained in:
Rasmus Wriedt Larsen
2023-03-29 11:24:36 +02:00
parent 8ea6b6f256
commit 683985a00a
3 changed files with 122 additions and 28 deletions

View File

@@ -15,35 +15,100 @@ import python
import semmle.python.dataflow.new.DataFlow
import semmle.python.ApiGraphs
API::Node getClient() {
API::Node getBlobServiceClient() {
result =
API::moduleImport("azure")
.getMember("storage")
.getMember("blob")
.getMember(["ContainerClient", "BlobClient", "BlobServiceClient"])
.getAMember()
.getMember("BlobServiceClient")
.getReturn()
or
result =
API::moduleImport("azure")
.getMember("storage")
.getMember("blob")
.getMember("BlobServiceClient")
.getMember("from_connection_string")
.getReturn()
}
API::CallNode getTransitionToContainerClient() {
result = getBlobServiceClient().getMember("get_container_client").getACall()
or
result = getBlobClient().getMember("_get_container_client").getACall()
}
API::Node getContainerClient() {
result = getTransitionToContainerClient().getReturn()
or
result =
API::moduleImport("azure")
.getMember("storage")
.getMember("blob")
.getMember("ContainerClient")
.getReturn()
or
result =
API::moduleImport("azure")
.getMember("storage")
.getMember("blob")
.getMember("ContainerClient")
.getMember(["from_connection_string", "from_container_url"])
.getReturn()
}
API::CallNode getTransitionToBlobClient() {
result = [getBlobServiceClient(), getContainerClient()].getMember("get_blob_client").getACall()
}
API::Node getBlobClient() {
result = getTransitionToBlobClient().getReturn()
or
result =
API::moduleImport("azure")
.getMember("storage")
.getMember("blob")
.getMember("BlobClient")
.getReturn()
or
result =
API::moduleImport("azure")
.getMember("storage")
.getMember("blob")
.getMember("BlobClient")
.getMember(["from_connection_string", "from_blob_url"])
.getReturn()
}
API::Node anyClient() { result in [getBlobServiceClient(), getContainerClient(), getBlobClient()] }
module AzureBlobClientConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) {
exists(DataFlow::AttrWrite attr |
node = getClient().getAValueReachableFromSource() and
node = anyClient().getAValueReachableFromSource() and
attr.accesses(node, ["key_encryption_key", "key_resolver_function"])
)
}
predicate isBarrier(DataFlow::Node node) {
exists(DataFlow::AttrWrite attr |
node = getClient().getAValueReachableFromSource() and
node = anyClient().getAValueReachableFromSource() and
attr.accesses(node, "encryption_version") and
attr.getValue().asExpr().(StrConst).getText() in ["'2.0'", "2.0"]
)
}
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(DataFlow::MethodCallNode call |
call in [getTransitionToContainerClient(), getTransitionToBlobClient()] and
node1 = call.getObject() and
node2 = call
)
}
predicate isSink(DataFlow::Node node) {
exists(DataFlow::MethodCallNode call |
call = getClient().getMember("upload_blob").getACall() and
call = getBlobClient().getMember("upload_blob").getACall() and
node = call.getObject()
)
}

View File

@@ -1,20 +1,28 @@
edges
| test.py:8:5:8:15 | ControlFlowNode for blob_client | test.py:10:9:10:19 | ControlFlowNode for blob_client |
| test.py:16:5:16:15 | ControlFlowNode for blob_client | test.py:22:9:22:19 | ControlFlowNode for blob_client |
| test.py:38:5:38:15 | ControlFlowNode for blob_client | test.py:39:12:39:22 | ControlFlowNode for blob_client |
| test.py:39:12:39:22 | ControlFlowNode for blob_client | test.py:43:10:43:33 | ControlFlowNode for get_unsafe_blob_client() |
| test.py:43:10:43:33 | ControlFlowNode for get_unsafe_blob_client() | test.py:45:9:45:10 | ControlFlowNode for bc |
| test.py:9:5:9:15 | ControlFlowNode for blob_client | test.py:11:9:11:19 | ControlFlowNode for blob_client |
| test.py:17:5:17:23 | ControlFlowNode for blob_service_client | test.py:21:9:21:19 | ControlFlowNode for blob_client |
| test.py:27:5:27:20 | ControlFlowNode for container_client | test.py:31:9:31:19 | ControlFlowNode for blob_client |
| test.py:37:5:37:15 | ControlFlowNode for blob_client | test.py:43:9:43:19 | ControlFlowNode for blob_client |
| test.py:59:5:59:15 | ControlFlowNode for blob_client | test.py:60:12:60:22 | ControlFlowNode for blob_client |
| test.py:60:12:60:22 | ControlFlowNode for blob_client | test.py:64:10:64:33 | ControlFlowNode for get_unsafe_blob_client() |
| test.py:64:10:64:33 | ControlFlowNode for get_unsafe_blob_client() | test.py:66:9:66:10 | ControlFlowNode for bc |
nodes
| test.py:8:5:8:15 | ControlFlowNode for blob_client | semmle.label | ControlFlowNode for blob_client |
| test.py:10:9:10:19 | ControlFlowNode for blob_client | semmle.label | ControlFlowNode for blob_client |
| test.py:16:5:16:15 | ControlFlowNode for blob_client | semmle.label | ControlFlowNode for blob_client |
| test.py:22:9:22:19 | ControlFlowNode for blob_client | semmle.label | ControlFlowNode for blob_client |
| test.py:38:5:38:15 | ControlFlowNode for blob_client | semmle.label | ControlFlowNode for blob_client |
| test.py:39:12:39:22 | ControlFlowNode for blob_client | semmle.label | ControlFlowNode for blob_client |
| test.py:43:10:43:33 | ControlFlowNode for get_unsafe_blob_client() | semmle.label | ControlFlowNode for get_unsafe_blob_client() |
| test.py:45:9:45:10 | ControlFlowNode for bc | semmle.label | ControlFlowNode for bc |
| test.py:9:5:9:15 | ControlFlowNode for blob_client | semmle.label | ControlFlowNode for blob_client |
| test.py:11:9:11:19 | ControlFlowNode for blob_client | semmle.label | ControlFlowNode for blob_client |
| test.py:17:5:17:23 | ControlFlowNode for blob_service_client | semmle.label | ControlFlowNode for blob_service_client |
| test.py:21:9:21:19 | ControlFlowNode for blob_client | semmle.label | ControlFlowNode for blob_client |
| test.py:27:5:27:20 | ControlFlowNode for container_client | semmle.label | ControlFlowNode for container_client |
| test.py:31:9:31:19 | ControlFlowNode for blob_client | semmle.label | ControlFlowNode for blob_client |
| test.py:37:5:37:15 | ControlFlowNode for blob_client | semmle.label | ControlFlowNode for blob_client |
| test.py:43:9:43:19 | ControlFlowNode for blob_client | semmle.label | ControlFlowNode for blob_client |
| test.py:59:5:59:15 | ControlFlowNode for blob_client | semmle.label | ControlFlowNode for blob_client |
| test.py:60:12:60:22 | ControlFlowNode for blob_client | semmle.label | ControlFlowNode for blob_client |
| test.py:64:10:64:33 | ControlFlowNode for get_unsafe_blob_client() | semmle.label | ControlFlowNode for get_unsafe_blob_client() |
| test.py:66:9:66:10 | ControlFlowNode for bc | semmle.label | ControlFlowNode for bc |
subpaths
#select
| test.py:10:9:10:19 | ControlFlowNode for blob_client | test.py:8:5:8:15 | ControlFlowNode for blob_client | test.py:10:9:10:19 | ControlFlowNode for blob_client | Unsafe usage of v1 version of Azure Storage client-side encryption |
| test.py:22:9:22:19 | ControlFlowNode for blob_client | test.py:16:5:16:15 | ControlFlowNode for blob_client | test.py:22:9:22:19 | ControlFlowNode for blob_client | Unsafe usage of v1 version of Azure Storage client-side encryption |
| test.py:45:9:45:10 | ControlFlowNode for bc | test.py:38:5:38:15 | ControlFlowNode for blob_client | test.py:45:9:45:10 | ControlFlowNode for bc | Unsafe usage of v1 version of Azure Storage client-side encryption |
| test.py:11:9:11:19 | ControlFlowNode for blob_client | test.py:9:5:9:15 | ControlFlowNode for blob_client | test.py:11:9:11:19 | ControlFlowNode for blob_client | Unsafe usage of v1 version of Azure Storage client-side encryption |
| test.py:21:9:21:19 | ControlFlowNode for blob_client | test.py:17:5:17:23 | ControlFlowNode for blob_service_client | test.py:21:9:21:19 | ControlFlowNode for blob_client | Unsafe usage of v1 version of Azure Storage client-side encryption |
| test.py:31:9:31:19 | ControlFlowNode for blob_client | test.py:27:5:27:20 | ControlFlowNode for container_client | test.py:31:9:31:19 | ControlFlowNode for blob_client | Unsafe usage of v1 version of Azure Storage client-side encryption |
| test.py:43:9:43:19 | ControlFlowNode for blob_client | test.py:37:5:37:15 | ControlFlowNode for blob_client | test.py:43:9:43:19 | ControlFlowNode for blob_client | Unsafe usage of v1 version of Azure Storage client-side encryption |
| test.py:66:9:66:10 | ControlFlowNode for bc | test.py:59:5:59:15 | ControlFlowNode for blob_client | test.py:66:9:66:10 | ControlFlowNode for bc | Unsafe usage of v1 version of Azure Storage client-side encryption |

View File

@@ -1,17 +1,38 @@
from azure.storage.blob import BlobServiceClient
from azure.storage.blob import BlobServiceClient, ContainerClient, BlobClient
BSC = BlobServiceClient.from_connection_string(...)
def unsafe():
# does not set encryption_version to 2.0, default is unsafe
blob_client = BlobServiceClient.get_blob_client(...)
blob_client = BSC.get_blob_client(...)
blob_client.require_encryption = True
blob_client.key_encryption_key = ...
with open("decryptedcontentfile.txt", "rb") as stream:
blob_client.upload_blob(stream) # BAD
def unsafe_setting_on_blob_service_client():
blob_service_client = BlobServiceClient.from_connection_string(...)
blob_service_client.require_encryption = True
blob_service_client.key_encryption_key = ...
blob_client = blob_service_client.get_blob_client(...)
with open("decryptedcontentfile.txt", "rb") as stream:
blob_client.upload_blob(stream)
def unsafe_setting_on_container_client():
container_client = ContainerClient.from_connection_string(...)
container_client.require_encryption = True
container_client.key_encryption_key = ...
blob_client = container_client.get_blob_client(...)
with open("decryptedcontentfile.txt", "rb") as stream:
blob_client.upload_blob(stream)
def potentially_unsafe(use_new_version=False):
blob_client = BlobServiceClient.get_blob_client(...)
blob_client = BSC.get_blob_client(...)
blob_client.require_encryption = True
blob_client.key_encryption_key = ...
@@ -23,7 +44,7 @@ def potentially_unsafe(use_new_version=False):
def safe():
blob_client = BlobServiceClient.get_blob_client(...)
blob_client = BSC.get_blob_client(...)
blob_client.require_encryption = True
blob_client.key_encryption_key = ...
# GOOD: Must use `encryption_version` set to `2.0`
@@ -33,7 +54,7 @@ def safe():
def get_unsafe_blob_client():
blob_client = BlobServiceClient.get_blob_client(...)
blob_client = BSC.get_blob_client(...)
blob_client.require_encryption = True
blob_client.key_encryption_key = ...
return blob_client
@@ -46,7 +67,7 @@ def unsafe_with_calls():
def get_safe_blob_client():
blob_client = BlobServiceClient.get_blob_client(...)
blob_client = BSC.get_blob_client(...)
blob_client.require_encryption = True
blob_client.key_encryption_key = ...
blob_client.encryption_version = '2.0'