From e0ab148ac324436aabb124d250325f497cb5fb06 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Fri, 26 Sep 2025 13:04:48 -0400 Subject: [PATCH 01/14] Azure SDK models for SSRF analysis. (cherry picked from commit 0274962612c02af09729526a3c44a545c1e69be8) --- python/ql/lib/semmle/python/Frameworks.qll | 1 + .../frameworks/Azure.Keyvault.model.yml | 9 +++++ .../python/frameworks/Azure.Storage.model.yml | 34 +++++++++++++++++ .../lib/semmle/python/frameworks/SSRFSink.qll | 38 +++++++++++++++++++ 4 files changed, 82 insertions(+) create mode 100644 python/ql/lib/semmle/python/frameworks/Azure.Keyvault.model.yml create mode 100644 python/ql/lib/semmle/python/frameworks/Azure.Storage.model.yml create mode 100644 python/ql/lib/semmle/python/frameworks/SSRFSink.qll diff --git a/python/ql/lib/semmle/python/Frameworks.qll b/python/ql/lib/semmle/python/Frameworks.qll index 7694419b41d5..2d893859c7d6 100644 --- a/python/ql/lib/semmle/python/Frameworks.qll +++ b/python/ql/lib/semmle/python/Frameworks.qll @@ -80,6 +80,7 @@ private import semmle.python.frameworks.Setuptools private import semmle.python.frameworks.Simplejson private import semmle.python.frameworks.Socketio private import semmle.python.frameworks.SqlAlchemy +private import semmle.python.frameworks.SSRFSink private import semmle.python.frameworks.Starlette private import semmle.python.frameworks.Stdlib private import semmle.python.frameworks.Streamlit diff --git a/python/ql/lib/semmle/python/frameworks/Azure.Keyvault.model.yml b/python/ql/lib/semmle/python/frameworks/Azure.Keyvault.model.yml new file mode 100644 index 000000000000..8f4efc9f4fe4 --- /dev/null +++ b/python/ql/lib/semmle/python/frameworks/Azure.Keyvault.model.yml @@ -0,0 +1,9 @@ +extensions: + - addsTo: + pack: codeql/python-all + extensible: sinkModel + data: + - ['azure.keyvault.certificates.CertificateClient!', 'Call.Argument[0,vault_url:]', 'ssrf'] + - ['azure.keyvault.certificates.DeletedCertificate!', 'Call.Argument[recovery_id:]', 'ssrf'] + - ['azure.keyvault.keys.KeyClient!', 'Call.Argument[0,vault_url:]', 'ssrf'] + - ['azure.keyvault.secrets.SecretClient!', 'Call.Argument[0,vault_url:]', 'ssrf'] \ No newline at end of file diff --git a/python/ql/lib/semmle/python/frameworks/Azure.Storage.model.yml b/python/ql/lib/semmle/python/frameworks/Azure.Storage.model.yml new file mode 100644 index 000000000000..974e6334a0ed --- /dev/null +++ b/python/ql/lib/semmle/python/frameworks/Azure.Storage.model.yml @@ -0,0 +1,34 @@ +extensions: + - addsTo: + pack: codeql/python-all + extensible: sinkModel + data: + - ['azure.storage.blob.BlobClient!', 'Call.Argument[0,account_url:]', 'ssrf'] + - ['azure.storage.blob.BlobClient', 'Member[append_block_from_url].Argument[0,copy_source_url:]', 'ssrf'] + - ['azure.storage.blob.BlobClient', 'Member[get_page_range_diff_for_managed_disk].Argument[0,previous_snapshot_url:]', 'ssrf'] + - ['azure.storage.blob.BlobClient', 'Member[stage_block_from_url].Argument[1,source_url:]', 'ssrf'] + - ['azure.storage.blob.BlobClient', 'Member[start_copy_from_url].Argument[0,source_url:]', 'ssrf'] + - ['azure.storage.blob.BlobClient', 'Member[upload_blob_from_url].Argument[0,source_url:]', 'ssrf'] + - ['azure.storage.blob.BlobClient', 'Member[upload_pages_from_url].Argument[0,source_url:]', 'ssrf'] + - ['azure.storage.blob.BlobClient!', 'Member[from_blob_url].Argument[0,blob_url:]', 'ssrf'] + - ['azure.storage.blob.BlobServiceClient!', 'Call.Argument[0,account_url:]', 'ssrf'] + - ['azure.storage.blob.ContainerClient!', 'Call.Argument[0,account_url:]', 'ssrf'] + - ['azure.storage.blob.ContainerClient!', 'Member[from_container_url].Argument[0,container_url:]', 'ssrf'] + - ['azure', 'Member[storage].Member[blob].Member[download_blob_from_url].Argument[0,blob_url:]', 'ssrf'] + - ['azure', 'Member[storage].Member[blob].Member[upload_blob_to_url].Argument[0,blob_url:]', 'ssrf'] + - ['azure.storage.filedatalake.DataLakeDirectoryClient!', 'Call.Argument[0,account_url:]', 'ssrf'] + - ['azure.storage.filedatalake.DataLakeFileClient!', 'Call.Argument[0,account_url:]', 'ssrf'] + - ['azure.storage.filedatalake.DataLakeServiceClient!', 'Call.Argument[0,account_url:]', 'ssrf'] + - ['azure.storage.filedatalake.FileSystemClient!', 'Call.Argument[0,account_url:]', 'ssrf'] + - ['azure.storage.fileshare.ShareClient!', 'Call.Argument[0,account_url:]', 'ssrf'] + - ['azure.storage.fileshare.ShareClient!', 'Member[from_share_url].Argument[0,share_url:]', 'ssrf'] + - ['azure.storage.fileshare.ShareDirectoryClient!', 'Call.Argument[0,account_url:]', 'ssrf'] + - ['azure.storage.fileshare.ShareDirectoryClient!', 'Member[from_directory_url].Argument[0,directory_url:]', 'ssrf'] + - ['azure.storage.fileshare.ShareFileClient!', 'Call.Argument[0,account_url:]', 'ssrf'] + - ['azure.storage.fileshare.ShareFileClient!', 'Member[from_file_url].Argument[0,file_url:]', 'ssrf'] + - ['azure.storage.fileshare.ShareFileClient', 'Member[start_copy_from_url].Argument[0,source_url:]', 'ssrf'] + - ['azure.storage.fileshare.ShareFileClient', 'Member[upload_range_from_url].Argument[0,source_url:]', 'ssrf'] + - ['azure.storage.fileshare.ShareServiceClient!', 'Call.Argument[0,account_url:]', 'ssrf'] + - ['azure.storage.queue.QueueClient!', 'Call.Argument[0,account_url:]', 'ssrf'] + - ['azure.storage.queue.QueueClient', 'Member[from_queue_url].Argument[0,queue_url:]', 'ssrf'] + - ['azure.storage.queue.QueueServiceClient!', 'Call.Argument[0,account_url:]', 'ssrf'] \ No newline at end of file diff --git a/python/ql/lib/semmle/python/frameworks/SSRFSink.qll b/python/ql/lib/semmle/python/frameworks/SSRFSink.qll new file mode 100644 index 000000000000..2460353e799a --- /dev/null +++ b/python/ql/lib/semmle/python/frameworks/SSRFSink.qll @@ -0,0 +1,38 @@ +private import python +private import semmle.python.Concepts +private import semmle.python.ApiGraphs +private import semmle.python.frameworks.data.ModelsAsData + +/** + * INTERNAL: Do not use. + * + * Sets up SSRF sinks as Http::CLient::Request + */ +module SSRFMaDModel { + class SSRFSink extends Http::Client::Request::Range instanceof API::CallNode { + DataFlow::Node urlArg; + + SSRFSink() { + ( + this.getArg(_) = urlArg + or + this.getArgByName(_) = urlArg + ) and + urlArg = ModelOutput::getASinkNode("ssrf").asSink() + } + + override DataFlow::Node getAUrlPart() { result = urlArg } + + override string getFramework() { + // TOOD: how to get type of this node? + result = "MaD" + } + + override predicate disablesCertificateValidation( + DataFlow::Node disablingNode, DataFlow::Node argumentOrigin + ) { + // TODO: if you need to define this, you have to special case it for every possible API in MaD + none() + } + } +} From 7cadf0322de2cd477cd550780f467ee2f02abad5 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 30 Sep 2025 13:31:48 -0400 Subject: [PATCH 02/14] Updating comments. --- python/ql/lib/semmle/python/frameworks/SSRFSink.qll | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/python/ql/lib/semmle/python/frameworks/SSRFSink.qll b/python/ql/lib/semmle/python/frameworks/SSRFSink.qll index 2460353e799a..bbc3d8285fd5 100644 --- a/python/ql/lib/semmle/python/frameworks/SSRFSink.qll +++ b/python/ql/lib/semmle/python/frameworks/SSRFSink.qll @@ -23,15 +23,12 @@ module SSRFMaDModel { override DataFlow::Node getAUrlPart() { result = urlArg } - override string getFramework() { - // TOOD: how to get type of this node? - result = "MaD" - } + override string getFramework() { result = "MaD" } override predicate disablesCertificateValidation( DataFlow::Node disablingNode, DataFlow::Node argumentOrigin ) { - // TODO: if you need to define this, you have to special case it for every possible API in MaD + // NOTE: if you need to define this, you have to special case it for every possible API in MaD none() } } From 5ea0957e91d30a3633f4abcbca4e3dffb9485754 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 30 Sep 2025 13:32:56 -0400 Subject: [PATCH 03/14] Adding azure sdk test cases and updated test expected file. --- .../FullServerSideRequestForgery.expected | 27 ++++++++++++ .../PartialServerSideRequestForgery.expected | 41 +++++++++++++++++++ .../test_azure_client.py | 40 ++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_azure_client.py diff --git a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/FullServerSideRequestForgery.expected b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/FullServerSideRequestForgery.expected index 0d4a39689301..ae554fa812c9 100644 --- a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/FullServerSideRequestForgery.expected +++ b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/FullServerSideRequestForgery.expected @@ -35,6 +35,17 @@ edges | full_partial_test.py:75:5:75:7 | ControlFlowNode for url | full_partial_test.py:76:18:76:20 | ControlFlowNode for url | provenance | | | full_partial_test.py:78:5:78:7 | ControlFlowNode for url | full_partial_test.py:79:18:79:20 | ControlFlowNode for url | provenance | | | full_partial_test.py:81:5:81:7 | ControlFlowNode for url | full_partial_test.py:82:18:82:20 | ControlFlowNode for url | provenance | | +| test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | test_azure_client.py:7:19:7:25 | ControlFlowNode for request | provenance | | +| test_azure_client.py:7:19:7:25 | ControlFlowNode for request | test_azure_client.py:10:18:10:24 | ControlFlowNode for request | provenance | | +| test_azure_client.py:7:19:7:25 | ControlFlowNode for request | test_azure_client.py:11:19:11:25 | ControlFlowNode for request | provenance | | +| test_azure_client.py:10:18:10:24 | ControlFlowNode for request | test_azure_client.py:11:5:11:15 | ControlFlowNode for user_input2 | provenance | AdditionalTaintStep | +| test_azure_client.py:11:5:11:15 | ControlFlowNode for user_input2 | test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | provenance | | +| test_azure_client.py:11:19:11:25 | ControlFlowNode for request | test_azure_client.py:11:5:11:15 | ControlFlowNode for user_input2 | provenance | AdditionalTaintStep | +| test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | test_azure_client.py:17:32:17:39 | ControlFlowNode for full_url | provenance | Sink:MaD:15 | +| test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | test_azure_client.py:19:39:19:46 | ControlFlowNode for full_url | provenance | Sink:MaD:38 | +| test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | test_azure_client.py:21:19:21:26 | ControlFlowNode for full_url | provenance | Sink:MaD:14 | +| test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | test_azure_client.py:23:58:23:65 | ControlFlowNode for full_url | provenance | Sink:MaD:26 | +| test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | test_azure_client.py:33:18:33:25 | ControlFlowNode for full_url | provenance | Sink:MaD:27 | | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | test_http_client.py:1:26:1:32 | ControlFlowNode for request | provenance | | | test_http_client.py:1:26:1:32 | ControlFlowNode for request | test_http_client.py:9:19:9:25 | ControlFlowNode for request | provenance | | | test_http_client.py:1:26:1:32 | ControlFlowNode for request | test_http_client.py:10:19:10:25 | ControlFlowNode for request | provenance | | @@ -89,6 +100,17 @@ nodes | full_partial_test.py:79:18:79:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | | full_partial_test.py:81:5:81:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | | full_partial_test.py:82:18:82:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | +| test_azure_client.py:7:19:7:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_azure_client.py:10:18:10:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_azure_client.py:11:5:11:15 | ControlFlowNode for user_input2 | semmle.label | ControlFlowNode for user_input2 | +| test_azure_client.py:11:19:11:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_azure_client.py:17:32:17:39 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_azure_client.py:19:39:19:46 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_azure_client.py:21:19:21:26 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_azure_client.py:23:58:23:65 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_azure_client.py:33:18:33:25 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | | test_http_client.py:1:26:1:32 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | test_http_client.py:9:5:9:15 | ControlFlowNode for unsafe_host | semmle.label | ControlFlowNode for unsafe_host | @@ -122,6 +144,11 @@ subpaths | full_partial_test.py:76:5:76:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:76:18:76:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | | full_partial_test.py:79:5:79:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:79:18:79:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | | full_partial_test.py:82:5:82:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:82:18:82:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| test_azure_client.py:17:9:17:63 | ControlFlowNode for SecretClient() | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | test_azure_client.py:17:32:17:39 | ControlFlowNode for full_url | The full URL of this request depends on a $@. | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value | +| test_azure_client.py:19:9:19:47 | ControlFlowNode for Attribute() | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | test_azure_client.py:19:39:19:46 | ControlFlowNode for full_url | The full URL of this request depends on a $@. | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value | +| test_azure_client.py:21:9:21:39 | ControlFlowNode for KeyClient() | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | test_azure_client.py:21:19:21:26 | ControlFlowNode for full_url | The full URL of this request depends on a $@. | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value | +| test_azure_client.py:23:9:23:89 | ControlFlowNode for Attribute() | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | test_azure_client.py:23:58:23:65 | ControlFlowNode for full_url | The full URL of this request depends on a $@. | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value | +| test_azure_client.py:32:5:37:5 | ControlFlowNode for download_blob_from_url() | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | test_azure_client.py:33:18:33:25 | ControlFlowNode for full_url | The full URL of this request depends on a $@. | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value | | test_http_client.py:14:5:14:36 | ControlFlowNode for Attribute() | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | test_http_client.py:13:27:13:37 | ControlFlowNode for unsafe_host | The full URL of this request depends on a $@. | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value | | test_http_client.py:14:5:14:36 | ControlFlowNode for Attribute() | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | test_http_client.py:14:25:14:35 | ControlFlowNode for unsafe_path | The full URL of this request depends on a $@. | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value | | test_http_client.py:19:5:19:36 | ControlFlowNode for Attribute() | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | test_http_client.py:18:27:18:37 | ControlFlowNode for unsafe_host | The full URL of this request depends on a $@. | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value | diff --git a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/PartialServerSideRequestForgery.expected b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/PartialServerSideRequestForgery.expected index 7a7f8a3366b4..bbe756e24b7c 100644 --- a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/PartialServerSideRequestForgery.expected +++ b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/PartialServerSideRequestForgery.expected @@ -77,6 +77,24 @@ edges | full_partial_test.py:119:5:119:14 | ControlFlowNode for user_input | full_partial_test.py:121:5:121:7 | ControlFlowNode for url | provenance | | | full_partial_test.py:119:18:119:24 | ControlFlowNode for request | full_partial_test.py:119:5:119:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | | full_partial_test.py:121:5:121:7 | ControlFlowNode for url | full_partial_test.py:122:18:122:20 | ControlFlowNode for url | provenance | | +| test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | test_azure_client.py:7:19:7:25 | ControlFlowNode for request | provenance | | +| test_azure_client.py:7:19:7:25 | ControlFlowNode for request | test_azure_client.py:10:18:10:24 | ControlFlowNode for request | provenance | | +| test_azure_client.py:7:19:7:25 | ControlFlowNode for request | test_azure_client.py:11:19:11:25 | ControlFlowNode for request | provenance | | +| test_azure_client.py:10:5:10:14 | ControlFlowNode for user_input | test_azure_client.py:13:5:13:7 | ControlFlowNode for url | provenance | | +| test_azure_client.py:10:18:10:24 | ControlFlowNode for request | test_azure_client.py:10:5:10:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | +| test_azure_client.py:10:18:10:24 | ControlFlowNode for request | test_azure_client.py:11:5:11:15 | ControlFlowNode for user_input2 | provenance | AdditionalTaintStep | +| test_azure_client.py:11:5:11:15 | ControlFlowNode for user_input2 | test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | provenance | | +| test_azure_client.py:11:19:11:25 | ControlFlowNode for request | test_azure_client.py:11:5:11:15 | ControlFlowNode for user_input2 | provenance | AdditionalTaintStep | +| test_azure_client.py:13:5:13:7 | ControlFlowNode for url | test_azure_client.py:16:32:16:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_azure_client.py:13:5:13:7 | ControlFlowNode for url | test_azure_client.py:18:39:18:41 | ControlFlowNode for url | provenance | Sink:MaD:38 | +| test_azure_client.py:13:5:13:7 | ControlFlowNode for url | test_azure_client.py:20:19:20:21 | ControlFlowNode for url | provenance | Sink:MaD:14 | +| test_azure_client.py:13:5:13:7 | ControlFlowNode for url | test_azure_client.py:22:58:22:60 | ControlFlowNode for url | provenance | Sink:MaD:26 | +| test_azure_client.py:13:5:13:7 | ControlFlowNode for url | test_azure_client.py:27:18:27:20 | ControlFlowNode for url | provenance | Sink:MaD:27 | +| test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | test_azure_client.py:17:32:17:39 | ControlFlowNode for full_url | provenance | Sink:MaD:15 | +| test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | test_azure_client.py:19:39:19:46 | ControlFlowNode for full_url | provenance | Sink:MaD:38 | +| test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | test_azure_client.py:21:19:21:26 | ControlFlowNode for full_url | provenance | Sink:MaD:14 | +| test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | test_azure_client.py:23:58:23:65 | ControlFlowNode for full_url | provenance | Sink:MaD:26 | +| test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | test_azure_client.py:33:18:33:25 | ControlFlowNode for full_url | provenance | Sink:MaD:27 | | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | test_http_client.py:1:26:1:32 | ControlFlowNode for request | provenance | | | test_http_client.py:1:26:1:32 | ControlFlowNode for request | test_http_client.py:9:19:9:25 | ControlFlowNode for request | provenance | | | test_http_client.py:1:26:1:32 | ControlFlowNode for request | test_http_client.py:10:19:10:25 | ControlFlowNode for request | provenance | | @@ -173,6 +191,24 @@ nodes | full_partial_test.py:119:18:119:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | full_partial_test.py:121:5:121:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | | full_partial_test.py:122:18:122:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | +| test_azure_client.py:7:19:7:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_azure_client.py:10:5:10:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| test_azure_client.py:10:18:10:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_azure_client.py:11:5:11:15 | ControlFlowNode for user_input2 | semmle.label | ControlFlowNode for user_input2 | +| test_azure_client.py:11:19:11:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_azure_client.py:13:5:13:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_azure_client.py:16:32:16:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_azure_client.py:17:32:17:39 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_azure_client.py:18:39:18:41 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_azure_client.py:19:39:19:46 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_azure_client.py:20:19:20:21 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_azure_client.py:21:19:21:26 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_azure_client.py:22:58:22:60 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_azure_client.py:23:58:23:65 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_azure_client.py:27:18:27:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_azure_client.py:33:18:33:25 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | | test_http_client.py:1:26:1:32 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | test_http_client.py:9:5:9:15 | ControlFlowNode for unsafe_host | semmle.label | ControlFlowNode for unsafe_host | @@ -205,6 +241,11 @@ subpaths | full_partial_test.py:107:5:107:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:107:18:107:20 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | | full_partial_test.py:116:5:116:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:116:18:116:20 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | | full_partial_test.py:122:5:122:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:122:18:122:20 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| test_azure_client.py:16:9:16:58 | ControlFlowNode for SecretClient() | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | test_azure_client.py:16:32:16:34 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value | +| test_azure_client.py:18:9:18:42 | ControlFlowNode for Attribute() | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | test_azure_client.py:18:39:18:41 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value | +| test_azure_client.py:20:9:20:34 | ControlFlowNode for KeyClient() | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | test_azure_client.py:20:19:20:21 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value | +| test_azure_client.py:22:9:22:84 | ControlFlowNode for Attribute() | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | test_azure_client.py:22:58:22:60 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value | +| test_azure_client.py:26:5:31:5 | ControlFlowNode for download_blob_from_url() | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | test_azure_client.py:27:18:27:20 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value | | test_http_client.py:22:5:22:31 | ControlFlowNode for Attribute() | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | test_http_client.py:18:27:18:37 | ControlFlowNode for unsafe_host | Part of the URL of this request depends on a $@. | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value | | test_http_client.py:26:5:26:31 | ControlFlowNode for Attribute() | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | test_http_client.py:25:27:25:37 | ControlFlowNode for unsafe_host | Part of the URL of this request depends on a $@. | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value | | test_http_client.py:29:5:29:36 | ControlFlowNode for Attribute() | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | test_http_client.py:29:25:29:35 | ControlFlowNode for unsafe_path | Part of the URL of this request depends on a $@. | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value | diff --git a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_azure_client.py b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_azure_client.py new file mode 100644 index 000000000000..f78b0a641370 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_azure_client.py @@ -0,0 +1,40 @@ +from azure.keyvault.secrets import SecretClient +from azure.storage.fileshare import ShareFileClient +from azure.keyvault.keys import KeyClient +from azure.storage.blob import ContainerClient +from azure.storage.blob import download_blob_from_url + +from flask import request + +def azure_sdk_test(credential, output_path): + user_input = request.args['untrusted_input'] + user_input2 = request.args['untrusted_input2'] + + url = f"https://example.com/foo#{user_input}" + full_url = f"https://{user_input2}" + # Testing Azure sink + c = SecretClient(vault_url=url, credential=credential)# NOT OK -- user only controlled fragment + c = SecretClient(vault_url=full_url, credential=credential) # NOT OK -- user has full control + c = ShareFileClient.from_file_url(url) # NOT OK -- user only controlled fragment + c = ShareFileClient.from_file_url(full_url) # NOT OK -- user has full control + c = KeyClient(url, credential)# NOT OK -- user only controlled fragment + c = KeyClient(full_url, credential) # NOT OK -- user has full control + c = ContainerClient.from_container_url(container_url=url, credential=credential) # NOT OK -- user only controlled fragment + c = ContainerClient.from_container_url(container_url=full_url, credential=credential) # NOT OK -- user has full control + + + download_blob_from_url( + blob_url=url, # NOT OK -- user only controlled fragment + output=output_path, + credential=credential, + overwrite=True + ) + download_blob_from_url( + blob_url=full_url, # NOT OK -- user has full control + output=output_path, + credential=credential, + overwrite=True + ) + + + From d7fb05e7459a872616447166996d0c0d28cf0ec3 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 30 Sep 2025 13:55:31 -0400 Subject: [PATCH 04/14] Added change logs. --- .../lib/change-notes/released/2025-09-30-azure_ssrf_models | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 python/ql/lib/change-notes/released/2025-09-30-azure_ssrf_models diff --git a/python/ql/lib/change-notes/released/2025-09-30-azure_ssrf_models b/python/ql/lib/change-notes/released/2025-09-30-azure_ssrf_models new file mode 100644 index 000000000000..573d5ea109df --- /dev/null +++ b/python/ql/lib/change-notes/released/2025-09-30-azure_ssrf_models @@ -0,0 +1,5 @@ +--- +category: minorAnalysis +--- +* Added `ssrf` MaD for the azure SDK +* Added MaD `ssrf` to `Http::Client::Request` \ No newline at end of file From 3d8829098e5a52e41be783d96f4bcdabbff4916e Mon Sep 17 00:00:00 2001 From: Ben Rodes Date: Tue, 30 Sep 2025 14:00:05 -0400 Subject: [PATCH 05/14] Update python/ql/lib/semmle/python/frameworks/SSRFSink.qll Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- python/ql/lib/semmle/python/frameworks/SSRFSink.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/lib/semmle/python/frameworks/SSRFSink.qll b/python/ql/lib/semmle/python/frameworks/SSRFSink.qll index bbc3d8285fd5..db9a1640214e 100644 --- a/python/ql/lib/semmle/python/frameworks/SSRFSink.qll +++ b/python/ql/lib/semmle/python/frameworks/SSRFSink.qll @@ -6,7 +6,7 @@ private import semmle.python.frameworks.data.ModelsAsData /** * INTERNAL: Do not use. * - * Sets up SSRF sinks as Http::CLient::Request + * Sets up SSRF sinks as Http::Client::Request */ module SSRFMaDModel { class SSRFSink extends Http::Client::Request::Range instanceof API::CallNode { From 25b66c718e1c1b0962b759c2a886670e8eaa3bbc Mon Sep 17 00:00:00 2001 From: Ben Rodes Date: Tue, 30 Sep 2025 14:00:16 -0400 Subject: [PATCH 06/14] Update python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_azure_client.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../CWE-918-ServerSideRequestForgery/test_azure_client.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_azure_client.py b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_azure_client.py index f78b0a641370..5d2b04b9c9dd 100644 --- a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_azure_client.py +++ b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_azure_client.py @@ -22,7 +22,6 @@ def azure_sdk_test(credential, output_path): c = ContainerClient.from_container_url(container_url=url, credential=credential) # NOT OK -- user only controlled fragment c = ContainerClient.from_container_url(container_url=full_url, credential=credential) # NOT OK -- user has full control - download_blob_from_url( blob_url=url, # NOT OK -- user only controlled fragment output=output_path, From fc091cef80d37c42a47e60f8b94e5ce179ccc83e Mon Sep 17 00:00:00 2001 From: Ben Rodes Date: Tue, 30 Sep 2025 14:00:25 -0400 Subject: [PATCH 07/14] Update python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_azure_client.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../CWE-918-ServerSideRequestForgery/test_azure_client.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_azure_client.py b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_azure_client.py index 5d2b04b9c9dd..d8de2092a2e5 100644 --- a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_azure_client.py +++ b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_azure_client.py @@ -34,6 +34,3 @@ def azure_sdk_test(credential, output_path): credential=credential, overwrite=True ) - - - From 023acb8e5f69f6199f7dbf26af37b8bfc6d09019 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 30 Sep 2025 14:02:43 -0400 Subject: [PATCH 08/14] Moved change log to correct location. --- ...25-09-30-azure_ssrf_models => 2025-09-30-azure_ssrf_models.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename python/ql/lib/change-notes/{released/2025-09-30-azure_ssrf_models => 2025-09-30-azure_ssrf_models.md} (100%) diff --git a/python/ql/lib/change-notes/released/2025-09-30-azure_ssrf_models b/python/ql/lib/change-notes/2025-09-30-azure_ssrf_models.md similarity index 100% rename from python/ql/lib/change-notes/released/2025-09-30-azure_ssrf_models rename to python/ql/lib/change-notes/2025-09-30-azure_ssrf_models.md From e964e401fa3f95f4c88f2e5007bebd7c01521c50 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 30 Sep 2025 14:07:32 -0400 Subject: [PATCH 09/14] Adding docs. --- python/ql/lib/semmle/python/frameworks/SSRFSink.qll | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/python/ql/lib/semmle/python/frameworks/SSRFSink.qll b/python/ql/lib/semmle/python/frameworks/SSRFSink.qll index db9a1640214e..e9b7ff9e4747 100644 --- a/python/ql/lib/semmle/python/frameworks/SSRFSink.qll +++ b/python/ql/lib/semmle/python/frameworks/SSRFSink.qll @@ -1,3 +1,7 @@ +/** + * Provides classes for SSRF sinks modeled using Models as Data (MaD). + */ + private import python private import semmle.python.Concepts private import semmle.python.ApiGraphs @@ -9,6 +13,9 @@ private import semmle.python.frameworks.data.ModelsAsData * Sets up SSRF sinks as Http::Client::Request */ module SSRFMaDModel { + /** + * An HTTP request modeled from `ssrf` sinks, modeled using MaD. + */ class SSRFSink extends Http::Client::Request::Range instanceof API::CallNode { DataFlow::Node urlArg; From 230b90922a88f40222c56f385ed70949e74e86f6 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 30 Sep 2025 14:09:06 -0400 Subject: [PATCH 10/14] Adjusting acryonym for SSRF for casing standards. --- python/ql/lib/semmle/python/frameworks/SSRFSink.qll | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/ql/lib/semmle/python/frameworks/SSRFSink.qll b/python/ql/lib/semmle/python/frameworks/SSRFSink.qll index e9b7ff9e4747..aeb228daf13b 100644 --- a/python/ql/lib/semmle/python/frameworks/SSRFSink.qll +++ b/python/ql/lib/semmle/python/frameworks/SSRFSink.qll @@ -12,14 +12,14 @@ private import semmle.python.frameworks.data.ModelsAsData * * Sets up SSRF sinks as Http::Client::Request */ -module SSRFMaDModel { +module SsrfMaDModel { /** * An HTTP request modeled from `ssrf` sinks, modeled using MaD. */ - class SSRFSink extends Http::Client::Request::Range instanceof API::CallNode { + class SsrfSink extends Http::Client::Request::Range instanceof API::CallNode { DataFlow::Node urlArg; - SSRFSink() { + SsrfSink() { ( this.getArg(_) = urlArg or From 8c2f188e974b5714b88d79c0bcb66828c97b7af5 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 2 Feb 2026 10:13:59 -0500 Subject: [PATCH 11/14] Removing an upstream change log, not needed for local fork update. --- python/ql/lib/change-notes/2025-09-30-azure_ssrf_models.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 python/ql/lib/change-notes/2025-09-30-azure_ssrf_models.md diff --git a/python/ql/lib/change-notes/2025-09-30-azure_ssrf_models.md b/python/ql/lib/change-notes/2025-09-30-azure_ssrf_models.md deleted file mode 100644 index 573d5ea109df..000000000000 --- a/python/ql/lib/change-notes/2025-09-30-azure_ssrf_models.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -category: minorAnalysis ---- -* Added `ssrf` MaD for the azure SDK -* Added MaD `ssrf` to `Http::Client::Request` \ No newline at end of file From 7637b66b18ac0766509d53dcf784cc481a851eca Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 2 Feb 2026 16:09:53 -0500 Subject: [PATCH 12/14] Added support for new URIValidator in AntiSSRF library. Updated test caes to use postprocessing results. Currently results for partial ssrf still need work, it is flagging cases where the URL is fully controlled, but is sanitized. I'm not sure if this should be flagged yet. --- ...ServerSideRequestForgeryCustomizations.qll | 45 ++ .../FullServerSideRequestForgery.expected | 364 +++++++----- .../FullServerSideRequestForgery.qlref | 3 +- .../PartialServerSideRequestForgery.expected | 538 +++++++++++------- .../PartialServerSideRequestForgery.qlref | 3 +- .../full_partial_test.py | 75 ++- .../test_azure_client.py | 235 +++++++- .../test_http_client.py | 33 +- .../test_path_validation.py | 132 +++++ .../test_requests.py | 48 +- 10 files changed, 1062 insertions(+), 414 deletions(-) create mode 100644 python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_path_validation.py diff --git a/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll index 274e7ee57ad6..2139ecf797db 100644 --- a/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll @@ -176,4 +176,49 @@ module ServerSideRequestForgery { strNode = [call.getArg(0), call.getArgByName("string")] ) } + + /** A validation that a string does not contain certain characters, considered as a sanitizer. */ + private class UriValidator extends FullUrlControlSanitizer { + UriValidator() { this = DataFlow::BarrierGuard::getABarrierNode() } + } + + import semmle.python.dataflow.new.internal.DataFlowPublic + + private predicate uri_validator(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) { + exists(DataFlow::CallCfgNode call, Node n, string funcs | + funcs in ["in_domain", "in_azure_keyvault_domain", "in_azure_storage_domain"] + | + call = API::moduleImport("AntiSSRF").getMember("URIValidator").getMember(funcs).getACall() and + call.getArg(0).asCfgNode() = node and + n.getALocalSource() = call and + ( + // validator used in a comparison + exists(CompareNode cn, Cmpop op | cn = g | + ( + // validator == true or validator == false or validator is True or validator is False + (op instanceof Eq or op instanceof Is) and + exists(ControlFlowNode l, boolean bool | + l.getNode().(BooleanLiteral).booleanValue() = bool and + bool in [true, false] and + branch = bool and + cn.operands(n.asCfgNode(), op, l) + ) + or + // validator != false or validator != true or validator is not True or validator is not False + (op instanceof NotEq or op instanceof IsNot) and + exists(ControlFlowNode l, boolean bool | + l.getNode().(BooleanLiteral).booleanValue() = bool and + bool in [true, false] and + branch = bool.booleanNot() and + cn.operands(n.asCfgNode(), op, l) + ) + ) + ) + or + // validator call directly (e.g., if URIValidator.in_domain(...) ) + g = call.asCfgNode() and + branch = true + ) + ) + } } diff --git a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/FullServerSideRequestForgery.expected b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/FullServerSideRequestForgery.expected index ae554fa812c9..19b5f7efa9d7 100644 --- a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/FullServerSideRequestForgery.expected +++ b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/FullServerSideRequestForgery.expected @@ -1,156 +1,260 @@ +#select +| full_partial_test.py:11:5:11:28 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:11:18:11:27 | ControlFlowNode for user_input | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| full_partial_test.py:15:5:15:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:15:18:15:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| full_partial_test.py:22:5:22:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:22:18:22:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| full_partial_test.py:27:5:27:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:27:18:27:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| full_partial_test.py:47:5:47:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:47:18:47:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| full_partial_test.py:51:5:51:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:51:18:51:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| full_partial_test.py:55:5:55:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:55:18:55:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| full_partial_test.py:59:5:59:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:59:18:59:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| full_partial_test.py:63:5:63:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:63:18:63:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| full_partial_test.py:72:5:72:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:72:18:72:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| full_partial_test.py:76:5:76:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:76:18:76:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| full_partial_test.py:89:5:89:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:89:18:89:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| full_partial_test.py:93:5:93:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:93:18:93:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| full_partial_test.py:97:5:97:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:97:18:97:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| test_azure_client.py:16:9:16:63 | ControlFlowNode for SecretClient() | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | test_azure_client.py:16:32:16:39 | ControlFlowNode for full_url | The full URL of this request depends on a $@. | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | user-provided value | +| test_azure_client.py:18:9:18:47 | ControlFlowNode for Attribute() | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | test_azure_client.py:18:39:18:46 | ControlFlowNode for full_url | The full URL of this request depends on a $@. | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | user-provided value | +| test_azure_client.py:20:9:20:39 | ControlFlowNode for KeyClient() | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | test_azure_client.py:20:19:20:26 | ControlFlowNode for full_url | The full URL of this request depends on a $@. | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | user-provided value | +| test_azure_client.py:22:9:22:89 | ControlFlowNode for Attribute() | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | test_azure_client.py:22:58:22:65 | ControlFlowNode for full_url | The full URL of this request depends on a $@. | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | user-provided value | +| test_azure_client.py:25:5:25:104 | ControlFlowNode for download_blob_from_url() | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | test_azure_client.py:25:37:25:44 | ControlFlowNode for full_url | The full URL of this request depends on a $@. | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | user-provided value | +| test_http_client.py:15:5:15:36 | ControlFlowNode for Attribute() | test_http_client.py:1:19:1:25 | ControlFlowNode for ImportMember | test_http_client.py:13:27:13:37 | ControlFlowNode for unsafe_host | The full URL of this request depends on a $@. | test_http_client.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| test_http_client.py:15:5:15:36 | ControlFlowNode for Attribute() | test_http_client.py:1:19:1:25 | ControlFlowNode for ImportMember | test_http_client.py:15:25:15:35 | ControlFlowNode for unsafe_path | The full URL of this request depends on a $@. | test_http_client.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| test_http_client.py:21:5:21:36 | ControlFlowNode for Attribute() | test_http_client.py:1:19:1:25 | ControlFlowNode for ImportMember | test_http_client.py:19:27:19:37 | ControlFlowNode for unsafe_host | The full URL of this request depends on a $@. | test_http_client.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| test_http_client.py:21:5:21:36 | ControlFlowNode for Attribute() | test_http_client.py:1:19:1:25 | ControlFlowNode for ImportMember | test_http_client.py:21:25:21:35 | ControlFlowNode for unsafe_path | The full URL of this request depends on a $@. | test_http_client.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:21:13:21:67 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:21:36:21:43 | ControlFlowNode for full_url | The full URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:37:13:37:64 | ControlFlowNode for KeyClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:37:33:37:40 | ControlFlowNode for full_url | The full URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:53:13:53:51 | ControlFlowNode for Attribute() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:53:43:53:50 | ControlFlowNode for full_url | The full URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:64:13:64:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:64:36:64:38 | ControlFlowNode for url | The full URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:71:13:71:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:71:36:71:38 | ControlFlowNode for url | The full URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:74:13:74:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:74:36:74:38 | ControlFlowNode for url | The full URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:79:13:79:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:79:36:79:38 | ControlFlowNode for url | The full URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:87:13:87:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:87:36:87:38 | ControlFlowNode for url | The full URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:90:13:90:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:90:36:90:38 | ControlFlowNode for url | The full URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:95:13:95:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:95:36:95:38 | ControlFlowNode for url | The full URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:102:13:102:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:102:36:102:38 | ControlFlowNode for url | The full URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:107:13:107:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:107:36:107:38 | ControlFlowNode for url | The full URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:110:13:110:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:110:36:110:38 | ControlFlowNode for url | The full URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:115:13:115:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:115:36:115:38 | ControlFlowNode for url | The full URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:122:13:122:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:122:36:122:38 | ControlFlowNode for url | The full URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:125:13:125:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:125:36:125:38 | ControlFlowNode for url | The full URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:132:13:132:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:132:36:132:38 | ControlFlowNode for url | The full URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_requests.py:9:5:9:28 | ControlFlowNode for Attribute() | test_requests.py:1:19:1:25 | ControlFlowNode for ImportMember | test_requests.py:9:18:9:27 | ControlFlowNode for user_input | The full URL of this request depends on a $@. | test_requests.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| test_requests.py:17:5:17:27 | ControlFlowNode for Attribute() | test_requests.py:1:19:1:25 | ControlFlowNode for ImportMember | test_requests.py:17:17:17:26 | ControlFlowNode for user_input | The full URL of this request depends on a $@. | test_requests.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| test_requests.py:22:16:22:55 | ControlFlowNode for Attribute() | test_requests.py:1:19:1:25 | ControlFlowNode for ImportMember | test_requests.py:22:45:22:54 | ControlFlowNode for user_input | The full URL of this request depends on a $@. | test_requests.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | edges | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:1:19:1:25 | ControlFlowNode for request | provenance | | | full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | provenance | | -| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:37:18:37:24 | ControlFlowNode for request | provenance | | -| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:57:18:57:24 | ControlFlowNode for request | provenance | | -| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:71:18:71:24 | ControlFlowNode for request | provenance | | -| full_partial_test.py:7:5:7:14 | ControlFlowNode for user_input | full_partial_test.py:10:18:10:27 | ControlFlowNode for user_input | provenance | | -| full_partial_test.py:7:5:7:14 | ControlFlowNode for user_input | full_partial_test.py:12:5:12:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:7:5:7:14 | ControlFlowNode for user_input | full_partial_test.py:18:5:18:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:7:5:7:14 | ControlFlowNode for user_input | full_partial_test.py:22:5:22:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:41:18:41:24 | ControlFlowNode for request | provenance | | +| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:66:18:66:24 | ControlFlowNode for request | provenance | | +| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:83:18:83:24 | ControlFlowNode for request | provenance | | +| full_partial_test.py:7:5:7:14 | ControlFlowNode for user_input | full_partial_test.py:11:18:11:27 | ControlFlowNode for user_input | provenance | | +| full_partial_test.py:7:5:7:14 | ControlFlowNode for user_input | full_partial_test.py:13:5:13:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:7:5:7:14 | ControlFlowNode for user_input | full_partial_test.py:20:5:20:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:7:5:7:14 | ControlFlowNode for user_input | full_partial_test.py:25:5:25:7 | ControlFlowNode for url | provenance | | | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:7:5:7:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | -| full_partial_test.py:12:5:12:7 | ControlFlowNode for url | full_partial_test.py:13:18:13:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:18:5:18:7 | ControlFlowNode for url | full_partial_test.py:19:18:19:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:22:5:22:7 | ControlFlowNode for url | full_partial_test.py:23:18:23:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:37:5:37:14 | ControlFlowNode for user_input | full_partial_test.py:41:5:41:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:37:5:37:14 | ControlFlowNode for user_input | full_partial_test.py:44:5:44:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:37:5:37:14 | ControlFlowNode for user_input | full_partial_test.py:47:5:47:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:37:5:37:14 | ControlFlowNode for user_input | full_partial_test.py:50:5:50:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:37:5:37:14 | ControlFlowNode for user_input | full_partial_test.py:53:5:53:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:37:18:37:24 | ControlFlowNode for request | full_partial_test.py:37:5:37:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | -| full_partial_test.py:41:5:41:7 | ControlFlowNode for url | full_partial_test.py:42:18:42:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:44:5:44:7 | ControlFlowNode for url | full_partial_test.py:45:18:45:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:47:5:47:7 | ControlFlowNode for url | full_partial_test.py:48:18:48:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:50:5:50:7 | ControlFlowNode for url | full_partial_test.py:51:18:51:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:53:5:53:7 | ControlFlowNode for url | full_partial_test.py:54:18:54:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:57:5:57:14 | ControlFlowNode for user_input | full_partial_test.py:61:5:61:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:57:5:57:14 | ControlFlowNode for user_input | full_partial_test.py:64:5:64:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:57:18:57:24 | ControlFlowNode for request | full_partial_test.py:57:5:57:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | -| full_partial_test.py:61:5:61:7 | ControlFlowNode for url | full_partial_test.py:62:18:62:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:64:5:64:7 | ControlFlowNode for url | full_partial_test.py:65:18:65:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:71:5:71:14 | ControlFlowNode for user_input | full_partial_test.py:75:5:75:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:71:5:71:14 | ControlFlowNode for user_input | full_partial_test.py:78:5:78:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:71:5:71:14 | ControlFlowNode for user_input | full_partial_test.py:81:5:81:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:71:18:71:24 | ControlFlowNode for request | full_partial_test.py:71:5:71:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | -| full_partial_test.py:75:5:75:7 | ControlFlowNode for url | full_partial_test.py:76:18:76:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:78:5:78:7 | ControlFlowNode for url | full_partial_test.py:79:18:79:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:81:5:81:7 | ControlFlowNode for url | full_partial_test.py:82:18:82:20 | ControlFlowNode for url | provenance | | -| test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | test_azure_client.py:7:19:7:25 | ControlFlowNode for request | provenance | | -| test_azure_client.py:7:19:7:25 | ControlFlowNode for request | test_azure_client.py:10:18:10:24 | ControlFlowNode for request | provenance | | -| test_azure_client.py:7:19:7:25 | ControlFlowNode for request | test_azure_client.py:11:19:11:25 | ControlFlowNode for request | provenance | | -| test_azure_client.py:10:18:10:24 | ControlFlowNode for request | test_azure_client.py:11:5:11:15 | ControlFlowNode for user_input2 | provenance | AdditionalTaintStep | -| test_azure_client.py:11:5:11:15 | ControlFlowNode for user_input2 | test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | provenance | | -| test_azure_client.py:11:19:11:25 | ControlFlowNode for request | test_azure_client.py:11:5:11:15 | ControlFlowNode for user_input2 | provenance | AdditionalTaintStep | -| test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | test_azure_client.py:17:32:17:39 | ControlFlowNode for full_url | provenance | Sink:MaD:15 | -| test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | test_azure_client.py:19:39:19:46 | ControlFlowNode for full_url | provenance | Sink:MaD:38 | -| test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | test_azure_client.py:21:19:21:26 | ControlFlowNode for full_url | provenance | Sink:MaD:14 | -| test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | test_azure_client.py:23:58:23:65 | ControlFlowNode for full_url | provenance | Sink:MaD:26 | -| test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | test_azure_client.py:33:18:33:25 | ControlFlowNode for full_url | provenance | Sink:MaD:27 | -| test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | test_http_client.py:1:26:1:32 | ControlFlowNode for request | provenance | | -| test_http_client.py:1:26:1:32 | ControlFlowNode for request | test_http_client.py:9:19:9:25 | ControlFlowNode for request | provenance | | -| test_http_client.py:1:26:1:32 | ControlFlowNode for request | test_http_client.py:10:19:10:25 | ControlFlowNode for request | provenance | | +| full_partial_test.py:13:5:13:7 | ControlFlowNode for url | full_partial_test.py:15:18:15:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:20:5:20:7 | ControlFlowNode for url | full_partial_test.py:22:18:22:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:25:5:25:7 | ControlFlowNode for url | full_partial_test.py:27:18:27:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:41:5:41:14 | ControlFlowNode for user_input | full_partial_test.py:45:5:45:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:41:5:41:14 | ControlFlowNode for user_input | full_partial_test.py:49:5:49:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:41:5:41:14 | ControlFlowNode for user_input | full_partial_test.py:53:5:53:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:41:5:41:14 | ControlFlowNode for user_input | full_partial_test.py:57:5:57:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:41:5:41:14 | ControlFlowNode for user_input | full_partial_test.py:61:5:61:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:41:18:41:24 | ControlFlowNode for request | full_partial_test.py:41:5:41:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | +| full_partial_test.py:45:5:45:7 | ControlFlowNode for url | full_partial_test.py:47:18:47:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:49:5:49:7 | ControlFlowNode for url | full_partial_test.py:51:18:51:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:53:5:53:7 | ControlFlowNode for url | full_partial_test.py:55:18:55:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:57:5:57:7 | ControlFlowNode for url | full_partial_test.py:59:18:59:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:61:5:61:7 | ControlFlowNode for url | full_partial_test.py:63:18:63:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:66:5:66:14 | ControlFlowNode for user_input | full_partial_test.py:70:5:70:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:66:5:66:14 | ControlFlowNode for user_input | full_partial_test.py:74:5:74:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:66:18:66:24 | ControlFlowNode for request | full_partial_test.py:66:5:66:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | +| full_partial_test.py:70:5:70:7 | ControlFlowNode for url | full_partial_test.py:72:18:72:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:74:5:74:7 | ControlFlowNode for url | full_partial_test.py:76:18:76:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:83:5:83:14 | ControlFlowNode for user_input | full_partial_test.py:87:5:87:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:83:5:83:14 | ControlFlowNode for user_input | full_partial_test.py:91:5:91:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:83:5:83:14 | ControlFlowNode for user_input | full_partial_test.py:95:5:95:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:83:18:83:24 | ControlFlowNode for request | full_partial_test.py:83:5:83:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | +| full_partial_test.py:87:5:87:7 | ControlFlowNode for url | full_partial_test.py:89:18:89:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:91:5:91:7 | ControlFlowNode for url | full_partial_test.py:93:18:93:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:95:5:95:7 | ControlFlowNode for url | full_partial_test.py:97:18:97:20 | ControlFlowNode for url | provenance | | +| test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | test_azure_client.py:6:19:6:25 | ControlFlowNode for request | provenance | | +| test_azure_client.py:6:19:6:25 | ControlFlowNode for request | test_azure_client.py:9:18:9:24 | ControlFlowNode for request | provenance | | +| test_azure_client.py:6:19:6:25 | ControlFlowNode for request | test_azure_client.py:10:19:10:25 | ControlFlowNode for request | provenance | | +| test_azure_client.py:9:18:9:24 | ControlFlowNode for request | test_azure_client.py:10:5:10:15 | ControlFlowNode for user_input2 | provenance | AdditionalTaintStep | +| test_azure_client.py:10:5:10:15 | ControlFlowNode for user_input2 | test_azure_client.py:13:5:13:12 | ControlFlowNode for full_url | provenance | | +| test_azure_client.py:10:19:10:25 | ControlFlowNode for request | test_azure_client.py:10:5:10:15 | ControlFlowNode for user_input2 | provenance | AdditionalTaintStep | +| test_azure_client.py:13:5:13:12 | ControlFlowNode for full_url | test_azure_client.py:16:32:16:39 | ControlFlowNode for full_url | provenance | Sink:MaD:15 | +| test_azure_client.py:13:5:13:12 | ControlFlowNode for full_url | test_azure_client.py:18:39:18:46 | ControlFlowNode for full_url | provenance | Sink:MaD:38 | +| test_azure_client.py:13:5:13:12 | ControlFlowNode for full_url | test_azure_client.py:20:19:20:26 | ControlFlowNode for full_url | provenance | Sink:MaD:14 | +| test_azure_client.py:13:5:13:12 | ControlFlowNode for full_url | test_azure_client.py:22:58:22:65 | ControlFlowNode for full_url | provenance | Sink:MaD:26 | +| test_azure_client.py:13:5:13:12 | ControlFlowNode for full_url | test_azure_client.py:25:37:25:44 | ControlFlowNode for full_url | provenance | Sink:MaD:27 | +| test_http_client.py:1:19:1:25 | ControlFlowNode for ImportMember | test_http_client.py:1:19:1:25 | ControlFlowNode for request | provenance | | +| test_http_client.py:1:19:1:25 | ControlFlowNode for request | test_http_client.py:9:19:9:25 | ControlFlowNode for request | provenance | | +| test_http_client.py:1:19:1:25 | ControlFlowNode for request | test_http_client.py:10:19:10:25 | ControlFlowNode for request | provenance | | | test_http_client.py:9:5:9:15 | ControlFlowNode for unsafe_host | test_http_client.py:13:27:13:37 | ControlFlowNode for unsafe_host | provenance | | -| test_http_client.py:9:5:9:15 | ControlFlowNode for unsafe_host | test_http_client.py:18:27:18:37 | ControlFlowNode for unsafe_host | provenance | | -| test_http_client.py:9:5:9:15 | ControlFlowNode for unsafe_host | test_http_client.py:25:27:25:37 | ControlFlowNode for unsafe_host | provenance | | +| test_http_client.py:9:5:9:15 | ControlFlowNode for unsafe_host | test_http_client.py:19:27:19:37 | ControlFlowNode for unsafe_host | provenance | | +| test_http_client.py:9:5:9:15 | ControlFlowNode for unsafe_host | test_http_client.py:28:27:28:37 | ControlFlowNode for unsafe_host | provenance | | | test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:9:5:9:15 | ControlFlowNode for unsafe_host | provenance | AdditionalTaintStep | | test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:10:5:10:15 | ControlFlowNode for unsafe_path | provenance | AdditionalTaintStep | -| test_http_client.py:10:5:10:15 | ControlFlowNode for unsafe_path | test_http_client.py:14:25:14:35 | ControlFlowNode for unsafe_path | provenance | | -| test_http_client.py:10:5:10:15 | ControlFlowNode for unsafe_path | test_http_client.py:19:25:19:35 | ControlFlowNode for unsafe_path | provenance | | -| test_http_client.py:10:5:10:15 | ControlFlowNode for unsafe_path | test_http_client.py:29:25:29:35 | ControlFlowNode for unsafe_path | provenance | | +| test_http_client.py:10:5:10:15 | ControlFlowNode for unsafe_path | test_http_client.py:15:25:15:35 | ControlFlowNode for unsafe_path | provenance | | +| test_http_client.py:10:5:10:15 | ControlFlowNode for unsafe_path | test_http_client.py:21:25:21:35 | ControlFlowNode for unsafe_path | provenance | | +| test_http_client.py:10:5:10:15 | ControlFlowNode for unsafe_path | test_http_client.py:34:25:34:35 | ControlFlowNode for unsafe_path | provenance | | | test_http_client.py:10:19:10:25 | ControlFlowNode for request | test_http_client.py:10:5:10:15 | ControlFlowNode for unsafe_path | provenance | AdditionalTaintStep | +| test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:5:19:5:25 | ControlFlowNode for request | provenance | | +| test_path_validation.py:5:19:5:25 | ControlFlowNode for request | test_path_validation.py:8:18:8:24 | ControlFlowNode for request | provenance | | +| test_path_validation.py:5:19:5:25 | ControlFlowNode for request | test_path_validation.py:9:19:9:25 | ControlFlowNode for request | provenance | | +| test_path_validation.py:5:19:5:25 | ControlFlowNode for request | test_path_validation.py:24:18:24:24 | ControlFlowNode for request | provenance | | +| test_path_validation.py:5:19:5:25 | ControlFlowNode for request | test_path_validation.py:25:19:25:25 | ControlFlowNode for request | provenance | | +| test_path_validation.py:5:19:5:25 | ControlFlowNode for request | test_path_validation.py:40:18:40:24 | ControlFlowNode for request | provenance | | +| test_path_validation.py:5:19:5:25 | ControlFlowNode for request | test_path_validation.py:41:19:41:25 | ControlFlowNode for request | provenance | | +| test_path_validation.py:5:19:5:25 | ControlFlowNode for request | test_path_validation.py:57:18:57:24 | ControlFlowNode for request | provenance | | +| test_path_validation.py:8:18:8:24 | ControlFlowNode for request | test_path_validation.py:9:5:9:15 | ControlFlowNode for user_input2 | provenance | AdditionalTaintStep | +| test_path_validation.py:9:5:9:15 | ControlFlowNode for user_input2 | test_path_validation.py:11:5:11:12 | ControlFlowNode for full_url | provenance | | +| test_path_validation.py:9:19:9:25 | ControlFlowNode for request | test_path_validation.py:9:5:9:15 | ControlFlowNode for user_input2 | provenance | AdditionalTaintStep | +| test_path_validation.py:11:5:11:12 | ControlFlowNode for full_url | test_path_validation.py:21:36:21:43 | ControlFlowNode for full_url | provenance | Sink:MaD:15 | +| test_path_validation.py:24:18:24:24 | ControlFlowNode for request | test_path_validation.py:25:5:25:15 | ControlFlowNode for user_input2 | provenance | AdditionalTaintStep | +| test_path_validation.py:25:5:25:15 | ControlFlowNode for user_input2 | test_path_validation.py:27:5:27:12 | ControlFlowNode for full_url | provenance | | +| test_path_validation.py:25:19:25:25 | ControlFlowNode for request | test_path_validation.py:25:5:25:15 | ControlFlowNode for user_input2 | provenance | AdditionalTaintStep | +| test_path_validation.py:27:5:27:12 | ControlFlowNode for full_url | test_path_validation.py:37:33:37:40 | ControlFlowNode for full_url | provenance | Sink:MaD:14 | +| test_path_validation.py:40:18:40:24 | ControlFlowNode for request | test_path_validation.py:41:5:41:15 | ControlFlowNode for user_input2 | provenance | AdditionalTaintStep | +| test_path_validation.py:41:5:41:15 | ControlFlowNode for user_input2 | test_path_validation.py:43:5:43:12 | ControlFlowNode for full_url | provenance | | +| test_path_validation.py:41:19:41:25 | ControlFlowNode for request | test_path_validation.py:41:5:41:15 | ControlFlowNode for user_input2 | provenance | AdditionalTaintStep | +| test_path_validation.py:43:5:43:12 | ControlFlowNode for full_url | test_path_validation.py:53:43:53:50 | ControlFlowNode for full_url | provenance | Sink:MaD:38 | +| test_path_validation.py:57:5:57:14 | ControlFlowNode for user_input | test_path_validation.py:61:5:61:7 | ControlFlowNode for url | provenance | | +| test_path_validation.py:57:18:57:24 | ControlFlowNode for request | test_path_validation.py:57:5:57:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:64:36:64:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:71:36:71:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:74:36:74:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:79:36:79:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:87:36:87:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:90:36:90:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:95:36:95:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:102:36:102:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:107:36:107:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:110:36:110:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:115:36:115:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:122:36:122:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:125:36:125:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:132:36:132:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | | test_requests.py:1:19:1:25 | ControlFlowNode for ImportMember | test_requests.py:1:19:1:25 | ControlFlowNode for request | provenance | | -| test_requests.py:1:19:1:25 | ControlFlowNode for request | test_requests.py:6:18:6:24 | ControlFlowNode for request | provenance | | -| test_requests.py:6:5:6:14 | ControlFlowNode for user_input | test_requests.py:8:18:8:27 | ControlFlowNode for user_input | provenance | | -| test_requests.py:6:18:6:24 | ControlFlowNode for request | test_requests.py:6:5:6:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | +| test_requests.py:1:19:1:25 | ControlFlowNode for request | test_requests.py:7:18:7:24 | ControlFlowNode for request | provenance | | +| test_requests.py:1:19:1:25 | ControlFlowNode for request | test_requests.py:14:18:14:24 | ControlFlowNode for request | provenance | | +| test_requests.py:1:19:1:25 | ControlFlowNode for request | test_requests.py:20:18:20:24 | ControlFlowNode for request | provenance | | +| test_requests.py:7:5:7:14 | ControlFlowNode for user_input | test_requests.py:9:18:9:27 | ControlFlowNode for user_input | provenance | | +| test_requests.py:7:18:7:24 | ControlFlowNode for request | test_requests.py:7:5:7:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | +| test_requests.py:14:5:14:14 | ControlFlowNode for user_input | test_requests.py:17:17:17:26 | ControlFlowNode for user_input | provenance | | +| test_requests.py:14:18:14:24 | ControlFlowNode for request | test_requests.py:14:5:14:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | +| test_requests.py:20:5:20:14 | ControlFlowNode for user_input | test_requests.py:22:45:22:54 | ControlFlowNode for user_input | provenance | | +| test_requests.py:20:18:20:24 | ControlFlowNode for request | test_requests.py:20:5:20:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | nodes | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | | full_partial_test.py:1:19:1:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | full_partial_test.py:7:5:7:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| full_partial_test.py:10:18:10:27 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | -| full_partial_test.py:12:5:12:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:13:18:13:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:18:5:18:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:19:18:19:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:22:5:22:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:23:18:23:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:37:5:37:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | -| full_partial_test.py:37:18:37:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| full_partial_test.py:41:5:41:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:42:18:42:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:44:5:44:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:45:18:45:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:47:5:47:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:48:18:48:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:50:5:50:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:11:18:11:27 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| full_partial_test.py:13:5:13:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:15:18:15:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:20:5:20:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:22:18:22:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:25:5:25:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:27:18:27:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:41:5:41:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| full_partial_test.py:41:18:41:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| full_partial_test.py:45:5:45:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:47:18:47:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:49:5:49:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | | full_partial_test.py:51:18:51:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | | full_partial_test.py:53:5:53:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:54:18:54:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:57:5:57:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | -| full_partial_test.py:57:18:57:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| full_partial_test.py:55:18:55:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:57:5:57:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:59:18:59:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | | full_partial_test.py:61:5:61:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:62:18:62:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:64:5:64:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:65:18:65:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:71:5:71:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | -| full_partial_test.py:71:18:71:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| full_partial_test.py:75:5:75:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:63:18:63:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:66:5:66:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| full_partial_test.py:66:18:66:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| full_partial_test.py:70:5:70:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:72:18:72:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:74:5:74:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | | full_partial_test.py:76:18:76:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:78:5:78:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:79:18:79:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:81:5:81:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:82:18:82:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | -| test_azure_client.py:7:19:7:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| test_azure_client.py:10:18:10:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| test_azure_client.py:11:5:11:15 | ControlFlowNode for user_input2 | semmle.label | ControlFlowNode for user_input2 | -| test_azure_client.py:11:19:11:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | -| test_azure_client.py:17:32:17:39 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | -| test_azure_client.py:19:39:19:46 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | -| test_azure_client.py:21:19:21:26 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | -| test_azure_client.py:23:58:23:65 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | -| test_azure_client.py:33:18:33:25 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | -| test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | -| test_http_client.py:1:26:1:32 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| full_partial_test.py:83:5:83:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| full_partial_test.py:83:18:83:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| full_partial_test.py:87:5:87:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:89:18:89:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:91:5:91:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:93:18:93:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:95:5:95:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:97:18:97:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | +| test_azure_client.py:6:19:6:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_azure_client.py:9:18:9:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_azure_client.py:10:5:10:15 | ControlFlowNode for user_input2 | semmle.label | ControlFlowNode for user_input2 | +| test_azure_client.py:10:19:10:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_azure_client.py:13:5:13:12 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_azure_client.py:16:32:16:39 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_azure_client.py:18:39:18:46 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_azure_client.py:20:19:20:26 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_azure_client.py:22:58:22:65 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_azure_client.py:25:37:25:44 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_http_client.py:1:19:1:25 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | +| test_http_client.py:1:19:1:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | test_http_client.py:9:5:9:15 | ControlFlowNode for unsafe_host | semmle.label | ControlFlowNode for unsafe_host | | test_http_client.py:9:19:9:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | test_http_client.py:10:5:10:15 | ControlFlowNode for unsafe_path | semmle.label | ControlFlowNode for unsafe_path | | test_http_client.py:10:19:10:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | test_http_client.py:13:27:13:37 | ControlFlowNode for unsafe_host | semmle.label | ControlFlowNode for unsafe_host | -| test_http_client.py:14:25:14:35 | ControlFlowNode for unsafe_path | semmle.label | ControlFlowNode for unsafe_path | -| test_http_client.py:18:27:18:37 | ControlFlowNode for unsafe_host | semmle.label | ControlFlowNode for unsafe_host | -| test_http_client.py:19:25:19:35 | ControlFlowNode for unsafe_path | semmle.label | ControlFlowNode for unsafe_path | -| test_http_client.py:25:27:25:37 | ControlFlowNode for unsafe_host | semmle.label | ControlFlowNode for unsafe_host | -| test_http_client.py:29:25:29:35 | ControlFlowNode for unsafe_path | semmle.label | ControlFlowNode for unsafe_path | +| test_http_client.py:15:25:15:35 | ControlFlowNode for unsafe_path | semmle.label | ControlFlowNode for unsafe_path | +| test_http_client.py:19:27:19:37 | ControlFlowNode for unsafe_host | semmle.label | ControlFlowNode for unsafe_host | +| test_http_client.py:21:25:21:35 | ControlFlowNode for unsafe_path | semmle.label | ControlFlowNode for unsafe_path | +| test_http_client.py:28:27:28:37 | ControlFlowNode for unsafe_host | semmle.label | ControlFlowNode for unsafe_host | +| test_http_client.py:34:25:34:35 | ControlFlowNode for unsafe_path | semmle.label | ControlFlowNode for unsafe_path | +| test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | +| test_path_validation.py:5:19:5:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_path_validation.py:8:18:8:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_path_validation.py:9:5:9:15 | ControlFlowNode for user_input2 | semmle.label | ControlFlowNode for user_input2 | +| test_path_validation.py:9:19:9:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_path_validation.py:11:5:11:12 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_path_validation.py:21:36:21:43 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_path_validation.py:24:18:24:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_path_validation.py:25:5:25:15 | ControlFlowNode for user_input2 | semmle.label | ControlFlowNode for user_input2 | +| test_path_validation.py:25:19:25:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_path_validation.py:27:5:27:12 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_path_validation.py:37:33:37:40 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_path_validation.py:40:18:40:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_path_validation.py:41:5:41:15 | ControlFlowNode for user_input2 | semmle.label | ControlFlowNode for user_input2 | +| test_path_validation.py:41:19:41:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_path_validation.py:43:5:43:12 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_path_validation.py:53:43:53:50 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_path_validation.py:57:5:57:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| test_path_validation.py:57:18:57:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:64:36:64:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:71:36:71:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:74:36:74:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:79:36:79:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:87:36:87:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:90:36:90:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:95:36:95:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:102:36:102:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:107:36:107:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:110:36:110:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:115:36:115:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:122:36:122:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:125:36:125:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:132:36:132:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | | test_requests.py:1:19:1:25 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | | test_requests.py:1:19:1:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| test_requests.py:6:5:6:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | -| test_requests.py:6:18:6:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| test_requests.py:8:18:8:27 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| test_requests.py:7:5:7:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| test_requests.py:7:18:7:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_requests.py:9:18:9:27 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| test_requests.py:14:5:14:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| test_requests.py:14:18:14:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_requests.py:17:17:17:26 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| test_requests.py:20:5:20:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| test_requests.py:20:18:20:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_requests.py:22:45:22:54 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | subpaths -#select -| full_partial_test.py:10:5:10:28 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:10:18:10:27 | ControlFlowNode for user_input | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | -| full_partial_test.py:13:5:13:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:13:18:13:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | -| full_partial_test.py:19:5:19:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:19:18:19:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | -| full_partial_test.py:23:5:23:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:23:18:23:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | -| full_partial_test.py:42:5:42:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:42:18:42:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | -| full_partial_test.py:45:5:45:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:45:18:45:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | -| full_partial_test.py:48:5:48:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:48:18:48:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | -| full_partial_test.py:51:5:51:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:51:18:51:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | -| full_partial_test.py:54:5:54:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:54:18:54:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | -| full_partial_test.py:62:5:62:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:62:18:62:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | -| full_partial_test.py:65:5:65:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:65:18:65:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | -| full_partial_test.py:76:5:76:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:76:18:76:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | -| full_partial_test.py:79:5:79:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:79:18:79:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | -| full_partial_test.py:82:5:82:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:82:18:82:20 | ControlFlowNode for url | The full URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | -| test_azure_client.py:17:9:17:63 | ControlFlowNode for SecretClient() | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | test_azure_client.py:17:32:17:39 | ControlFlowNode for full_url | The full URL of this request depends on a $@. | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value | -| test_azure_client.py:19:9:19:47 | ControlFlowNode for Attribute() | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | test_azure_client.py:19:39:19:46 | ControlFlowNode for full_url | The full URL of this request depends on a $@. | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value | -| test_azure_client.py:21:9:21:39 | ControlFlowNode for KeyClient() | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | test_azure_client.py:21:19:21:26 | ControlFlowNode for full_url | The full URL of this request depends on a $@. | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value | -| test_azure_client.py:23:9:23:89 | ControlFlowNode for Attribute() | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | test_azure_client.py:23:58:23:65 | ControlFlowNode for full_url | The full URL of this request depends on a $@. | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value | -| test_azure_client.py:32:5:37:5 | ControlFlowNode for download_blob_from_url() | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | test_azure_client.py:33:18:33:25 | ControlFlowNode for full_url | The full URL of this request depends on a $@. | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value | -| test_http_client.py:14:5:14:36 | ControlFlowNode for Attribute() | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | test_http_client.py:13:27:13:37 | ControlFlowNode for unsafe_host | The full URL of this request depends on a $@. | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value | -| test_http_client.py:14:5:14:36 | ControlFlowNode for Attribute() | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | test_http_client.py:14:25:14:35 | ControlFlowNode for unsafe_path | The full URL of this request depends on a $@. | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value | -| test_http_client.py:19:5:19:36 | ControlFlowNode for Attribute() | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | test_http_client.py:18:27:18:37 | ControlFlowNode for unsafe_host | The full URL of this request depends on a $@. | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value | -| test_http_client.py:19:5:19:36 | ControlFlowNode for Attribute() | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | test_http_client.py:19:25:19:35 | ControlFlowNode for unsafe_path | The full URL of this request depends on a $@. | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value | -| test_requests.py:8:5:8:28 | ControlFlowNode for Attribute() | test_requests.py:1:19:1:25 | ControlFlowNode for ImportMember | test_requests.py:8:18:8:27 | ControlFlowNode for user_input | The full URL of this request depends on a $@. | test_requests.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +testFailures +| test_requests.py:39:40:39:62 | Comment # $ Alert[py/full-ssrf] | Missing result: Alert[py/full-ssrf] | +| test_requests.py:49:40:49:62 | Comment # $ Alert[py/full-ssrf] | Missing result: Alert[py/full-ssrf] | diff --git a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/FullServerSideRequestForgery.qlref b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/FullServerSideRequestForgery.qlref index 50d53b5f47e2..35a2814e7f40 100644 --- a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/FullServerSideRequestForgery.qlref +++ b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/FullServerSideRequestForgery.qlref @@ -1 +1,2 @@ -Security/CWE-918/FullServerSideRequestForgery.ql +query: Security/CWE-918/FullServerSideRequestForgery.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/PartialServerSideRequestForgery.expected b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/PartialServerSideRequestForgery.expected index bbe756e24b7c..875d8b62b62f 100644 --- a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/PartialServerSideRequestForgery.expected +++ b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/PartialServerSideRequestForgery.expected @@ -1,124 +1,199 @@ +#select +| full_partial_test.py:80:5:80:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:80:18:80:20 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| full_partial_test.py:105:5:105:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:105:18:105:20 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| full_partial_test.py:112:5:112:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:112:18:112:20 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| full_partial_test.py:119:5:119:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:119:18:119:20 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| full_partial_test.py:126:5:126:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:126:18:126:20 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| full_partial_test.py:136:5:136:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:136:18:136:20 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| full_partial_test.py:143:5:143:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:143:18:143:20 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| test_azure_client.py:15:9:15:58 | ControlFlowNode for SecretClient() | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | test_azure_client.py:15:32:15:34 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | user-provided value | +| test_azure_client.py:17:9:17:42 | ControlFlowNode for Attribute() | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | test_azure_client.py:17:39:17:41 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | user-provided value | +| test_azure_client.py:19:9:19:34 | ControlFlowNode for KeyClient() | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | test_azure_client.py:19:19:19:21 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | user-provided value | +| test_azure_client.py:21:9:21:84 | ControlFlowNode for Attribute() | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | test_azure_client.py:21:58:21:60 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | user-provided value | +| test_azure_client.py:24:5:24:100 | ControlFlowNode for download_blob_from_url() | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | test_azure_client.py:24:37:24:39 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | user-provided value | +| test_http_client.py:25:5:25:31 | ControlFlowNode for Attribute() | test_http_client.py:1:19:1:25 | ControlFlowNode for ImportMember | test_http_client.py:19:27:19:37 | ControlFlowNode for unsafe_host | Part of the URL of this request depends on a $@. | test_http_client.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| test_http_client.py:30:5:30:31 | ControlFlowNode for Attribute() | test_http_client.py:1:19:1:25 | ControlFlowNode for ImportMember | test_http_client.py:28:27:28:37 | ControlFlowNode for unsafe_host | Part of the URL of this request depends on a $@. | test_http_client.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| test_http_client.py:34:5:34:36 | ControlFlowNode for Attribute() | test_http_client.py:1:19:1:25 | ControlFlowNode for ImportMember | test_http_client.py:34:25:34:35 | ControlFlowNode for unsafe_path | Part of the URL of this request depends on a $@. | test_http_client.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| test_http_client.py:39:5:39:29 | ControlFlowNode for Attribute() | test_http_client.py:1:19:1:25 | ControlFlowNode for ImportMember | test_http_client.py:39:25:39:28 | ControlFlowNode for path | Part of the URL of this request depends on a $@. | test_http_client.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| test_http_client.py:44:5:44:29 | ControlFlowNode for Attribute() | test_http_client.py:1:19:1:25 | ControlFlowNode for ImportMember | test_http_client.py:44:25:44:28 | ControlFlowNode for path | Part of the URL of this request depends on a $@. | test_http_client.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:14:13:14:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:14:36:14:38 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:16:13:16:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:16:36:16:38 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:30:13:30:59 | ControlFlowNode for KeyClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:30:33:30:35 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:32:13:32:59 | ControlFlowNode for KeyClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:32:33:32:35 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:46:13:46:46 | ControlFlowNode for Attribute() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:46:43:46:45 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:48:13:48:46 | ControlFlowNode for Attribute() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:48:43:48:45 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | edges | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:1:19:1:25 | ControlFlowNode for request | provenance | | | full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | provenance | | | full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:8:17:8:23 | ControlFlowNode for request | provenance | | -| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:37:18:37:24 | ControlFlowNode for request | provenance | | -| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:38:17:38:23 | ControlFlowNode for request | provenance | | -| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:57:18:57:24 | ControlFlowNode for request | provenance | | -| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:58:17:58:23 | ControlFlowNode for request | provenance | | -| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:71:18:71:24 | ControlFlowNode for request | provenance | | -| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:72:17:72:23 | ControlFlowNode for request | provenance | | -| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:86:18:86:24 | ControlFlowNode for request | provenance | | -| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:92:18:92:24 | ControlFlowNode for request | provenance | | -| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:98:18:98:24 | ControlFlowNode for request | provenance | | -| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:104:18:104:24 | ControlFlowNode for request | provenance | | -| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:110:18:110:24 | ControlFlowNode for request | provenance | | -| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:119:18:119:24 | ControlFlowNode for request | provenance | | -| full_partial_test.py:7:5:7:14 | ControlFlowNode for user_input | full_partial_test.py:10:18:10:27 | ControlFlowNode for user_input | provenance | | -| full_partial_test.py:7:5:7:14 | ControlFlowNode for user_input | full_partial_test.py:12:5:12:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:7:5:7:14 | ControlFlowNode for user_input | full_partial_test.py:18:5:18:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:7:5:7:14 | ControlFlowNode for user_input | full_partial_test.py:22:5:22:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:41:18:41:24 | ControlFlowNode for request | provenance | | +| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:42:17:42:23 | ControlFlowNode for request | provenance | | +| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:66:18:66:24 | ControlFlowNode for request | provenance | | +| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:67:17:67:23 | ControlFlowNode for request | provenance | | +| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:83:18:83:24 | ControlFlowNode for request | provenance | | +| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:84:17:84:23 | ControlFlowNode for request | provenance | | +| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:101:18:101:24 | ControlFlowNode for request | provenance | | +| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:108:18:108:24 | ControlFlowNode for request | provenance | | +| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:115:18:115:24 | ControlFlowNode for request | provenance | | +| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:122:18:122:24 | ControlFlowNode for request | provenance | | +| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:129:18:129:24 | ControlFlowNode for request | provenance | | +| full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:139:18:139:24 | ControlFlowNode for request | provenance | | +| full_partial_test.py:7:5:7:14 | ControlFlowNode for user_input | full_partial_test.py:11:18:11:27 | ControlFlowNode for user_input | provenance | | +| full_partial_test.py:7:5:7:14 | ControlFlowNode for user_input | full_partial_test.py:13:5:13:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:7:5:7:14 | ControlFlowNode for user_input | full_partial_test.py:20:5:20:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:7:5:7:14 | ControlFlowNode for user_input | full_partial_test.py:25:5:25:7 | ControlFlowNode for url | provenance | | | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:7:5:7:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:8:5:8:13 | ControlFlowNode for query_val | provenance | AdditionalTaintStep | -| full_partial_test.py:8:5:8:13 | ControlFlowNode for query_val | full_partial_test.py:22:5:22:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:8:5:8:13 | ControlFlowNode for query_val | full_partial_test.py:25:5:25:7 | ControlFlowNode for url | provenance | | | full_partial_test.py:8:17:8:23 | ControlFlowNode for request | full_partial_test.py:8:5:8:13 | ControlFlowNode for query_val | provenance | AdditionalTaintStep | -| full_partial_test.py:12:5:12:7 | ControlFlowNode for url | full_partial_test.py:13:18:13:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:18:5:18:7 | ControlFlowNode for url | full_partial_test.py:19:18:19:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:22:5:22:7 | ControlFlowNode for url | full_partial_test.py:23:18:23:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:37:5:37:14 | ControlFlowNode for user_input | full_partial_test.py:41:5:41:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:37:5:37:14 | ControlFlowNode for user_input | full_partial_test.py:44:5:44:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:37:5:37:14 | ControlFlowNode for user_input | full_partial_test.py:47:5:47:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:37:5:37:14 | ControlFlowNode for user_input | full_partial_test.py:50:5:50:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:37:5:37:14 | ControlFlowNode for user_input | full_partial_test.py:53:5:53:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:37:18:37:24 | ControlFlowNode for request | full_partial_test.py:37:5:37:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | -| full_partial_test.py:37:18:37:24 | ControlFlowNode for request | full_partial_test.py:38:5:38:13 | ControlFlowNode for query_val | provenance | AdditionalTaintStep | -| full_partial_test.py:38:5:38:13 | ControlFlowNode for query_val | full_partial_test.py:47:5:47:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:38:17:38:23 | ControlFlowNode for request | full_partial_test.py:38:5:38:13 | ControlFlowNode for query_val | provenance | AdditionalTaintStep | -| full_partial_test.py:41:5:41:7 | ControlFlowNode for url | full_partial_test.py:42:18:42:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:44:5:44:7 | ControlFlowNode for url | full_partial_test.py:45:18:45:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:47:5:47:7 | ControlFlowNode for url | full_partial_test.py:48:18:48:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:50:5:50:7 | ControlFlowNode for url | full_partial_test.py:51:18:51:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:53:5:53:7 | ControlFlowNode for url | full_partial_test.py:54:18:54:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:57:5:57:14 | ControlFlowNode for user_input | full_partial_test.py:61:5:61:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:57:5:57:14 | ControlFlowNode for user_input | full_partial_test.py:64:5:64:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:57:5:57:14 | ControlFlowNode for user_input | full_partial_test.py:67:5:67:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:57:18:57:24 | ControlFlowNode for request | full_partial_test.py:57:5:57:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | -| full_partial_test.py:57:18:57:24 | ControlFlowNode for request | full_partial_test.py:58:5:58:13 | ControlFlowNode for query_val | provenance | AdditionalTaintStep | -| full_partial_test.py:58:5:58:13 | ControlFlowNode for query_val | full_partial_test.py:67:5:67:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:58:17:58:23 | ControlFlowNode for request | full_partial_test.py:58:5:58:13 | ControlFlowNode for query_val | provenance | AdditionalTaintStep | -| full_partial_test.py:61:5:61:7 | ControlFlowNode for url | full_partial_test.py:62:18:62:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:64:5:64:7 | ControlFlowNode for url | full_partial_test.py:65:18:65:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:67:5:67:7 | ControlFlowNode for url | full_partial_test.py:68:18:68:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:71:5:71:14 | ControlFlowNode for user_input | full_partial_test.py:75:5:75:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:71:5:71:14 | ControlFlowNode for user_input | full_partial_test.py:78:5:78:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:71:5:71:14 | ControlFlowNode for user_input | full_partial_test.py:81:5:81:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:71:18:71:24 | ControlFlowNode for request | full_partial_test.py:71:5:71:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | -| full_partial_test.py:71:18:71:24 | ControlFlowNode for request | full_partial_test.py:72:5:72:13 | ControlFlowNode for query_val | provenance | AdditionalTaintStep | -| full_partial_test.py:72:5:72:13 | ControlFlowNode for query_val | full_partial_test.py:81:5:81:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:72:17:72:23 | ControlFlowNode for request | full_partial_test.py:72:5:72:13 | ControlFlowNode for query_val | provenance | AdditionalTaintStep | -| full_partial_test.py:75:5:75:7 | ControlFlowNode for url | full_partial_test.py:76:18:76:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:78:5:78:7 | ControlFlowNode for url | full_partial_test.py:79:18:79:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:81:5:81:7 | ControlFlowNode for url | full_partial_test.py:82:18:82:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:86:5:86:14 | ControlFlowNode for user_input | full_partial_test.py:88:5:88:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:86:18:86:24 | ControlFlowNode for request | full_partial_test.py:86:5:86:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | -| full_partial_test.py:88:5:88:7 | ControlFlowNode for url | full_partial_test.py:89:18:89:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:92:5:92:14 | ControlFlowNode for user_input | full_partial_test.py:94:5:94:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:92:18:92:24 | ControlFlowNode for request | full_partial_test.py:92:5:92:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | -| full_partial_test.py:94:5:94:7 | ControlFlowNode for url | full_partial_test.py:95:18:95:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:98:5:98:14 | ControlFlowNode for user_input | full_partial_test.py:100:5:100:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:98:18:98:24 | ControlFlowNode for request | full_partial_test.py:98:5:98:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | -| full_partial_test.py:100:5:100:7 | ControlFlowNode for url | full_partial_test.py:101:18:101:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:104:5:104:14 | ControlFlowNode for user_input | full_partial_test.py:106:5:106:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:104:18:104:24 | ControlFlowNode for request | full_partial_test.py:104:5:104:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | -| full_partial_test.py:106:5:106:7 | ControlFlowNode for url | full_partial_test.py:107:18:107:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:110:5:110:14 | ControlFlowNode for user_input | full_partial_test.py:115:5:115:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:110:18:110:24 | ControlFlowNode for request | full_partial_test.py:110:5:110:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | -| full_partial_test.py:115:5:115:7 | ControlFlowNode for url | full_partial_test.py:116:18:116:20 | ControlFlowNode for url | provenance | | -| full_partial_test.py:119:5:119:14 | ControlFlowNode for user_input | full_partial_test.py:121:5:121:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:119:18:119:24 | ControlFlowNode for request | full_partial_test.py:119:5:119:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | -| full_partial_test.py:121:5:121:7 | ControlFlowNode for url | full_partial_test.py:122:18:122:20 | ControlFlowNode for url | provenance | | -| test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | test_azure_client.py:7:19:7:25 | ControlFlowNode for request | provenance | | -| test_azure_client.py:7:19:7:25 | ControlFlowNode for request | test_azure_client.py:10:18:10:24 | ControlFlowNode for request | provenance | | -| test_azure_client.py:7:19:7:25 | ControlFlowNode for request | test_azure_client.py:11:19:11:25 | ControlFlowNode for request | provenance | | -| test_azure_client.py:10:5:10:14 | ControlFlowNode for user_input | test_azure_client.py:13:5:13:7 | ControlFlowNode for url | provenance | | -| test_azure_client.py:10:18:10:24 | ControlFlowNode for request | test_azure_client.py:10:5:10:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | -| test_azure_client.py:10:18:10:24 | ControlFlowNode for request | test_azure_client.py:11:5:11:15 | ControlFlowNode for user_input2 | provenance | AdditionalTaintStep | -| test_azure_client.py:11:5:11:15 | ControlFlowNode for user_input2 | test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | provenance | | -| test_azure_client.py:11:19:11:25 | ControlFlowNode for request | test_azure_client.py:11:5:11:15 | ControlFlowNode for user_input2 | provenance | AdditionalTaintStep | -| test_azure_client.py:13:5:13:7 | ControlFlowNode for url | test_azure_client.py:16:32:16:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_azure_client.py:13:5:13:7 | ControlFlowNode for url | test_azure_client.py:18:39:18:41 | ControlFlowNode for url | provenance | Sink:MaD:38 | -| test_azure_client.py:13:5:13:7 | ControlFlowNode for url | test_azure_client.py:20:19:20:21 | ControlFlowNode for url | provenance | Sink:MaD:14 | -| test_azure_client.py:13:5:13:7 | ControlFlowNode for url | test_azure_client.py:22:58:22:60 | ControlFlowNode for url | provenance | Sink:MaD:26 | -| test_azure_client.py:13:5:13:7 | ControlFlowNode for url | test_azure_client.py:27:18:27:20 | ControlFlowNode for url | provenance | Sink:MaD:27 | -| test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | test_azure_client.py:17:32:17:39 | ControlFlowNode for full_url | provenance | Sink:MaD:15 | -| test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | test_azure_client.py:19:39:19:46 | ControlFlowNode for full_url | provenance | Sink:MaD:38 | -| test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | test_azure_client.py:21:19:21:26 | ControlFlowNode for full_url | provenance | Sink:MaD:14 | -| test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | test_azure_client.py:23:58:23:65 | ControlFlowNode for full_url | provenance | Sink:MaD:26 | -| test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | test_azure_client.py:33:18:33:25 | ControlFlowNode for full_url | provenance | Sink:MaD:27 | -| test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | test_http_client.py:1:26:1:32 | ControlFlowNode for request | provenance | | -| test_http_client.py:1:26:1:32 | ControlFlowNode for request | test_http_client.py:9:19:9:25 | ControlFlowNode for request | provenance | | -| test_http_client.py:1:26:1:32 | ControlFlowNode for request | test_http_client.py:10:19:10:25 | ControlFlowNode for request | provenance | | -| test_http_client.py:1:26:1:32 | ControlFlowNode for request | test_http_client.py:11:18:11:24 | ControlFlowNode for request | provenance | | +| full_partial_test.py:13:5:13:7 | ControlFlowNode for url | full_partial_test.py:15:18:15:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:20:5:20:7 | ControlFlowNode for url | full_partial_test.py:22:18:22:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:25:5:25:7 | ControlFlowNode for url | full_partial_test.py:27:18:27:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:41:5:41:14 | ControlFlowNode for user_input | full_partial_test.py:45:5:45:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:41:5:41:14 | ControlFlowNode for user_input | full_partial_test.py:49:5:49:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:41:5:41:14 | ControlFlowNode for user_input | full_partial_test.py:53:5:53:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:41:5:41:14 | ControlFlowNode for user_input | full_partial_test.py:57:5:57:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:41:5:41:14 | ControlFlowNode for user_input | full_partial_test.py:61:5:61:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:41:18:41:24 | ControlFlowNode for request | full_partial_test.py:41:5:41:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | +| full_partial_test.py:41:18:41:24 | ControlFlowNode for request | full_partial_test.py:42:5:42:13 | ControlFlowNode for query_val | provenance | AdditionalTaintStep | +| full_partial_test.py:42:5:42:13 | ControlFlowNode for query_val | full_partial_test.py:53:5:53:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:42:17:42:23 | ControlFlowNode for request | full_partial_test.py:42:5:42:13 | ControlFlowNode for query_val | provenance | AdditionalTaintStep | +| full_partial_test.py:45:5:45:7 | ControlFlowNode for url | full_partial_test.py:47:18:47:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:49:5:49:7 | ControlFlowNode for url | full_partial_test.py:51:18:51:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:53:5:53:7 | ControlFlowNode for url | full_partial_test.py:55:18:55:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:57:5:57:7 | ControlFlowNode for url | full_partial_test.py:59:18:59:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:61:5:61:7 | ControlFlowNode for url | full_partial_test.py:63:18:63:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:66:5:66:14 | ControlFlowNode for user_input | full_partial_test.py:70:5:70:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:66:5:66:14 | ControlFlowNode for user_input | full_partial_test.py:74:5:74:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:66:5:66:14 | ControlFlowNode for user_input | full_partial_test.py:78:5:78:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:66:18:66:24 | ControlFlowNode for request | full_partial_test.py:66:5:66:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | +| full_partial_test.py:66:18:66:24 | ControlFlowNode for request | full_partial_test.py:67:5:67:13 | ControlFlowNode for query_val | provenance | AdditionalTaintStep | +| full_partial_test.py:67:5:67:13 | ControlFlowNode for query_val | full_partial_test.py:78:5:78:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:67:17:67:23 | ControlFlowNode for request | full_partial_test.py:67:5:67:13 | ControlFlowNode for query_val | provenance | AdditionalTaintStep | +| full_partial_test.py:70:5:70:7 | ControlFlowNode for url | full_partial_test.py:72:18:72:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:74:5:74:7 | ControlFlowNode for url | full_partial_test.py:76:18:76:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:78:5:78:7 | ControlFlowNode for url | full_partial_test.py:80:18:80:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:83:5:83:14 | ControlFlowNode for user_input | full_partial_test.py:87:5:87:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:83:5:83:14 | ControlFlowNode for user_input | full_partial_test.py:91:5:91:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:83:5:83:14 | ControlFlowNode for user_input | full_partial_test.py:95:5:95:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:83:18:83:24 | ControlFlowNode for request | full_partial_test.py:83:5:83:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | +| full_partial_test.py:83:18:83:24 | ControlFlowNode for request | full_partial_test.py:84:5:84:13 | ControlFlowNode for query_val | provenance | AdditionalTaintStep | +| full_partial_test.py:84:5:84:13 | ControlFlowNode for query_val | full_partial_test.py:95:5:95:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:84:17:84:23 | ControlFlowNode for request | full_partial_test.py:84:5:84:13 | ControlFlowNode for query_val | provenance | AdditionalTaintStep | +| full_partial_test.py:87:5:87:7 | ControlFlowNode for url | full_partial_test.py:89:18:89:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:91:5:91:7 | ControlFlowNode for url | full_partial_test.py:93:18:93:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:95:5:95:7 | ControlFlowNode for url | full_partial_test.py:97:18:97:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:101:5:101:14 | ControlFlowNode for user_input | full_partial_test.py:103:5:103:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:101:18:101:24 | ControlFlowNode for request | full_partial_test.py:101:5:101:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | +| full_partial_test.py:103:5:103:7 | ControlFlowNode for url | full_partial_test.py:105:18:105:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:108:5:108:14 | ControlFlowNode for user_input | full_partial_test.py:110:5:110:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:108:18:108:24 | ControlFlowNode for request | full_partial_test.py:108:5:108:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | +| full_partial_test.py:110:5:110:7 | ControlFlowNode for url | full_partial_test.py:112:18:112:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:115:5:115:14 | ControlFlowNode for user_input | full_partial_test.py:117:5:117:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:115:18:115:24 | ControlFlowNode for request | full_partial_test.py:115:5:115:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | +| full_partial_test.py:117:5:117:7 | ControlFlowNode for url | full_partial_test.py:119:18:119:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:122:5:122:14 | ControlFlowNode for user_input | full_partial_test.py:124:5:124:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:122:18:122:24 | ControlFlowNode for request | full_partial_test.py:122:5:122:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | +| full_partial_test.py:124:5:124:7 | ControlFlowNode for url | full_partial_test.py:126:18:126:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:129:5:129:14 | ControlFlowNode for user_input | full_partial_test.py:134:5:134:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:129:18:129:24 | ControlFlowNode for request | full_partial_test.py:129:5:129:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | +| full_partial_test.py:134:5:134:7 | ControlFlowNode for url | full_partial_test.py:136:18:136:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:139:5:139:14 | ControlFlowNode for user_input | full_partial_test.py:141:5:141:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:139:18:139:24 | ControlFlowNode for request | full_partial_test.py:139:5:139:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | +| full_partial_test.py:141:5:141:7 | ControlFlowNode for url | full_partial_test.py:143:18:143:20 | ControlFlowNode for url | provenance | | +| test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | test_azure_client.py:6:19:6:25 | ControlFlowNode for request | provenance | | +| test_azure_client.py:6:19:6:25 | ControlFlowNode for request | test_azure_client.py:9:18:9:24 | ControlFlowNode for request | provenance | | +| test_azure_client.py:6:19:6:25 | ControlFlowNode for request | test_azure_client.py:10:19:10:25 | ControlFlowNode for request | provenance | | +| test_azure_client.py:9:5:9:14 | ControlFlowNode for user_input | test_azure_client.py:12:5:12:7 | ControlFlowNode for url | provenance | | +| test_azure_client.py:9:18:9:24 | ControlFlowNode for request | test_azure_client.py:9:5:9:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | +| test_azure_client.py:9:18:9:24 | ControlFlowNode for request | test_azure_client.py:10:5:10:15 | ControlFlowNode for user_input2 | provenance | AdditionalTaintStep | +| test_azure_client.py:10:5:10:15 | ControlFlowNode for user_input2 | test_azure_client.py:13:5:13:12 | ControlFlowNode for full_url | provenance | | +| test_azure_client.py:10:19:10:25 | ControlFlowNode for request | test_azure_client.py:10:5:10:15 | ControlFlowNode for user_input2 | provenance | AdditionalTaintStep | +| test_azure_client.py:12:5:12:7 | ControlFlowNode for url | test_azure_client.py:15:32:15:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_azure_client.py:12:5:12:7 | ControlFlowNode for url | test_azure_client.py:17:39:17:41 | ControlFlowNode for url | provenance | Sink:MaD:38 | +| test_azure_client.py:12:5:12:7 | ControlFlowNode for url | test_azure_client.py:19:19:19:21 | ControlFlowNode for url | provenance | Sink:MaD:14 | +| test_azure_client.py:12:5:12:7 | ControlFlowNode for url | test_azure_client.py:21:58:21:60 | ControlFlowNode for url | provenance | Sink:MaD:26 | +| test_azure_client.py:12:5:12:7 | ControlFlowNode for url | test_azure_client.py:24:37:24:39 | ControlFlowNode for url | provenance | Sink:MaD:27 | +| test_azure_client.py:13:5:13:12 | ControlFlowNode for full_url | test_azure_client.py:16:32:16:39 | ControlFlowNode for full_url | provenance | Sink:MaD:15 | +| test_azure_client.py:13:5:13:12 | ControlFlowNode for full_url | test_azure_client.py:18:39:18:46 | ControlFlowNode for full_url | provenance | Sink:MaD:38 | +| test_azure_client.py:13:5:13:12 | ControlFlowNode for full_url | test_azure_client.py:20:19:20:26 | ControlFlowNode for full_url | provenance | Sink:MaD:14 | +| test_azure_client.py:13:5:13:12 | ControlFlowNode for full_url | test_azure_client.py:22:58:22:65 | ControlFlowNode for full_url | provenance | Sink:MaD:26 | +| test_azure_client.py:13:5:13:12 | ControlFlowNode for full_url | test_azure_client.py:25:37:25:44 | ControlFlowNode for full_url | provenance | Sink:MaD:27 | +| test_http_client.py:1:19:1:25 | ControlFlowNode for ImportMember | test_http_client.py:1:19:1:25 | ControlFlowNode for request | provenance | | +| test_http_client.py:1:19:1:25 | ControlFlowNode for request | test_http_client.py:9:19:9:25 | ControlFlowNode for request | provenance | | +| test_http_client.py:1:19:1:25 | ControlFlowNode for request | test_http_client.py:10:19:10:25 | ControlFlowNode for request | provenance | | +| test_http_client.py:1:19:1:25 | ControlFlowNode for request | test_http_client.py:11:18:11:24 | ControlFlowNode for request | provenance | | | test_http_client.py:9:5:9:15 | ControlFlowNode for unsafe_host | test_http_client.py:13:27:13:37 | ControlFlowNode for unsafe_host | provenance | | -| test_http_client.py:9:5:9:15 | ControlFlowNode for unsafe_host | test_http_client.py:18:27:18:37 | ControlFlowNode for unsafe_host | provenance | | -| test_http_client.py:9:5:9:15 | ControlFlowNode for unsafe_host | test_http_client.py:25:27:25:37 | ControlFlowNode for unsafe_host | provenance | | +| test_http_client.py:9:5:9:15 | ControlFlowNode for unsafe_host | test_http_client.py:19:27:19:37 | ControlFlowNode for unsafe_host | provenance | | +| test_http_client.py:9:5:9:15 | ControlFlowNode for unsafe_host | test_http_client.py:28:27:28:37 | ControlFlowNode for unsafe_host | provenance | | | test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:9:5:9:15 | ControlFlowNode for unsafe_host | provenance | AdditionalTaintStep | | test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:10:5:10:15 | ControlFlowNode for unsafe_path | provenance | AdditionalTaintStep | | test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:11:5:11:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | -| test_http_client.py:10:5:10:15 | ControlFlowNode for unsafe_path | test_http_client.py:14:25:14:35 | ControlFlowNode for unsafe_path | provenance | | -| test_http_client.py:10:5:10:15 | ControlFlowNode for unsafe_path | test_http_client.py:19:25:19:35 | ControlFlowNode for unsafe_path | provenance | | -| test_http_client.py:10:5:10:15 | ControlFlowNode for unsafe_path | test_http_client.py:29:25:29:35 | ControlFlowNode for unsafe_path | provenance | | +| test_http_client.py:10:5:10:15 | ControlFlowNode for unsafe_path | test_http_client.py:15:25:15:35 | ControlFlowNode for unsafe_path | provenance | | +| test_http_client.py:10:5:10:15 | ControlFlowNode for unsafe_path | test_http_client.py:21:25:21:35 | ControlFlowNode for unsafe_path | provenance | | +| test_http_client.py:10:5:10:15 | ControlFlowNode for unsafe_path | test_http_client.py:34:25:34:35 | ControlFlowNode for unsafe_path | provenance | | | test_http_client.py:10:19:10:25 | ControlFlowNode for request | test_http_client.py:10:5:10:15 | ControlFlowNode for unsafe_path | provenance | AdditionalTaintStep | | test_http_client.py:10:19:10:25 | ControlFlowNode for request | test_http_client.py:11:5:11:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | -| test_http_client.py:11:5:11:14 | ControlFlowNode for user_input | test_http_client.py:31:5:31:8 | ControlFlowNode for path | provenance | | -| test_http_client.py:11:5:11:14 | ControlFlowNode for user_input | test_http_client.py:35:5:35:8 | ControlFlowNode for path | provenance | | +| test_http_client.py:11:5:11:14 | ControlFlowNode for user_input | test_http_client.py:36:5:36:8 | ControlFlowNode for path | provenance | | +| test_http_client.py:11:5:11:14 | ControlFlowNode for user_input | test_http_client.py:41:5:41:8 | ControlFlowNode for path | provenance | | | test_http_client.py:11:18:11:24 | ControlFlowNode for request | test_http_client.py:11:5:11:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | -| test_http_client.py:31:5:31:8 | ControlFlowNode for path | test_http_client.py:33:25:33:28 | ControlFlowNode for path | provenance | | -| test_http_client.py:35:5:35:8 | ControlFlowNode for path | test_http_client.py:37:25:37:28 | ControlFlowNode for path | provenance | | +| test_http_client.py:36:5:36:8 | ControlFlowNode for path | test_http_client.py:39:25:39:28 | ControlFlowNode for path | provenance | | +| test_http_client.py:41:5:41:8 | ControlFlowNode for path | test_http_client.py:44:25:44:28 | ControlFlowNode for path | provenance | | +| test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:5:19:5:25 | ControlFlowNode for request | provenance | | +| test_path_validation.py:5:19:5:25 | ControlFlowNode for request | test_path_validation.py:8:18:8:24 | ControlFlowNode for request | provenance | | +| test_path_validation.py:5:19:5:25 | ControlFlowNode for request | test_path_validation.py:9:19:9:25 | ControlFlowNode for request | provenance | | +| test_path_validation.py:5:19:5:25 | ControlFlowNode for request | test_path_validation.py:24:18:24:24 | ControlFlowNode for request | provenance | | +| test_path_validation.py:5:19:5:25 | ControlFlowNode for request | test_path_validation.py:25:19:25:25 | ControlFlowNode for request | provenance | | +| test_path_validation.py:5:19:5:25 | ControlFlowNode for request | test_path_validation.py:40:18:40:24 | ControlFlowNode for request | provenance | | +| test_path_validation.py:5:19:5:25 | ControlFlowNode for request | test_path_validation.py:41:19:41:25 | ControlFlowNode for request | provenance | | +| test_path_validation.py:5:19:5:25 | ControlFlowNode for request | test_path_validation.py:57:18:57:24 | ControlFlowNode for request | provenance | | +| test_path_validation.py:8:5:8:14 | ControlFlowNode for user_input | test_path_validation.py:10:5:10:7 | ControlFlowNode for url | provenance | | +| test_path_validation.py:8:18:8:24 | ControlFlowNode for request | test_path_validation.py:8:5:8:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | +| test_path_validation.py:8:18:8:24 | ControlFlowNode for request | test_path_validation.py:9:5:9:15 | ControlFlowNode for user_input2 | provenance | AdditionalTaintStep | +| test_path_validation.py:9:5:9:15 | ControlFlowNode for user_input2 | test_path_validation.py:11:5:11:12 | ControlFlowNode for full_url | provenance | | +| test_path_validation.py:9:19:9:25 | ControlFlowNode for request | test_path_validation.py:9:5:9:15 | ControlFlowNode for user_input2 | provenance | AdditionalTaintStep | +| test_path_validation.py:10:5:10:7 | ControlFlowNode for url | test_path_validation.py:14:36:14:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:10:5:10:7 | ControlFlowNode for url | test_path_validation.py:16:36:16:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:11:5:11:12 | ControlFlowNode for full_url | test_path_validation.py:19:36:19:43 | ControlFlowNode for full_url | provenance | Sink:MaD:15 | +| test_path_validation.py:11:5:11:12 | ControlFlowNode for full_url | test_path_validation.py:21:36:21:43 | ControlFlowNode for full_url | provenance | Sink:MaD:15 | +| test_path_validation.py:24:5:24:14 | ControlFlowNode for user_input | test_path_validation.py:26:5:26:7 | ControlFlowNode for url | provenance | | +| test_path_validation.py:24:18:24:24 | ControlFlowNode for request | test_path_validation.py:24:5:24:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | +| test_path_validation.py:24:18:24:24 | ControlFlowNode for request | test_path_validation.py:25:5:25:15 | ControlFlowNode for user_input2 | provenance | AdditionalTaintStep | +| test_path_validation.py:25:5:25:15 | ControlFlowNode for user_input2 | test_path_validation.py:27:5:27:12 | ControlFlowNode for full_url | provenance | | +| test_path_validation.py:25:19:25:25 | ControlFlowNode for request | test_path_validation.py:25:5:25:15 | ControlFlowNode for user_input2 | provenance | AdditionalTaintStep | +| test_path_validation.py:26:5:26:7 | ControlFlowNode for url | test_path_validation.py:30:33:30:35 | ControlFlowNode for url | provenance | Sink:MaD:14 | +| test_path_validation.py:26:5:26:7 | ControlFlowNode for url | test_path_validation.py:32:33:32:35 | ControlFlowNode for url | provenance | Sink:MaD:14 | +| test_path_validation.py:27:5:27:12 | ControlFlowNode for full_url | test_path_validation.py:35:33:35:40 | ControlFlowNode for full_url | provenance | Sink:MaD:14 | +| test_path_validation.py:27:5:27:12 | ControlFlowNode for full_url | test_path_validation.py:37:33:37:40 | ControlFlowNode for full_url | provenance | Sink:MaD:14 | +| test_path_validation.py:40:5:40:14 | ControlFlowNode for user_input | test_path_validation.py:42:5:42:7 | ControlFlowNode for url | provenance | | +| test_path_validation.py:40:18:40:24 | ControlFlowNode for request | test_path_validation.py:40:5:40:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | +| test_path_validation.py:40:18:40:24 | ControlFlowNode for request | test_path_validation.py:41:5:41:15 | ControlFlowNode for user_input2 | provenance | AdditionalTaintStep | +| test_path_validation.py:41:5:41:15 | ControlFlowNode for user_input2 | test_path_validation.py:43:5:43:12 | ControlFlowNode for full_url | provenance | | +| test_path_validation.py:41:19:41:25 | ControlFlowNode for request | test_path_validation.py:41:5:41:15 | ControlFlowNode for user_input2 | provenance | AdditionalTaintStep | +| test_path_validation.py:42:5:42:7 | ControlFlowNode for url | test_path_validation.py:46:43:46:45 | ControlFlowNode for url | provenance | Sink:MaD:38 | +| test_path_validation.py:42:5:42:7 | ControlFlowNode for url | test_path_validation.py:48:43:48:45 | ControlFlowNode for url | provenance | Sink:MaD:38 | +| test_path_validation.py:43:5:43:12 | ControlFlowNode for full_url | test_path_validation.py:51:43:51:50 | ControlFlowNode for full_url | provenance | Sink:MaD:38 | +| test_path_validation.py:43:5:43:12 | ControlFlowNode for full_url | test_path_validation.py:53:43:53:50 | ControlFlowNode for full_url | provenance | Sink:MaD:38 | +| test_path_validation.py:57:5:57:14 | ControlFlowNode for user_input | test_path_validation.py:61:5:61:7 | ControlFlowNode for url | provenance | | +| test_path_validation.py:57:18:57:24 | ControlFlowNode for request | test_path_validation.py:57:5:57:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:64:36:64:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:66:36:66:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:69:36:69:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:71:36:71:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:74:36:74:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:76:36:76:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:79:36:79:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:81:36:81:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | | test_requests.py:1:19:1:25 | ControlFlowNode for ImportMember | test_requests.py:1:19:1:25 | ControlFlowNode for request | provenance | | -| test_requests.py:1:19:1:25 | ControlFlowNode for request | test_requests.py:6:18:6:24 | ControlFlowNode for request | provenance | | -| test_requests.py:6:5:6:14 | ControlFlowNode for user_input | test_requests.py:8:18:8:27 | ControlFlowNode for user_input | provenance | | -| test_requests.py:6:18:6:24 | ControlFlowNode for request | test_requests.py:6:5:6:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | +| test_requests.py:1:19:1:25 | ControlFlowNode for request | test_requests.py:7:18:7:24 | ControlFlowNode for request | provenance | | +| test_requests.py:1:19:1:25 | ControlFlowNode for request | test_requests.py:14:18:14:24 | ControlFlowNode for request | provenance | | +| test_requests.py:1:19:1:25 | ControlFlowNode for request | test_requests.py:20:18:20:24 | ControlFlowNode for request | provenance | | +| test_requests.py:7:5:7:14 | ControlFlowNode for user_input | test_requests.py:9:18:9:27 | ControlFlowNode for user_input | provenance | | +| test_requests.py:7:18:7:24 | ControlFlowNode for request | test_requests.py:7:5:7:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | +| test_requests.py:14:5:14:14 | ControlFlowNode for user_input | test_requests.py:17:17:17:26 | ControlFlowNode for user_input | provenance | | +| test_requests.py:14:18:14:24 | ControlFlowNode for request | test_requests.py:14:5:14:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | +| test_requests.py:20:5:20:14 | ControlFlowNode for user_input | test_requests.py:22:45:22:54 | ControlFlowNode for user_input | provenance | | +| test_requests.py:20:18:20:24 | ControlFlowNode for request | test_requests.py:20:5:20:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | nodes | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | | full_partial_test.py:1:19:1:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | @@ -126,91 +201,91 @@ nodes | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | full_partial_test.py:8:5:8:13 | ControlFlowNode for query_val | semmle.label | ControlFlowNode for query_val | | full_partial_test.py:8:17:8:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| full_partial_test.py:10:18:10:27 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | -| full_partial_test.py:12:5:12:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:13:18:13:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:18:5:18:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:19:18:19:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:22:5:22:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:23:18:23:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:37:5:37:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | -| full_partial_test.py:37:18:37:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| full_partial_test.py:38:5:38:13 | ControlFlowNode for query_val | semmle.label | ControlFlowNode for query_val | -| full_partial_test.py:38:17:38:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| full_partial_test.py:41:5:41:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:42:18:42:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:44:5:44:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:45:18:45:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:47:5:47:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:48:18:48:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:50:5:50:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:11:18:11:27 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| full_partial_test.py:13:5:13:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:15:18:15:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:20:5:20:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:22:18:22:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:25:5:25:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:27:18:27:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:41:5:41:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| full_partial_test.py:41:18:41:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| full_partial_test.py:42:5:42:13 | ControlFlowNode for query_val | semmle.label | ControlFlowNode for query_val | +| full_partial_test.py:42:17:42:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| full_partial_test.py:45:5:45:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:47:18:47:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:49:5:49:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | | full_partial_test.py:51:18:51:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | | full_partial_test.py:53:5:53:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:54:18:54:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:57:5:57:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | -| full_partial_test.py:57:18:57:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| full_partial_test.py:58:5:58:13 | ControlFlowNode for query_val | semmle.label | ControlFlowNode for query_val | -| full_partial_test.py:58:17:58:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| full_partial_test.py:55:18:55:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:57:5:57:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:59:18:59:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | | full_partial_test.py:61:5:61:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:62:18:62:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:64:5:64:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:65:18:65:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:67:5:67:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:68:18:68:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:71:5:71:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | -| full_partial_test.py:71:18:71:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| full_partial_test.py:72:5:72:13 | ControlFlowNode for query_val | semmle.label | ControlFlowNode for query_val | -| full_partial_test.py:72:17:72:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| full_partial_test.py:75:5:75:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:63:18:63:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:66:5:66:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| full_partial_test.py:66:18:66:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| full_partial_test.py:67:5:67:13 | ControlFlowNode for query_val | semmle.label | ControlFlowNode for query_val | +| full_partial_test.py:67:17:67:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| full_partial_test.py:70:5:70:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:72:18:72:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:74:5:74:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | | full_partial_test.py:76:18:76:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | | full_partial_test.py:78:5:78:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:79:18:79:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:81:5:81:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:82:18:82:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:86:5:86:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | -| full_partial_test.py:86:18:86:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| full_partial_test.py:88:5:88:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:80:18:80:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:83:5:83:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| full_partial_test.py:83:18:83:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| full_partial_test.py:84:5:84:13 | ControlFlowNode for query_val | semmle.label | ControlFlowNode for query_val | +| full_partial_test.py:84:17:84:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| full_partial_test.py:87:5:87:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | | full_partial_test.py:89:18:89:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:92:5:92:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | -| full_partial_test.py:92:18:92:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| full_partial_test.py:94:5:94:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:95:18:95:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:98:5:98:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | -| full_partial_test.py:98:18:98:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| full_partial_test.py:100:5:100:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:101:18:101:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:104:5:104:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | -| full_partial_test.py:104:18:104:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| full_partial_test.py:106:5:106:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:107:18:107:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:110:5:110:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | -| full_partial_test.py:110:18:110:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| full_partial_test.py:115:5:115:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:116:18:116:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:119:5:119:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | -| full_partial_test.py:119:18:119:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| full_partial_test.py:121:5:121:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| full_partial_test.py:122:18:122:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | -| test_azure_client.py:7:19:7:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| test_azure_client.py:10:5:10:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | -| test_azure_client.py:10:18:10:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| test_azure_client.py:11:5:11:15 | ControlFlowNode for user_input2 | semmle.label | ControlFlowNode for user_input2 | -| test_azure_client.py:11:19:11:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| test_azure_client.py:13:5:13:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| test_azure_client.py:14:5:14:12 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | -| test_azure_client.py:16:32:16:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| test_azure_client.py:17:32:17:39 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | -| test_azure_client.py:18:39:18:41 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| test_azure_client.py:19:39:19:46 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | -| test_azure_client.py:20:19:20:21 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| test_azure_client.py:21:19:21:26 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | -| test_azure_client.py:22:58:22:60 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| test_azure_client.py:23:58:23:65 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | -| test_azure_client.py:27:18:27:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| test_azure_client.py:33:18:33:25 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | -| test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | -| test_http_client.py:1:26:1:32 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| full_partial_test.py:91:5:91:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:93:18:93:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:95:5:95:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:97:18:97:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:101:5:101:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| full_partial_test.py:101:18:101:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| full_partial_test.py:103:5:103:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:105:18:105:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:108:5:108:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| full_partial_test.py:108:18:108:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| full_partial_test.py:110:5:110:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:112:18:112:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:115:5:115:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| full_partial_test.py:115:18:115:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| full_partial_test.py:117:5:117:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:119:18:119:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:122:5:122:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| full_partial_test.py:122:18:122:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| full_partial_test.py:124:5:124:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:126:18:126:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:129:5:129:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| full_partial_test.py:129:18:129:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| full_partial_test.py:134:5:134:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:136:18:136:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:139:5:139:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| full_partial_test.py:139:18:139:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| full_partial_test.py:141:5:141:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:143:18:143:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | +| test_azure_client.py:6:19:6:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_azure_client.py:9:5:9:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| test_azure_client.py:9:18:9:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_azure_client.py:10:5:10:15 | ControlFlowNode for user_input2 | semmle.label | ControlFlowNode for user_input2 | +| test_azure_client.py:10:19:10:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_azure_client.py:12:5:12:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_azure_client.py:13:5:13:12 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_azure_client.py:15:32:15:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_azure_client.py:16:32:16:39 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_azure_client.py:17:39:17:41 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_azure_client.py:18:39:18:46 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_azure_client.py:19:19:19:21 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_azure_client.py:20:19:20:26 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_azure_client.py:21:58:21:60 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_azure_client.py:22:58:22:65 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_azure_client.py:24:37:24:39 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_azure_client.py:25:37:25:44 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_http_client.py:1:19:1:25 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | +| test_http_client.py:1:19:1:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | test_http_client.py:9:5:9:15 | ControlFlowNode for unsafe_host | semmle.label | ControlFlowNode for unsafe_host | | test_http_client.py:9:19:9:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | test_http_client.py:10:5:10:15 | ControlFlowNode for unsafe_path | semmle.label | ControlFlowNode for unsafe_path | @@ -218,36 +293,69 @@ nodes | test_http_client.py:11:5:11:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | | test_http_client.py:11:18:11:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | test_http_client.py:13:27:13:37 | ControlFlowNode for unsafe_host | semmle.label | ControlFlowNode for unsafe_host | -| test_http_client.py:14:25:14:35 | ControlFlowNode for unsafe_path | semmle.label | ControlFlowNode for unsafe_path | -| test_http_client.py:18:27:18:37 | ControlFlowNode for unsafe_host | semmle.label | ControlFlowNode for unsafe_host | -| test_http_client.py:19:25:19:35 | ControlFlowNode for unsafe_path | semmle.label | ControlFlowNode for unsafe_path | -| test_http_client.py:25:27:25:37 | ControlFlowNode for unsafe_host | semmle.label | ControlFlowNode for unsafe_host | -| test_http_client.py:29:25:29:35 | ControlFlowNode for unsafe_path | semmle.label | ControlFlowNode for unsafe_path | -| test_http_client.py:31:5:31:8 | ControlFlowNode for path | semmle.label | ControlFlowNode for path | -| test_http_client.py:33:25:33:28 | ControlFlowNode for path | semmle.label | ControlFlowNode for path | -| test_http_client.py:35:5:35:8 | ControlFlowNode for path | semmle.label | ControlFlowNode for path | -| test_http_client.py:37:25:37:28 | ControlFlowNode for path | semmle.label | ControlFlowNode for path | +| test_http_client.py:15:25:15:35 | ControlFlowNode for unsafe_path | semmle.label | ControlFlowNode for unsafe_path | +| test_http_client.py:19:27:19:37 | ControlFlowNode for unsafe_host | semmle.label | ControlFlowNode for unsafe_host | +| test_http_client.py:21:25:21:35 | ControlFlowNode for unsafe_path | semmle.label | ControlFlowNode for unsafe_path | +| test_http_client.py:28:27:28:37 | ControlFlowNode for unsafe_host | semmle.label | ControlFlowNode for unsafe_host | +| test_http_client.py:34:25:34:35 | ControlFlowNode for unsafe_path | semmle.label | ControlFlowNode for unsafe_path | +| test_http_client.py:36:5:36:8 | ControlFlowNode for path | semmle.label | ControlFlowNode for path | +| test_http_client.py:39:25:39:28 | ControlFlowNode for path | semmle.label | ControlFlowNode for path | +| test_http_client.py:41:5:41:8 | ControlFlowNode for path | semmle.label | ControlFlowNode for path | +| test_http_client.py:44:25:44:28 | ControlFlowNode for path | semmle.label | ControlFlowNode for path | +| test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | +| test_path_validation.py:5:19:5:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_path_validation.py:8:5:8:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| test_path_validation.py:8:18:8:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_path_validation.py:9:5:9:15 | ControlFlowNode for user_input2 | semmle.label | ControlFlowNode for user_input2 | +| test_path_validation.py:9:19:9:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_path_validation.py:10:5:10:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:11:5:11:12 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_path_validation.py:14:36:14:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:16:36:16:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:19:36:19:43 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_path_validation.py:21:36:21:43 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_path_validation.py:24:5:24:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| test_path_validation.py:24:18:24:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_path_validation.py:25:5:25:15 | ControlFlowNode for user_input2 | semmle.label | ControlFlowNode for user_input2 | +| test_path_validation.py:25:19:25:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_path_validation.py:26:5:26:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:27:5:27:12 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_path_validation.py:30:33:30:35 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:32:33:32:35 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:35:33:35:40 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_path_validation.py:37:33:37:40 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_path_validation.py:40:5:40:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| test_path_validation.py:40:18:40:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_path_validation.py:41:5:41:15 | ControlFlowNode for user_input2 | semmle.label | ControlFlowNode for user_input2 | +| test_path_validation.py:41:19:41:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_path_validation.py:42:5:42:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:43:5:43:12 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_path_validation.py:46:43:46:45 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:48:43:48:45 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:51:43:51:50 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_path_validation.py:53:43:53:50 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_path_validation.py:57:5:57:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| test_path_validation.py:57:18:57:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:64:36:64:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:66:36:66:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:69:36:69:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:71:36:71:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:74:36:74:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:76:36:76:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:79:36:79:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:81:36:81:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | | test_requests.py:1:19:1:25 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | | test_requests.py:1:19:1:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| test_requests.py:6:5:6:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | -| test_requests.py:6:18:6:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| test_requests.py:8:18:8:27 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| test_requests.py:7:5:7:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| test_requests.py:7:18:7:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_requests.py:9:18:9:27 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| test_requests.py:14:5:14:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| test_requests.py:14:18:14:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_requests.py:17:17:17:26 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| test_requests.py:20:5:20:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| test_requests.py:20:18:20:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test_requests.py:22:45:22:54 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | subpaths -#select -| full_partial_test.py:68:5:68:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:68:18:68:20 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | -| full_partial_test.py:89:5:89:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:89:18:89:20 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | -| full_partial_test.py:95:5:95:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:95:18:95:20 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | -| full_partial_test.py:101:5:101:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:101:18:101:20 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | -| full_partial_test.py:107:5:107:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:107:18:107:20 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | -| full_partial_test.py:116:5:116:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:116:18:116:20 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | -| full_partial_test.py:122:5:122:21 | ControlFlowNode for Attribute() | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:122:18:122:20 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | -| test_azure_client.py:16:9:16:58 | ControlFlowNode for SecretClient() | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | test_azure_client.py:16:32:16:34 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value | -| test_azure_client.py:18:9:18:42 | ControlFlowNode for Attribute() | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | test_azure_client.py:18:39:18:41 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value | -| test_azure_client.py:20:9:20:34 | ControlFlowNode for KeyClient() | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | test_azure_client.py:20:19:20:21 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value | -| test_azure_client.py:22:9:22:84 | ControlFlowNode for Attribute() | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | test_azure_client.py:22:58:22:60 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value | -| test_azure_client.py:26:5:31:5 | ControlFlowNode for download_blob_from_url() | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | test_azure_client.py:27:18:27:20 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value | -| test_http_client.py:22:5:22:31 | ControlFlowNode for Attribute() | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | test_http_client.py:18:27:18:37 | ControlFlowNode for unsafe_host | Part of the URL of this request depends on a $@. | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value | -| test_http_client.py:26:5:26:31 | ControlFlowNode for Attribute() | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | test_http_client.py:25:27:25:37 | ControlFlowNode for unsafe_host | Part of the URL of this request depends on a $@. | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value | -| test_http_client.py:29:5:29:36 | ControlFlowNode for Attribute() | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | test_http_client.py:29:25:29:35 | ControlFlowNode for unsafe_path | Part of the URL of this request depends on a $@. | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value | -| test_http_client.py:33:5:33:29 | ControlFlowNode for Attribute() | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | test_http_client.py:33:25:33:28 | ControlFlowNode for path | Part of the URL of this request depends on a $@. | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value | -| test_http_client.py:37:5:37:29 | ControlFlowNode for Attribute() | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | test_http_client.py:37:25:37:28 | ControlFlowNode for path | Part of the URL of this request depends on a $@. | test_http_client.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value | +testFailures +| test_requests.py:1:27:1:36 | Comment # $ Source | Missing result: Source | diff --git a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/PartialServerSideRequestForgery.qlref b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/PartialServerSideRequestForgery.qlref index 88de9285e5e9..651b07aea55b 100644 --- a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/PartialServerSideRequestForgery.qlref +++ b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/PartialServerSideRequestForgery.qlref @@ -1 +1,2 @@ -Security/CWE-918/PartialServerSideRequestForgery.ql +query: Security/CWE-918/PartialServerSideRequestForgery.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/full_partial_test.py b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/full_partial_test.py index 95ff9d64944e..e2f4fc050262 100644 --- a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/full_partial_test.py +++ b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/full_partial_test.py @@ -1,26 +1,30 @@ -from flask import request +from flask import request # $ Source -import requests +import requests import re def full_ssrf(): user_input = request.args['untrusted_input'] query_val = request.args['query_val'] - requests.get(user_input) # NOT OK -- user has full control + # NOT OK -- user has full control + requests.get(user_input) # $ Alert[py/full-ssrf] url = "https://" + user_input - requests.get(url) # NOT OK -- user has full control + # NOT OK -- user has full control + requests.get(url) # $ Alert[py/full-ssrf] # although the path `/foo` is added here, this can be circumvented such that the # final URL is `https://evil.com/#/foo" -- since the fragment (#) is not sent to the # server. url = "https://" + user_input + "/foo" - requests.get(url) # NOT OK -- user has full control + # NOT OK -- user has full control + requests.get(url) # $ Alert[py/full-ssrf] # this might seem like a dummy test, but it serves to check how our sanitizers work. url = "https://" + user_input + "/foo?key=" + query_val - requests.get(url) # NOT OK -- user has full control + # NOT OK -- user has full control + requests.get(url) # $ Alert[py/full-ssrf] # taint-steps are added as `fromNode -> toNode`, but when adding a sanitizer it's # currently only possible to so on either `fromNode` or `toNode` (either all edges in @@ -39,19 +43,24 @@ def full_ssrf_format(): # using .format url = "https://{}".format(user_input) - requests.get(url) # NOT OK -- user has full control + # NOT OK -- user has full control + requests.get(url) # $ Alert[py/full-ssrf] url = "https://{}/foo".format(user_input) - requests.get(url) # NOT OK -- user has full control + # NOT OK -- user has full control + requests.get(url) # $ Alert[py/full-ssrf] url = "https://{}/foo?key={}".format(user_input, query_val) - requests.get(url) # NOT OK -- user has full control + # NOT OK -- user has full control + requests.get(url) # $ Alert[py/full-ssrf] url = "https://{x}".format(x=user_input) - requests.get(url) # NOT OK -- user has full control + # NOT OK -- user has full control + requests.get(url) # $ Alert[py/full-ssrf] url = "https://{1}".format(0, user_input) - requests.get(url) # NOT OK -- user has full control + # NOT OK -- user has full control + requests.get(url) # $ Alert[py/full-ssrf] def full_ssrf_percent_format(): user_input = request.args['untrusted_input'] @@ -59,13 +68,16 @@ def full_ssrf_percent_format(): # using %-formatting url = "https://%s" % user_input - requests.get(url) # NOT OK -- user has full control + # NOT OK -- user has full control + requests.get(url) # $ Alert[py/full-ssrf] url = "https://%s/foo" % user_input - requests.get(url) # NOT OK -- user has full control + # NOT OK -- user has full control + requests.get(url) # $ Alert[py/full-ssrf] url = "https://%s/foo/key=%s" % (user_input, query_val) - requests.get(url) # NOT OK -- user has full control + # NOT OK -- user has full and partial control + requests.get(url) # $ Alert[py/partial-ssrf] $ MISSING: Alert[py/full-ssrf] def full_ssrf_f_strings(): user_input = request.args['untrusted_input'] @@ -73,38 +85,45 @@ def full_ssrf_f_strings(): # using f-strings url = f"https://{user_input}" - requests.get(url) # NOT OK -- user has full control + # NOT OK -- user has full control + requests.get(url) # $ Alert[py/full-ssrf] url = f"https://{user_input}/foo" - requests.get(url) # NOT OK -- user has full control + # NOT OK -- user has full control + requests.get(url) # $ Alert[py/full-ssrf] url = f"https://{user_input}/foo?key={query_val}" - requests.get(url) # NOT OK -- user has full control + # NOT OK -- user has full control + requests.get(url) # $ Alert[py/full-ssrf] def partial_ssrf_1(): user_input = request.args['untrusted_input'] url = "https://example.com/foo?" + user_input - requests.get(url) # NOT OK -- user controls query parameters + # NOT OK -- user controls query parameters + requests.get(url) # $ Alert[py/partial-ssrf] def partial_ssrf_2(): user_input = request.args['untrusted_input'] url = "https://example.com/" + user_input - requests.get(url) # NOT OK -- user controls path + # NOT OK -- user controls path + requests.get(url) # $ Alert[py/partial-ssrf] def partial_ssrf_3(): user_input = request.args['untrusted_input'] url = "https://example.com/" + user_input - requests.get(url) # NOT OK -- user controls path + # NOT OK -- user controls path + requests.get(url) # $ Alert[py/partial-ssrf] def partial_ssrf_4(): user_input = request.args['untrusted_input'] url = "https://example.com/foo#{}".format(user_input) - requests.get(url) # NOT OK -- user contollred fragment + # NOT OK -- user contollred fragment + requests.get(url) # $ Alert[py/partial-ssrf] def partial_ssrf_5(): user_input = request.args['untrusted_input'] @@ -113,20 +132,22 @@ def partial_ssrf_5(): # controlled url = "https://example.com/foo#%s" % user_input - requests.get(url) # NOT OK -- user contollred fragment + # NOT OK -- user contollred fragment + requests.get(url) # $ Alert[py/partial-ssrf] def partial_ssrf_6(): user_input = request.args['untrusted_input'] url = f"https://example.com/foo#{user_input}" - requests.get(url) # NOT OK -- user only controlled fragment + # NOT OK -- user only controlled fragment + requests.get(url) # $ Alert[py/partial-ssrf] def partial_ssrf_7(): user_input = request.args['untrusted_input'] if user_input.isalnum(): url = f"https://example.com/foo#{user_input}" - requests.get(url) # OK - user input can only contain alphanumerical characters + requests.get(url) # OK - user input can only contain alphanumerical characters if user_input.isalpha(): url = f"https://example.com/foo#{user_input}" @@ -154,7 +175,8 @@ def partial_ssrf_7(): if re.fullmatch(r'.*[a-zA-Z0-9]+.*', user_input): url = f"https://example.com/foo#{user_input}" - requests.get(url) # NOT OK, but NOT FOUND - user input can contain arbitrary characters + # NOT OK, but NOT FOUND - user input can contain arbitrary characters + requests.get(url) # $ MISSING: Alert[py/partial-ssrf] if re.match(r'^[a-zA-Z0-9]+$', user_input): @@ -163,7 +185,8 @@ def partial_ssrf_7(): if re.match(r'[a-zA-Z0-9]+', user_input): url = f"https://example.com/foo#{user_input}" - requests.get(url) # NOT OK, but NOT FOUND - user input can contain arbitrary character as a suffix. + # NOT OK, but NOT FOUND - user input can contain arbitrary character as a suffix. + requests.get(url) # $ MISSING: Alert[py/partial-ssrf] reg = re.compile(r'^[a-zA-Z0-9]+$') diff --git a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_azure_client.py b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_azure_client.py index d8de2092a2e5..28570b7ab4a4 100644 --- a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_azure_client.py +++ b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_azure_client.py @@ -3,8 +3,7 @@ from azure.keyvault.keys import KeyClient from azure.storage.blob import ContainerClient from azure.storage.blob import download_blob_from_url - -from flask import request +from flask import request # $ Source def azure_sdk_test(credential, output_path): user_input = request.args['untrusted_input'] @@ -13,24 +12,214 @@ def azure_sdk_test(credential, output_path): url = f"https://example.com/foo#{user_input}" full_url = f"https://{user_input2}" # Testing Azure sink - c = SecretClient(vault_url=url, credential=credential)# NOT OK -- user only controlled fragment - c = SecretClient(vault_url=full_url, credential=credential) # NOT OK -- user has full control - c = ShareFileClient.from_file_url(url) # NOT OK -- user only controlled fragment - c = ShareFileClient.from_file_url(full_url) # NOT OK -- user has full control - c = KeyClient(url, credential)# NOT OK -- user only controlled fragment - c = KeyClient(full_url, credential) # NOT OK -- user has full control - c = ContainerClient.from_container_url(container_url=url, credential=credential) # NOT OK -- user only controlled fragment - c = ContainerClient.from_container_url(container_url=full_url, credential=credential) # NOT OK -- user has full control - - download_blob_from_url( - blob_url=url, # NOT OK -- user only controlled fragment - output=output_path, - credential=credential, - overwrite=True - ) - download_blob_from_url( - blob_url=full_url, # NOT OK -- user has full control - output=output_path, - credential=credential, - overwrite=True - ) + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] + c = SecretClient(vault_url=full_url, credential=credential) # $ Alert[py/full-ssrf] + c = ShareFileClient.from_file_url(url) # $ Alert[py/partial-ssrf] + c = ShareFileClient.from_file_url(full_url) # $ Alert[py/full-ssrf] + c = KeyClient(url, credential) # $ Alert[py/partial-ssrf] + c = KeyClient(full_url, credential) # $ Alert[py/full-ssrf] + c = ContainerClient.from_container_url(container_url=url, credential=credential) # $ Alert[py/partial-ssrf] + c = ContainerClient.from_container_url(container_url=full_url, credential=credential) # $ Alert[py/full-ssrf] + + download_blob_from_url(blob_url=url, output=output_path, credential=credential, overwrite=True ) # $ Alert[py/partial-ssrf] + download_blob_from_url(blob_url=full_url, output=output_path, credential=credential, overwrite=True) # $ Alert[py/full-ssrf] + +# if URIValidator.in_domain(url, trusted_domain): +# # Testing Azure sink +# c = SecretClient(vault_url=url, credential=credential)# OK +# c = ShareFileClient.from_file_url(url) # OK +# c = KeyClient(url, credential)# OK +# c = ContainerClient.from_container_url(container_url=url, credential=credential) # OK + +# download_blob_from_url( +# blob_url=url, # OK +# output=output_path, +# credential=credential, +# overwrite=True +# ) +# else: +# # Testing Azure sink +# c = SecretClient(vault_url=url, credential=credential)# NOT OK -- user only controlled fragment +# c = ShareFileClient.from_file_url(url) # NOT OK -- user only controlled fragment +# c = KeyClient(url, credential)# NOT OK -- user only controlled fragment +# c = ContainerClient.from_container_url(container_url=url, credential=credential) # NOT OK -- user only controlled fragment + +# download_blob_from_url( +# blob_url=url, # NOT OK -- user only controlled fragment +# output=output_path, +# credential=credential, +# overwrite=True +# ) + + +# if URIValidator.in_domain(full_url, trusted_domain): +# # Testing Azure sink +# c = SecretClient(vault_url=full_url, credential=credential) # OK +# c = ShareFileClient.from_file_url(full_url) # OK +# c = KeyClient(full_url, credential) # OK +# c = ContainerClient.from_container_url(container_url=full_url, credential=credential) # OK + +# download_blob_from_url( +# blob_url=full_url, # OK +# output=output_path, +# credential=credential, +# overwrite=True +# ) +# else: +# # Testing Azure sink +# c = SecretClient(vault_url=full_url, credential=credential) # NOT OK -- user has full control +# c = ShareFileClient.from_file_url(full_url) # NOT OK -- user has full control +# c = KeyClient(full_url, credential) # NOT OK -- user has full control +# c = ContainerClient.from_container_url(container_url=full_url, credential=credential) # NOT OK -- user has full control + +# download_blob_from_url( +# blob_url=full_url, # NOT OK -- user has full control +# output=output_path, +# credential=credential, +# overwrite=True +# ) + + +# if URIValidator.in_azure_keyvault_domain(url): +# # Testing Azure sink +# c = SecretClient(vault_url=url, credential=credential)# OK +# c = ShareFileClient.from_file_url(url) # OK +# c = KeyClient(url, credential)# OK +# c = ContainerClient.from_container_url(container_url=url, credential=credential) # OK + +# download_blob_from_url( +# blob_url=url, # OK +# output=output_path, +# credential=credential, +# overwrite=True +# ) +# else: +# # Testing Azure sink +# c = SecretClient(vault_url=url, credential=credential)# NOT OK -- user only controlled fragment +# c = ShareFileClient.from_file_url(url) # NOT OK -- user only controlled fragment +# c = KeyClient(url, credential)# NOT OK -- user only controlled fragment +# c = ContainerClient.from_container_url(container_url=url, credential=credential) # NOT OK -- user only controlled fragment + +# download_blob_from_url( +# blob_url=url, # NOT OK -- user only controlled fragment +# output=output_path, +# credential=credential, +# overwrite=True +# ) + + +# if URIValidator.in_azure_keyvault_domain(full_url): +# # Testing Azure sink +# c = SecretClient(vault_url=full_url, credential=credential) # OK +# c = ShareFileClient.from_file_url(full_url) # OK +# c = KeyClient(full_url, credential) # OK +# c = ContainerClient.from_container_url(container_url=full_url, credential=credential) # OK + +# download_blob_from_url( +# blob_url=full_url, # OK +# output=output_path, +# credential=credential, +# overwrite=True +# ) +# else: +# # Testing Azure sink +# c = SecretClient(vault_url=full_url, credential=credential) # NOT OK -- user has full control +# c = ShareFileClient.from_file_url(full_url) # NOT OK -- user has full control +# c = KeyClient(full_url, credential) # NOT OK -- user has full control +# c = ContainerClient.from_container_url(container_url=full_url, credential=credential) # NOT OK -- user has full control + +# download_blob_from_url( +# blob_url=full_url, # NOT OK -- user has full control +# output=output_path, +# credential=credential, +# overwrite=True +# ) + +# if URIValidator.in_azure_storage_domain(url): +# # Testing Azure sink +# c = SecretClient(vault_url=url, credential=credential)# OK +# c = ShareFileClient.from_file_url(url) # OK +# c = KeyClient(url, credential)# OK +# c = ContainerClient.from_container_url(container_url=url, credential=credential) # OK + +# download_blob_from_url( +# blob_url=url, # OK +# output=output_path, +# credential=credential, +# overwrite=True +# ) +# else: +# # Testing Azure sink +# c = SecretClient(vault_url=url, credential=credential)# NOT OK -- user only controlled fragment +# c = ShareFileClient.from_file_url(url) # NOT OK -- user only controlled fragment +# c = KeyClient(url, credential)# NOT OK -- user only controlled fragment +# c = ContainerClient.from_container_url(container_url=url, credential=credential) # NOT OK -- user only controlled fragment + +# download_blob_from_url( +# blob_url=url, # NOT OK -- user only controlled fragment +# output=output_path, +# credential=credential, +# overwrite=True +# ) + + +# if URIValidator.in_azure_storage_domain(full_url): +# # Testing Azure sink +# c = SecretClient(vault_url=full_url, credential=credential) # OK +# c = ShareFileClient.from_file_url(full_url) # OK +# c = KeyClient(full_url, credential) # OK +# c = ContainerClient.from_container_url(container_url=full_url, credential=credential) # OK + +# download_blob_from_url( +# blob_url=full_url, # OK +# output=output_path, +# credential=credential, +# overwrite=True +# ) +# else: +# # Testing Azure sink +# c = SecretClient(vault_url=full_url, credential=credential) # NOT OK -- user has full control +# c = ShareFileClient.from_file_url(full_url) # NOT OK -- user has full control +# c = KeyClient(full_url, credential) # NOT OK -- user has full control +# c = ContainerClient.from_container_url(container_url=full_url, credential=credential) # NOT OK -- user has full control + +# download_blob_from_url( +# blob_url=full_url, # NOT OK -- user has full control +# output=output_path, +# credential=credential, +# overwrite=True +# ) + +# def azure_sdk_logic_sanity_test(credential, output_path, trusted_domain): +# user_input = request.args['untrusted_input'] +# full_url = f"https://{user_input}" +# if not URIValidator.in_azure_storage_domain(full_url): +# # Testing Azure sink +# c = SecretClient(vault_url=full_url, credential=credential) # NOT OK -- user has full control +# else: +# # Testing Azure sink +# c = SecretClient(vault_url=full_url, credential=credential) # OK + + + +# if not not URIValidator.in_azure_storage_domain(full_url): +# # Testing Azure sink +# c = SecretClient(vault_url=full_url, credential=credential) # OK +# else: +# # Testing Azure sink +# c = SecretClient(vault_url=full_url, credential=credential) # NOT OK -- user has full control + + +# if URIValidator.URIValidator.in_domain(full_url, trusted_domain) and trusted_domain == "example.com": +# # Testing Azure sink +# c = SecretClient(vault_url=full_url, credential=credential) # OK +# else: +# # Testing Azure sink +# c = SecretClient(vault_url=full_url, credential=credential) # NOT OK -- user has full control + +# if not (URIValidator.URIValidator.in_domain(full_url, trusted_domain) and trusted_domain == "example.com"): +# # Testing Azure sink +# c = SecretClient(vault_url=full_url, credential=credential) # NOT OK -- user has full control +# else: +# # Testing Azure sink +# c = SecretClient(vault_url=full_url, credential=credential) # OK \ No newline at end of file diff --git a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_http_client.py b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_http_client.py index c833907d8430..0a238878e35e 100644 --- a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_http_client.py +++ b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_http_client.py @@ -1,5 +1,5 @@ -from flask import Flask, request - +from flask import request # $ Source +from flask import Flask from http.client import HTTPConnection app = Flask(__name__) @@ -10,28 +10,35 @@ def ssrf_test(): unsafe_path = request.args["path"] user_input = request.args['untrusted_input'] - conn = HTTPConnection(unsafe_host) - conn.request("GET", unsafe_path) # NOT OK -- user has full control + conn = HTTPConnection(unsafe_host) # $ Sink[py/full-ssrf] + # NOT OK -- user has full control + conn.request("GET", unsafe_path) # $ Alert[py/full-ssrf] - # Full SSRF variant, where there is ALSO made a request with fixed URL on the same + # Full SSRF variant, where there is AlSO made a request with fixed URL on the same # connection later on. This should not change anything on the overall SSRF alerts. - conn = HTTPConnection(unsafe_host) - conn.request("GET", unsafe_path) # NOT OK -- user has full control + conn = HTTPConnection(unsafe_host) # $ Sink + # NOT OK -- user has full control + conn.request("GET", unsafe_path) # $ Alert[py/full-ssrf] # partial SSRF on SAME connection - conn.request("GET", "/foo") # NOT OK -- user has control of host + # NOT OK -- user has control of host + conn.request("GET", "/foo") # $ Alert[py/partial-ssrf] # the rest are partial SSRF - conn = HTTPConnection(unsafe_host) - conn.request("GET", "/foo") # NOT OK -- user controlled domain + conn = HTTPConnection(unsafe_host) # $ Sink[py/partial-ssrf] + # NOT OK -- user controlled domain + conn.request("GET", "/foo") # $ Alert[py/partial-ssrf] conn = HTTPConnection("example.com") - conn.request("GET", unsafe_path) # NOT OK -- user controlled path + # NOT OK -- user controlled path + conn.request("GET", unsafe_path) # $ Alert[py/partial-ssrf] path = "foo?" + user_input conn = HTTPConnection("example.com") - conn.request("GET", path) # NOT OK -- user controlled query parameters + # NOT OK -- user controlled query parameters + conn.request("GET", path) # $ Alert[py/partial-ssrf] path = "foo#" + user_input conn = HTTPConnection("example.com") - conn.request("GET", path) # NOT OK -- user controlled fragment + # NOT OK -- user controlled fragment + conn.request("GET", path) # $ Alert[py/partial-ssrf] \ No newline at end of file diff --git a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_path_validation.py b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_path_validation.py new file mode 100644 index 000000000000..44c569f625d0 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_path_validation.py @@ -0,0 +1,132 @@ +from azure.keyvault.secrets import SecretClient +from azure.storage.fileshare import ShareFileClient +from azure.keyvault.keys import KeyClient +from AntiSSRF import URIValidator +from flask import request # $ Source + +def urivalidator_path_in_domain_validation(credential, trusted_domain): + user_input = request.args['untrusted_input'] + user_input2 = request.args['untrusted_input2'] + url = f"https://example.com/foo#{user_input}" + full_url = f"https://{user_input2}" + + if URIValidator.in_domain(url, trusted_domain): + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] + else: + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] + + if URIValidator.in_domain(full_url, trusted_domain): + c = SecretClient(vault_url=full_url, credential=credential) # OK + else: + c = SecretClient(vault_url=full_url, credential=credential) # $ Alert[py/full-ssrf] + +def urivalidator_path_in_azure_keyvault_domain_validation(credential): + user_input = request.args['untrusted_input'] + user_input2 = request.args['untrusted_input2'] + url = f"https://example.com/foo#{user_input}" + full_url = f"https://{user_input2}" + + if URIValidator.in_azure_keyvault_domain(url): + c = KeyClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] + else: + c = KeyClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] + + if URIValidator.in_azure_keyvault_domain(full_url): + c = KeyClient(vault_url=full_url, credential=credential) # OK + else: + c = KeyClient(vault_url=full_url, credential=credential) # $ Alert[py/full-ssrf] + +def urivalidator_path_in_azure_storage_domain_validation(credential): + user_input = request.args['untrusted_input'] + user_input2 = request.args['untrusted_input2'] + url = f"https://example.com/foo#{user_input}" + full_url = f"https://{user_input2}" + + if URIValidator.in_azure_storage_domain(url): + c = ShareFileClient.from_file_url(url) # $ Alert[py/partial-ssrf] + else: + c = ShareFileClient.from_file_url(url) # $ Alert[py/partial-ssrf] + + if URIValidator.in_azure_storage_domain(full_url): + c = ShareFileClient.from_file_url(full_url) # OK + else: + c = ShareFileClient.from_file_url(full_url) # $ Alert[py/full-ssrf] + + +def complex_urivalidator_checks(credential, trusted_domain): + user_input = request.args['untrusted_input'] + # Focus on in_domain only here for simplicity + # It assumed the logic underlying checking paths would apply + # similarly other validator methods + url = f"https://{user_input}" + + if not URIValidator.in_domain(url, trusted_domain): + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] + else: + c = SecretClient(vault_url=url, credential=credential) # OK + + if URIValidator.in_domain(url, trusted_domain) and trusted_domain == "example.com": + c = SecretClient(vault_url=url, credential=credential) # OK + else: + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] + + if not (URIValidator.in_domain(url, trusted_domain) and trusted_domain == "example.com"): + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] + else: + c = SecretClient(vault_url=url, credential=credential) # OK + + if not not not URIValidator.in_domain(url, trusted_domain): + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] + else: + c = SecretClient(vault_url=url, credential=credential) # OK + + + if URIValidator.in_domain(url, trusted_domain) == True: + c = SecretClient(vault_url=url, credential=credential) # OK + else: + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] + + if URIValidator.in_domain(url, trusted_domain) == False: + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] + else: + c = SecretClient(vault_url=url, credential=credential) # OK + + if URIValidator.in_domain(url, trusted_domain) != True: + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] + else: + c = SecretClient(vault_url=url, credential=credential) # OK + + if URIValidator.in_domain(url, trusted_domain) != False: + c = SecretClient(vault_url=url, credential=credential) # OK + else: + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] + + if URIValidator.in_domain(url, trusted_domain) is True: + c = SecretClient(vault_url=url, credential=credential) # OK + else: + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] + + if URIValidator.in_domain(url, trusted_domain) is False: + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] + else: + c = SecretClient(vault_url=url, credential=credential) # OK + + if URIValidator.in_domain(url, trusted_domain) is not True: + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] + else: + c = SecretClient(vault_url=url, credential=credential) # OK + + if URIValidator.in_domain(url, trusted_domain) is not False: + c = SecretClient(vault_url=url, credential=credential) # OK + else: + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] + + if not URIValidator.in_domain(url, trusted_domain) is True: + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] + else: + c = SecretClient(vault_url=url, credential=credential) # OK + + if not URIValidator.in_domain(url, trusted_domain) is False: + c = SecretClient(vault_url=url, credential=credential) # OK + else: + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] \ No newline at end of file diff --git a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_requests.py b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_requests.py index 443f5c3b81fe..cfa2e6aa5a70 100644 --- a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_requests.py +++ b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_requests.py @@ -1,11 +1,49 @@ -from flask import request +from flask import request # $ Source +from AntiSSRF import AntiSSRFPolicy import requests -def ssrf_test(): +def ssrf_test1(): user_input = request.args['untrusted_input'] - - requests.get(user_input) # NOT OK -- user has full control - + # NOT OK -- user has full control + requests.get(user_input) # $ Alert[py/full-ssrf] # since `requests`` always uses complete URLs, it's not interesting to test more of # the framework directly. See `full_partial_test.py` for different ways to do SSRF. + +def ssrf_test2(): + user_input = request.args['untrusted_input'] + # NOT OK -- user has full control + session = requests.Session() + session.get(user_input) # $ Alert[py/full-ssrf] + +def ssrf_test3(): + user_input = request.args['untrusted_input'] + # NOT OK -- user has full control + response = requests.request('', user_input) # $ Alert[py/full-ssrf] + +def ssrf_test_with_policy1(): + user_input = request.args['untrusted_input'] + policy = AntiSSRFPolicy() + session = policy.get_antissrf_session() + # OK -- dangerous user input is filtered by AntiSSRFPolicy + response = session.get(user_input) + +def ssrf_test_with_policy2(): + user_input = request.args['untrusted_input'] + policy = AntiSSRFPolicy() + session = policy.get_antissrf_session() + # overwriting the HTTPAdapter to default requests adapter + # this makes the session unsafe again + session.mount("http://", requests.adapters.HTTPAdapter()) + # NOT OK -- dangerous user input is no longer filtered by AntiSSRFPolicy + response = session.get(user_input) # $ Alert[py/full-ssrf] + +def ssrf_test_with_policy3(adapter): + user_input = request.args['untrusted_input'] + policy = AntiSSRFPolicy() + session = policy.get_antissrf_session() + # overwriting the HTTPAdapter to a custom requests adapter + # this could make the session unsafe again + session.mount("http://", adapter) + # NOT OK -- dangerous user input is no longer filtered by AntiSSRFPolicy + response = session.get(user_input) # $ Alert[py/full-ssrf] \ No newline at end of file From b62c16027e16af42238ef993bcf081f40dd1cfcd Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 2 Feb 2026 16:21:34 -0500 Subject: [PATCH 13/14] Updating test case expected alerts. --- .../PartialServerSideRequestForgery.expected | 57 +++++++++++++++++++ .../test_path_validation.py | 34 +++++------ 2 files changed, 74 insertions(+), 17 deletions(-) diff --git a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/PartialServerSideRequestForgery.expected b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/PartialServerSideRequestForgery.expected index 875d8b62b62f..0d3f49c200eb 100644 --- a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/PartialServerSideRequestForgery.expected +++ b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/PartialServerSideRequestForgery.expected @@ -18,10 +18,27 @@ | test_http_client.py:44:5:44:29 | ControlFlowNode for Attribute() | test_http_client.py:1:19:1:25 | ControlFlowNode for ImportMember | test_http_client.py:44:25:44:28 | ControlFlowNode for path | Part of the URL of this request depends on a $@. | test_http_client.py:1:19:1:25 | ControlFlowNode for ImportMember | user-provided value | | test_path_validation.py:14:13:14:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:14:36:14:38 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | | test_path_validation.py:16:13:16:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:16:36:16:38 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:19:13:19:67 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:19:36:19:43 | ControlFlowNode for full_url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | | test_path_validation.py:30:13:30:59 | ControlFlowNode for KeyClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:30:33:30:35 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | | test_path_validation.py:32:13:32:59 | ControlFlowNode for KeyClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:32:33:32:35 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:35:13:35:64 | ControlFlowNode for KeyClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:35:33:35:40 | ControlFlowNode for full_url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | | test_path_validation.py:46:13:46:46 | ControlFlowNode for Attribute() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:46:43:46:45 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | | test_path_validation.py:48:13:48:46 | ControlFlowNode for Attribute() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:48:43:48:45 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:51:13:51:51 | ControlFlowNode for Attribute() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:51:43:51:50 | ControlFlowNode for full_url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:66:13:66:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:66:36:66:38 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:69:13:69:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:69:36:69:38 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:76:13:76:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:76:36:76:38 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:81:13:81:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:81:36:81:38 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:85:13:85:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:85:36:85:38 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:92:13:92:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:92:36:92:38 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:97:13:97:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:97:36:97:38 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:100:13:100:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:100:36:100:38 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:105:13:105:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:105:36:105:38 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:112:13:112:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:112:36:112:38 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:117:13:117:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:117:36:117:38 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:120:13:120:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:120:36:120:38 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:127:13:127:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:127:36:127:38 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | +| test_path_validation.py:130:13:130:62 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:130:36:130:38 | ControlFlowNode for url | Part of the URL of this request depends on a $@. | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | user-provided value | edges | full_partial_test.py:1:19:1:25 | ControlFlowNode for ImportMember | full_partial_test.py:1:19:1:25 | ControlFlowNode for request | provenance | | | full_partial_test.py:1:19:1:25 | ControlFlowNode for request | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | provenance | | @@ -184,6 +201,26 @@ edges | test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:76:36:76:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | | test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:79:36:79:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | | test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:81:36:81:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:85:36:85:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:87:36:87:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:90:36:90:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:92:36:92:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:95:36:95:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:97:36:97:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:100:36:100:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:102:36:102:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:105:36:105:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:107:36:107:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:110:36:110:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:112:36:112:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:115:36:115:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:117:36:117:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:120:36:120:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:122:36:122:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:125:36:125:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:127:36:127:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:130:36:130:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:132:36:132:38 | ControlFlowNode for url | provenance | Sink:MaD:15 | | test_requests.py:1:19:1:25 | ControlFlowNode for ImportMember | test_requests.py:1:19:1:25 | ControlFlowNode for request | provenance | | | test_requests.py:1:19:1:25 | ControlFlowNode for request | test_requests.py:7:18:7:24 | ControlFlowNode for request | provenance | | | test_requests.py:1:19:1:25 | ControlFlowNode for request | test_requests.py:14:18:14:24 | ControlFlowNode for request | provenance | | @@ -345,6 +382,26 @@ nodes | test_path_validation.py:76:36:76:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | | test_path_validation.py:79:36:79:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | | test_path_validation.py:81:36:81:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:85:36:85:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:87:36:87:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:90:36:90:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:92:36:92:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:95:36:95:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:97:36:97:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:100:36:100:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:102:36:102:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:105:36:105:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:107:36:107:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:110:36:110:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:112:36:112:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:115:36:115:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:117:36:117:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:120:36:120:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:122:36:122:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:125:36:125:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:127:36:127:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:130:36:130:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:132:36:132:38 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | | test_requests.py:1:19:1:25 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | | test_requests.py:1:19:1:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | test_requests.py:7:5:7:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | diff --git a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_path_validation.py b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_path_validation.py index 44c569f625d0..3e6bb1f6d0c2 100644 --- a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_path_validation.py +++ b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_path_validation.py @@ -16,7 +16,7 @@ def urivalidator_path_in_domain_validation(credential, trusted_domain): c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] if URIValidator.in_domain(full_url, trusted_domain): - c = SecretClient(vault_url=full_url, credential=credential) # OK + c = SecretClient(vault_url=full_url, credential=credential) # $ Alert[py/partial-ssrf] else: c = SecretClient(vault_url=full_url, credential=credential) # $ Alert[py/full-ssrf] @@ -32,7 +32,7 @@ def urivalidator_path_in_azure_keyvault_domain_validation(credential): c = KeyClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] if URIValidator.in_azure_keyvault_domain(full_url): - c = KeyClient(vault_url=full_url, credential=credential) # OK + c = KeyClient(vault_url=full_url, credential=credential) # $ Alert[py/partial-ssrf] else: c = KeyClient(vault_url=full_url, credential=credential) # $ Alert[py/full-ssrf] @@ -48,7 +48,7 @@ def urivalidator_path_in_azure_storage_domain_validation(credential): c = ShareFileClient.from_file_url(url) # $ Alert[py/partial-ssrf] if URIValidator.in_azure_storage_domain(full_url): - c = ShareFileClient.from_file_url(full_url) # OK + c = ShareFileClient.from_file_url(full_url) # $ Alert[py/partial-ssrf] else: c = ShareFileClient.from_file_url(full_url) # $ Alert[py/full-ssrf] @@ -63,70 +63,70 @@ def complex_urivalidator_checks(credential, trusted_domain): if not URIValidator.in_domain(url, trusted_domain): c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] else: - c = SecretClient(vault_url=url, credential=credential) # OK + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] if URIValidator.in_domain(url, trusted_domain) and trusted_domain == "example.com": - c = SecretClient(vault_url=url, credential=credential) # OK + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] else: c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] if not (URIValidator.in_domain(url, trusted_domain) and trusted_domain == "example.com"): c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] else: - c = SecretClient(vault_url=url, credential=credential) # OK + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] if not not not URIValidator.in_domain(url, trusted_domain): c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] else: - c = SecretClient(vault_url=url, credential=credential) # OK + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] if URIValidator.in_domain(url, trusted_domain) == True: - c = SecretClient(vault_url=url, credential=credential) # OK + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] else: c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] if URIValidator.in_domain(url, trusted_domain) == False: c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] else: - c = SecretClient(vault_url=url, credential=credential) # OK + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] if URIValidator.in_domain(url, trusted_domain) != True: c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] else: - c = SecretClient(vault_url=url, credential=credential) # OK + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] if URIValidator.in_domain(url, trusted_domain) != False: - c = SecretClient(vault_url=url, credential=credential) # OK + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] else: c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] if URIValidator.in_domain(url, trusted_domain) is True: - c = SecretClient(vault_url=url, credential=credential) # OK + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] else: c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] if URIValidator.in_domain(url, trusted_domain) is False: c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] else: - c = SecretClient(vault_url=url, credential=credential) # OK + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] if URIValidator.in_domain(url, trusted_domain) is not True: c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] else: - c = SecretClient(vault_url=url, credential=credential) # OK + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] if URIValidator.in_domain(url, trusted_domain) is not False: - c = SecretClient(vault_url=url, credential=credential) # OK + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] else: c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] if not URIValidator.in_domain(url, trusted_domain) is True: c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] else: - c = SecretClient(vault_url=url, credential=credential) # OK + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] if not URIValidator.in_domain(url, trusted_domain) is False: - c = SecretClient(vault_url=url, credential=credential) # OK + c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] else: c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] \ No newline at end of file From 6df671eea2e4b54a3e4c9068eb1843a3aac9a7a5 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 3 Feb 2026 11:44:34 -0500 Subject: [PATCH 14/14] Fixing inefficiently passed variable in nested existential quantification. --- .../dataflow/ServerSideRequestForgeryCustomizations.qll | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll index 2139ecf797db..e75217d608f5 100644 --- a/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll @@ -185,15 +185,14 @@ module ServerSideRequestForgery { import semmle.python.dataflow.new.internal.DataFlowPublic private predicate uri_validator(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) { - exists(DataFlow::CallCfgNode call, Node n, string funcs | + exists(DataFlow::CallCfgNode call, string funcs | funcs in ["in_domain", "in_azure_keyvault_domain", "in_azure_storage_domain"] | call = API::moduleImport("AntiSSRF").getMember("URIValidator").getMember(funcs).getACall() and call.getArg(0).asCfgNode() = node and - n.getALocalSource() = call and ( // validator used in a comparison - exists(CompareNode cn, Cmpop op | cn = g | + exists(CompareNode cn, Cmpop op, Node n | cn = g and n.getALocalSource() = call | ( // validator == true or validator == false or validator is True or validator is False (op instanceof Eq or op instanceof Is) and