From 9912aaaf1ab2a66f9d5bc28c777214bf09eb4773 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 30 Sep 2025 13:32:56 -0400 Subject: [PATCH 01/56] Adding azure sdk test cases and updated test expected file. --- .../FullServerSideRequestForgery.expected | 6 +++--- .../PartialServerSideRequestForgery.expected | 10 +++++----- .../test_azure_client.py | 4 ++++ 3 files changed, 12 insertions(+), 8 deletions(-) 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 33970318461..ae554fa812c 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 @@ -45,7 +45,7 @@ edges | 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:32:18:32:25 | ControlFlowNode for full_url | provenance | Sink:MaD:27 | +| 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 | | @@ -110,7 +110,7 @@ nodes | 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:32:18:32:25 | 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 | @@ -148,7 +148,7 @@ subpaths | 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:31:5:36:5 | ControlFlowNode for download_blob_from_url() | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | test_azure_client.py:32:18:32: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_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 bdcac746538..bbe756e24b7 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 @@ -89,12 +89,12 @@ edges | 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:26:18:26:20 | ControlFlowNode for url | provenance | Sink:MaD:27 | +| 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:32:18:32:25 | ControlFlowNode for full_url | provenance | Sink:MaD:27 | +| 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 | | @@ -207,8 +207,8 @@ nodes | 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:26:18:26:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | -| test_azure_client.py:32:18:32:25 | 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 | @@ -245,7 +245,7 @@ subpaths | 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:25:5:30:5 | ControlFlowNode for download_blob_from_url() | test_azure_client.py:7:19:7:25 | ControlFlowNode for ImportMember | test_azure_client.py:26:18:26: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_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 index d8de2092a2e..f78b0a64137 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,6 +22,7 @@ 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, @@ -34,3 +35,6 @@ def azure_sdk_test(credential, output_path): credential=credential, overwrite=True ) + + + From b8ba9052533e62f30f361db281e86e0be583a685 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 30 Sep 2025 13:55:31 -0400 Subject: [PATCH 02/56] 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 00000000000..573d5ea109d --- /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 46a2a249f9d7d982bdb3dc829abade9c69f82e51 Mon Sep 17 00:00:00 2001 From: Ben Rodes Date: Tue, 30 Sep 2025 14:00:16 -0400 Subject: [PATCH 03/56] 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 f78b0a64137..5d2b04b9c9d 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 08b72d0a868fe57d42bdac4f0c73f71e2851176f Mon Sep 17 00:00:00 2001 From: Ben Rodes Date: Tue, 30 Sep 2025 14:00:25 -0400 Subject: [PATCH 04/56] 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 5d2b04b9c9d..d8de2092a2e 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 7db97799c1415a70502009fa31ca9fa5084253ae Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 30 Sep 2025 14:02:43 -0400 Subject: [PATCH 05/56] Moved change log to correct location. --- python/ql/lib/change-notes/2025-09-30-azure_ssrf_models.md | 4 ++-- .../lib/change-notes/released/2025-09-30-azure_ssrf_models | 5 ----- 2 files changed, 2 insertions(+), 7 deletions(-) delete mode 100644 python/ql/lib/change-notes/released/2025-09-30-azure_ssrf_models 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 index 283b703a438..573d5ea109d 100644 --- 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 @@ -1,5 +1,5 @@ --- category: minorAnalysis --- -* Added request forgery sink models for the Azure SDK. -* Made it so that models-as-data sinks with the kind `request-forgery` contribute to the class `Http::Client::Request` which represents HTTP client requests. \ No newline at end of file +* Added `ssrf` MaD for the azure SDK +* Added MaD `ssrf` to `Http::Client::Request` \ No newline at end of file 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 deleted file mode 100644 index 573d5ea109d..00000000000 --- a/python/ql/lib/change-notes/released/2025-09-30-azure_ssrf_models +++ /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 265922d2e509bbd93fbbe87227eb49204a083a5f Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 30 Sep 2025 14:07:32 -0400 Subject: [PATCH 06/56] Adding docs. --- .../lib/semmle/python/frameworks/SSRFSink.qll | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 python/ql/lib/semmle/python/frameworks/SSRFSink.qll 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 00000000000..e9b7ff9e474 --- /dev/null +++ b/python/ql/lib/semmle/python/frameworks/SSRFSink.qll @@ -0,0 +1,42 @@ +/** + * Provides classes for SSRF sinks modeled using Models as Data (MaD). + */ + +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 { + /** + * An HTTP request modeled from `ssrf` sinks, modeled using MaD. + */ + 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() { result = "MaD" } + + override predicate disablesCertificateValidation( + DataFlow::Node disablingNode, DataFlow::Node argumentOrigin + ) { + // NOTE: if you need to define this, you have to special case it for every possible API in MaD + none() + } + } +} From 88adb05d4b21928d26aafc8e5c3d083268ac2068 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 30 Sep 2025 14:09:06 -0400 Subject: [PATCH 07/56] 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 e9b7ff9e474..aeb228daf13 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 27e19813bea78bcfbfca6b13354a8637529066aa Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 2 Feb 2026 10:13:59 -0500 Subject: [PATCH 08/56] 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 573d5ea109d..00000000000 --- 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 97ddab0724bc37dec318f02ff79d091423236db5 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 2 Feb 2026 16:09:53 -0500 Subject: [PATCH 09/56] 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 | 233 +++++++- .../test_http_client.py | 33 +- .../test_path_validation.py | 132 +++++ .../test_requests.py | 48 +- 10 files changed, 1061 insertions(+), 413 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 274e7ee57ad..2139ecf797d 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 ae554fa812c..19b5f7efa9d 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 50d53b5f47e..35a2814e7f4 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 bbe756e24b7..875d8b62b62 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 88de9285e5e..651b07aea55 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 95ff9d64944..e2f4fc05026 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 d8de2092a2e..28570b7ab4a 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.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 +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 + 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, # 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 - ) + 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 c833907d843..0a238878e35 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 00000000000..44c569f625d --- /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 443f5c3b81f..cfa2e6aa5a7 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 97f19d03ad7b2f9fa8873f58ce91d46dee225e2a Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 2 Feb 2026 16:21:34 -0500 Subject: [PATCH 10/56] 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 875d8b62b62..0d3f49c200e 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 44c569f625d..3e6bb1f6d0c 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 42f6e6a19cdba380b2b0245cfe091c894204fda9 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 3 Feb 2026 11:44:34 -0500 Subject: [PATCH 11/56] 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 2139ecf797d..e75217d608f 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 From 4f11913ee53aab76fff8457007c2bf83bce8b6ae Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Fri, 6 Feb 2026 11:23:58 -0500 Subject: [PATCH 12/56] removing SSRFSink.qll --- .../lib/semmle/python/frameworks/SSRFSink.qll | 42 ------------------- 1 file changed, 42 deletions(-) delete mode 100644 python/ql/lib/semmle/python/frameworks/SSRFSink.qll diff --git a/python/ql/lib/semmle/python/frameworks/SSRFSink.qll b/python/ql/lib/semmle/python/frameworks/SSRFSink.qll deleted file mode 100644 index aeb228daf13..00000000000 --- a/python/ql/lib/semmle/python/frameworks/SSRFSink.qll +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Provides classes for SSRF sinks modeled using Models as Data (MaD). - */ - -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 { - /** - * An HTTP request modeled from `ssrf` sinks, modeled using MaD. - */ - 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() { result = "MaD" } - - override predicate disablesCertificateValidation( - DataFlow::Node disablingNode, DataFlow::Node argumentOrigin - ) { - // NOTE: if you need to define this, you have to special case it for every possible API in MaD - none() - } - } -} From f6c302b68cc107d8a361a9dd1e5dbddc88015ee2 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Fri, 6 Feb 2026 11:28:48 -0500 Subject: [PATCH 13/56] Removing commented out test cases. --- .../test_azure_client.py | 200 ------------------ 1 file changed, 200 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 28570b7ab4a..5dbb7e1ede4 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 @@ -23,203 +23,3 @@ def azure_sdk_test(credential, output_path): 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 From df544595525a8df1dab3030753ce673602550910 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 9 Feb 2026 13:19:02 -0500 Subject: [PATCH 14/56] Restore prior PR change log (accidentally removed) --- python/ql/lib/change-notes/2025-09-30-azure_ssrf_models.md | 5 +++++ 1 file changed, 5 insertions(+) create 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 new file mode 100644 index 00000000000..283b703a438 --- /dev/null +++ b/python/ql/lib/change-notes/2025-09-30-azure_ssrf_models.md @@ -0,0 +1,5 @@ +--- +category: minorAnalysis +--- +* Added request forgery sink models for the Azure SDK. +* Made it so that models-as-data sinks with the kind `request-forgery` contribute to the class `Http::Client::Request` which represents HTTP client requests. \ No newline at end of file From 23bab818553cd00fac1704e75567d775981150b1 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 9 Feb 2026 13:22:35 -0500 Subject: [PATCH 15/56] Added change log --- ...026-02-09-ssrf_test_case_cleanup_and_new_ssrf_barriers.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 python/ql/lib/change-notes/2026-02-09-ssrf_test_case_cleanup_and_new_ssrf_barriers.md diff --git a/python/ql/lib/change-notes/2026-02-09-ssrf_test_case_cleanup_and_new_ssrf_barriers.md b/python/ql/lib/change-notes/2026-02-09-ssrf_test_case_cleanup_and_new_ssrf_barriers.md new file mode 100644 index 00000000000..d4373eadfb2 --- /dev/null +++ b/python/ql/lib/change-notes/2026-02-09-ssrf_test_case_cleanup_and_new_ssrf_barriers.md @@ -0,0 +1,5 @@ +--- +category: minorAnalysis +--- +* Modified SSRF tests to use postprocessing to more easily debug results. +* Added new full SSRF sanitization barrier from the new AntiSSRF library. \ No newline at end of file From 9f8ed710e28870ccd1044c53055bcce57ce062cf Mon Sep 17 00:00:00 2001 From: Ben Rodes Date: Tue, 10 Feb 2026 11:09:25 -0500 Subject: [PATCH 16/56] Update python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/test_path_validation.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../CWE-918-ServerSideRequestForgery/test_path_validation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 3e6bb1f6d0c..4560479d2b3 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 @@ -56,8 +56,8 @@ def urivalidator_path_in_azure_storage_domain_validation(credential): 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 + # It is assumed that the logic underlying path checking would apply + # similarly to other validator methods. url = f"https://{user_input}" if not URIValidator.in_domain(url, trusted_domain): From a91cf6b7cba78087cc011eea9cd15555c07a0c29 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 10 Feb 2026 11:37:11 -0500 Subject: [PATCH 17/56] Applying copilot PR suggestions. --- ...ServerSideRequestForgeryCustomizations.qll | 2 +- .../FullServerSideRequestForgery.expected | 129 +++++---- .../PartialServerSideRequestForgery.expected | 248 +++++++++--------- .../full_partial_test.py | 6 +- .../test_azure_client.py | 16 +- .../test_http_client.py | 2 +- .../test_path_validation.py | 80 +++--- .../test_requests.py | 8 +- 8 files changed, 244 insertions(+), 247 deletions(-) diff --git a/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll index e75217d608f..afce799d8ac 100644 --- a/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll @@ -177,7 +177,7 @@ module ServerSideRequestForgery { ) } - /** A validation that a string does not contain certain characters, considered as a sanitizer. */ + /** A validation of a URI using the `AntiSSRF` library, considered as a full-ssrf sanitizer. */ private class UriValidator extends FullUrlControlSanitizer { UriValidator() { this = DataFlow::BarrierGuard::getABarrierNode() } } 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 19b5f7efa9d..cb46c54ca70 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 @@ -13,32 +13,32 @@ | 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:16:5:16:59 | ControlFlowNode for SecretClient() | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | test_azure_client.py:16:28:16:35 | 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:5:18:43 | ControlFlowNode for Attribute() | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | test_azure_client.py:18:35:18:42 | 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:5:20:35 | ControlFlowNode for KeyClient() | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | test_azure_client.py:20:15:20:22 | 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:5:22:85 | ControlFlowNode for Attribute() | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | test_azure_client.py:22:54:22:61 | 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_path_validation.py:21:9:21:63 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:21:32:21:39 | 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:9:37:60 | ControlFlowNode for KeyClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:37:29:37:36 | 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:9:53:47 | ControlFlowNode for Attribute() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:53:39:53:46 | 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:9:64:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:64:32:64:34 | 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:9:71:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:71:32:71:34 | 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:9:74:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:74:32:74:34 | 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:9:79:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:79:32:79:34 | 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:9:87:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:87:32:87:34 | 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:9:90:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:90:32:90:34 | 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:9:95:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:95:32:95:34 | 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:9:102:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:102:32:102:34 | 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:9:107:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:107:32:107:34 | 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:9:110:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:110:32:110:34 | 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:9:115:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:115:32:115:34 | 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:9:122:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:122:32:122:34 | 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:9:125:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:125:32:125:34 | 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:9:132:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:132:32:132:34 | 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 | @@ -85,10 +85,10 @@ edges | 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:16:28:16:35 | 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:35:18:42 | 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:15:20:22 | 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:54:22:61 | 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 | | @@ -113,31 +113,31 @@ edges | 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:11:5:11:12 | ControlFlowNode for full_url | test_path_validation.py:21:32:21:39 | 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:27:5:27:12 | ControlFlowNode for full_url | test_path_validation.py:37:29:37:36 | 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:43:5:43:12 | ControlFlowNode for full_url | test_path_validation.py:53:39:53:46 | 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_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:64:32:64:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:71:32:71:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:74:32:74:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:79:32:79:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:87:32:87:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:90:32:90:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:95:32:95:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:102:32:102:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:107:32:107:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:110:32:110:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:115:32:115:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:122:32:122:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:125:32:125:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:132:32:132:34 | 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 | | @@ -192,10 +192,10 @@ nodes | 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:16:28:16:35 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_azure_client.py:18:35:18:42 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_azure_client.py:20:15:20:22 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_azure_client.py:22:54:22:61 | 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 | @@ -215,34 +215,34 @@ nodes | 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:21:32:21:39 | 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:37:29:37:36 | 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:53:39:53:46 | 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_path_validation.py:64:32:64:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:71:32:71:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:74:32:74:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:79:32:79:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:87:32:87:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:90:32:90:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:95:32:95:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:102:32:102:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:107:32:107:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:110:32:110:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:115:32:115:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:122:32:122:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:125:32:125:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:132:32:132:34 | 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 | @@ -255,6 +255,3 @@ nodes | 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 -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/PartialServerSideRequestForgery.expected b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/PartialServerSideRequestForgery.expected index 0d3f49c200e..314aa99ef60 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 @@ -6,39 +6,39 @@ | 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:15:5:15:54 | ControlFlowNode for SecretClient() | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | test_azure_client.py:15:28:15:30 | 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:5:17:38 | ControlFlowNode for Attribute() | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | test_azure_client.py:17:35:17:37 | 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:5:19:30 | ControlFlowNode for KeyClient() | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | test_azure_client.py:19:15:19:17 | 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:5:21:80 | ControlFlowNode for Attribute() | test_azure_client.py:6:19:6:25 | ControlFlowNode for ImportMember | test_azure_client.py:21:54:21:56 | 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: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 | +| test_path_validation.py:14:9:14:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:14:32:14:34 | 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:9:16:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:16:32:16:34 | 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:9:19:63 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:19:32:19:39 | 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:9:30:55 | ControlFlowNode for KeyClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:30:29:30:31 | 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:9:32:55 | ControlFlowNode for KeyClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:32:29:32:31 | 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:9:35:60 | ControlFlowNode for KeyClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:35:29:35:36 | 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:9:46:42 | ControlFlowNode for Attribute() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:46:39:46:41 | 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:9:48:42 | ControlFlowNode for Attribute() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:48:39:48:41 | 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:9:51:47 | ControlFlowNode for Attribute() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:51:39:51:46 | 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:9:66:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:66:32:66:34 | 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:9:69:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:69:32:69:34 | 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:9:76:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:76:32:76:34 | 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:9:81:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:81:32:81:34 | 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:9:85:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:85:32:85:34 | 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:9:92:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:92:32:92:34 | 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:9:97:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:97:32:97:34 | 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:9:100:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:100:32:100:34 | 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:9:105:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:105:32:105:34 | 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:9:112:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:112:32:112:34 | 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:9:117:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:117:32:117:34 | 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:9:120:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:120:32:120:34 | 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:9:127:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:127:32:127:34 | 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:9:130:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:130:32:130:34 | 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 | | @@ -126,15 +126,15 @@ edges | 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:15:28:15:30 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_azure_client.py:12:5:12:7 | ControlFlowNode for url | test_azure_client.py:17:35:17:37 | ControlFlowNode for url | provenance | Sink:MaD:38 | +| test_azure_client.py:12:5:12:7 | ControlFlowNode for url | test_azure_client.py:19:15:19:17 | ControlFlowNode for url | provenance | Sink:MaD:14 | +| test_azure_client.py:12:5:12:7 | ControlFlowNode for url | test_azure_client.py:21:54:21:56 | 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:16:28:16:35 | 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:35:18:42 | 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:15:20:22 | 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:54:22:61 | 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 | | @@ -169,58 +169,58 @@ edges | 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:10:5:10:7 | ControlFlowNode for url | test_path_validation.py:14:32:14:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:10:5:10:7 | ControlFlowNode for url | test_path_validation.py:16:32:16:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:11:5:11:12 | ControlFlowNode for full_url | test_path_validation.py:19:32:19:39 | 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:32:21:39 | 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:26:5:26:7 | ControlFlowNode for url | test_path_validation.py:30:29:30:31 | ControlFlowNode for url | provenance | Sink:MaD:14 | +| test_path_validation.py:26:5:26:7 | ControlFlowNode for url | test_path_validation.py:32:29:32:31 | ControlFlowNode for url | provenance | Sink:MaD:14 | +| test_path_validation.py:27:5:27:12 | ControlFlowNode for full_url | test_path_validation.py:35:29:35:36 | 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:29:37:36 | 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:42:5:42:7 | ControlFlowNode for url | test_path_validation.py:46:39:46:41 | ControlFlowNode for url | provenance | Sink:MaD:38 | +| test_path_validation.py:42:5:42:7 | ControlFlowNode for url | test_path_validation.py:48:39:48:41 | ControlFlowNode for url | provenance | Sink:MaD:38 | +| test_path_validation.py:43:5:43:12 | ControlFlowNode for full_url | test_path_validation.py:51:39:51:46 | 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:39:53:46 | 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_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_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:64:32:64:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:66:32:66:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:69:32:69:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:71:32:71:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:74:32:74:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:76:32:76:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:79:32:79:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:81:32:81:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:85:32:85:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:87:32:87:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:90:32:90:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:92:32:92:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:95:32:95:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:97:32:97:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:100:32:100:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:102:32:102:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:105:32:105:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:107:32:107:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:110:32:110:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:112:32:112:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:115:32:115:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:117:32:117:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:120:32:120:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:122:32:122:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:125:32:125:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:127:32:127:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:130:32:130:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:132:32:132:34 | 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 | | @@ -311,14 +311,14 @@ nodes | 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:15:28:15:30 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_azure_client.py:16:28:16:35 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_azure_client.py:17:35:17:37 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_azure_client.py:18:35:18:42 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_azure_client.py:19:15:19:17 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_azure_client.py:20:15:20:22 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_azure_client.py:21:54:21:56 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_azure_client.py:22:54:22:61 | 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 | @@ -347,61 +347,61 @@ nodes | 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:14:32:14:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:16:32:16:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:19:32:19:39 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_path_validation.py:21:32:21:39 | 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:30:29:30:31 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:32:29:32:31 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:35:29:35:36 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_path_validation.py:37:29:37:36 | 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:46:39:46:41 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:48:39:48:41 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:51:39:51:46 | ControlFlowNode for full_url | semmle.label | ControlFlowNode for full_url | +| test_path_validation.py:53:39:53:46 | 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_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_path_validation.py:64:32:64:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:66:32:66:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:69:32:69:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:71:32:71:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:74:32:74:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:76:32:76:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:79:32:79:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:81:32:81:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:85:32:85:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:87:32:87:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:90:32:90:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:92:32:92:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:95:32:95:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:97:32:97:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:100:32:100:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:102:32:102:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:105:32:105:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:107:32:107:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:110:32:110:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:112:32:112:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:115:32:115:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:117:32:117:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:120:32:120:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:122:32:122:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:125:32:125:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:127:32:127:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:130:32:130:34 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| test_path_validation.py:132:32:132:34 | 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 | @@ -414,5 +414,3 @@ nodes | 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 -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/full_partial_test.py b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/full_partial_test.py index e2f4fc05026..7b7d067b611 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,6 +1,6 @@ from flask import request # $ Source -import requests +import requests import re def full_ssrf(): @@ -122,7 +122,7 @@ def partial_ssrf_4(): user_input = request.args['untrusted_input'] url = "https://example.com/foo#{}".format(user_input) - # NOT OK -- user contollred fragment + # NOT OK -- user controlled fragment requests.get(url) # $ Alert[py/partial-ssrf] def partial_ssrf_5(): @@ -132,7 +132,7 @@ def partial_ssrf_5(): # controlled url = "https://example.com/foo#%s" % user_input - # NOT OK -- user contollred fragment + # NOT OK -- user controlled fragment requests.get(url) # $ Alert[py/partial-ssrf] def partial_ssrf_6(): 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 5dbb7e1ede4..ac4ff4d365e 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 @@ -12,14 +12,14 @@ 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) # $ 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] + SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] + SecretClient(vault_url=full_url, credential=credential) # $ Alert[py/full-ssrf] + ShareFileClient.from_file_url(url) # $ Alert[py/partial-ssrf] + ShareFileClient.from_file_url(full_url) # $ Alert[py/full-ssrf] + KeyClient(url, credential) # $ Alert[py/partial-ssrf] + KeyClient(full_url, credential) # $ Alert[py/full-ssrf] + ContainerClient.from_container_url(container_url=url, credential=credential) # $ Alert[py/partial-ssrf] + 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] 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 0a238878e35..da812eb6c54 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 @@ -14,7 +14,7 @@ def ssrf_test(): # 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 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) # $ Sink # NOT OK -- user has full control 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 4560479d2b3..ce5a6d33833 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 @@ -11,14 +11,14 @@ def urivalidator_path_in_domain_validation(credential, trusted_domain): full_url = f"https://{user_input2}" if URIValidator.in_domain(url, trusted_domain): - c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] + SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] else: - c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] + 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) # $ Alert[py/partial-ssrf] + SecretClient(vault_url=full_url, credential=credential) # $ Alert[py/partial-ssrf] else: - c = SecretClient(vault_url=full_url, credential=credential) # $ Alert[py/full-ssrf] + 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'] @@ -27,14 +27,14 @@ def urivalidator_path_in_azure_keyvault_domain_validation(credential): full_url = f"https://{user_input2}" if URIValidator.in_azure_keyvault_domain(url): - c = KeyClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] + KeyClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] else: - c = KeyClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] + 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) # $ Alert[py/partial-ssrf] + KeyClient(vault_url=full_url, credential=credential) # $ Alert[py/partial-ssrf] else: - c = KeyClient(vault_url=full_url, credential=credential) # $ Alert[py/full-ssrf] + 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'] @@ -43,14 +43,14 @@ def urivalidator_path_in_azure_storage_domain_validation(credential): full_url = f"https://{user_input2}" if URIValidator.in_azure_storage_domain(url): - c = ShareFileClient.from_file_url(url) # $ Alert[py/partial-ssrf] + ShareFileClient.from_file_url(url) # $ Alert[py/partial-ssrf] else: - c = ShareFileClient.from_file_url(url) # $ Alert[py/partial-ssrf] + ShareFileClient.from_file_url(url) # $ Alert[py/partial-ssrf] if URIValidator.in_azure_storage_domain(full_url): - c = ShareFileClient.from_file_url(full_url) # $ Alert[py/partial-ssrf] + ShareFileClient.from_file_url(full_url) # $ Alert[py/partial-ssrf] else: - c = ShareFileClient.from_file_url(full_url) # $ Alert[py/full-ssrf] + ShareFileClient.from_file_url(full_url) # $ Alert[py/full-ssrf] def complex_urivalidator_checks(credential, trusted_domain): @@ -61,72 +61,72 @@ def complex_urivalidator_checks(credential, trusted_domain): url = f"https://{user_input}" if not URIValidator.in_domain(url, trusted_domain): - c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] + SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] else: - c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] + 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) # $ Alert[py/partial-ssrf] + SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] else: - c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] + 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] + SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] else: - c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] + 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] + SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] else: - c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] + 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/partial-ssrf] + SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] else: - c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] + 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] + SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] else: - c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] + 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] + SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] else: - c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] + 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) # $ Alert[py/partial-ssrf] + SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] else: - c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] + 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) # $ Alert[py/partial-ssrf] + SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] else: - c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] + 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] + SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] else: - c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] + 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] + SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] else: - c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] + 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) # $ Alert[py/partial-ssrf] + SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] else: - c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] + 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] + SecretClient(vault_url=url, credential=credential) # $ Alert[py/full-ssrf] else: - c = SecretClient(vault_url=url, credential=credential) # $ Alert[py/partial-ssrf] + 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) # $ Alert[py/partial-ssrf] + 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 + 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 cfa2e6aa5a7..fa5da6cef49 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,4 +1,4 @@ -from flask import request # $ Source +from flask import request # $ Source[py/full-ssrf] from AntiSSRF import AntiSSRFPolicy import requests @@ -36,7 +36,8 @@ def ssrf_test_with_policy2(): # 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] + # TODO: not currently a scenario we detect. + session.get(user_input) # $ MISSING: Alert[py/full-ssrf] def ssrf_test_with_policy3(adapter): user_input = request.args['untrusted_input'] @@ -46,4 +47,5 @@ def ssrf_test_with_policy3(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 + # TODO: not currently a scenario we detect. + session.get(user_input) # $ MISSING: Alert[py/full-ssrf] \ No newline at end of file From 4bb110beb87256fdef01f122b2bf9f8c7d254b70 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 10 Feb 2026 11:46:16 -0500 Subject: [PATCH 18/56] More copilot suggestions. --- .../CWE-918-ServerSideRequestForgery/test_requests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 fa5da6cef49..6489096c969 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 @@ -19,14 +19,14 @@ def ssrf_test2(): def ssrf_test3(): user_input = request.args['untrusted_input'] # NOT OK -- user has full control - response = requests.request('', user_input) # $ Alert[py/full-ssrf] + 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) + session.get(user_input) def ssrf_test_with_policy2(): user_input = request.args['untrusted_input'] From 9f9c353806ae05361c5bf4515036e722f89b4176 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 10 Feb 2026 11:47:23 -0500 Subject: [PATCH 19/56] Update expected files. Copilot suggestions broke unit test expected results (column numbers). --- .../FullServerSideRequestForgery.expected | 6 +++--- .../PartialServerSideRequestForgery.expected | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) 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 cb46c54ca70..c2fd16bfab7 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 @@ -41,7 +41,7 @@ | test_path_validation.py:132:9:132:58 | ControlFlowNode for SecretClient() | test_path_validation.py:5:19:5:25 | ControlFlowNode for ImportMember | test_path_validation.py:132:32:132:34 | 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 | +| test_requests.py:22:5:22:44 | ControlFlowNode for Attribute() | test_requests.py:1:19:1:25 | ControlFlowNode for ImportMember | test_requests.py:22:34:22:43 | 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 | | @@ -146,7 +146,7 @@ edges | 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:5:20:14 | ControlFlowNode for user_input | test_requests.py:22:34:22:43 | 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 | @@ -253,5 +253,5 @@ nodes | 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 | +| test_requests.py:22:34:22:43 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | subpaths 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 314aa99ef60..8cc69590b7e 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 @@ -229,7 +229,7 @@ edges | 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:5:20:14 | ControlFlowNode for user_input | test_requests.py:22:34:22:43 | 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 | @@ -412,5 +412,5 @@ nodes | 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 | +| test_requests.py:22:34:22:43 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | subpaths From 4d4e7a1b5c21badf976d5647c34aae8eac49908d Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Thu, 12 Feb 2026 08:28:08 -0500 Subject: [PATCH 20/56] Pretty print for tests. --- .../FullServerSideRequestForgery.expected | 50 +++++---- .../FullServerSideRequestForgery.qlref | 4 +- .../PartialServerSideRequestForgery.expected | 106 +++++++++--------- .../PartialServerSideRequestForgery.qlref | 4 +- 4 files changed, 90 insertions(+), 74 deletions(-) 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 c2fd16bfab7..7434eca6978 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 @@ -85,11 +85,11 @@ edges | 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:28:16:35 | 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:35:18:42 | 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:15:20:22 | 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:54:22:61 | 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_azure_client.py:13:5:13:12 | ControlFlowNode for full_url | test_azure_client.py:16:28:16:35 | ControlFlowNode for full_url | provenance | Sink:MaD:2 | +| test_azure_client.py:13:5:13:12 | ControlFlowNode for full_url | test_azure_client.py:18:35:18:42 | ControlFlowNode for full_url | provenance | Sink:MaD:4 | +| test_azure_client.py:13:5:13:12 | ControlFlowNode for full_url | test_azure_client.py:20:15:20:22 | ControlFlowNode for full_url | provenance | Sink:MaD:1 | +| test_azure_client.py:13:5:13:12 | ControlFlowNode for full_url | test_azure_client.py:22:54:22:61 | ControlFlowNode for full_url | provenance | Sink:MaD:3 | +| 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:5 | | 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 | | @@ -113,31 +113,31 @@ edges | 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:32:21:39 | 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:32:21:39 | ControlFlowNode for full_url | provenance | Sink:MaD:2 | | 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:29:37:36 | 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:29:37:36 | ControlFlowNode for full_url | provenance | Sink:MaD:1 | | 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:39:53:46 | 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:39:53:46 | ControlFlowNode for full_url | provenance | Sink:MaD:4 | | 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:32:64:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:71:32:71:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:74:32:74:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:79:32:79:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:87:32:87:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:90:32:90:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:95:32:95:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:102:32:102:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:107:32:107:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:110:32:110:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:115:32:115:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:122:32:122:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:125:32:125:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:132:32:132:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:64:32:64:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:71:32:71:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:74:32:74:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:79:32:79:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:87:32:87:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:90:32:90:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:95:32:95:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:102:32:102:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:107:32:107:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:110:32:110:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:115:32:115:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:122:32:122:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:125:32:125:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:132:32:132:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | | 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 | | @@ -148,6 +148,12 @@ edges | 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:34:22:43 | 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 | +models +| 1 | Sink: azure.keyvault.keys.KeyClient!; Call.Argument[0,vault_url:]; request-forgery | +| 2 | Sink: azure.keyvault.secrets.SecretClient!; Call.Argument[0,vault_url:]; request-forgery | +| 3 | Sink: azure.storage.blob.ContainerClient!; Member[from_container_url].Argument[0,container_url:]; request-forgery | +| 4 | Sink: azure.storage.fileshare.ShareFileClient!; Member[from_file_url].Argument[0,file_url:]; request-forgery | +| 5 | Sink: azure; Member[storage].Member[blob].Member[download_blob_from_url].Argument[0,blob_url:]; request-forgery | 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 | 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 35a2814e7f4..f0a8d1e6b15 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,2 +1,4 @@ query: Security/CWE-918/FullServerSideRequestForgery.ql -postprocess: utils/test/InlineExpectationsTestQuery.ql \ No newline at end of file +postprocess: +- utils/test/InlineExpectationsTestQuery.ql +- utils/test/PrettyPrintModels.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 8cc69590b7e..0b875607157 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 @@ -126,16 +126,16 @@ edges | 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:28:15:30 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_azure_client.py:12:5:12:7 | ControlFlowNode for url | test_azure_client.py:17:35:17:37 | ControlFlowNode for url | provenance | Sink:MaD:38 | -| test_azure_client.py:12:5:12:7 | ControlFlowNode for url | test_azure_client.py:19:15:19:17 | ControlFlowNode for url | provenance | Sink:MaD:14 | -| test_azure_client.py:12:5:12:7 | ControlFlowNode for url | test_azure_client.py:21:54:21:56 | 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:28:16:35 | 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:35:18:42 | 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:15:20:22 | 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:54:22:61 | 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_azure_client.py:12:5:12:7 | ControlFlowNode for url | test_azure_client.py:15:28:15:30 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_azure_client.py:12:5:12:7 | ControlFlowNode for url | test_azure_client.py:17:35:17:37 | ControlFlowNode for url | provenance | Sink:MaD:4 | +| test_azure_client.py:12:5:12:7 | ControlFlowNode for url | test_azure_client.py:19:15:19:17 | ControlFlowNode for url | provenance | Sink:MaD:1 | +| test_azure_client.py:12:5:12:7 | ControlFlowNode for url | test_azure_client.py:21:54:21:56 | ControlFlowNode for url | provenance | Sink:MaD:3 | +| 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:5 | +| test_azure_client.py:13:5:13:12 | ControlFlowNode for full_url | test_azure_client.py:16:28:16:35 | ControlFlowNode for full_url | provenance | Sink:MaD:2 | +| test_azure_client.py:13:5:13:12 | ControlFlowNode for full_url | test_azure_client.py:18:35:18:42 | ControlFlowNode for full_url | provenance | Sink:MaD:4 | +| test_azure_client.py:13:5:13:12 | ControlFlowNode for full_url | test_azure_client.py:20:15:20:22 | ControlFlowNode for full_url | provenance | Sink:MaD:1 | +| test_azure_client.py:13:5:13:12 | ControlFlowNode for full_url | test_azure_client.py:22:54:22:61 | ControlFlowNode for full_url | provenance | Sink:MaD:3 | +| 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:5 | | 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 | | @@ -169,58 +169,58 @@ edges | 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:32:14:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:10:5:10:7 | ControlFlowNode for url | test_path_validation.py:16:32:16:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:11:5:11:12 | ControlFlowNode for full_url | test_path_validation.py:19:32:19:39 | 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:32:21:39 | ControlFlowNode for full_url | provenance | Sink:MaD:15 | +| test_path_validation.py:10:5:10:7 | ControlFlowNode for url | test_path_validation.py:14:32:14:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:10:5:10:7 | ControlFlowNode for url | test_path_validation.py:16:32:16:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:11:5:11:12 | ControlFlowNode for full_url | test_path_validation.py:19:32:19:39 | ControlFlowNode for full_url | provenance | Sink:MaD:2 | +| test_path_validation.py:11:5:11:12 | ControlFlowNode for full_url | test_path_validation.py:21:32:21:39 | ControlFlowNode for full_url | provenance | Sink:MaD:2 | | 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:29:30:31 | ControlFlowNode for url | provenance | Sink:MaD:14 | -| test_path_validation.py:26:5:26:7 | ControlFlowNode for url | test_path_validation.py:32:29:32:31 | ControlFlowNode for url | provenance | Sink:MaD:14 | -| test_path_validation.py:27:5:27:12 | ControlFlowNode for full_url | test_path_validation.py:35:29:35:36 | 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:29:37:36 | ControlFlowNode for full_url | provenance | Sink:MaD:14 | +| test_path_validation.py:26:5:26:7 | ControlFlowNode for url | test_path_validation.py:30:29:30:31 | ControlFlowNode for url | provenance | Sink:MaD:1 | +| test_path_validation.py:26:5:26:7 | ControlFlowNode for url | test_path_validation.py:32:29:32:31 | ControlFlowNode for url | provenance | Sink:MaD:1 | +| test_path_validation.py:27:5:27:12 | ControlFlowNode for full_url | test_path_validation.py:35:29:35:36 | ControlFlowNode for full_url | provenance | Sink:MaD:1 | +| test_path_validation.py:27:5:27:12 | ControlFlowNode for full_url | test_path_validation.py:37:29:37:36 | ControlFlowNode for full_url | provenance | Sink:MaD:1 | | 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:39:46:41 | ControlFlowNode for url | provenance | Sink:MaD:38 | -| test_path_validation.py:42:5:42:7 | ControlFlowNode for url | test_path_validation.py:48:39:48:41 | ControlFlowNode for url | provenance | Sink:MaD:38 | -| test_path_validation.py:43:5:43:12 | ControlFlowNode for full_url | test_path_validation.py:51:39:51:46 | 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:39:53:46 | ControlFlowNode for full_url | provenance | Sink:MaD:38 | +| test_path_validation.py:42:5:42:7 | ControlFlowNode for url | test_path_validation.py:46:39:46:41 | ControlFlowNode for url | provenance | Sink:MaD:4 | +| test_path_validation.py:42:5:42:7 | ControlFlowNode for url | test_path_validation.py:48:39:48:41 | ControlFlowNode for url | provenance | Sink:MaD:4 | +| test_path_validation.py:43:5:43:12 | ControlFlowNode for full_url | test_path_validation.py:51:39:51:46 | ControlFlowNode for full_url | provenance | Sink:MaD:4 | +| test_path_validation.py:43:5:43:12 | ControlFlowNode for full_url | test_path_validation.py:53:39:53:46 | ControlFlowNode for full_url | provenance | Sink:MaD:4 | | 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:32:64:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:66:32:66:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:69:32:69:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:71:32:71:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:74:32:74:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:76:32:76:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:79:32:79:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:81:32:81:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:85:32:85:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:87:32:87:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:90:32:90:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:92:32:92:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:95:32:95:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:97:32:97:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:100:32:100:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:102:32:102:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:105:32:105:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:107:32:107:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:110:32:110:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:112:32:112:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:115:32:115:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:117:32:117:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:120:32:120:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:122:32:122:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:125:32:125:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:127:32:127:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:130:32:130:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | -| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:132:32:132:34 | ControlFlowNode for url | provenance | Sink:MaD:15 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:64:32:64:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:66:32:66:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:69:32:69:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:71:32:71:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:74:32:74:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:76:32:76:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:79:32:79:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:81:32:81:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:85:32:85:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:87:32:87:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:90:32:90:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:92:32:92:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:95:32:95:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:97:32:97:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:100:32:100:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:102:32:102:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:105:32:105:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:107:32:107:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:110:32:110:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:112:32:112:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:115:32:115:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:117:32:117:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:120:32:120:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:122:32:122:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:125:32:125:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:127:32:127:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:130:32:130:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | +| test_path_validation.py:61:5:61:7 | ControlFlowNode for url | test_path_validation.py:132:32:132:34 | ControlFlowNode for url | provenance | Sink:MaD:2 | | 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 | | @@ -231,6 +231,12 @@ edges | 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:34:22:43 | 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 | +models +| 1 | Sink: azure.keyvault.keys.KeyClient!; Call.Argument[0,vault_url:]; request-forgery | +| 2 | Sink: azure.keyvault.secrets.SecretClient!; Call.Argument[0,vault_url:]; request-forgery | +| 3 | Sink: azure.storage.blob.ContainerClient!; Member[from_container_url].Argument[0,container_url:]; request-forgery | +| 4 | Sink: azure.storage.fileshare.ShareFileClient!; Member[from_file_url].Argument[0,file_url:]; request-forgery | +| 5 | Sink: azure; Member[storage].Member[blob].Member[download_blob_from_url].Argument[0,blob_url:]; request-forgery | 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 | 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 651b07aea55..1161c990e22 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,2 +1,4 @@ query: Security/CWE-918/PartialServerSideRequestForgery.ql -postprocess: utils/test/InlineExpectationsTestQuery.ql \ No newline at end of file +postprocess: +- utils/test/InlineExpectationsTestQuery.ql +- utils/test/PrettyPrintModels.ql \ No newline at end of file From edb2ed8df278890c96267cf49a1498f29a73a1a8 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Wed, 11 Feb 2026 14:10:14 +0100 Subject: [PATCH 21/56] C#: Extract the implicit property backing field when referenced via the field keyword. --- .../Entities/Expressions/Factory.cs | 3 ++ .../Expressions/PropertyFieldAccess.cs | 28 +++++++++++ .../Entities/Field.cs | 2 +- .../Entities/PropertyField.cs | 50 +++++++++++++++++++ 4 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/PropertyFieldAccess.cs create mode 100644 csharp/extractor/Semmle.Extraction.CSharp/Entities/PropertyField.cs diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Factory.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Factory.cs index 0da8de1e5d5..ed8dae3738f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Factory.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Factory.cs @@ -160,6 +160,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions case SyntaxKind.ThisExpression: return This.CreateExplicit(info); + case SyntaxKind.FieldExpression: + return PropertyFieldAccess.Create(info); + case SyntaxKind.AddressOfExpression: return Unary.Create(info.SetKind(ExprKind.ADDRESS_OF)); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/PropertyFieldAccess.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/PropertyFieldAccess.cs new file mode 100644 index 00000000000..a9d2afa84c9 --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/PropertyFieldAccess.cs @@ -0,0 +1,28 @@ +using System.IO; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Semmle.Extraction.Kinds; + +namespace Semmle.Extraction.CSharp.Entities.Expressions +{ + internal class PropertyFieldAccess : Expression + { + private PropertyFieldAccess(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.FIELD_ACCESS)) { } + + public static Expression Create(ExpressionNodeInfo info) => new PropertyFieldAccess(info).TryPopulate(); + + protected override void PopulateExpression(TextWriter trapFile) + { + var symbolInfo = Context.GetSymbolInfo(Syntax); + if (symbolInfo.Symbol is IFieldSymbol field) + { + var target = PropertyField.Create(Context, field); + trapFile.expr_access(this, target); + if (!field.IsStatic) + { + This.CreateImplicit(Context, field.ContainingType, Location, this, -1); + } + } + } + } +} diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs index 9a010aad376..329115f11c7 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs @@ -10,7 +10,7 @@ namespace Semmle.Extraction.CSharp.Entities { internal class Field : CachedSymbol, IExpressionParentEntity { - private Field(Context cx, IFieldSymbol init) + protected Field(Context cx, IFieldSymbol init) : base(cx, init) { type = new Lazy(() => Entities.Type.Create(cx, Symbol.Type)); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/PropertyField.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/PropertyField.cs new file mode 100644 index 00000000000..7567a9e8960 --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/PropertyField.cs @@ -0,0 +1,50 @@ +using System.IO; +using Microsoft.CodeAnalysis; +using Semmle.Extraction.CSharp.Util; +using Semmle.Extraction.Kinds; + +namespace Semmle.Extraction.CSharp.Entities +{ + /// + /// Represents the autogenerated backing field `field` for a property. + /// It is only created for properties that use the `field` keyword in their getter or setter, and + /// is not created for auto-properties. + /// + internal class PropertyField : Field + { + protected PropertyField(Context cx, IFieldSymbol init) + : base(cx, init) + { + } + + public static new PropertyField Create(Context cx, IFieldSymbol field) => PropertyFieldFactory.Instance.CreateEntity(cx, (field, field.AssociatedSymbol), field); + + public override bool NeedsPopulation => true; + + public override void Populate(TextWriter trapFile) + { + PopulateNullability(trapFile, Symbol.GetAnnotatedType()); + + var unboundFieldKey = PropertyField.Create(Context, Symbol.OriginalDefinition); + var name = Symbol.AssociatedSymbol is not null ? $"{Symbol.AssociatedSymbol.GetName()}.field" : Symbol.Name; + trapFile.fields(this, VariableKind.None, name, ContainingType!, Type.TypeRef, unboundFieldKey); + + if (Context.OnlyScaffold) + { + return; + } + + if (Context.ExtractLocation(Symbol)) + { + WriteLocationsToTrap(trapFile.field_location, this, Locations); + } + } + + private class PropertyFieldFactory : CachedEntityFactory + { + public static PropertyFieldFactory Instance { get; } = new PropertyFieldFactory(); + + public override PropertyField Create(Context cx, IFieldSymbol init) => new PropertyField(cx, init); + } + } +} From 113f3e880b403b1bf04bc60ca8cc11df5546a4d9 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Wed, 11 Feb 2026 14:55:55 +0100 Subject: [PATCH 22/56] C#: Add property test case where the field keyword is used. --- .../library-tests/properties/PrintAst.expected | 16 ++++++++++++++++ .../properties/Properties17.expected | 1 + .../library-tests/properties/Properties17.ql | 2 +- .../test/library-tests/properties/properties.cs | 9 +++++++++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/csharp/ql/test/library-tests/properties/PrintAst.expected b/csharp/ql/test/library-tests/properties/PrintAst.expected index 2df3ee3f5e8..711e417558e 100644 --- a/csharp/ql/test/library-tests/properties/PrintAst.expected +++ b/csharp/ql/test/library-tests/properties/PrintAst.expected @@ -230,3 +230,19 @@ properties.cs: #-----| 2: (Parameters) # 124| 0: [Parameter] value # 124| 4: [BlockStmt] {...} +# 128| 10: [Class] UseFieldKeyword +# 130| 6: [Property] Prop +# 130| -1: [TypeMention] object +# 132| 3: [Getter] get_Prop +# 132| 4: [BlockStmt] {...} +# 132| 0: [ReturnStmt] return ...; +# 132| 0: [FieldAccess] access to field Prop.field +# 133| 4: [Setter] set_Prop +#-----| 2: (Parameters) +# 133| 0: [Parameter] value +# 133| 4: [BlockStmt] {...} +# 133| 0: [ExprStmt] ...; +# 133| 0: [AssignExpr] ... = ... +# 133| 0: [FieldAccess] access to field Prop.field +# 133| 1: [ParameterAccess] access to parameter value +# 130| 7: [Field] Prop.field diff --git a/csharp/ql/test/library-tests/properties/Properties17.expected b/csharp/ql/test/library-tests/properties/Properties17.expected index 47b563e2676..ee817a63df9 100644 --- a/csharp/ql/test/library-tests/properties/Properties17.expected +++ b/csharp/ql/test/library-tests/properties/Properties17.expected @@ -1,3 +1,4 @@ +| Prop.field | | caption | | next | | y | diff --git a/csharp/ql/test/library-tests/properties/Properties17.ql b/csharp/ql/test/library-tests/properties/Properties17.ql index ca53f5423aa..6bd668ec118 100644 --- a/csharp/ql/test/library-tests/properties/Properties17.ql +++ b/csharp/ql/test/library-tests/properties/Properties17.ql @@ -1,5 +1,5 @@ /** - * @name Test that there are no backing fields + * @name Test that there are no backing fields except for properties that use the `field` keyword in their getter or setter. */ import csharp diff --git a/csharp/ql/test/library-tests/properties/properties.cs b/csharp/ql/test/library-tests/properties/properties.cs index 57ffa7a31a5..2f88214ec75 100644 --- a/csharp/ql/test/library-tests/properties/properties.cs +++ b/csharp/ql/test/library-tests/properties/properties.cs @@ -124,4 +124,13 @@ namespace Properties set { } } } + + class UseFieldKeyword + { + public object Prop + { + get { return field; } + set { field = value; } + } + } } From 97c026761478941374a9975de02709973177487a Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Thu, 12 Feb 2026 11:44:39 +0100 Subject: [PATCH 23/56] C#: Add data flow test for properties using the field keyword. --- .../test/library-tests/dataflow/fields/D.cs | 62 +++++ .../dataflow/fields/FieldFlow.expected | 254 ++++++++++++++++++ 2 files changed, 316 insertions(+) diff --git a/csharp/ql/test/library-tests/dataflow/fields/D.cs b/csharp/ql/test/library-tests/dataflow/fields/D.cs index 7f07cf5ca0b..45dfbffc801 100644 --- a/csharp/ql/test/library-tests/dataflow/fields/D.cs +++ b/csharp/ql/test/library-tests/dataflow/fields/D.cs @@ -89,3 +89,65 @@ public partial class DPartial static T Source(object source) => throw null; } + +public class DFieldProps +{ + object FieldProp0 + { + get { return field; } + set { field = value; } + } = Source(0); + + object FieldProp1 + { + get { return field; } + set { field = value; } + } + + object FieldProp2 + { + get { return field; } + set + { + var x = value; + field = x; + } + } + + static object StaticFieldProp + { + get { return field; } + set { field = value; } + } + + private void M() + { + var d0 = new DFieldProps(); + Sink(d0.FieldProp0); // $ hasValueFlow=0 + Sink(d0.FieldProp1); // no flow + Sink(d0.FieldProp2); // no flow + + var d1 = new DFieldProps(); + var o1 = Source(1); + d1.FieldProp1 = o1; + Sink(d1.FieldProp0); // $ hasValueFlow=0 + Sink(d1.FieldProp1); // $ hasValueFlow=1 + Sink(d1.FieldProp2); // no flow + + var d2 = new DFieldProps(); + var o2 = Source(2); + d2.FieldProp2 = o2; + Sink(d2.FieldProp0); // $ hasValueFlow=0 + Sink(d2.FieldProp1); // no flow + Sink(d2.FieldProp2); // $ hasValueFlow=2 + + var o3 = Source(3); + DFieldProps.StaticFieldProp = o3; + Sink(DFieldProps.StaticFieldProp); // $ hasValueFlow=3 + } + + public static void Sink(object o) { } + + static T Source(object source) => throw null; + +} diff --git a/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected b/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected index 4e469e11887..44789d1f847 100644 --- a/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected @@ -532,6 +532,118 @@ edges | D.cs:84:14:84:14 | access to local variable d : DPartial [field _backingField] : Object | D.cs:60:9:60:11 | this : DPartial [field _backingField] : Object | provenance | | | D.cs:84:14:84:14 | access to local variable d : DPartial [field _backingField] : Object | D.cs:84:14:84:27 | access to property PartialProp1 | provenance | | | D.cs:84:14:84:14 | access to local variable d : DPartial [field _backingField] : Object | D.cs:84:14:84:27 | access to property PartialProp1 | provenance | | +| D.cs:93:14:93:24 | [post] this access : DFieldProps [field FieldProp0.field] : Object | D.cs:125:18:125:34 | object creation of type DFieldProps : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:93:14:93:24 | [post] this access : DFieldProps [field FieldProp0.field] : Object | D.cs:125:18:125:34 | object creation of type DFieldProps : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:93:14:93:24 | [post] this access : DFieldProps [field FieldProp0.field] : Object | D.cs:130:18:130:34 | object creation of type DFieldProps : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:93:14:93:24 | [post] this access : DFieldProps [field FieldProp0.field] : Object | D.cs:130:18:130:34 | object creation of type DFieldProps : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:93:14:93:24 | [post] this access : DFieldProps [field FieldProp0.field] : Object | D.cs:137:18:137:34 | object creation of type DFieldProps : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:93:14:93:24 | [post] this access : DFieldProps [field FieldProp0.field] : Object | D.cs:137:18:137:34 | object creation of type DFieldProps : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:95:12:95:21 | [post] this access : DFieldProps [field FieldProp0.field] : Object | D.cs:93:14:93:24 | [post] this access : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:95:12:95:21 | [post] this access : DFieldProps [field FieldProp0.field] : Object | D.cs:93:14:93:24 | [post] this access : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:97:9:97:11 | this : DFieldProps [field FieldProp0.field] : Object | D.cs:97:22:97:26 | this access : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:97:9:97:11 | this : DFieldProps [field FieldProp0.field] : Object | D.cs:97:22:97:26 | this access : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:97:22:97:26 | this access : DFieldProps [field FieldProp0.field] : Object | D.cs:97:22:97:26 | access to field FieldProp0.field : Object | provenance | | +| D.cs:97:22:97:26 | this access : DFieldProps [field FieldProp0.field] : Object | D.cs:97:22:97:26 | access to field FieldProp0.field : Object | provenance | | +| D.cs:98:9:98:11 | value : Object | D.cs:98:23:98:27 | access to parameter value : Object | provenance | | +| D.cs:98:9:98:11 | value : Object | D.cs:98:23:98:27 | access to parameter value : Object | provenance | | +| D.cs:98:15:98:19 | [post] this access : DFieldProps [field FieldProp0.field] : Object | D.cs:98:9:98:11 | this [Return] : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:98:15:98:19 | [post] this access : DFieldProps [field FieldProp0.field] : Object | D.cs:98:9:98:11 | this [Return] : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:98:23:98:27 | access to parameter value : Object | D.cs:98:15:98:19 | [post] this access : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:98:23:98:27 | access to parameter value : Object | D.cs:98:15:98:19 | [post] this access : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:99:9:99:25 | call to method Source : Object | D.cs:95:12:95:21 | [post] this access : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:99:9:99:25 | call to method Source : Object | D.cs:95:12:95:21 | [post] this access : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:99:9:99:25 | call to method Source : Object | D.cs:98:9:98:11 | value : Object | provenance | | +| D.cs:99:9:99:25 | call to method Source : Object | D.cs:98:9:98:11 | value : Object | provenance | | +| D.cs:103:9:103:11 | this : DFieldProps [field FieldProp1.field] : Object | D.cs:103:22:103:26 | this access : DFieldProps [field FieldProp1.field] : Object | provenance | | +| D.cs:103:9:103:11 | this : DFieldProps [field FieldProp1.field] : Object | D.cs:103:22:103:26 | this access : DFieldProps [field FieldProp1.field] : Object | provenance | | +| D.cs:103:22:103:26 | this access : DFieldProps [field FieldProp1.field] : Object | D.cs:103:22:103:26 | access to field FieldProp1.field : Object | provenance | | +| D.cs:103:22:103:26 | this access : DFieldProps [field FieldProp1.field] : Object | D.cs:103:22:103:26 | access to field FieldProp1.field : Object | provenance | | +| D.cs:104:9:104:11 | value : Object | D.cs:104:23:104:27 | access to parameter value : Object | provenance | | +| D.cs:104:9:104:11 | value : Object | D.cs:104:23:104:27 | access to parameter value : Object | provenance | | +| D.cs:104:15:104:19 | [post] this access : DFieldProps [field FieldProp1.field] : Object | D.cs:104:9:104:11 | this [Return] : DFieldProps [field FieldProp1.field] : Object | provenance | | +| D.cs:104:15:104:19 | [post] this access : DFieldProps [field FieldProp1.field] : Object | D.cs:104:9:104:11 | this [Return] : DFieldProps [field FieldProp1.field] : Object | provenance | | +| D.cs:104:23:104:27 | access to parameter value : Object | D.cs:104:15:104:19 | [post] this access : DFieldProps [field FieldProp1.field] : Object | provenance | | +| D.cs:104:23:104:27 | access to parameter value : Object | D.cs:104:15:104:19 | [post] this access : DFieldProps [field FieldProp1.field] : Object | provenance | | +| D.cs:109:9:109:11 | this : DFieldProps [field FieldProp2.field] : Object | D.cs:109:22:109:26 | this access : DFieldProps [field FieldProp2.field] : Object | provenance | | +| D.cs:109:9:109:11 | this : DFieldProps [field FieldProp2.field] : Object | D.cs:109:22:109:26 | this access : DFieldProps [field FieldProp2.field] : Object | provenance | | +| D.cs:109:22:109:26 | this access : DFieldProps [field FieldProp2.field] : Object | D.cs:109:22:109:26 | access to field FieldProp2.field : Object | provenance | | +| D.cs:109:22:109:26 | this access : DFieldProps [field FieldProp2.field] : Object | D.cs:109:22:109:26 | access to field FieldProp2.field : Object | provenance | | +| D.cs:110:9:110:11 | value : Object | D.cs:112:17:112:17 | access to local variable x : Object | provenance | | +| D.cs:110:9:110:11 | value : Object | D.cs:112:17:112:17 | access to local variable x : Object | provenance | | +| D.cs:112:17:112:17 | access to local variable x : Object | D.cs:113:21:113:21 | access to local variable x : Object | provenance | | +| D.cs:112:17:112:17 | access to local variable x : Object | D.cs:113:21:113:21 | access to local variable x : Object | provenance | | +| D.cs:113:13:113:17 | [post] this access : DFieldProps [field FieldProp2.field] : Object | D.cs:110:9:110:11 | this [Return] : DFieldProps [field FieldProp2.field] : Object | provenance | | +| D.cs:113:13:113:17 | [post] this access : DFieldProps [field FieldProp2.field] : Object | D.cs:110:9:110:11 | this [Return] : DFieldProps [field FieldProp2.field] : Object | provenance | | +| D.cs:113:21:113:21 | access to local variable x : Object | D.cs:113:13:113:17 | [post] this access : DFieldProps [field FieldProp2.field] : Object | provenance | | +| D.cs:113:21:113:21 | access to local variable x : Object | D.cs:113:13:113:17 | [post] this access : DFieldProps [field FieldProp2.field] : Object | provenance | | +| D.cs:119:22:119:26 | access to field StaticFieldProp.field : Object | D.cs:146:14:146:40 | access to property StaticFieldProp | provenance | | +| D.cs:119:22:119:26 | access to field StaticFieldProp.field : Object | D.cs:146:14:146:40 | access to property StaticFieldProp | provenance | | +| D.cs:120:9:120:11 | value : Object | D.cs:120:23:120:27 | access to parameter value : Object | provenance | | +| D.cs:120:9:120:11 | value : Object | D.cs:120:23:120:27 | access to parameter value : Object | provenance | | +| D.cs:120:23:120:27 | access to parameter value : Object | D.cs:119:22:119:26 | access to field StaticFieldProp.field : Object | provenance | | +| D.cs:120:23:120:27 | access to parameter value : Object | D.cs:119:22:119:26 | access to field StaticFieldProp.field : Object | provenance | | +| D.cs:125:13:125:14 | access to local variable d0 : DFieldProps [field FieldProp0.field] : Object | D.cs:126:14:126:15 | access to local variable d0 : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:125:13:125:14 | access to local variable d0 : DFieldProps [field FieldProp0.field] : Object | D.cs:126:14:126:15 | access to local variable d0 : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:125:18:125:34 | object creation of type DFieldProps : DFieldProps [field FieldProp0.field] : Object | D.cs:125:13:125:14 | access to local variable d0 : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:125:18:125:34 | object creation of type DFieldProps : DFieldProps [field FieldProp0.field] : Object | D.cs:125:13:125:14 | access to local variable d0 : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:126:14:126:15 | access to local variable d0 : DFieldProps [field FieldProp0.field] : Object | D.cs:97:9:97:11 | this : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:126:14:126:15 | access to local variable d0 : DFieldProps [field FieldProp0.field] : Object | D.cs:97:9:97:11 | this : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:126:14:126:15 | access to local variable d0 : DFieldProps [field FieldProp0.field] : Object | D.cs:126:14:126:26 | access to property FieldProp0 | provenance | | +| D.cs:126:14:126:15 | access to local variable d0 : DFieldProps [field FieldProp0.field] : Object | D.cs:126:14:126:26 | access to property FieldProp0 | provenance | | +| D.cs:130:13:130:14 | access to local variable d1 : DFieldProps [field FieldProp0.field] : Object | D.cs:133:14:133:15 | access to local variable d1 : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:130:13:130:14 | access to local variable d1 : DFieldProps [field FieldProp0.field] : Object | D.cs:133:14:133:15 | access to local variable d1 : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:130:18:130:34 | object creation of type DFieldProps : DFieldProps [field FieldProp0.field] : Object | D.cs:130:13:130:14 | access to local variable d1 : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:130:18:130:34 | object creation of type DFieldProps : DFieldProps [field FieldProp0.field] : Object | D.cs:130:13:130:14 | access to local variable d1 : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:131:13:131:14 | access to local variable o1 : Object | D.cs:132:25:132:26 | access to local variable o1 : Object | provenance | | +| D.cs:131:13:131:14 | access to local variable o1 : Object | D.cs:132:25:132:26 | access to local variable o1 : Object | provenance | | +| D.cs:131:18:131:34 | call to method Source : Object | D.cs:131:13:131:14 | access to local variable o1 : Object | provenance | | +| D.cs:131:18:131:34 | call to method Source : Object | D.cs:131:13:131:14 | access to local variable o1 : Object | provenance | | +| D.cs:132:9:132:10 | [post] access to local variable d1 : DFieldProps [field FieldProp1.field] : Object | D.cs:134:14:134:15 | access to local variable d1 : DFieldProps [field FieldProp1.field] : Object | provenance | | +| D.cs:132:9:132:10 | [post] access to local variable d1 : DFieldProps [field FieldProp1.field] : Object | D.cs:134:14:134:15 | access to local variable d1 : DFieldProps [field FieldProp1.field] : Object | provenance | | +| D.cs:132:25:132:26 | access to local variable o1 : Object | D.cs:104:9:104:11 | value : Object | provenance | | +| D.cs:132:25:132:26 | access to local variable o1 : Object | D.cs:104:9:104:11 | value : Object | provenance | | +| D.cs:132:25:132:26 | access to local variable o1 : Object | D.cs:132:9:132:10 | [post] access to local variable d1 : DFieldProps [field FieldProp1.field] : Object | provenance | | +| D.cs:132:25:132:26 | access to local variable o1 : Object | D.cs:132:9:132:10 | [post] access to local variable d1 : DFieldProps [field FieldProp1.field] : Object | provenance | | +| D.cs:133:14:133:15 | access to local variable d1 : DFieldProps [field FieldProp0.field] : Object | D.cs:97:9:97:11 | this : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:133:14:133:15 | access to local variable d1 : DFieldProps [field FieldProp0.field] : Object | D.cs:97:9:97:11 | this : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:133:14:133:15 | access to local variable d1 : DFieldProps [field FieldProp0.field] : Object | D.cs:133:14:133:26 | access to property FieldProp0 | provenance | | +| D.cs:133:14:133:15 | access to local variable d1 : DFieldProps [field FieldProp0.field] : Object | D.cs:133:14:133:26 | access to property FieldProp0 | provenance | | +| D.cs:134:14:134:15 | access to local variable d1 : DFieldProps [field FieldProp1.field] : Object | D.cs:103:9:103:11 | this : DFieldProps [field FieldProp1.field] : Object | provenance | | +| D.cs:134:14:134:15 | access to local variable d1 : DFieldProps [field FieldProp1.field] : Object | D.cs:103:9:103:11 | this : DFieldProps [field FieldProp1.field] : Object | provenance | | +| D.cs:134:14:134:15 | access to local variable d1 : DFieldProps [field FieldProp1.field] : Object | D.cs:134:14:134:26 | access to property FieldProp1 | provenance | | +| D.cs:134:14:134:15 | access to local variable d1 : DFieldProps [field FieldProp1.field] : Object | D.cs:134:14:134:26 | access to property FieldProp1 | provenance | | +| D.cs:137:13:137:14 | access to local variable d2 : DFieldProps [field FieldProp0.field] : Object | D.cs:140:14:140:15 | access to local variable d2 : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:137:13:137:14 | access to local variable d2 : DFieldProps [field FieldProp0.field] : Object | D.cs:140:14:140:15 | access to local variable d2 : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:137:18:137:34 | object creation of type DFieldProps : DFieldProps [field FieldProp0.field] : Object | D.cs:137:13:137:14 | access to local variable d2 : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:137:18:137:34 | object creation of type DFieldProps : DFieldProps [field FieldProp0.field] : Object | D.cs:137:13:137:14 | access to local variable d2 : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:138:13:138:14 | access to local variable o2 : Object | D.cs:139:25:139:26 | access to local variable o2 : Object | provenance | | +| D.cs:138:13:138:14 | access to local variable o2 : Object | D.cs:139:25:139:26 | access to local variable o2 : Object | provenance | | +| D.cs:138:18:138:34 | call to method Source : Object | D.cs:138:13:138:14 | access to local variable o2 : Object | provenance | | +| D.cs:138:18:138:34 | call to method Source : Object | D.cs:138:13:138:14 | access to local variable o2 : Object | provenance | | +| D.cs:139:9:139:10 | [post] access to local variable d2 : DFieldProps [field FieldProp2.field] : Object | D.cs:142:14:142:15 | access to local variable d2 : DFieldProps [field FieldProp2.field] : Object | provenance | | +| D.cs:139:9:139:10 | [post] access to local variable d2 : DFieldProps [field FieldProp2.field] : Object | D.cs:142:14:142:15 | access to local variable d2 : DFieldProps [field FieldProp2.field] : Object | provenance | | +| D.cs:139:25:139:26 | access to local variable o2 : Object | D.cs:110:9:110:11 | value : Object | provenance | | +| D.cs:139:25:139:26 | access to local variable o2 : Object | D.cs:110:9:110:11 | value : Object | provenance | | +| D.cs:139:25:139:26 | access to local variable o2 : Object | D.cs:139:9:139:10 | [post] access to local variable d2 : DFieldProps [field FieldProp2.field] : Object | provenance | | +| D.cs:139:25:139:26 | access to local variable o2 : Object | D.cs:139:9:139:10 | [post] access to local variable d2 : DFieldProps [field FieldProp2.field] : Object | provenance | | +| D.cs:140:14:140:15 | access to local variable d2 : DFieldProps [field FieldProp0.field] : Object | D.cs:97:9:97:11 | this : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:140:14:140:15 | access to local variable d2 : DFieldProps [field FieldProp0.field] : Object | D.cs:97:9:97:11 | this : DFieldProps [field FieldProp0.field] : Object | provenance | | +| D.cs:140:14:140:15 | access to local variable d2 : DFieldProps [field FieldProp0.field] : Object | D.cs:140:14:140:26 | access to property FieldProp0 | provenance | | +| D.cs:140:14:140:15 | access to local variable d2 : DFieldProps [field FieldProp0.field] : Object | D.cs:140:14:140:26 | access to property FieldProp0 | provenance | | +| D.cs:142:14:142:15 | access to local variable d2 : DFieldProps [field FieldProp2.field] : Object | D.cs:109:9:109:11 | this : DFieldProps [field FieldProp2.field] : Object | provenance | | +| D.cs:142:14:142:15 | access to local variable d2 : DFieldProps [field FieldProp2.field] : Object | D.cs:109:9:109:11 | this : DFieldProps [field FieldProp2.field] : Object | provenance | | +| D.cs:142:14:142:15 | access to local variable d2 : DFieldProps [field FieldProp2.field] : Object | D.cs:142:14:142:26 | access to property FieldProp2 | provenance | | +| D.cs:142:14:142:15 | access to local variable d2 : DFieldProps [field FieldProp2.field] : Object | D.cs:142:14:142:26 | access to property FieldProp2 | provenance | | +| D.cs:144:13:144:14 | access to local variable o3 : Object | D.cs:145:9:145:35 | access to property StaticFieldProp : Object | provenance | | +| D.cs:144:13:144:14 | access to local variable o3 : Object | D.cs:145:9:145:35 | access to property StaticFieldProp : Object | provenance | | +| D.cs:144:13:144:14 | access to local variable o3 : Object | D.cs:145:39:145:40 | access to local variable o3 : Object | provenance | | +| D.cs:144:13:144:14 | access to local variable o3 : Object | D.cs:145:39:145:40 | access to local variable o3 : Object | provenance | | +| D.cs:144:18:144:34 | call to method Source : Object | D.cs:144:13:144:14 | access to local variable o3 : Object | provenance | | +| D.cs:144:18:144:34 | call to method Source : Object | D.cs:144:13:144:14 | access to local variable o3 : Object | provenance | | +| D.cs:145:9:145:35 | access to property StaticFieldProp : Object | D.cs:146:14:146:40 | access to property StaticFieldProp | provenance | | +| D.cs:145:9:145:35 | access to property StaticFieldProp : Object | D.cs:146:14:146:40 | access to property StaticFieldProp | provenance | | +| D.cs:145:39:145:40 | access to local variable o3 : Object | D.cs:120:9:120:11 | value : Object | provenance | | +| D.cs:145:39:145:40 | access to local variable o3 : Object | D.cs:120:9:120:11 | value : Object | provenance | | | E.cs:8:29:8:29 | o : Object | E.cs:11:21:11:21 | access to parameter o : Object | provenance | | | E.cs:8:29:8:29 | o : Object | E.cs:11:21:11:21 | access to parameter o : Object | provenance | | | E.cs:11:9:11:11 | [post] access to local variable ret : S [field Field] : Object | E.cs:12:16:12:18 | access to local variable ret : S [field Field] : Object | provenance | | @@ -1807,6 +1919,120 @@ nodes | D.cs:84:14:84:14 | access to local variable d : DPartial [field _backingField] : Object | semmle.label | access to local variable d : DPartial [field _backingField] : Object | | D.cs:84:14:84:27 | access to property PartialProp1 | semmle.label | access to property PartialProp1 | | D.cs:84:14:84:27 | access to property PartialProp1 | semmle.label | access to property PartialProp1 | +| D.cs:93:14:93:24 | [post] this access : DFieldProps [field FieldProp0.field] : Object | semmle.label | [post] this access : DFieldProps [field FieldProp0.field] : Object | +| D.cs:93:14:93:24 | [post] this access : DFieldProps [field FieldProp0.field] : Object | semmle.label | [post] this access : DFieldProps [field FieldProp0.field] : Object | +| D.cs:95:12:95:21 | [post] this access : DFieldProps [field FieldProp0.field] : Object | semmle.label | [post] this access : DFieldProps [field FieldProp0.field] : Object | +| D.cs:95:12:95:21 | [post] this access : DFieldProps [field FieldProp0.field] : Object | semmle.label | [post] this access : DFieldProps [field FieldProp0.field] : Object | +| D.cs:97:9:97:11 | this : DFieldProps [field FieldProp0.field] : Object | semmle.label | this : DFieldProps [field FieldProp0.field] : Object | +| D.cs:97:9:97:11 | this : DFieldProps [field FieldProp0.field] : Object | semmle.label | this : DFieldProps [field FieldProp0.field] : Object | +| D.cs:97:22:97:26 | access to field FieldProp0.field : Object | semmle.label | access to field FieldProp0.field : Object | +| D.cs:97:22:97:26 | access to field FieldProp0.field : Object | semmle.label | access to field FieldProp0.field : Object | +| D.cs:97:22:97:26 | this access : DFieldProps [field FieldProp0.field] : Object | semmle.label | this access : DFieldProps [field FieldProp0.field] : Object | +| D.cs:97:22:97:26 | this access : DFieldProps [field FieldProp0.field] : Object | semmle.label | this access : DFieldProps [field FieldProp0.field] : Object | +| D.cs:98:9:98:11 | this [Return] : DFieldProps [field FieldProp0.field] : Object | semmle.label | this [Return] : DFieldProps [field FieldProp0.field] : Object | +| D.cs:98:9:98:11 | this [Return] : DFieldProps [field FieldProp0.field] : Object | semmle.label | this [Return] : DFieldProps [field FieldProp0.field] : Object | +| D.cs:98:9:98:11 | value : Object | semmle.label | value : Object | +| D.cs:98:9:98:11 | value : Object | semmle.label | value : Object | +| D.cs:98:15:98:19 | [post] this access : DFieldProps [field FieldProp0.field] : Object | semmle.label | [post] this access : DFieldProps [field FieldProp0.field] : Object | +| D.cs:98:15:98:19 | [post] this access : DFieldProps [field FieldProp0.field] : Object | semmle.label | [post] this access : DFieldProps [field FieldProp0.field] : Object | +| D.cs:98:23:98:27 | access to parameter value : Object | semmle.label | access to parameter value : Object | +| D.cs:98:23:98:27 | access to parameter value : Object | semmle.label | access to parameter value : Object | +| D.cs:99:9:99:25 | call to method Source : Object | semmle.label | call to method Source : Object | +| D.cs:99:9:99:25 | call to method Source : Object | semmle.label | call to method Source : Object | +| D.cs:103:9:103:11 | this : DFieldProps [field FieldProp1.field] : Object | semmle.label | this : DFieldProps [field FieldProp1.field] : Object | +| D.cs:103:9:103:11 | this : DFieldProps [field FieldProp1.field] : Object | semmle.label | this : DFieldProps [field FieldProp1.field] : Object | +| D.cs:103:22:103:26 | access to field FieldProp1.field : Object | semmle.label | access to field FieldProp1.field : Object | +| D.cs:103:22:103:26 | access to field FieldProp1.field : Object | semmle.label | access to field FieldProp1.field : Object | +| D.cs:103:22:103:26 | this access : DFieldProps [field FieldProp1.field] : Object | semmle.label | this access : DFieldProps [field FieldProp1.field] : Object | +| D.cs:103:22:103:26 | this access : DFieldProps [field FieldProp1.field] : Object | semmle.label | this access : DFieldProps [field FieldProp1.field] : Object | +| D.cs:104:9:104:11 | this [Return] : DFieldProps [field FieldProp1.field] : Object | semmle.label | this [Return] : DFieldProps [field FieldProp1.field] : Object | +| D.cs:104:9:104:11 | this [Return] : DFieldProps [field FieldProp1.field] : Object | semmle.label | this [Return] : DFieldProps [field FieldProp1.field] : Object | +| D.cs:104:9:104:11 | value : Object | semmle.label | value : Object | +| D.cs:104:9:104:11 | value : Object | semmle.label | value : Object | +| D.cs:104:15:104:19 | [post] this access : DFieldProps [field FieldProp1.field] : Object | semmle.label | [post] this access : DFieldProps [field FieldProp1.field] : Object | +| D.cs:104:15:104:19 | [post] this access : DFieldProps [field FieldProp1.field] : Object | semmle.label | [post] this access : DFieldProps [field FieldProp1.field] : Object | +| D.cs:104:23:104:27 | access to parameter value : Object | semmle.label | access to parameter value : Object | +| D.cs:104:23:104:27 | access to parameter value : Object | semmle.label | access to parameter value : Object | +| D.cs:109:9:109:11 | this : DFieldProps [field FieldProp2.field] : Object | semmle.label | this : DFieldProps [field FieldProp2.field] : Object | +| D.cs:109:9:109:11 | this : DFieldProps [field FieldProp2.field] : Object | semmle.label | this : DFieldProps [field FieldProp2.field] : Object | +| D.cs:109:22:109:26 | access to field FieldProp2.field : Object | semmle.label | access to field FieldProp2.field : Object | +| D.cs:109:22:109:26 | access to field FieldProp2.field : Object | semmle.label | access to field FieldProp2.field : Object | +| D.cs:109:22:109:26 | this access : DFieldProps [field FieldProp2.field] : Object | semmle.label | this access : DFieldProps [field FieldProp2.field] : Object | +| D.cs:109:22:109:26 | this access : DFieldProps [field FieldProp2.field] : Object | semmle.label | this access : DFieldProps [field FieldProp2.field] : Object | +| D.cs:110:9:110:11 | this [Return] : DFieldProps [field FieldProp2.field] : Object | semmle.label | this [Return] : DFieldProps [field FieldProp2.field] : Object | +| D.cs:110:9:110:11 | this [Return] : DFieldProps [field FieldProp2.field] : Object | semmle.label | this [Return] : DFieldProps [field FieldProp2.field] : Object | +| D.cs:110:9:110:11 | value : Object | semmle.label | value : Object | +| D.cs:110:9:110:11 | value : Object | semmle.label | value : Object | +| D.cs:112:17:112:17 | access to local variable x : Object | semmle.label | access to local variable x : Object | +| D.cs:112:17:112:17 | access to local variable x : Object | semmle.label | access to local variable x : Object | +| D.cs:113:13:113:17 | [post] this access : DFieldProps [field FieldProp2.field] : Object | semmle.label | [post] this access : DFieldProps [field FieldProp2.field] : Object | +| D.cs:113:13:113:17 | [post] this access : DFieldProps [field FieldProp2.field] : Object | semmle.label | [post] this access : DFieldProps [field FieldProp2.field] : Object | +| D.cs:113:21:113:21 | access to local variable x : Object | semmle.label | access to local variable x : Object | +| D.cs:113:21:113:21 | access to local variable x : Object | semmle.label | access to local variable x : Object | +| D.cs:119:22:119:26 | access to field StaticFieldProp.field : Object | semmle.label | access to field StaticFieldProp.field : Object | +| D.cs:119:22:119:26 | access to field StaticFieldProp.field : Object | semmle.label | access to field StaticFieldProp.field : Object | +| D.cs:120:9:120:11 | value : Object | semmle.label | value : Object | +| D.cs:120:9:120:11 | value : Object | semmle.label | value : Object | +| D.cs:120:23:120:27 | access to parameter value : Object | semmle.label | access to parameter value : Object | +| D.cs:120:23:120:27 | access to parameter value : Object | semmle.label | access to parameter value : Object | +| D.cs:125:13:125:14 | access to local variable d0 : DFieldProps [field FieldProp0.field] : Object | semmle.label | access to local variable d0 : DFieldProps [field FieldProp0.field] : Object | +| D.cs:125:13:125:14 | access to local variable d0 : DFieldProps [field FieldProp0.field] : Object | semmle.label | access to local variable d0 : DFieldProps [field FieldProp0.field] : Object | +| D.cs:125:18:125:34 | object creation of type DFieldProps : DFieldProps [field FieldProp0.field] : Object | semmle.label | object creation of type DFieldProps : DFieldProps [field FieldProp0.field] : Object | +| D.cs:125:18:125:34 | object creation of type DFieldProps : DFieldProps [field FieldProp0.field] : Object | semmle.label | object creation of type DFieldProps : DFieldProps [field FieldProp0.field] : Object | +| D.cs:126:14:126:15 | access to local variable d0 : DFieldProps [field FieldProp0.field] : Object | semmle.label | access to local variable d0 : DFieldProps [field FieldProp0.field] : Object | +| D.cs:126:14:126:15 | access to local variable d0 : DFieldProps [field FieldProp0.field] : Object | semmle.label | access to local variable d0 : DFieldProps [field FieldProp0.field] : Object | +| D.cs:126:14:126:26 | access to property FieldProp0 | semmle.label | access to property FieldProp0 | +| D.cs:126:14:126:26 | access to property FieldProp0 | semmle.label | access to property FieldProp0 | +| D.cs:130:13:130:14 | access to local variable d1 : DFieldProps [field FieldProp0.field] : Object | semmle.label | access to local variable d1 : DFieldProps [field FieldProp0.field] : Object | +| D.cs:130:13:130:14 | access to local variable d1 : DFieldProps [field FieldProp0.field] : Object | semmle.label | access to local variable d1 : DFieldProps [field FieldProp0.field] : Object | +| D.cs:130:18:130:34 | object creation of type DFieldProps : DFieldProps [field FieldProp0.field] : Object | semmle.label | object creation of type DFieldProps : DFieldProps [field FieldProp0.field] : Object | +| D.cs:130:18:130:34 | object creation of type DFieldProps : DFieldProps [field FieldProp0.field] : Object | semmle.label | object creation of type DFieldProps : DFieldProps [field FieldProp0.field] : Object | +| D.cs:131:13:131:14 | access to local variable o1 : Object | semmle.label | access to local variable o1 : Object | +| D.cs:131:13:131:14 | access to local variable o1 : Object | semmle.label | access to local variable o1 : Object | +| D.cs:131:18:131:34 | call to method Source : Object | semmle.label | call to method Source : Object | +| D.cs:131:18:131:34 | call to method Source : Object | semmle.label | call to method Source : Object | +| D.cs:132:9:132:10 | [post] access to local variable d1 : DFieldProps [field FieldProp1.field] : Object | semmle.label | [post] access to local variable d1 : DFieldProps [field FieldProp1.field] : Object | +| D.cs:132:9:132:10 | [post] access to local variable d1 : DFieldProps [field FieldProp1.field] : Object | semmle.label | [post] access to local variable d1 : DFieldProps [field FieldProp1.field] : Object | +| D.cs:132:25:132:26 | access to local variable o1 : Object | semmle.label | access to local variable o1 : Object | +| D.cs:132:25:132:26 | access to local variable o1 : Object | semmle.label | access to local variable o1 : Object | +| D.cs:133:14:133:15 | access to local variable d1 : DFieldProps [field FieldProp0.field] : Object | semmle.label | access to local variable d1 : DFieldProps [field FieldProp0.field] : Object | +| D.cs:133:14:133:15 | access to local variable d1 : DFieldProps [field FieldProp0.field] : Object | semmle.label | access to local variable d1 : DFieldProps [field FieldProp0.field] : Object | +| D.cs:133:14:133:26 | access to property FieldProp0 | semmle.label | access to property FieldProp0 | +| D.cs:133:14:133:26 | access to property FieldProp0 | semmle.label | access to property FieldProp0 | +| D.cs:134:14:134:15 | access to local variable d1 : DFieldProps [field FieldProp1.field] : Object | semmle.label | access to local variable d1 : DFieldProps [field FieldProp1.field] : Object | +| D.cs:134:14:134:15 | access to local variable d1 : DFieldProps [field FieldProp1.field] : Object | semmle.label | access to local variable d1 : DFieldProps [field FieldProp1.field] : Object | +| D.cs:134:14:134:26 | access to property FieldProp1 | semmle.label | access to property FieldProp1 | +| D.cs:134:14:134:26 | access to property FieldProp1 | semmle.label | access to property FieldProp1 | +| D.cs:137:13:137:14 | access to local variable d2 : DFieldProps [field FieldProp0.field] : Object | semmle.label | access to local variable d2 : DFieldProps [field FieldProp0.field] : Object | +| D.cs:137:13:137:14 | access to local variable d2 : DFieldProps [field FieldProp0.field] : Object | semmle.label | access to local variable d2 : DFieldProps [field FieldProp0.field] : Object | +| D.cs:137:18:137:34 | object creation of type DFieldProps : DFieldProps [field FieldProp0.field] : Object | semmle.label | object creation of type DFieldProps : DFieldProps [field FieldProp0.field] : Object | +| D.cs:137:18:137:34 | object creation of type DFieldProps : DFieldProps [field FieldProp0.field] : Object | semmle.label | object creation of type DFieldProps : DFieldProps [field FieldProp0.field] : Object | +| D.cs:138:13:138:14 | access to local variable o2 : Object | semmle.label | access to local variable o2 : Object | +| D.cs:138:13:138:14 | access to local variable o2 : Object | semmle.label | access to local variable o2 : Object | +| D.cs:138:18:138:34 | call to method Source : Object | semmle.label | call to method Source : Object | +| D.cs:138:18:138:34 | call to method Source : Object | semmle.label | call to method Source : Object | +| D.cs:139:9:139:10 | [post] access to local variable d2 : DFieldProps [field FieldProp2.field] : Object | semmle.label | [post] access to local variable d2 : DFieldProps [field FieldProp2.field] : Object | +| D.cs:139:9:139:10 | [post] access to local variable d2 : DFieldProps [field FieldProp2.field] : Object | semmle.label | [post] access to local variable d2 : DFieldProps [field FieldProp2.field] : Object | +| D.cs:139:25:139:26 | access to local variable o2 : Object | semmle.label | access to local variable o2 : Object | +| D.cs:139:25:139:26 | access to local variable o2 : Object | semmle.label | access to local variable o2 : Object | +| D.cs:140:14:140:15 | access to local variable d2 : DFieldProps [field FieldProp0.field] : Object | semmle.label | access to local variable d2 : DFieldProps [field FieldProp0.field] : Object | +| D.cs:140:14:140:15 | access to local variable d2 : DFieldProps [field FieldProp0.field] : Object | semmle.label | access to local variable d2 : DFieldProps [field FieldProp0.field] : Object | +| D.cs:140:14:140:26 | access to property FieldProp0 | semmle.label | access to property FieldProp0 | +| D.cs:140:14:140:26 | access to property FieldProp0 | semmle.label | access to property FieldProp0 | +| D.cs:142:14:142:15 | access to local variable d2 : DFieldProps [field FieldProp2.field] : Object | semmle.label | access to local variable d2 : DFieldProps [field FieldProp2.field] : Object | +| D.cs:142:14:142:15 | access to local variable d2 : DFieldProps [field FieldProp2.field] : Object | semmle.label | access to local variable d2 : DFieldProps [field FieldProp2.field] : Object | +| D.cs:142:14:142:26 | access to property FieldProp2 | semmle.label | access to property FieldProp2 | +| D.cs:142:14:142:26 | access to property FieldProp2 | semmle.label | access to property FieldProp2 | +| D.cs:144:13:144:14 | access to local variable o3 : Object | semmle.label | access to local variable o3 : Object | +| D.cs:144:13:144:14 | access to local variable o3 : Object | semmle.label | access to local variable o3 : Object | +| D.cs:144:18:144:34 | call to method Source : Object | semmle.label | call to method Source : Object | +| D.cs:144:18:144:34 | call to method Source : Object | semmle.label | call to method Source : Object | +| D.cs:145:9:145:35 | access to property StaticFieldProp : Object | semmle.label | access to property StaticFieldProp : Object | +| D.cs:145:9:145:35 | access to property StaticFieldProp : Object | semmle.label | access to property StaticFieldProp : Object | +| D.cs:145:39:145:40 | access to local variable o3 : Object | semmle.label | access to local variable o3 : Object | +| D.cs:145:39:145:40 | access to local variable o3 : Object | semmle.label | access to local variable o3 : Object | +| D.cs:146:14:146:40 | access to property StaticFieldProp | semmle.label | access to property StaticFieldProp | +| D.cs:146:14:146:40 | access to property StaticFieldProp | semmle.label | access to property StaticFieldProp | | E.cs:8:29:8:29 | o : Object | semmle.label | o : Object | | E.cs:8:29:8:29 | o : Object | semmle.label | o : Object | | E.cs:11:9:11:11 | [post] access to local variable ret : S [field Field] : Object | semmle.label | [post] access to local variable ret : S [field Field] : Object | @@ -2648,6 +2874,22 @@ subpaths | D.cs:81:26:81:26 | access to local variable o : Object | D.cs:61:9:61:11 | value : Object | D.cs:61:9:61:11 | this [Return] : DPartial [field _backingField] : Object | D.cs:81:9:81:9 | [post] access to local variable d : DPartial [field _backingField] : Object | | D.cs:84:14:84:14 | access to local variable d : DPartial [field _backingField] : Object | D.cs:60:9:60:11 | this : DPartial [field _backingField] : Object | D.cs:60:22:60:34 | access to field _backingField : Object | D.cs:84:14:84:27 | access to property PartialProp1 | | D.cs:84:14:84:14 | access to local variable d : DPartial [field _backingField] : Object | D.cs:60:9:60:11 | this : DPartial [field _backingField] : Object | D.cs:60:22:60:34 | access to field _backingField : Object | D.cs:84:14:84:27 | access to property PartialProp1 | +| D.cs:99:9:99:25 | call to method Source : Object | D.cs:98:9:98:11 | value : Object | D.cs:98:9:98:11 | this [Return] : DFieldProps [field FieldProp0.field] : Object | D.cs:95:12:95:21 | [post] this access : DFieldProps [field FieldProp0.field] : Object | +| D.cs:99:9:99:25 | call to method Source : Object | D.cs:98:9:98:11 | value : Object | D.cs:98:9:98:11 | this [Return] : DFieldProps [field FieldProp0.field] : Object | D.cs:95:12:95:21 | [post] this access : DFieldProps [field FieldProp0.field] : Object | +| D.cs:126:14:126:15 | access to local variable d0 : DFieldProps [field FieldProp0.field] : Object | D.cs:97:9:97:11 | this : DFieldProps [field FieldProp0.field] : Object | D.cs:97:22:97:26 | access to field FieldProp0.field : Object | D.cs:126:14:126:26 | access to property FieldProp0 | +| D.cs:126:14:126:15 | access to local variable d0 : DFieldProps [field FieldProp0.field] : Object | D.cs:97:9:97:11 | this : DFieldProps [field FieldProp0.field] : Object | D.cs:97:22:97:26 | access to field FieldProp0.field : Object | D.cs:126:14:126:26 | access to property FieldProp0 | +| D.cs:132:25:132:26 | access to local variable o1 : Object | D.cs:104:9:104:11 | value : Object | D.cs:104:9:104:11 | this [Return] : DFieldProps [field FieldProp1.field] : Object | D.cs:132:9:132:10 | [post] access to local variable d1 : DFieldProps [field FieldProp1.field] : Object | +| D.cs:132:25:132:26 | access to local variable o1 : Object | D.cs:104:9:104:11 | value : Object | D.cs:104:9:104:11 | this [Return] : DFieldProps [field FieldProp1.field] : Object | D.cs:132:9:132:10 | [post] access to local variable d1 : DFieldProps [field FieldProp1.field] : Object | +| D.cs:133:14:133:15 | access to local variable d1 : DFieldProps [field FieldProp0.field] : Object | D.cs:97:9:97:11 | this : DFieldProps [field FieldProp0.field] : Object | D.cs:97:22:97:26 | access to field FieldProp0.field : Object | D.cs:133:14:133:26 | access to property FieldProp0 | +| D.cs:133:14:133:15 | access to local variable d1 : DFieldProps [field FieldProp0.field] : Object | D.cs:97:9:97:11 | this : DFieldProps [field FieldProp0.field] : Object | D.cs:97:22:97:26 | access to field FieldProp0.field : Object | D.cs:133:14:133:26 | access to property FieldProp0 | +| D.cs:134:14:134:15 | access to local variable d1 : DFieldProps [field FieldProp1.field] : Object | D.cs:103:9:103:11 | this : DFieldProps [field FieldProp1.field] : Object | D.cs:103:22:103:26 | access to field FieldProp1.field : Object | D.cs:134:14:134:26 | access to property FieldProp1 | +| D.cs:134:14:134:15 | access to local variable d1 : DFieldProps [field FieldProp1.field] : Object | D.cs:103:9:103:11 | this : DFieldProps [field FieldProp1.field] : Object | D.cs:103:22:103:26 | access to field FieldProp1.field : Object | D.cs:134:14:134:26 | access to property FieldProp1 | +| D.cs:139:25:139:26 | access to local variable o2 : Object | D.cs:110:9:110:11 | value : Object | D.cs:110:9:110:11 | this [Return] : DFieldProps [field FieldProp2.field] : Object | D.cs:139:9:139:10 | [post] access to local variable d2 : DFieldProps [field FieldProp2.field] : Object | +| D.cs:139:25:139:26 | access to local variable o2 : Object | D.cs:110:9:110:11 | value : Object | D.cs:110:9:110:11 | this [Return] : DFieldProps [field FieldProp2.field] : Object | D.cs:139:9:139:10 | [post] access to local variable d2 : DFieldProps [field FieldProp2.field] : Object | +| D.cs:140:14:140:15 | access to local variable d2 : DFieldProps [field FieldProp0.field] : Object | D.cs:97:9:97:11 | this : DFieldProps [field FieldProp0.field] : Object | D.cs:97:22:97:26 | access to field FieldProp0.field : Object | D.cs:140:14:140:26 | access to property FieldProp0 | +| D.cs:140:14:140:15 | access to local variable d2 : DFieldProps [field FieldProp0.field] : Object | D.cs:97:9:97:11 | this : DFieldProps [field FieldProp0.field] : Object | D.cs:97:22:97:26 | access to field FieldProp0.field : Object | D.cs:140:14:140:26 | access to property FieldProp0 | +| D.cs:142:14:142:15 | access to local variable d2 : DFieldProps [field FieldProp2.field] : Object | D.cs:109:9:109:11 | this : DFieldProps [field FieldProp2.field] : Object | D.cs:109:22:109:26 | access to field FieldProp2.field : Object | D.cs:142:14:142:26 | access to property FieldProp2 | +| D.cs:142:14:142:15 | access to local variable d2 : DFieldProps [field FieldProp2.field] : Object | D.cs:109:9:109:11 | this : DFieldProps [field FieldProp2.field] : Object | D.cs:109:22:109:26 | access to field FieldProp2.field : Object | D.cs:142:14:142:26 | access to property FieldProp2 | | E.cs:23:25:23:25 | access to local variable o : Object | E.cs:8:29:8:29 | o : Object | E.cs:12:16:12:18 | access to local variable ret : S [field Field] : Object | E.cs:23:17:23:26 | call to method CreateS : S [field Field] : Object | | E.cs:23:25:23:25 | access to local variable o : Object | E.cs:8:29:8:29 | o : Object | E.cs:12:16:12:18 | access to local variable ret : S [field Field] : Object | E.cs:23:17:23:26 | call to method CreateS : S [field Field] : Object | | E.cs:55:29:55:33 | access to local variable taint : Object | E.cs:43:46:43:46 | o : Object | E.cs:43:36:43:36 | s [Return] : RefS [field RefField] : Object | E.cs:55:23:55:26 | [post] access to local variable refs : RefS [field RefField] : Object | @@ -2758,6 +3000,18 @@ testFailures | D.cs:47:14:47:26 | access to property ComplexProp | D.cs:43:32:43:48 | call to method Source : Object | D.cs:47:14:47:26 | access to property ComplexProp | $@ | D.cs:43:32:43:48 | call to method Source : Object | call to method Source : Object | | D.cs:84:14:84:27 | access to property PartialProp1 | D.cs:78:17:78:33 | call to method Source : Object | D.cs:84:14:84:27 | access to property PartialProp1 | $@ | D.cs:78:17:78:33 | call to method Source : Object | call to method Source : Object | | D.cs:84:14:84:27 | access to property PartialProp1 | D.cs:78:17:78:33 | call to method Source : Object | D.cs:84:14:84:27 | access to property PartialProp1 | $@ | D.cs:78:17:78:33 | call to method Source : Object | call to method Source : Object | +| D.cs:126:14:126:26 | access to property FieldProp0 | D.cs:99:9:99:25 | call to method Source : Object | D.cs:126:14:126:26 | access to property FieldProp0 | $@ | D.cs:99:9:99:25 | call to method Source : Object | call to method Source : Object | +| D.cs:126:14:126:26 | access to property FieldProp0 | D.cs:99:9:99:25 | call to method Source : Object | D.cs:126:14:126:26 | access to property FieldProp0 | $@ | D.cs:99:9:99:25 | call to method Source : Object | call to method Source : Object | +| D.cs:133:14:133:26 | access to property FieldProp0 | D.cs:99:9:99:25 | call to method Source : Object | D.cs:133:14:133:26 | access to property FieldProp0 | $@ | D.cs:99:9:99:25 | call to method Source : Object | call to method Source : Object | +| D.cs:133:14:133:26 | access to property FieldProp0 | D.cs:99:9:99:25 | call to method Source : Object | D.cs:133:14:133:26 | access to property FieldProp0 | $@ | D.cs:99:9:99:25 | call to method Source : Object | call to method Source : Object | +| D.cs:134:14:134:26 | access to property FieldProp1 | D.cs:131:18:131:34 | call to method Source : Object | D.cs:134:14:134:26 | access to property FieldProp1 | $@ | D.cs:131:18:131:34 | call to method Source : Object | call to method Source : Object | +| D.cs:134:14:134:26 | access to property FieldProp1 | D.cs:131:18:131:34 | call to method Source : Object | D.cs:134:14:134:26 | access to property FieldProp1 | $@ | D.cs:131:18:131:34 | call to method Source : Object | call to method Source : Object | +| D.cs:140:14:140:26 | access to property FieldProp0 | D.cs:99:9:99:25 | call to method Source : Object | D.cs:140:14:140:26 | access to property FieldProp0 | $@ | D.cs:99:9:99:25 | call to method Source : Object | call to method Source : Object | +| D.cs:140:14:140:26 | access to property FieldProp0 | D.cs:99:9:99:25 | call to method Source : Object | D.cs:140:14:140:26 | access to property FieldProp0 | $@ | D.cs:99:9:99:25 | call to method Source : Object | call to method Source : Object | +| D.cs:142:14:142:26 | access to property FieldProp2 | D.cs:138:18:138:34 | call to method Source : Object | D.cs:142:14:142:26 | access to property FieldProp2 | $@ | D.cs:138:18:138:34 | call to method Source : Object | call to method Source : Object | +| D.cs:142:14:142:26 | access to property FieldProp2 | D.cs:138:18:138:34 | call to method Source : Object | D.cs:142:14:142:26 | access to property FieldProp2 | $@ | D.cs:138:18:138:34 | call to method Source : Object | call to method Source : Object | +| D.cs:146:14:146:40 | access to property StaticFieldProp | D.cs:144:18:144:34 | call to method Source : Object | D.cs:146:14:146:40 | access to property StaticFieldProp | $@ | D.cs:144:18:144:34 | call to method Source : Object | call to method Source : Object | +| D.cs:146:14:146:40 | access to property StaticFieldProp | D.cs:144:18:144:34 | call to method Source : Object | D.cs:146:14:146:40 | access to property StaticFieldProp | $@ | D.cs:144:18:144:34 | call to method Source : Object | call to method Source : Object | | E.cs:24:14:24:20 | access to field Field | E.cs:22:17:22:33 | call to method Source : Object | E.cs:24:14:24:20 | access to field Field | $@ | E.cs:22:17:22:33 | call to method Source : Object | call to method Source : Object | | E.cs:24:14:24:20 | access to field Field | E.cs:22:17:22:33 | call to method Source : Object | E.cs:24:14:24:20 | access to field Field | $@ | E.cs:22:17:22:33 | call to method Source : Object | call to method Source : Object | | E.cs:57:14:57:26 | access to field RefField | E.cs:54:21:54:37 | call to method Source : Object | E.cs:57:14:57:26 | access to field RefField | $@ | E.cs:54:21:54:37 | call to method Source : Object | call to method Source : Object | From d93f4850df3c46b5dcb61a033afde056bebea8c0 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Thu, 12 Feb 2026 11:53:05 +0100 Subject: [PATCH 24/56] C#: Add change-note. --- csharp/ql/lib/change-notes/2026-02-12-field-keyword.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 csharp/ql/lib/change-notes/2026-02-12-field-keyword.md diff --git a/csharp/ql/lib/change-notes/2026-02-12-field-keyword.md b/csharp/ql/lib/change-notes/2026-02-12-field-keyword.md new file mode 100644 index 00000000000..7ca6548b27f --- /dev/null +++ b/csharp/ql/lib/change-notes/2026-02-12-field-keyword.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* C# 14: Added support for the `field` keyword in properties. From c3a1eb181eb7643d023eae9058d40e5e296cd8aa Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Fri, 13 Feb 2026 12:41:51 +0100 Subject: [PATCH 25/56] C#: Extract field modifiers and tag the field as being compiler generated. --- .../Semmle.Extraction.CSharp/Entities/PropertyField.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/PropertyField.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/PropertyField.cs index 7567a9e8960..9e9b1f41fff 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/PropertyField.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/PropertyField.cs @@ -28,6 +28,9 @@ namespace Semmle.Extraction.CSharp.Entities var unboundFieldKey = PropertyField.Create(Context, Symbol.OriginalDefinition); var name = Symbol.AssociatedSymbol is not null ? $"{Symbol.AssociatedSymbol.GetName()}.field" : Symbol.Name; trapFile.fields(this, VariableKind.None, name, ContainingType!, Type.TypeRef, unboundFieldKey); + trapFile.compiler_generated(this); + + PopulateModifiers(trapFile); if (Context.OnlyScaffold) { From 2b78a7b256ace097aa9cbd06b4003dd8b214aabd Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 16 Feb 2026 15:21:30 +0100 Subject: [PATCH 26/56] C#: Add PartialEvent test case. --- .../partial/MethodIsPartial.expected | 14 +- .../ql/test/library-tests/partial/Partial.cs | 8 + .../library-tests/partial/Partial1.expected | 24 +-- .../library-tests/partial/Partial2.expected | 26 +-- .../partial/PartialAccessors.expected | 20 +- .../partial/PartialConstructors.expected | 6 +- .../partial/PartialEvents.expected | 2 + .../library-tests/partial/PartialEvents.ql | 7 + .../partial/PartialIndexers.expected | 4 +- .../partial/PartialMethodBody.expected | 6 +- .../partial/PartialProperties.expected | 4 +- .../library-tests/partial/PrintAst.expected | 178 ++++++++++-------- 12 files changed, 168 insertions(+), 131 deletions(-) create mode 100644 csharp/ql/test/library-tests/partial/PartialEvents.expected create mode 100644 csharp/ql/test/library-tests/partial/PartialEvents.ql diff --git a/csharp/ql/test/library-tests/partial/MethodIsPartial.expected b/csharp/ql/test/library-tests/partial/MethodIsPartial.expected index 4c0e905d8c5..a0f1f88fb98 100644 --- a/csharp/ql/test/library-tests/partial/MethodIsPartial.expected +++ b/csharp/ql/test/library-tests/partial/MethodIsPartial.expected @@ -1,7 +1,7 @@ -| Partial.cs:4:18:4:42 | PartialMethodWithoutBody1 | true | -| Partial.cs:5:17:5:23 | Method2 | false | -| Partial.cs:14:18:14:39 | PartialMethodWithBody1 | true | -| Partial.cs:15:17:15:23 | Method3 | false | -| Partial.cs:34:18:34:42 | PartialMethodWithoutBody2 | true | -| Partial.cs:35:17:35:23 | Method4 | false | -| Partial.cs:40:17:40:23 | Method5 | false | +| Partial.cs:6:18:6:42 | PartialMethodWithoutBody1 | true | +| Partial.cs:7:17:7:23 | Method2 | false | +| Partial.cs:18:18:18:39 | PartialMethodWithBody1 | true | +| Partial.cs:19:17:19:23 | Method3 | false | +| Partial.cs:41:18:41:42 | PartialMethodWithoutBody2 | true | +| Partial.cs:42:17:42:23 | Method4 | false | +| Partial.cs:47:17:47:23 | Method5 | false | diff --git a/csharp/ql/test/library-tests/partial/Partial.cs b/csharp/ql/test/library-tests/partial/Partial.cs index 5a3e4af2e8c..8dd757fcd24 100644 --- a/csharp/ql/test/library-tests/partial/Partial.cs +++ b/csharp/ql/test/library-tests/partial/Partial.cs @@ -1,3 +1,5 @@ +using System; + partial class TwoPartClass { partial void PartialMethodWithBody1(); @@ -7,6 +9,8 @@ partial class TwoPartClass public partial object PartialProperty1 { get; set; } // Declaring declaration. public partial object this[int index] { get; set; } + // Declaring declaration. + public partial event EventHandler PartialEvent1; } partial class TwoPartClass @@ -27,6 +31,9 @@ partial class TwoPartClass get { return _backingArray[index]; } set { _backingArray[index] = value; } } + + // Implementation declaration. + public partial event EventHandler PartialEvent1 { add { } remove { } } } partial class OnePartPartialClass @@ -44,4 +51,5 @@ class NonPartialClass get { return null; } set { } } + public event EventHandler Event; } diff --git a/csharp/ql/test/library-tests/partial/Partial1.expected b/csharp/ql/test/library-tests/partial/Partial1.expected index 55dcaabcea7..3fd8b9e2fd4 100644 --- a/csharp/ql/test/library-tests/partial/Partial1.expected +++ b/csharp/ql/test/library-tests/partial/Partial1.expected @@ -1,14 +1,14 @@ -| Partial.cs:1:15:1:26 | TwoPartClass | -| Partial.cs:4:18:4:42 | PartialMethodWithoutBody1 | -| Partial.cs:12:15:12:26 | TwoPartClass | -| Partial.cs:14:18:14:39 | PartialMethodWithBody1 | -| Partial.cs:18:27:18:42 | PartialProperty1 | -| Partial.cs:20:9:20:11 | get_PartialProperty1 | -| Partial.cs:21:9:21:11 | set_PartialProperty1 | -| Partial.cs:25:27:25:30 | Item | -| Partial.cs:27:9:27:11 | get_Item | -| Partial.cs:28:9:28:11 | set_Item | -| Partial.cs:32:15:32:33 | OnePartPartialClass | -| Partial.cs:34:18:34:42 | PartialMethodWithoutBody2 | +| Partial.cs:3:15:3:26 | TwoPartClass | +| Partial.cs:6:18:6:42 | PartialMethodWithoutBody1 | +| Partial.cs:16:15:16:26 | TwoPartClass | +| Partial.cs:18:18:18:39 | PartialMethodWithBody1 | +| Partial.cs:22:27:22:42 | PartialProperty1 | +| Partial.cs:24:9:24:11 | get_PartialProperty1 | +| Partial.cs:25:9:25:11 | set_PartialProperty1 | +| Partial.cs:29:27:29:30 | Item | +| Partial.cs:31:9:31:11 | get_Item | +| Partial.cs:32:9:32:11 | set_Item | +| Partial.cs:39:15:39:33 | OnePartPartialClass | +| Partial.cs:41:18:41:42 | PartialMethodWithoutBody2 | | PartialMultipleFiles1.cs:1:22:1:41 | PartialMultipleFiles | | PartialMultipleFiles2.cs:1:22:1:41 | PartialMultipleFiles | diff --git a/csharp/ql/test/library-tests/partial/Partial2.expected b/csharp/ql/test/library-tests/partial/Partial2.expected index 87194dd3f9e..8d608c26011 100644 --- a/csharp/ql/test/library-tests/partial/Partial2.expected +++ b/csharp/ql/test/library-tests/partial/Partial2.expected @@ -1,15 +1,15 @@ -| Partial.cs:1:15:1:26 | TwoPartClass | Partial.cs:1:15:1:26 | | -| Partial.cs:1:15:1:26 | TwoPartClass | Partial.cs:4:18:4:42 | PartialMethodWithoutBody1 | -| Partial.cs:1:15:1:26 | TwoPartClass | Partial.cs:5:17:5:23 | Method2 | -| Partial.cs:1:15:1:26 | TwoPartClass | Partial.cs:14:18:14:39 | PartialMethodWithBody1 | -| Partial.cs:1:15:1:26 | TwoPartClass | Partial.cs:15:17:15:23 | Method3 | -| Partial.cs:12:15:12:26 | TwoPartClass | Partial.cs:1:15:1:26 | | -| Partial.cs:12:15:12:26 | TwoPartClass | Partial.cs:4:18:4:42 | PartialMethodWithoutBody1 | -| Partial.cs:12:15:12:26 | TwoPartClass | Partial.cs:5:17:5:23 | Method2 | -| Partial.cs:12:15:12:26 | TwoPartClass | Partial.cs:14:18:14:39 | PartialMethodWithBody1 | -| Partial.cs:12:15:12:26 | TwoPartClass | Partial.cs:15:17:15:23 | Method3 | -| Partial.cs:32:15:32:33 | OnePartPartialClass | Partial.cs:32:15:32:33 | | -| Partial.cs:32:15:32:33 | OnePartPartialClass | Partial.cs:34:18:34:42 | PartialMethodWithoutBody2 | -| Partial.cs:32:15:32:33 | OnePartPartialClass | Partial.cs:35:17:35:23 | Method4 | +| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:3:15:3:26 | | +| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:6:18:6:42 | PartialMethodWithoutBody1 | +| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:7:17:7:23 | Method2 | +| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:18:18:18:39 | PartialMethodWithBody1 | +| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:19:17:19:23 | Method3 | +| Partial.cs:16:15:16:26 | TwoPartClass | Partial.cs:3:15:3:26 | | +| Partial.cs:16:15:16:26 | TwoPartClass | Partial.cs:6:18:6:42 | PartialMethodWithoutBody1 | +| Partial.cs:16:15:16:26 | TwoPartClass | Partial.cs:7:17:7:23 | Method2 | +| Partial.cs:16:15:16:26 | TwoPartClass | Partial.cs:18:18:18:39 | PartialMethodWithBody1 | +| Partial.cs:16:15:16:26 | TwoPartClass | Partial.cs:19:17:19:23 | Method3 | +| Partial.cs:39:15:39:33 | OnePartPartialClass | Partial.cs:39:15:39:33 | | +| Partial.cs:39:15:39:33 | OnePartPartialClass | Partial.cs:41:18:41:42 | PartialMethodWithoutBody2 | +| Partial.cs:39:15:39:33 | OnePartPartialClass | Partial.cs:42:17:42:23 | Method4 | | PartialMultipleFiles1.cs:1:22:1:41 | PartialMultipleFiles | PartialMultipleFiles1.cs:1:22:1:41 | | | PartialMultipleFiles2.cs:1:22:1:41 | PartialMultipleFiles | PartialMultipleFiles1.cs:1:22:1:41 | | diff --git a/csharp/ql/test/library-tests/partial/PartialAccessors.expected b/csharp/ql/test/library-tests/partial/PartialAccessors.expected index 2c69ed620ae..e9180ffce56 100644 --- a/csharp/ql/test/library-tests/partial/PartialAccessors.expected +++ b/csharp/ql/test/library-tests/partial/PartialAccessors.expected @@ -1,8 +1,12 @@ -| Partial.cs:20:9:20:11 | get_PartialProperty1 | true | -| Partial.cs:21:9:21:11 | set_PartialProperty1 | true | -| Partial.cs:27:9:27:11 | get_Item | true | -| Partial.cs:28:9:28:11 | set_Item | true | -| Partial.cs:41:30:41:32 | get_Property | false | -| Partial.cs:41:35:41:37 | set_Property | false | -| Partial.cs:44:9:44:11 | get_Item | false | -| Partial.cs:45:9:45:11 | set_Item | false | +| Partial.cs:13:39:13:51 | add_PartialEvent1 | false | +| Partial.cs:13:39:13:51 | remove_PartialEvent1 | false | +| Partial.cs:24:9:24:11 | get_PartialProperty1 | true | +| Partial.cs:25:9:25:11 | set_PartialProperty1 | true | +| Partial.cs:31:9:31:11 | get_Item | true | +| Partial.cs:32:9:32:11 | set_Item | true | +| Partial.cs:48:30:48:32 | get_Property | false | +| Partial.cs:48:35:48:37 | set_Property | false | +| Partial.cs:51:9:51:11 | get_Item | false | +| Partial.cs:52:9:52:11 | set_Item | false | +| Partial.cs:54:31:54:35 | add_Event | false | +| Partial.cs:54:31:54:35 | remove_Event | false | diff --git a/csharp/ql/test/library-tests/partial/PartialConstructors.expected b/csharp/ql/test/library-tests/partial/PartialConstructors.expected index 01779f1b81e..69cabb244f5 100644 --- a/csharp/ql/test/library-tests/partial/PartialConstructors.expected +++ b/csharp/ql/test/library-tests/partial/PartialConstructors.expected @@ -1,4 +1,4 @@ -| Partial.cs:1:15:1:26 | TwoPartClass | Partial.cs:1:15:1:26 | {...} | -| Partial.cs:32:15:32:33 | OnePartPartialClass | Partial.cs:32:15:32:33 | {...} | -| Partial.cs:38:7:38:21 | NonPartialClass | Partial.cs:38:7:38:21 | {...} | +| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:3:15:3:26 | {...} | +| Partial.cs:39:15:39:33 | OnePartPartialClass | Partial.cs:39:15:39:33 | {...} | +| Partial.cs:45:7:45:21 | NonPartialClass | Partial.cs:45:7:45:21 | {...} | | PartialMultipleFiles1.cs:1:22:1:41 | PartialMultipleFiles | PartialMultipleFiles1.cs:1:22:1:41 | {...} | diff --git a/csharp/ql/test/library-tests/partial/PartialEvents.expected b/csharp/ql/test/library-tests/partial/PartialEvents.expected new file mode 100644 index 00000000000..e3ee325248a --- /dev/null +++ b/csharp/ql/test/library-tests/partial/PartialEvents.expected @@ -0,0 +1,2 @@ +| Partial.cs:13:39:13:51 | PartialEvent1 | false | +| Partial.cs:54:31:54:35 | Event | false | diff --git a/csharp/ql/test/library-tests/partial/PartialEvents.ql b/csharp/ql/test/library-tests/partial/PartialEvents.ql new file mode 100644 index 00000000000..e9f45250266 --- /dev/null +++ b/csharp/ql/test/library-tests/partial/PartialEvents.ql @@ -0,0 +1,7 @@ +import csharp + +private boolean isPartial(Event e) { if e.isPartial() then result = true else result = false } + +from Event e +where e.fromSource() +select e, isPartial(e) diff --git a/csharp/ql/test/library-tests/partial/PartialIndexers.expected b/csharp/ql/test/library-tests/partial/PartialIndexers.expected index 151ed5aad03..be625fc4ad5 100644 --- a/csharp/ql/test/library-tests/partial/PartialIndexers.expected +++ b/csharp/ql/test/library-tests/partial/PartialIndexers.expected @@ -1,2 +1,2 @@ -| Partial.cs:25:27:25:30 | Item | true | -| Partial.cs:42:19:42:22 | Item | false | +| Partial.cs:29:27:29:30 | Item | true | +| Partial.cs:49:19:49:22 | Item | false | diff --git a/csharp/ql/test/library-tests/partial/PartialMethodBody.expected b/csharp/ql/test/library-tests/partial/PartialMethodBody.expected index b75a105bea0..a91a156cb62 100644 --- a/csharp/ql/test/library-tests/partial/PartialMethodBody.expected +++ b/csharp/ql/test/library-tests/partial/PartialMethodBody.expected @@ -1,3 +1,3 @@ -| Partial.cs:4:18:4:42 | PartialMethodWithoutBody1 | false | -| Partial.cs:14:18:14:39 | PartialMethodWithBody1 | true | -| Partial.cs:34:18:34:42 | PartialMethodWithoutBody2 | false | +| Partial.cs:6:18:6:42 | PartialMethodWithoutBody1 | false | +| Partial.cs:18:18:18:39 | PartialMethodWithBody1 | true | +| Partial.cs:41:18:41:42 | PartialMethodWithoutBody2 | false | diff --git a/csharp/ql/test/library-tests/partial/PartialProperties.expected b/csharp/ql/test/library-tests/partial/PartialProperties.expected index 8d2dfc01e74..4686bbf087a 100644 --- a/csharp/ql/test/library-tests/partial/PartialProperties.expected +++ b/csharp/ql/test/library-tests/partial/PartialProperties.expected @@ -1,2 +1,2 @@ -| Partial.cs:18:27:18:42 | PartialProperty1 | true | -| Partial.cs:41:19:41:26 | Property | false | +| Partial.cs:22:27:22:42 | PartialProperty1 | true | +| Partial.cs:48:19:48:26 | Property | false | diff --git a/csharp/ql/test/library-tests/partial/PrintAst.expected b/csharp/ql/test/library-tests/partial/PrintAst.expected index 0729946b18b..90471e0a80c 100644 --- a/csharp/ql/test/library-tests/partial/PrintAst.expected +++ b/csharp/ql/test/library-tests/partial/PrintAst.expected @@ -1,94 +1,110 @@ Partial.cs: -# 1| [Class] TwoPartClass -# 4| 6: [Method] PartialMethodWithoutBody1 -# 4| -1: [TypeMention] Void -# 5| 7: [Method] Method2 +# 3| [Class] TwoPartClass +# 6| 6: [Method] PartialMethodWithoutBody1 +# 6| -1: [TypeMention] Void +# 7| 7: [Method] Method2 +# 7| -1: [TypeMention] Void +# 7| 4: [BlockStmt] {...} +# 13| 8: [Event] PartialEvent1 +# 13| -1: [TypeMention] EventHandler +# 13| 3: [AddEventAccessor] add_PartialEvent1 +#-----| 2: (Parameters) +# 13| 0: [Parameter] value +# 13| 4: [RemoveEventAccessor] remove_PartialEvent1 +#-----| 2: (Parameters) +# 13| 0: [Parameter] value +# 18| 9: [Method] PartialMethodWithBody1 # 5| -1: [TypeMention] Void -# 5| 4: [BlockStmt] {...} -# 14| 8: [Method] PartialMethodWithBody1 -# 3| -1: [TypeMention] Void -# 14| 4: [BlockStmt] {...} -# 15| 9: [Method] Method3 -# 15| -1: [TypeMention] Void -# 15| 4: [BlockStmt] {...} -# 16| 10: [Field] _backingField -# 16| -1: [TypeMention] object -# 18| 11: [Property] PartialProperty1 -# 7| -1: [TypeMention] object -# 18| -1: [TypeMention] object -# 20| 3: [Getter] get_PartialProperty1 -# 20| 4: [BlockStmt] {...} -# 20| 0: [ReturnStmt] return ...; -# 20| 0: [FieldAccess] access to field _backingField -# 21| 4: [Setter] set_PartialProperty1 -#-----| 2: (Parameters) -# 21| 0: [Parameter] value -# 21| 4: [BlockStmt] {...} -# 21| 0: [ExprStmt] ...; -# 21| 0: [AssignExpr] ... = ... -# 21| 0: [FieldAccess] access to field _backingField -# 21| 1: [ParameterAccess] access to parameter value -# 23| 12: [Field] _backingArray -# 23| -1: [TypeMention] Object[] -# 23| 1: [TypeMention] object -# 25| 13: [Indexer] Item +# 18| 4: [BlockStmt] {...} +# 19| 10: [Method] Method3 +# 19| -1: [TypeMention] Void +# 19| 4: [BlockStmt] {...} +# 20| 11: [Field] _backingField +# 20| -1: [TypeMention] object +# 22| 12: [Property] PartialProperty1 # 9| -1: [TypeMention] object -# 25| -1: [TypeMention] object +# 22| -1: [TypeMention] object +# 24| 3: [Getter] get_PartialProperty1 +# 24| 4: [BlockStmt] {...} +# 24| 0: [ReturnStmt] return ...; +# 24| 0: [FieldAccess] access to field _backingField +# 25| 4: [Setter] set_PartialProperty1 +#-----| 2: (Parameters) +# 25| 0: [Parameter] value +# 25| 4: [BlockStmt] {...} +# 25| 0: [ExprStmt] ...; +# 25| 0: [AssignExpr] ... = ... +# 25| 0: [FieldAccess] access to field _backingField +# 25| 1: [ParameterAccess] access to parameter value +# 27| 13: [Field] _backingArray +# 27| -1: [TypeMention] Object[] +# 27| 1: [TypeMention] object +# 29| 14: [Indexer] Item +# 11| -1: [TypeMention] object +# 29| -1: [TypeMention] object #-----| 1: (Parameters) -# 9| 0: [Parameter] index -# 9| -1: [TypeMention] int -# 25| -1: [TypeMention] int -# 27| 3: [Getter] get_Item +# 11| 0: [Parameter] index +# 11| -1: [TypeMention] int +# 29| -1: [TypeMention] int +# 31| 3: [Getter] get_Item #-----| 2: (Parameters) -# 25| 0: [Parameter] index -# 27| 4: [BlockStmt] {...} -# 27| 0: [ReturnStmt] return ...; -# 27| 0: [ArrayAccess] access to array element -# 27| -1: [FieldAccess] access to field _backingArray -# 27| 0: [ParameterAccess] access to parameter index -# 28| 4: [Setter] set_Item +# 29| 0: [Parameter] index +# 31| 4: [BlockStmt] {...} +# 31| 0: [ReturnStmt] return ...; +# 31| 0: [ArrayAccess] access to array element +# 31| -1: [FieldAccess] access to field _backingArray +# 31| 0: [ParameterAccess] access to parameter index +# 32| 4: [Setter] set_Item #-----| 2: (Parameters) -# 25| 0: [Parameter] index -# 28| 1: [Parameter] value -# 28| 4: [BlockStmt] {...} -# 28| 0: [ExprStmt] ...; -# 28| 0: [AssignExpr] ... = ... -# 28| 0: [ArrayAccess] access to array element -# 28| -1: [FieldAccess] access to field _backingArray -# 28| 0: [ParameterAccess] access to parameter index -# 28| 1: [ParameterAccess] access to parameter value -# 32| [Class] OnePartPartialClass -# 34| 6: [Method] PartialMethodWithoutBody2 -# 34| -1: [TypeMention] Void -# 35| 7: [Method] Method4 -# 35| -1: [TypeMention] Void -# 35| 4: [BlockStmt] {...} -# 38| [Class] NonPartialClass -# 40| 6: [Method] Method5 -# 40| -1: [TypeMention] Void -# 40| 4: [BlockStmt] {...} -# 41| 7: [Property] Property -# 41| -1: [TypeMention] object -# 41| 3: [Getter] get_Property -# 41| 4: [Setter] set_Property +# 29| 0: [Parameter] index +# 32| 1: [Parameter] value +# 32| 4: [BlockStmt] {...} +# 32| 0: [ExprStmt] ...; +# 32| 0: [AssignExpr] ... = ... +# 32| 0: [ArrayAccess] access to array element +# 32| -1: [FieldAccess] access to field _backingArray +# 32| 0: [ParameterAccess] access to parameter index +# 32| 1: [ParameterAccess] access to parameter value +# 39| [Class] OnePartPartialClass +# 41| 6: [Method] PartialMethodWithoutBody2 +# 41| -1: [TypeMention] Void +# 42| 7: [Method] Method4 +# 42| -1: [TypeMention] Void +# 42| 4: [BlockStmt] {...} +# 45| [Class] NonPartialClass +# 47| 6: [Method] Method5 +# 47| -1: [TypeMention] Void +# 47| 4: [BlockStmt] {...} +# 48| 7: [Property] Property +# 48| -1: [TypeMention] object +# 48| 3: [Getter] get_Property +# 48| 4: [Setter] set_Property #-----| 2: (Parameters) -# 41| 0: [Parameter] value -# 42| 8: [Indexer] Item -# 42| -1: [TypeMention] object +# 48| 0: [Parameter] value +# 49| 8: [Indexer] Item +# 49| -1: [TypeMention] object #-----| 1: (Parameters) -# 42| 0: [Parameter] index -# 42| -1: [TypeMention] int -# 44| 3: [Getter] get_Item +# 49| 0: [Parameter] index +# 49| -1: [TypeMention] int +# 51| 3: [Getter] get_Item #-----| 2: (Parameters) -# 42| 0: [Parameter] index -# 44| 4: [BlockStmt] {...} -# 44| 0: [ReturnStmt] return ...; -# 44| 0: [NullLiteral] null -# 45| 4: [Setter] set_Item +# 49| 0: [Parameter] index +# 51| 4: [BlockStmt] {...} +# 51| 0: [ReturnStmt] return ...; +# 51| 0: [NullLiteral] null +# 52| 4: [Setter] set_Item #-----| 2: (Parameters) -# 42| 0: [Parameter] index -# 45| 1: [Parameter] value -# 45| 4: [BlockStmt] {...} +# 49| 0: [Parameter] index +# 52| 1: [Parameter] value +# 52| 4: [BlockStmt] {...} +# 54| 9: [Event] Event +# 54| -1: [TypeMention] EventHandler +# 54| 3: [AddEventAccessor] add_Event +#-----| 2: (Parameters) +# 54| 0: [Parameter] value +# 54| 4: [RemoveEventAccessor] remove_Event +#-----| 2: (Parameters) +# 54| 0: [Parameter] value PartialMultipleFiles1.cs: # 1| [Class] PartialMultipleFiles PartialMultipleFiles2.cs: From eec47394972bb2955728dd702fb17c900ed8d824 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 16 Feb 2026 16:00:26 +0100 Subject: [PATCH 27/56] C#: Add viable callable test for partial event. --- .../ql/test/library-tests/dispatch/CallGraph.expected | 10 ++++++---- .../library-tests/dispatch/GetADynamicTarget.expected | 10 ++++++---- .../ql/test/library-tests/dispatch/ViableCallable.cs | 8 ++++++++ 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/csharp/ql/test/library-tests/dispatch/CallGraph.expected b/csharp/ql/test/library-tests/dispatch/CallGraph.expected index 4eed880f0a3..0ed729859a4 100644 --- a/csharp/ql/test/library-tests/dispatch/CallGraph.expected +++ b/csharp/ql/test/library-tests/dispatch/CallGraph.expected @@ -270,7 +270,9 @@ | ViableCallable.cs:679:17:679:20 | Run3 | ViableCallable.cs:637:21:637:21 | M | | ViableCallable.cs:679:17:679:20 | Run3 | ViableCallable.cs:646:21:646:21 | M | | ViableCallable.cs:679:17:679:20 | Run3 | ViableCallable.cs:648:21:648:21 | M | -| ViableCallable.cs:707:17:707:20 | Run1 | ViableCallable.cs:702:42:702:44 | get_Property | -| ViableCallable.cs:707:17:707:20 | Run1 | ViableCallable.cs:702:63:702:65 | set_Property | -| ViableCallable.cs:707:17:707:20 | Run1 | ViableCallable.cs:704:49:704:51 | get_Item | -| ViableCallable.cs:707:17:707:20 | Run1 | ViableCallable.cs:704:70:704:72 | set_Item | +| ViableCallable.cs:709:17:709:20 | Run1 | ViableCallable.cs:698:43:698:47 | add_Event | +| ViableCallable.cs:709:17:709:20 | Run1 | ViableCallable.cs:698:43:698:47 | remove_Event | +| ViableCallable.cs:709:17:709:20 | Run1 | ViableCallable.cs:703:42:703:44 | get_Property | +| ViableCallable.cs:709:17:709:20 | Run1 | ViableCallable.cs:703:63:703:65 | set_Property | +| ViableCallable.cs:709:17:709:20 | Run1 | ViableCallable.cs:705:49:705:51 | get_Item | +| ViableCallable.cs:709:17:709:20 | Run1 | ViableCallable.cs:705:70:705:72 | set_Item | diff --git a/csharp/ql/test/library-tests/dispatch/GetADynamicTarget.expected b/csharp/ql/test/library-tests/dispatch/GetADynamicTarget.expected index b38eed691b9..5d6b4be4f87 100644 --- a/csharp/ql/test/library-tests/dispatch/GetADynamicTarget.expected +++ b/csharp/ql/test/library-tests/dispatch/GetADynamicTarget.expected @@ -518,7 +518,9 @@ | ViableCallable.cs:683:9:683:16 | call to method M | C22+TestOverloadResolution2.M(Int32[]) | | ViableCallable.cs:687:9:687:16 | call to method M | C22+TestOverloadResolution1.M(List) | | ViableCallable.cs:687:9:687:16 | call to method M | C22+TestOverloadResolution2.M(List) | -| ViableCallable.cs:712:9:712:18 | access to property Property | C23+Partial1.set_Property(object) | -| ViableCallable.cs:715:13:715:22 | access to property Property | C23+Partial1.get_Property() | -| ViableCallable.cs:718:9:718:12 | access to indexer | C23+Partial1.set_Item(int, object) | -| ViableCallable.cs:721:13:721:16 | access to indexer | C23+Partial1.get_Item(int) | +| ViableCallable.cs:714:9:714:18 | access to property Property | C23+Partial1.set_Property(object) | +| ViableCallable.cs:717:13:717:22 | access to property Property | C23+Partial1.get_Property() | +| ViableCallable.cs:720:9:720:12 | access to indexer | C23+Partial1.set_Item(int, object) | +| ViableCallable.cs:723:13:723:16 | access to indexer | C23+Partial1.get_Item(int) | +| ViableCallable.cs:726:9:726:15 | access to event Event | C23+Partial1.add_Event(EventHandler) | +| ViableCallable.cs:729:9:729:15 | access to event Event | C23+Partial1.remove_Event(EventHandler) | diff --git a/csharp/ql/test/library-tests/dispatch/ViableCallable.cs b/csharp/ql/test/library-tests/dispatch/ViableCallable.cs index 99b4ec54a99..e904eb01a86 100644 --- a/csharp/ql/test/library-tests/dispatch/ViableCallable.cs +++ b/csharp/ql/test/library-tests/dispatch/ViableCallable.cs @@ -695,6 +695,7 @@ public class C23 public partial object Property { get; set; } public partial object this[int index] { get; set; } + public partial event EventHandler Event; } public partial class Partial1 @@ -702,6 +703,7 @@ public class C23 public partial object Property { get { return null; } set { } } public partial object this[int index] { get { return null; } set { } } + public partial event EventHandler Event { add { } remove { } } } public void Run1(Partial1 p) @@ -719,5 +721,11 @@ public class C23 // Viable callable: Partial1.get_Item(int) o = p[0]; + + // Viable callable: Partial1.add_Event + p.Event += (sender, e) => { }; + + // Viable callable: Partial1.remove_Event + p.Event -= (sender, e) => { }; } } From 9289ac283871d030a57abc1890611c076e8866d7 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Fri, 13 Feb 2026 16:24:06 +0100 Subject: [PATCH 28/56] C#: Support for partial event declarations. --- .../Semmle.Extraction.CSharp/Entities/Event.cs | 12 ++++++++---- .../Entities/EventAccessor.cs | 4 ++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs index bbd90989617..c93df9ccc4f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs @@ -11,6 +11,10 @@ namespace Semmle.Extraction.CSharp.Entities private Event(Context cx, IEventSymbol init) : base(cx, init) { } + protected override IEventSymbol BodyDeclaringSymbol => Symbol.PartialImplementationPart ?? Symbol; + + public override Microsoft.CodeAnalysis.Location? ReportingLocation => BodyDeclaringSymbol.Locations.BestOrDefault(); + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(ContainingType!); @@ -27,13 +31,13 @@ namespace Semmle.Extraction.CSharp.Entities var type = Type.Create(Context, Symbol.Type); trapFile.events(this, Symbol.GetName(), ContainingType!, type.TypeRef, Create(Context, Symbol.OriginalDefinition)); - var adder = Symbol.AddMethod; - var remover = Symbol.RemoveMethod; + var adder = BodyDeclaringSymbol.AddMethod; + var remover = BodyDeclaringSymbol.RemoveMethod; - if (!(adder is null)) + if (adder is not null) Method.Create(Context, adder); - if (!(remover is null)) + if (remover is not null) Method.Create(Context, remover); PopulateModifiers(trapFile); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/EventAccessor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/EventAccessor.cs index 254e7c76956..3e8ab9431be 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/EventAccessor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/EventAccessor.cs @@ -13,6 +13,10 @@ namespace Semmle.Extraction.CSharp.Entities this.@event = @event; } + public override bool NeedsPopulation => + base.NeedsPopulation && + !Symbol.IsPartialDefinition; // Accessors always have an implementing declaration as well. + /// /// Gets the event symbol associated with accessor `symbol`, or `null` /// if there is no associated symbol. From 43c6e7d2500ea7518a55a8794bc7270c5f3b491b Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 16 Feb 2026 15:29:01 +0100 Subject: [PATCH 29/56] C#: Update test expected output. --- .../library-tests/dispatch/CallGraph.expected | 4 +-- .../library-tests/partial/Partial1.expected | 3 ++ .../partial/PartialAccessors.expected | 4 +-- .../partial/PartialEvents.expected | 2 +- .../library-tests/partial/PrintAst.expected | 30 ++++++++++--------- 5 files changed, 24 insertions(+), 19 deletions(-) diff --git a/csharp/ql/test/library-tests/dispatch/CallGraph.expected b/csharp/ql/test/library-tests/dispatch/CallGraph.expected index 0ed729859a4..2feb959dd86 100644 --- a/csharp/ql/test/library-tests/dispatch/CallGraph.expected +++ b/csharp/ql/test/library-tests/dispatch/CallGraph.expected @@ -270,9 +270,9 @@ | ViableCallable.cs:679:17:679:20 | Run3 | ViableCallable.cs:637:21:637:21 | M | | ViableCallable.cs:679:17:679:20 | Run3 | ViableCallable.cs:646:21:646:21 | M | | ViableCallable.cs:679:17:679:20 | Run3 | ViableCallable.cs:648:21:648:21 | M | -| ViableCallable.cs:709:17:709:20 | Run1 | ViableCallable.cs:698:43:698:47 | add_Event | -| ViableCallable.cs:709:17:709:20 | Run1 | ViableCallable.cs:698:43:698:47 | remove_Event | | ViableCallable.cs:709:17:709:20 | Run1 | ViableCallable.cs:703:42:703:44 | get_Property | | ViableCallable.cs:709:17:709:20 | Run1 | ViableCallable.cs:703:63:703:65 | set_Property | | ViableCallable.cs:709:17:709:20 | Run1 | ViableCallable.cs:705:49:705:51 | get_Item | | ViableCallable.cs:709:17:709:20 | Run1 | ViableCallable.cs:705:70:705:72 | set_Item | +| ViableCallable.cs:709:17:709:20 | Run1 | ViableCallable.cs:706:51:706:53 | add_Event | +| ViableCallable.cs:709:17:709:20 | Run1 | ViableCallable.cs:706:59:706:64 | remove_Event | diff --git a/csharp/ql/test/library-tests/partial/Partial1.expected b/csharp/ql/test/library-tests/partial/Partial1.expected index 3fd8b9e2fd4..fe8f5658f48 100644 --- a/csharp/ql/test/library-tests/partial/Partial1.expected +++ b/csharp/ql/test/library-tests/partial/Partial1.expected @@ -8,6 +8,9 @@ | Partial.cs:29:27:29:30 | Item | | Partial.cs:31:9:31:11 | get_Item | | Partial.cs:32:9:32:11 | set_Item | +| Partial.cs:36:39:36:51 | PartialEvent1 | +| Partial.cs:36:55:36:57 | add_PartialEvent1 | +| Partial.cs:36:63:36:68 | remove_PartialEvent1 | | Partial.cs:39:15:39:33 | OnePartPartialClass | | Partial.cs:41:18:41:42 | PartialMethodWithoutBody2 | | PartialMultipleFiles1.cs:1:22:1:41 | PartialMultipleFiles | diff --git a/csharp/ql/test/library-tests/partial/PartialAccessors.expected b/csharp/ql/test/library-tests/partial/PartialAccessors.expected index e9180ffce56..a9d70645420 100644 --- a/csharp/ql/test/library-tests/partial/PartialAccessors.expected +++ b/csharp/ql/test/library-tests/partial/PartialAccessors.expected @@ -1,9 +1,9 @@ -| Partial.cs:13:39:13:51 | add_PartialEvent1 | false | -| Partial.cs:13:39:13:51 | remove_PartialEvent1 | false | | Partial.cs:24:9:24:11 | get_PartialProperty1 | true | | Partial.cs:25:9:25:11 | set_PartialProperty1 | true | | Partial.cs:31:9:31:11 | get_Item | true | | Partial.cs:32:9:32:11 | set_Item | true | +| Partial.cs:36:55:36:57 | add_PartialEvent1 | true | +| Partial.cs:36:63:36:68 | remove_PartialEvent1 | true | | Partial.cs:48:30:48:32 | get_Property | false | | Partial.cs:48:35:48:37 | set_Property | false | | Partial.cs:51:9:51:11 | get_Item | false | diff --git a/csharp/ql/test/library-tests/partial/PartialEvents.expected b/csharp/ql/test/library-tests/partial/PartialEvents.expected index e3ee325248a..b12f8a07a9d 100644 --- a/csharp/ql/test/library-tests/partial/PartialEvents.expected +++ b/csharp/ql/test/library-tests/partial/PartialEvents.expected @@ -1,2 +1,2 @@ -| Partial.cs:13:39:13:51 | PartialEvent1 | false | +| Partial.cs:36:39:36:51 | PartialEvent1 | true | | Partial.cs:54:31:54:35 | Event | false | diff --git a/csharp/ql/test/library-tests/partial/PrintAst.expected b/csharp/ql/test/library-tests/partial/PrintAst.expected index 90471e0a80c..315de869fb3 100644 --- a/csharp/ql/test/library-tests/partial/PrintAst.expected +++ b/csharp/ql/test/library-tests/partial/PrintAst.expected @@ -5,23 +5,15 @@ Partial.cs: # 7| 7: [Method] Method2 # 7| -1: [TypeMention] Void # 7| 4: [BlockStmt] {...} -# 13| 8: [Event] PartialEvent1 -# 13| -1: [TypeMention] EventHandler -# 13| 3: [AddEventAccessor] add_PartialEvent1 -#-----| 2: (Parameters) -# 13| 0: [Parameter] value -# 13| 4: [RemoveEventAccessor] remove_PartialEvent1 -#-----| 2: (Parameters) -# 13| 0: [Parameter] value -# 18| 9: [Method] PartialMethodWithBody1 +# 18| 8: [Method] PartialMethodWithBody1 # 5| -1: [TypeMention] Void # 18| 4: [BlockStmt] {...} -# 19| 10: [Method] Method3 +# 19| 9: [Method] Method3 # 19| -1: [TypeMention] Void # 19| 4: [BlockStmt] {...} -# 20| 11: [Field] _backingField +# 20| 10: [Field] _backingField # 20| -1: [TypeMention] object -# 22| 12: [Property] PartialProperty1 +# 22| 11: [Property] PartialProperty1 # 9| -1: [TypeMention] object # 22| -1: [TypeMention] object # 24| 3: [Getter] get_PartialProperty1 @@ -36,10 +28,10 @@ Partial.cs: # 25| 0: [AssignExpr] ... = ... # 25| 0: [FieldAccess] access to field _backingField # 25| 1: [ParameterAccess] access to parameter value -# 27| 13: [Field] _backingArray +# 27| 12: [Field] _backingArray # 27| -1: [TypeMention] Object[] # 27| 1: [TypeMention] object -# 29| 14: [Indexer] Item +# 29| 13: [Indexer] Item # 11| -1: [TypeMention] object # 29| -1: [TypeMention] object #-----| 1: (Parameters) @@ -65,6 +57,16 @@ Partial.cs: # 32| -1: [FieldAccess] access to field _backingArray # 32| 0: [ParameterAccess] access to parameter index # 32| 1: [ParameterAccess] access to parameter value +# 36| 14: [Event] PartialEvent1 +# 13| -1: [TypeMention] EventHandler +# 36| 3: [AddEventAccessor] add_PartialEvent1 +#-----| 2: (Parameters) +# 36| 0: [Parameter] value +# 36| 4: [BlockStmt] {...} +# 36| 4: [RemoveEventAccessor] remove_PartialEvent1 +#-----| 2: (Parameters) +# 36| 0: [Parameter] value +# 36| 4: [BlockStmt] {...} # 39| [Class] OnePartPartialClass # 41| 6: [Method] PartialMethodWithoutBody2 # 41| -1: [TypeMention] Void From c29bac2bc19409afda9bf777c9258f348128249f Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 16 Feb 2026 16:13:06 +0100 Subject: [PATCH 30/56] C#: Add change-note. --- csharp/ql/lib/change-notes/2026-02-16-partial-events.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 csharp/ql/lib/change-notes/2026-02-16-partial-events.md diff --git a/csharp/ql/lib/change-notes/2026-02-16-partial-events.md b/csharp/ql/lib/change-notes/2026-02-16-partial-events.md new file mode 100644 index 00000000000..3bbc1ae829a --- /dev/null +++ b/csharp/ql/lib/change-notes/2026-02-16-partial-events.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* C# 14: Added support for partial events. From 26a1f4888a7fc82e3361d880e0abb31a8c52f5b4 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Mon, 16 Feb 2026 17:19:44 +0100 Subject: [PATCH 31/56] C++: Modernize `MustFlow` using parameterized modules --- .../semmle/code/cpp/ir/dataflow/MustFlow.qll | 505 +++++++++--------- .../ReturnStackAllocatedMemory.ql | 24 +- .../Memory Management/UninitializedLocal.ql | 21 +- cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.ql | 26 +- 4 files changed, 282 insertions(+), 294 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll index b085440f6bc..07e79a907c7 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll @@ -7,290 +7,279 @@ private import cpp private import semmle.code.cpp.ir.IR -/** - * A configuration of a data flow analysis that performs must-flow analysis. This is different - * from `DataFlow.qll` which performs may-flow analysis (i.e., it finds paths where the source _may_ - * flow to the sink). - * - * Like in `DataFlow.qll`, each use of the `MustFlow.qll` library must define its own unique extension - * of this abstract class. To create a configuration, extend this class with a subclass whose - * characteristic predicate is a unique singleton string and override `isSource`, `isSink` (and - * `isAdditionalFlowStep` if additional steps are required). - */ -abstract class MustFlowConfiguration extends string { - bindingset[this] - MustFlowConfiguration() { any() } - +module MustFlow { /** - * Holds if `source` is a relevant data flow source. + * An input configuration of a data flow analysis that performs must-flow analysis. This is different + * from `DataFlow.qll` which performs may-flow analysis (i.e., it finds paths where the source _may_ + * flow to the sink). */ - abstract predicate isSource(Instruction source); + signature module ConfigSig { + /** + * Holds if `source` is a relevant data flow source. + */ + predicate isSource(Instruction source); - /** - * Holds if `sink` is a relevant data flow sink. - */ - abstract predicate isSink(Operand sink); + /** + * Holds if `sink` is a relevant data flow sink. + */ + predicate isSink(Operand sink); - /** - * Holds if data flow through `instr` is prohibited. - */ - predicate isBarrier(Instruction instr) { none() } + /** + * Holds if data flow through `instr` is prohibited. + */ + default predicate isBarrier(Instruction instr) { none() } - /** - * Holds if the additional flow step from `node1` to `node2` must be taken - * into account in the analysis. - */ - predicate isAdditionalFlowStep(Operand node1, Instruction node2) { none() } + /** + * Holds if the additional flow step from `node1` to `node2` must be taken + * into account in the analysis. + */ + default predicate isAdditionalFlowStep(Operand node1, Instruction node2) { none() } - /** Holds if this configuration allows flow from arguments to parameters. */ - predicate allowInterproceduralFlow() { any() } - - /** - * Holds if data must flow from `source` to `sink` for this configuration. - * - * The corresponding paths are generated from the end-points and the graph - * included in the module `PathGraph`. - */ - final predicate hasFlowPath(MustFlowPathNode source, MustFlowPathSink sink) { - this.isSource(source.getInstruction()) and - source.getASuccessor*() = sink - } -} - -/** Holds if `node` flows from a source. */ -pragma[nomagic] -private predicate flowsFromSource(Instruction node, MustFlowConfiguration config) { - not config.isBarrier(node) and - ( - config.isSource(node) - or - exists(Instruction mid | - step(mid, node, config) and - flowsFromSource(mid, pragma[only_bind_into](config)) - ) - ) -} - -/** Holds if `node` flows to a sink. */ -pragma[nomagic] -private predicate flowsToSink(Instruction node, MustFlowConfiguration config) { - flowsFromSource(node, pragma[only_bind_into](config)) and - ( - config.isSink(node.getAUse()) - or - exists(Instruction mid | - step(node, mid, config) and - flowsToSink(mid, pragma[only_bind_into](config)) - ) - ) -} - -cached -private module Cached { - /** Holds if `p` is the `n`'th parameter of the non-virtual function `f`. */ - private predicate parameterOf(Parameter p, Function f, int n) { - not f.isVirtual() and f.getParameter(n) = p + /** Holds if this configuration allows flow from arguments to parameters. */ + default predicate allowInterproceduralFlow() { any() } } - /** - * Holds if `instr` is the `n`'th argument to a call to the non-virtual function `f`, and - * `init` is the corresponding initialization instruction that receives the value of `instr` in `f`. - */ - private predicate flowIntoParameter( - Function f, int n, CallInstruction call, Instruction instr, InitializeParameterInstruction init - ) { - not f.isVirtual() and - call.getPositionalArgument(n) = instr and - f = call.getStaticCallTarget() and - getEnclosingNonVirtualFunctionInitializeParameter(init, f) and - init.getParameter().getIndex() = pragma[only_bind_into](pragma[only_bind_out](n)) - } + module Global { + import Config - /** - * Holds if `instr` is an argument to a call to the function `f`, and `init` is the - * corresponding initialization instruction that receives the value of `instr` in `f`. - */ - pragma[noinline] - private predicate getPositionalArgumentInitParam( - CallInstruction call, Instruction instr, InitializeParameterInstruction init, Function f - ) { - exists(int n | - parameterOf(_, f, n) and - flowIntoParameter(f, pragma[only_bind_into](pragma[only_bind_out](n)), call, instr, init) - ) - } + /** + * Holds if data must flow from `source` to `sink`. + * + * The corresponding paths are generated from the end-points and the graph + * included in the module `PathGraph`. + */ + predicate flowPath(PathNode source, PathSink sink) { + isSource(source.getInstruction()) and + source.getASuccessor*() = sink + } - /** - * Holds if `instr` is the qualifier to a call to the non-virtual function `f`, and - * `init` is the corresponding initialization instruction that receives the value of - * `instr` in `f`. - */ - pragma[noinline] - private predicate getThisArgumentInitParam( - CallInstruction call, Instruction instr, InitializeParameterInstruction init, Function f - ) { - not f.isVirtual() and - call.getStaticCallTarget() = f and - getEnclosingNonVirtualFunctionInitializeParameter(init, f) and - call.getThisArgument() = instr and - init.getIRVariable() instanceof IRThisVariable - } + /** Holds if `node` flows from a source. */ + pragma[nomagic] + private predicate flowsFromSource(Instruction node) { + not isBarrier(node) and + ( + isSource(node) + or + exists(Instruction mid | + step(mid, node) and + flowsFromSource(mid) + ) + ) + } - /** Holds if `f` is the enclosing non-virtual function of `init`. */ - private predicate getEnclosingNonVirtualFunctionInitializeParameter( - InitializeParameterInstruction init, Function f - ) { - not f.isVirtual() and - init.getEnclosingFunction() = f - } + /** Holds if `node` flows to a sink. */ + pragma[nomagic] + private predicate flowsToSink(Instruction node) { + flowsFromSource(node) and + ( + isSink(node.getAUse()) + or + exists(Instruction mid | + step(node, mid) and + flowsToSink(mid) + ) + ) + } - /** Holds if `f` is the enclosing non-virtual function of `init`. */ - private predicate getEnclosingNonVirtualFunctionInitializeIndirection( - InitializeIndirectionInstruction init, Function f - ) { - not f.isVirtual() and - init.getEnclosingFunction() = f - } + /** + * Gets the enclosing callable of `n`. Unlike `n.getEnclosingCallable()`, this + * predicate ensures that joins go from `n` to the result instead of the other + * way around. + */ + pragma[inline] + private IRFunction getEnclosingCallable(Instruction n) { + pragma[only_bind_into](result) = pragma[only_bind_out](n).getEnclosingIRFunction() + } - /** - * Holds if `instr` is an argument (or argument indirection) to a call, and - * `succ` is the corresponding initialization instruction in the call target. - */ - private predicate flowThroughCallable(Instruction argument, Instruction parameter) { - // Flow from an argument to a parameter - exists(CallInstruction call, InitializeParameterInstruction init | init = parameter | - getPositionalArgumentInitParam(call, argument, init, call.getStaticCallTarget()) - or - getThisArgumentInitParam(call, argument, init, call.getStaticCallTarget()) - ) - or - // Flow from argument indirection to parameter indirection - exists( - CallInstruction call, ReadSideEffectInstruction read, InitializeIndirectionInstruction init - | - init = parameter and - read.getPrimaryInstruction() = call and - getEnclosingNonVirtualFunctionInitializeIndirection(init, call.getStaticCallTarget()) - | - exists(int n | - read.getSideEffectOperand().getAnyDef() = argument and - read.getIndex() = pragma[only_bind_into](n) and - init.getParameter().getIndex() = pragma[only_bind_into](n) + /** Holds if `nodeFrom` flows to `nodeTo`. */ + private predicate step(Instruction nodeFrom, Instruction nodeTo) { + Cached::step(pragma[only_bind_into](nodeFrom), pragma[only_bind_into](nodeTo)) and + ( + allowInterproceduralFlow() + or + getEnclosingCallable(nodeFrom) = getEnclosingCallable(nodeTo) ) or - call.getThisArgument() = argument and - init.getIRVariable() instanceof IRThisVariable - ) - } + isAdditionalFlowStep(nodeFrom.getAUse(), nodeTo) + } - private predicate instructionToOperandStep(Instruction instr, Operand operand) { - operand.getDef() = instr - } + private newtype TLocalPathNode = + MkLocalPathNode(Instruction n) { + flowsToSink(n) and + ( + isSource(n) + or + exists(PathNode mid | step(mid.getInstruction(), n)) + ) + } - /** - * Holds if data flows from `operand` to `instr`. - * - * This predicate ignores flow through `PhiInstruction`s to create a 'must flow' relation. - */ - private predicate operandToInstructionStep(Operand operand, Instruction instr) { - instr.(CopyInstruction).getSourceValueOperand() = operand - or - instr.(ConvertInstruction).getUnaryOperand() = operand - or - instr.(CheckedConvertOrNullInstruction).getUnaryOperand() = operand - or - instr.(InheritanceConversionInstruction).getUnaryOperand() = operand - or - instr.(ChiInstruction).getTotalOperand() = operand + /** A `Node` that is in a path from a source to a sink. */ + class PathNode extends TLocalPathNode { + Instruction n; + + PathNode() { this = MkLocalPathNode(n) } + + /** Gets the underlying node. */ + Instruction getInstruction() { result = n } + + /** Gets a textual representation of this node. */ + string toString() { result = n.getAst().toString() } + + /** Gets the location of this element. */ + Location getLocation() { result = n.getLocation() } + + /** Gets a successor node, if any. */ + PathNode getASuccessor() { step(this.getInstruction(), result.getInstruction()) } + } + + private class PathSink extends PathNode { + PathSink() { isSink(this.getInstruction().getAUse()) } + } + + /** + * Provides the query predicates needed to include a graph in a path-problem query. + */ + module PathGraph { + private predicate reach(PathNode n) { n instanceof PathSink or reach(n.getASuccessor()) } + + /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ + query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) } + + /** Holds if `n` is a node in the graph of data flow path explanations. */ + query predicate nodes(PathNode n, string key, string val) { + reach(n) and key = "semmle.label" and val = n.toString() + } + } } cached - predicate step(Instruction nodeFrom, Instruction nodeTo) { - exists(Operand mid | - instructionToOperandStep(nodeFrom, mid) and - operandToInstructionStep(mid, nodeTo) - ) - or - flowThroughCallable(nodeFrom, nodeTo) - } -} + private module Cached { + /** Holds if `p` is the `n`'th parameter of the non-virtual function `f`. */ + private predicate parameterOf(Parameter p, Function f, int n) { + not f.isVirtual() and f.getParameter(n) = p + } -/** - * Gets the enclosing callable of `n`. Unlike `n.getEnclosingCallable()`, this - * predicate ensures that joins go from `n` to the result instead of the other - * way around. - */ -pragma[inline] -private IRFunction getEnclosingCallable(Instruction n) { - pragma[only_bind_into](result) = pragma[only_bind_out](n).getEnclosingIRFunction() -} + /** + * Holds if `instr` is the `n`'th argument to a call to the non-virtual function `f`, and + * `init` is the corresponding initialization instruction that receives the value of `instr` in `f`. + */ + private predicate flowIntoParameter( + Function f, int n, CallInstruction call, Instruction instr, + InitializeParameterInstruction init + ) { + not f.isVirtual() and + call.getPositionalArgument(n) = instr and + f = call.getStaticCallTarget() and + getEnclosingNonVirtualFunctionInitializeParameter(init, f) and + init.getParameter().getIndex() = pragma[only_bind_into](pragma[only_bind_out](n)) + } -/** Holds if `nodeFrom` flows to `nodeTo`. */ -private predicate step(Instruction nodeFrom, Instruction nodeTo, MustFlowConfiguration config) { - exists(config) and - Cached::step(pragma[only_bind_into](nodeFrom), pragma[only_bind_into](nodeTo)) and - ( - config.allowInterproceduralFlow() - or - getEnclosingCallable(nodeFrom) = getEnclosingCallable(nodeTo) - ) - or - config.isAdditionalFlowStep(nodeFrom.getAUse(), nodeTo) -} + /** + * Holds if `instr` is an argument to a call to the function `f`, and `init` is the + * corresponding initialization instruction that receives the value of `instr` in `f`. + */ + pragma[noinline] + private predicate getPositionalArgumentInitParam( + CallInstruction call, Instruction instr, InitializeParameterInstruction init, Function f + ) { + exists(int n | + parameterOf(_, f, n) and + flowIntoParameter(f, pragma[only_bind_into](pragma[only_bind_out](n)), call, instr, init) + ) + } -private newtype TLocalPathNode = - MkLocalPathNode(Instruction n, MustFlowConfiguration config) { - flowsToSink(n, config) and - ( - config.isSource(n) + /** + * Holds if `instr` is the qualifier to a call to the non-virtual function `f`, and + * `init` is the corresponding initialization instruction that receives the value of + * `instr` in `f`. + */ + pragma[noinline] + private predicate getThisArgumentInitParam( + CallInstruction call, Instruction instr, InitializeParameterInstruction init, Function f + ) { + not f.isVirtual() and + call.getStaticCallTarget() = f and + getEnclosingNonVirtualFunctionInitializeParameter(init, f) and + call.getThisArgument() = instr and + init.getIRVariable() instanceof IRThisVariable + } + + /** Holds if `f` is the enclosing non-virtual function of `init`. */ + private predicate getEnclosingNonVirtualFunctionInitializeParameter( + InitializeParameterInstruction init, Function f + ) { + not f.isVirtual() and + init.getEnclosingFunction() = f + } + + /** Holds if `f` is the enclosing non-virtual function of `init`. */ + private predicate getEnclosingNonVirtualFunctionInitializeIndirection( + InitializeIndirectionInstruction init, Function f + ) { + not f.isVirtual() and + init.getEnclosingFunction() = f + } + + /** + * Holds if `instr` is an argument (or argument indirection) to a call, and + * `succ` is the corresponding initialization instruction in the call target. + */ + private predicate flowThroughCallable(Instruction argument, Instruction parameter) { + // Flow from an argument to a parameter + exists(CallInstruction call, InitializeParameterInstruction init | init = parameter | + getPositionalArgumentInitParam(call, argument, init, call.getStaticCallTarget()) + or + getThisArgumentInitParam(call, argument, init, call.getStaticCallTarget()) + ) or - exists(MustFlowPathNode mid | step(mid.getInstruction(), n, config)) - ) - } + // Flow from argument indirection to parameter indirection + exists( + CallInstruction call, ReadSideEffectInstruction read, InitializeIndirectionInstruction init + | + init = parameter and + read.getPrimaryInstruction() = call and + getEnclosingNonVirtualFunctionInitializeIndirection(init, call.getStaticCallTarget()) + | + exists(int n | + read.getSideEffectOperand().getAnyDef() = argument and + read.getIndex() = pragma[only_bind_into](n) and + init.getParameter().getIndex() = pragma[only_bind_into](n) + ) + or + call.getThisArgument() = argument and + init.getIRVariable() instanceof IRThisVariable + ) + } -/** A `Node` that is in a path from a source to a sink. */ -class MustFlowPathNode extends TLocalPathNode { - Instruction n; + private predicate instructionToOperandStep(Instruction instr, Operand operand) { + operand.getDef() = instr + } - MustFlowPathNode() { this = MkLocalPathNode(n, _) } + /** + * Holds if data flows from `operand` to `instr`. + * + * This predicate ignores flow through `PhiInstruction`s to create a 'must flow' relation. + */ + private predicate operandToInstructionStep(Operand operand, Instruction instr) { + instr.(CopyInstruction).getSourceValueOperand() = operand + or + instr.(ConvertInstruction).getUnaryOperand() = operand + or + instr.(CheckedConvertOrNullInstruction).getUnaryOperand() = operand + or + instr.(InheritanceConversionInstruction).getUnaryOperand() = operand + or + instr.(ChiInstruction).getTotalOperand() = operand + } - /** Gets the underlying node. */ - Instruction getInstruction() { result = n } - - /** Gets a textual representation of this node. */ - string toString() { result = n.getAst().toString() } - - /** Gets the location of this element. */ - Location getLocation() { result = n.getLocation() } - - /** Gets a successor node, if any. */ - MustFlowPathNode getASuccessor() { - step(this.getInstruction(), result.getInstruction(), this.getConfiguration()) - } - - /** Gets the associated configuration. */ - MustFlowConfiguration getConfiguration() { this = MkLocalPathNode(_, result) } -} - -private class MustFlowPathSink extends MustFlowPathNode { - MustFlowPathSink() { this.getConfiguration().isSink(this.getInstruction().getAUse()) } -} - -/** - * Provides the query predicates needed to include a graph in a path-problem query. - */ -module PathGraph { - private predicate reach(MustFlowPathNode n) { - n instanceof MustFlowPathSink or reach(n.getASuccessor()) - } - - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(MustFlowPathNode a, MustFlowPathNode b) { - a.getASuccessor() = b and reach(b) - } - - /** Holds if `n` is a node in the graph of data flow path explanations. */ - query predicate nodes(MustFlowPathNode n, string key, string val) { - reach(n) and key = "semmle.label" and val = n.toString() + cached + predicate step(Instruction nodeFrom, Instruction nodeTo) { + exists(Operand mid | + instructionToOperandStep(nodeFrom, mid) and + operandToInstructionStep(mid, nodeTo) + ) + or + flowThroughCallable(nodeFrom, nodeTo) + } } } diff --git a/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql b/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql index b8788910332..efd136bcd2d 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql +++ b/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql @@ -16,17 +16,15 @@ import cpp import semmle.code.cpp.ir.IR import semmle.code.cpp.ir.dataflow.MustFlow -import PathGraph +import ReturnStackAllocatedMemory::PathGraph /** Holds if `f` has a name that we interpret as evidence of intentionally returning the value of the stack pointer. */ predicate intentionallyReturnsStackPointer(Function f) { f.getName().toLowerCase().matches(["%stack%", "%sp%"]) } -class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration { - ReturnStackAllocatedMemoryConfig() { this = "ReturnStackAllocatedMemoryConfig" } - - override predicate isSource(Instruction source) { +module ReturnStackAllocatedMemoryConfig implements MustFlow::ConfigSig { + predicate isSource(Instruction source) { exists(Function func | // Rule out FPs caused by extraction errors. not func.hasErrors() and @@ -50,7 +48,7 @@ class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration { ) } - override predicate isSink(Operand sink) { + predicate isSink(Operand sink) { // Holds if `sink` is a node that represents the `StoreInstruction` that is subsequently used in // a `ReturnValueInstruction`. // We use the `StoreInstruction` instead of the instruction that defines the @@ -72,7 +70,7 @@ class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration { // int* px = id(&x); // } // ``` - override predicate allowInterproceduralFlow() { none() } + predicate allowInterproceduralFlow() { none() } /** * This configuration intentionally conflates addresses of fields and their object, and pointer offsets @@ -87,20 +85,22 @@ class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration { * } * ``` */ - override predicate isAdditionalFlowStep(Operand node1, Instruction node2) { + predicate isAdditionalFlowStep(Operand node1, Instruction node2) { node2.(FieldAddressInstruction).getObjectAddressOperand() = node1 or node2.(PointerOffsetInstruction).getLeftOperand() = node1 } - override predicate isBarrier(Instruction n) { n.getResultType() instanceof ErroneousType } + predicate isBarrier(Instruction n) { n.getResultType() instanceof ErroneousType } } +module ReturnStackAllocatedMemory = MustFlow::Global; + from - MustFlowPathNode source, MustFlowPathNode sink, Instruction instr, - ReturnStackAllocatedMemoryConfig conf + ReturnStackAllocatedMemory::PathNode source, ReturnStackAllocatedMemory::PathNode sink, + Instruction instr where - conf.hasFlowPath(pragma[only_bind_into](source), pragma[only_bind_into](sink)) and + ReturnStackAllocatedMemory::flowPath(pragma[only_bind_into](source), pragma[only_bind_into](sink)) and source.getInstruction() = instr select sink.getInstruction(), source, sink, "May return stack-allocated memory from $@.", instr.getAst(), instr.getAst().toString() diff --git a/cpp/ql/src/Likely Bugs/Memory Management/UninitializedLocal.ql b/cpp/ql/src/Likely Bugs/Memory Management/UninitializedLocal.ql index 763a142f1b9..1697ad31810 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/UninitializedLocal.ql +++ b/cpp/ql/src/Likely Bugs/Memory Management/UninitializedLocal.ql @@ -15,7 +15,7 @@ import cpp import semmle.code.cpp.ir.IR import semmle.code.cpp.ir.dataflow.MustFlow -import PathGraph +import UninitializedLocal::PathGraph /** * Auxiliary predicate: Types that don't require initialization @@ -70,25 +70,26 @@ predicate isSinkImpl(Instruction sink, VariableAccess va) { ) } -class MustFlow extends MustFlowConfiguration { - MustFlow() { this = "MustFlow" } - - override predicate isSource(Instruction source) { +module UninitializedLocalConfig implements MustFlow::ConfigSig { + predicate isSource(Instruction source) { source instanceof UninitializedInstruction and exists(Type t | t = source.getResultType() | not allocatedType(t)) } - override predicate isSink(Operand sink) { isSinkImpl(sink.getDef(), _) } + predicate isSink(Operand sink) { isSinkImpl(sink.getDef(), _) } - override predicate allowInterproceduralFlow() { none() } + predicate allowInterproceduralFlow() { none() } - override predicate isBarrier(Instruction instr) { instr instanceof ChiInstruction } + predicate isBarrier(Instruction instr) { instr instanceof ChiInstruction } } +module UninitializedLocal = MustFlow::Global; + from - VariableAccess va, LocalVariable v, MustFlow conf, MustFlowPathNode source, MustFlowPathNode sink + VariableAccess va, LocalVariable v, UninitializedLocal::PathNode source, + UninitializedLocal::PathNode sink where - conf.hasFlowPath(source, sink) and + UninitializedLocal::flowPath(source, sink) and isSinkImpl(sink.getInstruction(), va) and v = va.getTarget() select va, source, sink, "The variable $@ may not be initialized at this access.", v, v.getName() diff --git a/cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.ql b/cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.ql index bb62cfc1755..63b56d470e2 100644 --- a/cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.ql +++ b/cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.ql @@ -17,16 +17,16 @@ import cpp import semmle.code.cpp.ir.IR import semmle.code.cpp.ir.dataflow.MustFlow -import PathGraph +import UnsafeUseOfThis::PathGraph -class UnsafeUseOfThisConfig extends MustFlowConfiguration { - UnsafeUseOfThisConfig() { this = "UnsafeUseOfThisConfig" } +module UnsafeUseOfThisConfig implements MustFlow::ConfigSig { + predicate isSource(Instruction source) { isSource(source, _, _) } - override predicate isSource(Instruction source) { isSource(source, _, _) } - - override predicate isSink(Operand sink) { isSink(sink, _) } + predicate isSink(Operand sink) { isSink(sink, _) } } +module UnsafeUseOfThis = MustFlow::Global; + /** Holds if `sink` is a `this` pointer used by the call instruction `call`. */ predicate isSink(Operand sink, CallInstruction call) { exists(PureVirtualFunction func | @@ -66,19 +66,17 @@ predicate isSource(InitializeParameterInstruction source, string msg, Class c) { * - `msg` is a string describing whether `source` is from a constructor or destructor. */ predicate flows( - MustFlowPathNode source, string msg, Class sourceClass, MustFlowPathNode sink, + UnsafeUseOfThis::PathNode source, string msg, Class sourceClass, UnsafeUseOfThis::PathNode sink, CallInstruction call ) { - exists(UnsafeUseOfThisConfig conf | - conf.hasFlowPath(source, sink) and - isSource(source.getInstruction(), msg, sourceClass) and - isSink(sink.getInstruction().getAUse(), call) - ) + UnsafeUseOfThis::flowPath(source, sink) and + isSource(source.getInstruction(), msg, sourceClass) and + isSink(sink.getInstruction().getAUse(), call) } from - MustFlowPathNode source, MustFlowPathNode sink, CallInstruction call, string msg, - Class sourceClass + UnsafeUseOfThis::PathNode source, UnsafeUseOfThis::PathNode sink, CallInstruction call, + string msg, Class sourceClass where flows(source, msg, sourceClass, sink, call) and // Only raise an alert if there is no override of the pure virtual function in any base class. From 73194a5e86b5e6abc614fa75caaeed3110857554 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Mon, 16 Feb 2026 17:40:18 +0100 Subject: [PATCH 32/56] C++: Fix QL-for-QL warnings and missing QLDoc --- .../semmle/code/cpp/ir/dataflow/MustFlow.qll | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll index 07e79a907c7..5a0f34c6dc1 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll @@ -7,6 +7,9 @@ private import cpp private import semmle.code.cpp.ir.IR +/** + * Provides an inter-procedural must-flow data flow analysis. + */ module MustFlow { /** * An input configuration of a data flow analysis that performs must-flow analysis. This is different @@ -39,6 +42,9 @@ module MustFlow { default predicate allowInterproceduralFlow() { any() } } + /** + * Constructs a global must-flow computation. + */ module Global { import Config @@ -170,7 +176,7 @@ module MustFlow { not f.isVirtual() and call.getPositionalArgument(n) = instr and f = call.getStaticCallTarget() and - getEnclosingNonVirtualFunctionInitializeParameter(init, f) and + isEnclosingNonVirtualFunctionInitializeParameter(init, f) and init.getParameter().getIndex() = pragma[only_bind_into](pragma[only_bind_out](n)) } @@ -179,7 +185,7 @@ module MustFlow { * corresponding initialization instruction that receives the value of `instr` in `f`. */ pragma[noinline] - private predicate getPositionalArgumentInitParam( + private predicate isPositionalArgumentInitParam( CallInstruction call, Instruction instr, InitializeParameterInstruction init, Function f ) { exists(int n | @@ -194,18 +200,18 @@ module MustFlow { * `instr` in `f`. */ pragma[noinline] - private predicate getThisArgumentInitParam( + private predicate isThisArgumentInitParam( CallInstruction call, Instruction instr, InitializeParameterInstruction init, Function f ) { not f.isVirtual() and call.getStaticCallTarget() = f and - getEnclosingNonVirtualFunctionInitializeParameter(init, f) and + isEnclosingNonVirtualFunctionInitializeParameter(init, f) and call.getThisArgument() = instr and init.getIRVariable() instanceof IRThisVariable } /** Holds if `f` is the enclosing non-virtual function of `init`. */ - private predicate getEnclosingNonVirtualFunctionInitializeParameter( + private predicate isEnclosingNonVirtualFunctionInitializeParameter( InitializeParameterInstruction init, Function f ) { not f.isVirtual() and @@ -213,7 +219,7 @@ module MustFlow { } /** Holds if `f` is the enclosing non-virtual function of `init`. */ - private predicate getEnclosingNonVirtualFunctionInitializeIndirection( + private predicate isEnclosingNonVirtualFunctionInitializeIndirection( InitializeIndirectionInstruction init, Function f ) { not f.isVirtual() and @@ -221,15 +227,15 @@ module MustFlow { } /** - * Holds if `instr` is an argument (or argument indirection) to a call, and - * `succ` is the corresponding initialization instruction in the call target. + * Holds if `argument` is an argument (or argument indirection) to a call, and + * `parameter` is the corresponding initialization instruction in the call target. */ private predicate flowThroughCallable(Instruction argument, Instruction parameter) { // Flow from an argument to a parameter exists(CallInstruction call, InitializeParameterInstruction init | init = parameter | - getPositionalArgumentInitParam(call, argument, init, call.getStaticCallTarget()) + isPositionalArgumentInitParam(call, argument, init, call.getStaticCallTarget()) or - getThisArgumentInitParam(call, argument, init, call.getStaticCallTarget()) + isThisArgumentInitParam(call, argument, init, call.getStaticCallTarget()) ) or // Flow from argument indirection to parameter indirection @@ -238,7 +244,7 @@ module MustFlow { | init = parameter and read.getPrimaryInstruction() = call and - getEnclosingNonVirtualFunctionInitializeIndirection(init, call.getStaticCallTarget()) + isEnclosingNonVirtualFunctionInitializeIndirection(init, call.getStaticCallTarget()) | exists(int n | read.getSideEffectOperand().getAnyDef() = argument and From 366ebcad834dc2a8b93e314fa80624f7ed7b5124 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Mon, 16 Feb 2026 18:58:39 +0100 Subject: [PATCH 33/56] C++: Add `cpp/return-stack-allocated-memory` test case --- .../ReturnStackAllocatedMemory.expected | 8 ++++++++ .../Memory Management/ReturnStackAllocatedMemory/test.cpp | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/ReturnStackAllocatedMemory.expected b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/ReturnStackAllocatedMemory.expected index 6aa457b1e8a..23b23dc4a3b 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/ReturnStackAllocatedMemory.expected +++ b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/ReturnStackAllocatedMemory.expected @@ -48,6 +48,9 @@ edges | test.cpp:249:13:249:20 | call to strndupa | test.cpp:249:13:249:20 | call to strndupa | | test.cpp:249:13:249:20 | call to strndupa | test.cpp:250:9:250:10 | s2 | | test.cpp:250:9:250:10 | s2 | test.cpp:250:9:250:10 | (void *)... | +| test.cpp:253:17:253:17 | p | test.cpp:256:10:256:10 | p | +| test.cpp:255:19:255:20 | & ... | test.cpp:253:17:253:17 | p | +| test.cpp:255:20:255:20 | x | test.cpp:255:19:255:20 | & ... | nodes | test.cpp:17:9:17:11 | & ... | semmle.label | & ... | | test.cpp:17:10:17:11 | mc | semmle.label | mc | @@ -114,6 +117,10 @@ nodes | test.cpp:249:13:249:20 | call to strndupa | semmle.label | call to strndupa | | test.cpp:250:9:250:10 | (void *)... | semmle.label | (void *)... | | test.cpp:250:9:250:10 | s2 | semmle.label | s2 | +| test.cpp:253:17:253:17 | p | semmle.label | p | +| test.cpp:255:19:255:20 | & ... | semmle.label | & ... | +| test.cpp:255:20:255:20 | x | semmle.label | x | +| test.cpp:256:10:256:10 | p | semmle.label | p | #select | test.cpp:17:9:17:11 | CopyValue: & ... | test.cpp:17:10:17:11 | mc | test.cpp:17:9:17:11 | & ... | May return stack-allocated memory from $@. | test.cpp:17:10:17:11 | mc | mc | | test.cpp:25:9:25:11 | Load: ptr | test.cpp:23:18:23:19 | mc | test.cpp:25:9:25:11 | ptr | May return stack-allocated memory from $@. | test.cpp:23:18:23:19 | mc | mc | @@ -131,3 +138,4 @@ nodes | test.cpp:238:9:238:9 | Load: p | test.cpp:237:12:237:17 | call to alloca | test.cpp:238:9:238:9 | p | May return stack-allocated memory from $@. | test.cpp:237:12:237:17 | call to alloca | call to alloca | | test.cpp:245:9:245:15 | Call: call to strdupa | test.cpp:245:9:245:15 | call to strdupa | test.cpp:245:9:245:15 | call to strdupa | May return stack-allocated memory from $@. | test.cpp:245:9:245:15 | call to strdupa | call to strdupa | | test.cpp:250:9:250:10 | Convert: (void *)... | test.cpp:249:13:249:20 | call to strndupa | test.cpp:250:9:250:10 | (void *)... | May return stack-allocated memory from $@. | test.cpp:249:13:249:20 | call to strndupa | call to strndupa | +| test.cpp:256:10:256:10 | Load: p | test.cpp:255:20:255:20 | x | test.cpp:256:10:256:10 | p | May return stack-allocated memory from $@. | test.cpp:255:20:255:20 | x | x | diff --git a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/test.cpp b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/test.cpp index abc21aa74d8..ab1a626a4b0 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/test.cpp +++ b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/test.cpp @@ -250,3 +250,8 @@ void* test_strndupa(const char* s, size_t size) { return s2; // BAD } +int* f_rec(int *p, bool b) { + int x; + int* px = f_rec(&x, b); // GOOD [FALSE POSITIVE] + return p; +} From 4efbc6ea9beebfe21544fee894c2e0dcf1cdc11f Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Mon, 16 Feb 2026 19:04:23 +0100 Subject: [PATCH 34/56] C++: Handle `allowInterproceduralFlow` correctly in case of recursive functions --- .../semmle/code/cpp/ir/dataflow/MustFlow.qll | 27 +++++-------------- .../ReturnStackAllocatedMemory.expected | 8 ------ .../ReturnStackAllocatedMemory/test.cpp | 2 +- 3 files changed, 8 insertions(+), 29 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll index 5a0f34c6dc1..a8adb16849c 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll @@ -87,24 +87,12 @@ module MustFlow { ) } - /** - * Gets the enclosing callable of `n`. Unlike `n.getEnclosingCallable()`, this - * predicate ensures that joins go from `n` to the result instead of the other - * way around. - */ - pragma[inline] - private IRFunction getEnclosingCallable(Instruction n) { - pragma[only_bind_into](result) = pragma[only_bind_out](n).getEnclosingIRFunction() - } - /** Holds if `nodeFrom` flows to `nodeTo`. */ private predicate step(Instruction nodeFrom, Instruction nodeTo) { - Cached::step(pragma[only_bind_into](nodeFrom), pragma[only_bind_into](nodeTo)) and - ( - allowInterproceduralFlow() - or - getEnclosingCallable(nodeFrom) = getEnclosingCallable(nodeTo) - ) + Cached::localStep(pragma[only_bind_into](nodeFrom), pragma[only_bind_into](nodeTo)) + or + allowInterproceduralFlow() and + Cached::flowThroughCallable(pragma[only_bind_into](nodeFrom), pragma[only_bind_into](nodeTo)) or isAdditionalFlowStep(nodeFrom.getAUse(), nodeTo) } @@ -230,7 +218,8 @@ module MustFlow { * Holds if `argument` is an argument (or argument indirection) to a call, and * `parameter` is the corresponding initialization instruction in the call target. */ - private predicate flowThroughCallable(Instruction argument, Instruction parameter) { + cached + predicate flowThroughCallable(Instruction argument, Instruction parameter) { // Flow from an argument to a parameter exists(CallInstruction call, InitializeParameterInstruction init | init = parameter | isPositionalArgumentInitParam(call, argument, init, call.getStaticCallTarget()) @@ -279,13 +268,11 @@ module MustFlow { } cached - predicate step(Instruction nodeFrom, Instruction nodeTo) { + predicate localStep(Instruction nodeFrom, Instruction nodeTo) { exists(Operand mid | instructionToOperandStep(nodeFrom, mid) and operandToInstructionStep(mid, nodeTo) ) - or - flowThroughCallable(nodeFrom, nodeTo) } } } diff --git a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/ReturnStackAllocatedMemory.expected b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/ReturnStackAllocatedMemory.expected index 23b23dc4a3b..6aa457b1e8a 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/ReturnStackAllocatedMemory.expected +++ b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/ReturnStackAllocatedMemory.expected @@ -48,9 +48,6 @@ edges | test.cpp:249:13:249:20 | call to strndupa | test.cpp:249:13:249:20 | call to strndupa | | test.cpp:249:13:249:20 | call to strndupa | test.cpp:250:9:250:10 | s2 | | test.cpp:250:9:250:10 | s2 | test.cpp:250:9:250:10 | (void *)... | -| test.cpp:253:17:253:17 | p | test.cpp:256:10:256:10 | p | -| test.cpp:255:19:255:20 | & ... | test.cpp:253:17:253:17 | p | -| test.cpp:255:20:255:20 | x | test.cpp:255:19:255:20 | & ... | nodes | test.cpp:17:9:17:11 | & ... | semmle.label | & ... | | test.cpp:17:10:17:11 | mc | semmle.label | mc | @@ -117,10 +114,6 @@ nodes | test.cpp:249:13:249:20 | call to strndupa | semmle.label | call to strndupa | | test.cpp:250:9:250:10 | (void *)... | semmle.label | (void *)... | | test.cpp:250:9:250:10 | s2 | semmle.label | s2 | -| test.cpp:253:17:253:17 | p | semmle.label | p | -| test.cpp:255:19:255:20 | & ... | semmle.label | & ... | -| test.cpp:255:20:255:20 | x | semmle.label | x | -| test.cpp:256:10:256:10 | p | semmle.label | p | #select | test.cpp:17:9:17:11 | CopyValue: & ... | test.cpp:17:10:17:11 | mc | test.cpp:17:9:17:11 | & ... | May return stack-allocated memory from $@. | test.cpp:17:10:17:11 | mc | mc | | test.cpp:25:9:25:11 | Load: ptr | test.cpp:23:18:23:19 | mc | test.cpp:25:9:25:11 | ptr | May return stack-allocated memory from $@. | test.cpp:23:18:23:19 | mc | mc | @@ -138,4 +131,3 @@ nodes | test.cpp:238:9:238:9 | Load: p | test.cpp:237:12:237:17 | call to alloca | test.cpp:238:9:238:9 | p | May return stack-allocated memory from $@. | test.cpp:237:12:237:17 | call to alloca | call to alloca | | test.cpp:245:9:245:15 | Call: call to strdupa | test.cpp:245:9:245:15 | call to strdupa | test.cpp:245:9:245:15 | call to strdupa | May return stack-allocated memory from $@. | test.cpp:245:9:245:15 | call to strdupa | call to strdupa | | test.cpp:250:9:250:10 | Convert: (void *)... | test.cpp:249:13:249:20 | call to strndupa | test.cpp:250:9:250:10 | (void *)... | May return stack-allocated memory from $@. | test.cpp:249:13:249:20 | call to strndupa | call to strndupa | -| test.cpp:256:10:256:10 | Load: p | test.cpp:255:20:255:20 | x | test.cpp:256:10:256:10 | p | May return stack-allocated memory from $@. | test.cpp:255:20:255:20 | x | x | diff --git a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/test.cpp b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/test.cpp index ab1a626a4b0..abde10eb6e7 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/test.cpp +++ b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/test.cpp @@ -252,6 +252,6 @@ void* test_strndupa(const char* s, size_t size) { int* f_rec(int *p, bool b) { int x; - int* px = f_rec(&x, b); // GOOD [FALSE POSITIVE] + int* px = f_rec(&x, b); // GOOD return p; } From e299cccb6e7133c05bbb1e6c0fcc2f55151bd766 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Mon, 16 Feb 2026 19:09:30 +0100 Subject: [PATCH 35/56] C++: Simplify test --- .../Memory Management/ReturnStackAllocatedMemory/test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/test.cpp b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/test.cpp index abde10eb6e7..07e3520fa81 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/test.cpp +++ b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/test.cpp @@ -250,8 +250,8 @@ void* test_strndupa(const char* s, size_t size) { return s2; // BAD } -int* f_rec(int *p, bool b) { +int* f_rec(int *p) { int x; - int* px = f_rec(&x, b); // GOOD + int* px = f_rec(&x); // GOOD return p; } From 31895c04f80e56b832cba8f39359a4a56a0d45f4 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 17 Feb 2026 09:06:36 +0100 Subject: [PATCH 36/56] C++: `MustFlow` minor clean up --- .../semmle/code/cpp/ir/dataflow/MustFlow.qll | 257 +++++++++--------- 1 file changed, 128 insertions(+), 129 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll index a8adb16849c..2b61190fb71 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll @@ -89,10 +89,10 @@ module MustFlow { /** Holds if `nodeFrom` flows to `nodeTo`. */ private predicate step(Instruction nodeFrom, Instruction nodeTo) { - Cached::localStep(pragma[only_bind_into](nodeFrom), pragma[only_bind_into](nodeTo)) + Cached::localStep(nodeFrom, nodeTo) or allowInterproceduralFlow() and - Cached::flowThroughCallable(pragma[only_bind_into](nodeFrom), pragma[only_bind_into](nodeTo)) + Cached::flowThroughCallable(nodeFrom, nodeTo) or isAdditionalFlowStep(nodeFrom.getAUse(), nodeTo) } @@ -145,134 +145,133 @@ module MustFlow { } } } +} + +cached +private module Cached { + /** Holds if `p` is the `n`'th parameter of the non-virtual function `f`. */ + private predicate parameterOf(Parameter p, Function f, int n) { + not f.isVirtual() and f.getParameter(n) = p + } + + /** + * Holds if `instr` is the `n`'th argument to a call to the non-virtual function `f`, and + * `init` is the corresponding initialization instruction that receives the value of `instr` in `f`. + */ + private predicate flowIntoParameter( + Function f, int n, CallInstruction call, Instruction instr, InitializeParameterInstruction init + ) { + not f.isVirtual() and + call.getPositionalArgument(n) = instr and + f = call.getStaticCallTarget() and + isEnclosingNonVirtualFunctionInitializeParameter(init, f) and + init.getParameter().getIndex() = pragma[only_bind_into](pragma[only_bind_out](n)) + } + + /** + * Holds if `instr` is an argument to a call to the function `f`, and `init` is the + * corresponding initialization instruction that receives the value of `instr` in `f`. + */ + pragma[noinline] + private predicate isPositionalArgumentInitParam( + CallInstruction call, Instruction instr, InitializeParameterInstruction init, Function f + ) { + exists(int n | + parameterOf(_, f, n) and + flowIntoParameter(f, pragma[only_bind_into](pragma[only_bind_out](n)), call, instr, init) + ) + } + + /** + * Holds if `instr` is the qualifier to a call to the non-virtual function `f`, and + * `init` is the corresponding initialization instruction that receives the value of + * `instr` in `f`. + */ + pragma[noinline] + private predicate isThisArgumentInitParam( + CallInstruction call, Instruction instr, InitializeParameterInstruction init, Function f + ) { + not f.isVirtual() and + call.getStaticCallTarget() = f and + isEnclosingNonVirtualFunctionInitializeParameter(init, f) and + call.getThisArgument() = instr and + init.getIRVariable() instanceof IRThisVariable + } + + /** Holds if `f` is the enclosing non-virtual function of `init`. */ + private predicate isEnclosingNonVirtualFunctionInitializeParameter( + InitializeParameterInstruction init, Function f + ) { + not f.isVirtual() and + init.getEnclosingFunction() = f + } + + /** Holds if `f` is the enclosing non-virtual function of `init`. */ + private predicate isEnclosingNonVirtualFunctionInitializeIndirection( + InitializeIndirectionInstruction init, Function f + ) { + not f.isVirtual() and + init.getEnclosingFunction() = f + } + + /** + * Holds if `argument` is an argument (or argument indirection) to a call, and + * `parameter` is the corresponding initialization instruction in the call target. + */ + cached + predicate flowThroughCallable(Instruction argument, Instruction parameter) { + // Flow from an argument to a parameter + exists(CallInstruction call, InitializeParameterInstruction init | init = parameter | + isPositionalArgumentInitParam(call, argument, init, call.getStaticCallTarget()) + or + isThisArgumentInitParam(call, argument, init, call.getStaticCallTarget()) + ) + or + // Flow from argument indirection to parameter indirection + exists( + CallInstruction call, ReadSideEffectInstruction read, InitializeIndirectionInstruction init + | + init = parameter and + read.getPrimaryInstruction() = call and + isEnclosingNonVirtualFunctionInitializeIndirection(init, call.getStaticCallTarget()) + | + exists(int n | + read.getSideEffectOperand().getAnyDef() = argument and + read.getIndex() = pragma[only_bind_into](n) and + init.getParameter().getIndex() = pragma[only_bind_into](n) + ) + or + call.getThisArgument() = argument and + init.getIRVariable() instanceof IRThisVariable + ) + } + + private predicate instructionToOperandStep(Instruction instr, Operand operand) { + operand.getDef() = instr + } + + /** + * Holds if data flows from `operand` to `instr`. + * + * This predicate ignores flow through `PhiInstruction`s to create a 'must flow' relation. + */ + private predicate operandToInstructionStep(Operand operand, Instruction instr) { + instr.(CopyInstruction).getSourceValueOperand() = operand + or + instr.(ConvertInstruction).getUnaryOperand() = operand + or + instr.(CheckedConvertOrNullInstruction).getUnaryOperand() = operand + or + instr.(InheritanceConversionInstruction).getUnaryOperand() = operand + or + instr.(ChiInstruction).getTotalOperand() = operand + } cached - private module Cached { - /** Holds if `p` is the `n`'th parameter of the non-virtual function `f`. */ - private predicate parameterOf(Parameter p, Function f, int n) { - not f.isVirtual() and f.getParameter(n) = p - } - - /** - * Holds if `instr` is the `n`'th argument to a call to the non-virtual function `f`, and - * `init` is the corresponding initialization instruction that receives the value of `instr` in `f`. - */ - private predicate flowIntoParameter( - Function f, int n, CallInstruction call, Instruction instr, - InitializeParameterInstruction init - ) { - not f.isVirtual() and - call.getPositionalArgument(n) = instr and - f = call.getStaticCallTarget() and - isEnclosingNonVirtualFunctionInitializeParameter(init, f) and - init.getParameter().getIndex() = pragma[only_bind_into](pragma[only_bind_out](n)) - } - - /** - * Holds if `instr` is an argument to a call to the function `f`, and `init` is the - * corresponding initialization instruction that receives the value of `instr` in `f`. - */ - pragma[noinline] - private predicate isPositionalArgumentInitParam( - CallInstruction call, Instruction instr, InitializeParameterInstruction init, Function f - ) { - exists(int n | - parameterOf(_, f, n) and - flowIntoParameter(f, pragma[only_bind_into](pragma[only_bind_out](n)), call, instr, init) - ) - } - - /** - * Holds if `instr` is the qualifier to a call to the non-virtual function `f`, and - * `init` is the corresponding initialization instruction that receives the value of - * `instr` in `f`. - */ - pragma[noinline] - private predicate isThisArgumentInitParam( - CallInstruction call, Instruction instr, InitializeParameterInstruction init, Function f - ) { - not f.isVirtual() and - call.getStaticCallTarget() = f and - isEnclosingNonVirtualFunctionInitializeParameter(init, f) and - call.getThisArgument() = instr and - init.getIRVariable() instanceof IRThisVariable - } - - /** Holds if `f` is the enclosing non-virtual function of `init`. */ - private predicate isEnclosingNonVirtualFunctionInitializeParameter( - InitializeParameterInstruction init, Function f - ) { - not f.isVirtual() and - init.getEnclosingFunction() = f - } - - /** Holds if `f` is the enclosing non-virtual function of `init`. */ - private predicate isEnclosingNonVirtualFunctionInitializeIndirection( - InitializeIndirectionInstruction init, Function f - ) { - not f.isVirtual() and - init.getEnclosingFunction() = f - } - - /** - * Holds if `argument` is an argument (or argument indirection) to a call, and - * `parameter` is the corresponding initialization instruction in the call target. - */ - cached - predicate flowThroughCallable(Instruction argument, Instruction parameter) { - // Flow from an argument to a parameter - exists(CallInstruction call, InitializeParameterInstruction init | init = parameter | - isPositionalArgumentInitParam(call, argument, init, call.getStaticCallTarget()) - or - isThisArgumentInitParam(call, argument, init, call.getStaticCallTarget()) - ) - or - // Flow from argument indirection to parameter indirection - exists( - CallInstruction call, ReadSideEffectInstruction read, InitializeIndirectionInstruction init - | - init = parameter and - read.getPrimaryInstruction() = call and - isEnclosingNonVirtualFunctionInitializeIndirection(init, call.getStaticCallTarget()) - | - exists(int n | - read.getSideEffectOperand().getAnyDef() = argument and - read.getIndex() = pragma[only_bind_into](n) and - init.getParameter().getIndex() = pragma[only_bind_into](n) - ) - or - call.getThisArgument() = argument and - init.getIRVariable() instanceof IRThisVariable - ) - } - - private predicate instructionToOperandStep(Instruction instr, Operand operand) { - operand.getDef() = instr - } - - /** - * Holds if data flows from `operand` to `instr`. - * - * This predicate ignores flow through `PhiInstruction`s to create a 'must flow' relation. - */ - private predicate operandToInstructionStep(Operand operand, Instruction instr) { - instr.(CopyInstruction).getSourceValueOperand() = operand - or - instr.(ConvertInstruction).getUnaryOperand() = operand - or - instr.(CheckedConvertOrNullInstruction).getUnaryOperand() = operand - or - instr.(InheritanceConversionInstruction).getUnaryOperand() = operand - or - instr.(ChiInstruction).getTotalOperand() = operand - } - - cached - predicate localStep(Instruction nodeFrom, Instruction nodeTo) { - exists(Operand mid | - instructionToOperandStep(nodeFrom, mid) and - operandToInstructionStep(mid, nodeTo) - ) - } + predicate localStep(Instruction nodeFrom, Instruction nodeTo) { + exists(Operand mid | + instructionToOperandStep(nodeFrom, mid) and + operandToInstructionStep(mid, nodeTo) + ) } } From 3aa21242cdf08ac14dfc0ef85832734a2251df87 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 17 Feb 2026 10:28:29 +0100 Subject: [PATCH 37/56] C++: Add change notes --- cpp/ql/lib/change-notes/2026-02-14-must-flow-fix.md | 4 ++++ cpp/ql/lib/change-notes/2026-02-14-must-flow.md | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 cpp/ql/lib/change-notes/2026-02-14-must-flow-fix.md create mode 100644 cpp/ql/lib/change-notes/2026-02-14-must-flow.md diff --git a/cpp/ql/lib/change-notes/2026-02-14-must-flow-fix.md b/cpp/ql/lib/change-notes/2026-02-14-must-flow-fix.md new file mode 100644 index 00000000000..fc838f51c06 --- /dev/null +++ b/cpp/ql/lib/change-notes/2026-02-14-must-flow-fix.md @@ -0,0 +1,4 @@ +--- +category: fix +--- +* The `allowInterproceduralFlow` predicate of must-flow data flow configurations now correctly handles direct recursion. diff --git a/cpp/ql/lib/change-notes/2026-02-14-must-flow.md b/cpp/ql/lib/change-notes/2026-02-14-must-flow.md new file mode 100644 index 00000000000..3d1afaa6344 --- /dev/null +++ b/cpp/ql/lib/change-notes/2026-02-14-must-flow.md @@ -0,0 +1,4 @@ +--- +category: breaking +--- +* `MustFlow`, the inter-procedural must-flow data flow analysis library, has been re-worked to use parameterized modules. Like in the case of data flow and taint tracking, instead of extending the `MustFlowConfiguration` class, the user should now implement a module with the `MustFlow::ConfigSig` signature, and instantiate the `MustFlow::Global` parameterized module with the implemented module. From c3f0967e9bde3ab6251d6492a8511271ec23f109 Mon Sep 17 00:00:00 2001 From: idrissrio Date: Fri, 12 Dec 2025 09:03:16 +0100 Subject: [PATCH 38/56] C/C++ overlay: discard single location elements --- .../lib/semmle/code/cpp/internal/Overlay.qll | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/cpp/ql/lib/semmle/code/cpp/internal/Overlay.qll b/cpp/ql/lib/semmle/code/cpp/internal/Overlay.qll index 7e98177f323..3dea144bbf6 100644 --- a/cpp/ql/lib/semmle/code/cpp/internal/Overlay.qll +++ b/cpp/ql/lib/semmle/code/cpp/internal/Overlay.qll @@ -34,6 +34,38 @@ private string getSingleLocationFilePath(@element e) { macroinvocations(e, _, loc, _) or preprocdirects(e, _, loc) + or + diagnostics(e, _, _, _, _, loc) + or + usings(e, _, loc, _) + or + static_asserts(e, _, _, loc, _) + or + derivations(e, _, _, _, loc) + or + frienddecls(e, _, _, loc) + or + comments(e, _, loc) + or + exprs(e, _, loc) + or + stmts(e, _, loc) + or + initialisers(e, _, _, loc) + or + attributes(e, _, _, _, loc) + or + attribute_args(e, _, _, _, loc) + or + namequalifiers(e, _, _, loc) + or + enumconstants(e, _, _, _, _, loc) + or + type_mentions(e, _, loc, _) + or + lambda_capture(e, _, _, _, _, _, loc) + or + concept_templates(e, _, loc) | result = getLocationFilePath(loc) ) From d3541b87d6d7945f6758092c305ba137c49b34aa Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 11 Feb 2026 16:07:43 +0100 Subject: [PATCH 39/56] Rust: Make path resolution robust against invalid code with conflicting declarations --- .../codeql/rust/internal/PathResolution.qll | 73 +++++++++++++------ .../path-resolution/invalid/main.rs | 6 ++ .../path-resolution/invalid/options.yml | 1 + .../path-resolution/path-resolution.expected | 1 + 4 files changed, 58 insertions(+), 23 deletions(-) create mode 100644 rust/ql/test/library-tests/path-resolution/invalid/main.rs create mode 100644 rust/ql/test/library-tests/path-resolution/invalid/options.yml diff --git a/rust/ql/lib/codeql/rust/internal/PathResolution.qll b/rust/ql/lib/codeql/rust/internal/PathResolution.qll index 432608f6f9d..76c15485bb9 100644 --- a/rust/ql/lib/codeql/rust/internal/PathResolution.qll +++ b/rust/ql/lib/codeql/rust/internal/PathResolution.qll @@ -107,7 +107,7 @@ class SuccessorKind extends TSuccessorKind { } pragma[nomagic] -private ItemNode getAChildSuccessor(ItemNode item, string name, SuccessorKind kind) { +private ItemNode getAChildSuccessor0(ItemNode item, string name, SuccessorKind kind) { item = result.getImmediateParent() and name = result.getName() and // Associated items in `impl` and `trait` blocks are handled elsewhere @@ -116,7 +116,7 @@ private ItemNode getAChildSuccessor(ItemNode item, string name, SuccessorKind ki if result instanceof TypeParam then kind.isInternal() else - if result.isPublic() + if result.isPublic() or item instanceof SourceFile then kind.isBoth() else kind.isInternal() or @@ -130,6 +130,41 @@ private ItemNode getAChildSuccessor(ItemNode item, string name, SuccessorKind ki result = item } +pragma[nomagic] +private NamedItemNode getANamedNonModuleChildSuccessor( + ItemNode item, string name, Namespace ns, int startline, int startcolumn, int endline, + int endcolumn +) { + result.getLocation().hasLocationInfo(_, startline, startcolumn, endline, endcolumn) and + result = getAChildSuccessor0(item, name, _) and + ns = result.getNamespace() and + not result instanceof ModuleItemNode +} + +pragma[nomagic] +private ItemNode getAChildSuccessor(ItemNode item, string name, SuccessorKind kind) { + result = getAChildSuccessor0(item, name, kind) and + // In valid Rust code, there cannot be multiple children with the same name and namespace, + // but to safeguard against combinatorial explosions in invalid code, we always pick the + // last child, except for modules, where we take the union. + ( + not result instanceof NamedItemNode + or + result instanceof ModuleItemNode + or + exists(Namespace ns | + result = + max(NamedItemNode i, int startline, int startcolumn, int endline, int endcolumn | + i = + getANamedNonModuleChildSuccessor(item, name, ns, startline, startcolumn, endline, + endcolumn) + | + i order by startline, startcolumn, endline, endcolumn + ) + ) + ) +} + private module UseOption = Option; private class UseOption = UseOption::Option; @@ -288,10 +323,6 @@ abstract class ItemNode extends Locatable { cached ItemNode getASuccessor(string name, SuccessorKind kind, UseOption useOpt) { Stages::PathResolutionStage::ref() and - sourceFileEdge(this, name, result) and - kind.isBoth() and - useOpt.isNone() - or result = getAChildSuccessor(this, name, kind) and useOpt.isNone() or @@ -471,6 +502,8 @@ abstract class ItemNode extends Locatable { Location getLocation() { result = super.getLocation() } } +abstract class NamedItemNode extends ItemNode { } + abstract class TypeItemNode extends ItemNode { } /** A module or a source file. */ @@ -509,7 +542,7 @@ private class SourceFileItemNode extends ModuleLikeNode instanceof SourceFile { override string getCanonicalPath(Crate c) { none() } } -class CrateItemNode extends ItemNode instanceof Crate { +class CrateItemNode extends NamedItemNode instanceof Crate { /** * Gets the source file that defines this crate. */ @@ -565,7 +598,7 @@ class CrateItemNode extends ItemNode instanceof Crate { override string getCanonicalPath(Crate c) { c = this and result = Crate.super.getName() } } -class ExternCrateItemNode extends ItemNode instanceof ExternCrate { +class ExternCrateItemNode extends NamedItemNode instanceof ExternCrate { override string getName() { result = super.getRename().getName().getText() or @@ -573,7 +606,7 @@ class ExternCrateItemNode extends ItemNode instanceof ExternCrate { result = super.getIdentifier().getText() } - override Namespace getNamespace() { none() } + override Namespace getNamespace() { result.isType() } override Visibility getVisibility() { result = ExternCrate.super.getVisibility() } @@ -587,7 +620,7 @@ class ExternCrateItemNode extends ItemNode instanceof ExternCrate { } /** An item that can occur in a trait or an `impl` block. */ -abstract private class AssocItemNode extends ItemNode instanceof AssocItem { +abstract private class AssocItemNode extends NamedItemNode instanceof AssocItem { /** Holds if this associated item has an implementation. */ abstract predicate hasImplementation(); @@ -626,7 +659,7 @@ private class ConstItemNode extends AssocItemNode instanceof Const { override TypeParam getTypeParam(int i) { none() } } -private class TypeItemTypeItemNode extends TypeItemNode instanceof TypeItem { +private class TypeItemTypeItemNode extends NamedItemNode, TypeItemNode instanceof TypeItem { override string getName() { result = TypeItem.super.getName().getText() } override Namespace getNamespace() { result.isType() } @@ -659,7 +692,7 @@ private class TypeItemTypeItemNode extends TypeItemNode instanceof TypeItem { } /** An item that can be referenced with arguments. */ -abstract class ParameterizableItemNode extends ItemNode { +abstract class ParameterizableItemNode extends NamedItemNode { /** Gets the arity this item. */ abstract int getArity(); } @@ -911,7 +944,7 @@ private class ImplTraitTypeReprItemNodeImpl extends ImplTraitTypeReprItemNode { ItemNode resolveABoundCand() { result = resolvePathCand(this.getABoundPath()) } } -private class ModuleItemNode extends ModuleLikeNode instanceof Module { +private class ModuleItemNode extends NamedItemNode, ModuleLikeNode instanceof Module { override string getName() { result = Module.super.getName().getText() } override Namespace getNamespace() { result.isType() } @@ -929,7 +962,7 @@ private class ModuleItemNode extends ModuleLikeNode instanceof Module { ( exists(SourceFile f | fileImport(this, f) and - sourceFileEdge(f, _, child) + child = getAChildSuccessor(f, _, _) ) or this = child.getImmediateParent() @@ -1001,7 +1034,7 @@ private class StructItemNode extends TypeItemTypeItemNode, ParameterizableItemNo } } -final class TraitItemNode extends ImplOrTraitItemNode, TypeItemNode instanceof Trait { +final class TraitItemNode extends ImplOrTraitItemNode, NamedItemNode, TypeItemNode instanceof Trait { pragma[nomagic] Path getABoundPath() { result = super.getATypeBound().getTypeRepr().(PathTypeRepr).getPath() } @@ -1126,7 +1159,7 @@ private class BlockExprItemNode extends ItemNode instanceof BlockExpr { pragma[nomagic] private Path getWherePredPath(WherePred wp) { result = wp.getTypeRepr().(PathTypeRepr).getPath() } -final class TypeParamItemNode extends TypeItemNode instanceof TypeParam { +final class TypeParamItemNode extends NamedItemNode, TypeItemNode instanceof TypeParam { /** Gets a where predicate for this type parameter, if any */ pragma[nomagic] private WherePred getAWherePred() { @@ -1214,7 +1247,7 @@ final private class TypeParamItemNodeImpl extends TypeParamItemNode instanceof T ItemNode resolveABoundCand() { result = resolvePathCand(this.getABoundPathCand()) } } -abstract private class MacroItemNode extends ItemNode { +abstract private class MacroItemNode extends NamedItemNode { override Namespace getNamespace() { result.isMacro() } override TypeParam getTypeParam(int i) { none() } @@ -1256,12 +1289,6 @@ private class MacroDefItemNode extends MacroItemNode instanceof MacroDef { override Attr getAnAttr() { result = MacroDef.super.getAnAttr() } } -/** Holds if `item` has the name `name` and is a top-level item inside `f`. */ -private predicate sourceFileEdge(SourceFile f, string name, ItemNode item) { - item = f.(ItemNode).getADescendant() and - name = item.getName() -} - /** Holds if `f` is available as `mod name;` inside `folder`. */ pragma[nomagic] private predicate fileModule(SourceFile f, string name, Folder folder) { diff --git a/rust/ql/test/library-tests/path-resolution/invalid/main.rs b/rust/ql/test/library-tests/path-resolution/invalid/main.rs new file mode 100644 index 00000000000..b58fcb2d934 --- /dev/null +++ b/rust/ql/test/library-tests/path-resolution/invalid/main.rs @@ -0,0 +1,6 @@ +// The code in this file is not valid Rust code + +struct A; // A1 +struct A; // A2 + +fn f(x: A) {} // $ item=A2 (the latter occurence takes precedence) diff --git a/rust/ql/test/library-tests/path-resolution/invalid/options.yml b/rust/ql/test/library-tests/path-resolution/invalid/options.yml new file mode 100644 index 00000000000..cf148dd35f8 --- /dev/null +++ b/rust/ql/test/library-tests/path-resolution/invalid/options.yml @@ -0,0 +1 @@ +qltest_cargo_check: false diff --git a/rust/ql/test/library-tests/path-resolution/path-resolution.expected b/rust/ql/test/library-tests/path-resolution/path-resolution.expected index 153d80db4cc..e85bb7876da 100644 --- a/rust/ql/test/library-tests/path-resolution/path-resolution.expected +++ b/rust/ql/test/library-tests/path-resolution/path-resolution.expected @@ -51,6 +51,7 @@ mod | my/nested.rs:1:1:17:1 | mod nested1 | | my/nested.rs:2:5:11:5 | mod nested2 | resolvePath +| invalid/main.rs:6:9:6:9 | A | invalid/main.rs:3:11:4:9 | struct A | | main.rs:4:8:4:9 | my | main.rs:1:1:1:7 | mod my | | main.rs:4:14:4:17 | self | main.rs:1:1:1:7 | mod my | | main.rs:6:5:6:6 | my | main.rs:1:1:1:7 | mod my | From bd94ceddd9b2a23082b3920270b520543c70db03 Mon Sep 17 00:00:00 2001 From: idrissrio Date: Tue, 17 Feb 2026 13:58:55 +0100 Subject: [PATCH 40/56] Java: Add change note for JDK 26 --- java/ql/src/change-notes/2026-02-17-support-java-26 | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 java/ql/src/change-notes/2026-02-17-support-java-26 diff --git a/java/ql/src/change-notes/2026-02-17-support-java-26 b/java/ql/src/change-notes/2026-02-17-support-java-26 new file mode 100644 index 00000000000..50feec4224d --- /dev/null +++ b/java/ql/src/change-notes/2026-02-17-support-java-26 @@ -0,0 +1,4 @@ +--- +category: feature +--- +* The Java extractor and QL libraries now support Java 26. From 1a35a05cccee80af03871f31fad1f7422e58c8cd Mon Sep 17 00:00:00 2001 From: idrissrio Date: Tue, 17 Feb 2026 13:59:45 +0100 Subject: [PATCH 41/56] Java: Update supported language versions to include Java 26 --- docs/codeql/reusables/supported-versions-compilers.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/codeql/reusables/supported-versions-compilers.rst b/docs/codeql/reusables/supported-versions-compilers.rst index 6216bae08df..566238658f3 100644 --- a/docs/codeql/reusables/supported-versions-compilers.rst +++ b/docs/codeql/reusables/supported-versions-compilers.rst @@ -18,7 +18,7 @@ .NET 5, .NET 6, .NET 7, .NET 8, .NET 9","``.sln``, ``.slnx``, ``.csproj``, ``.cs``, ``.cshtml``, ``.xaml``" GitHub Actions,"Not applicable",Not applicable,"``.github/workflows/*.yml``, ``.github/workflows/*.yaml``, ``**/action.yml``, ``**/action.yaml``" Go (aka Golang), "Go up to 1.26", "Go 1.11 or more recent", ``.go`` - Java,"Java 7 to 25 [6]_","javac (OpenJDK and Oracle JDK), + Java,"Java 7 to 26 [6]_","javac (OpenJDK and Oracle JDK), Eclipse compiler for Java (ECJ) [7]_",``.java`` Kotlin,"Kotlin 1.8.0 to 2.3.0\ *x*","kotlinc",``.kt`` @@ -36,7 +36,7 @@ .. [3] Objective-C, Objective-C++, C++/CLI, and C++/CX are not supported. .. [4] Support for the clang-cl compiler is preliminary. .. [5] Support for the Arm Compiler (armcc) is preliminary. - .. [6] Builds that execute on Java 7 to 25 can be analyzed. The analysis understands standard language features in Java 8 to 25; "preview" and "incubator" features are not supported. Source code using Java language versions older than Java 8 are analyzed as Java 8 code. + .. [6] Builds that execute on Java 7 to 26 can be analyzed. The analysis understands standard language features in Java 8 to 26; "preview" and "incubator" features are not supported. Source code using Java language versions older than Java 8 are analyzed as Java 8 code. .. [7] ECJ is supported when the build invokes it via the Maven Compiler plugin or the Takari Lifecycle plugin. .. [8] JSX and Flow code, YAML, JSON, HTML, and XML files may also be analyzed with JavaScript files. .. [9] The extractor requires Python 3 to run. To analyze Python 2.7 you should install both versions of Python. From 8aa839f4c0a8f9c9e30926fd7515db8f49276b98 Mon Sep 17 00:00:00 2001 From: idrissrio Date: Tue, 17 Feb 2026 14:19:12 +0100 Subject: [PATCH 42/56] Java: Address review comments --- ...2026-02-17-support-java-26 => 2026-02-17-support-java-26.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename java/ql/src/change-notes/{2026-02-17-support-java-26 => 2026-02-17-support-java-26.md} (73%) diff --git a/java/ql/src/change-notes/2026-02-17-support-java-26 b/java/ql/src/change-notes/2026-02-17-support-java-26.md similarity index 73% rename from java/ql/src/change-notes/2026-02-17-support-java-26 rename to java/ql/src/change-notes/2026-02-17-support-java-26.md index 50feec4224d..db0a108f0ca 100644 --- a/java/ql/src/change-notes/2026-02-17-support-java-26 +++ b/java/ql/src/change-notes/2026-02-17-support-java-26.md @@ -1,4 +1,4 @@ --- -category: feature +category: minorAnalysis --- * The Java extractor and QL libraries now support Java 26. From 5151df456ce482483850faa24f1172776cd5a3ce Mon Sep 17 00:00:00 2001 From: idrissrio Date: Tue, 17 Feb 2026 14:27:27 +0100 Subject: [PATCH 43/56] Java: Fix Maven change note --- ...-maven-default-java-17 => 2025-11-13-maven-default-java-17.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename java/ql/src/change-notes/{2025-11-13-maven-default-java-17 => 2025-11-13-maven-default-java-17.md} (100%) diff --git a/java/ql/src/change-notes/2025-11-13-maven-default-java-17 b/java/ql/src/change-notes/2025-11-13-maven-default-java-17.md similarity index 100% rename from java/ql/src/change-notes/2025-11-13-maven-default-java-17 rename to java/ql/src/change-notes/2025-11-13-maven-default-java-17.md From ceb3b21e0f2e27f7cf2926fa235c286eea34c1e9 Mon Sep 17 00:00:00 2001 From: Ben Rodes Date: Tue, 17 Feb 2026 10:28:43 -0500 Subject: [PATCH 44/56] Update python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll Co-authored-by: Taus --- ...ServerSideRequestForgeryCustomizations.qll | 46 +++++++------------ 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll index afce799d8ac..3fb260e425d 100644 --- a/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll @@ -186,37 +186,25 @@ module ServerSideRequestForgery { private predicate uri_validator(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) { exists(DataFlow::CallCfgNode call, string funcs | - funcs in ["in_domain", "in_azure_keyvault_domain", "in_azure_storage_domain"] - | + funcs in ["in_domain", "in_azure_keyvault_domain", "in_azure_storage_domain"] and call = API::moduleImport("AntiSSRF").getMember("URIValidator").getMember(funcs).getACall() and - call.getArg(0).asCfgNode() = node and - ( - // validator used in a comparison - 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 - 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) - ) - ) - ) + call.getArg(0).asCfgNode() = node + | + // validator call directly (e.g., if URIValidator.in_domain(...) ) + g = call.asCfgNode() and + branch = true + or + // validator used in a comparison + exists(Cmpop op, Node n, ControlFlowNode l | + n.getALocalSource() = call and g.(CompareNode).operands(n.asCfgNode(), op, l) + | + // validator == true or validator == false or validator is True or validator is False + (op instanceof Eq or op instanceof Is) and + branch = l.getNode().(BooleanLiteral).booleanValue() or - // validator call directly (e.g., if URIValidator.in_domain(...) ) - g = call.asCfgNode() and - branch = true + // validator != false or validator != true or validator is not True or validator is not False + (op instanceof NotEq or op instanceof IsNot) and + branch = l.getNode().(BooleanLiteral).booleanValue().booleanNot() ) ) } From 0676ba1c07d9741b04fe077379588ccbf6d06fa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20San=20Jos=C3=A9?= Date: Tue, 17 Feb 2026 17:23:38 +0100 Subject: [PATCH 45/56] Skip csharp integration tests on macos-26 --- .../posix/standalone_dependencies_no_framework/test.py | 6 ++++-- .../posix/standalone_dependencies_nuget with_space/test.py | 6 ++++-- .../posix/standalone_dependencies_nuget/test.py | 6 ++++-- .../posix/standalone_dependencies_nuget_no_sources/test.py | 6 ++++-- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/csharp/ql/integration-tests/posix/standalone_dependencies_no_framework/test.py b/csharp/ql/integration-tests/posix/standalone_dependencies_no_framework/test.py index d1c1745d69b..e0181a1ec5d 100644 --- a/csharp/ql/integration-tests/posix/standalone_dependencies_no_framework/test.py +++ b/csharp/ql/integration-tests/posix/standalone_dependencies_no_framework/test.py @@ -3,10 +3,12 @@ import pytest import os -# Skipping the test on the ARM runners and macos-15, as we're running into trouble with Mono and nuget. +# Skipping the test on the ARM runners, macos-15 and macos-26, as we're running +# into trouble with Mono and nuget. @pytest.mark.only_if( runs_on.linux - or (runs_on.macos and runs_on.x86_64 and not runs_on.macos_15) + or (runs_on.macos and runs_on.x86_64 + and not runs_on.macos_15 and not runs_on.macos_26) ) def test(codeql, csharp): os.environ["CODEQL_EXTRACTOR_CSHARP_BUILDLESS_DOTNET_FRAMEWORK_REFERENCES"] = ( diff --git a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget with_space/test.py b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget with_space/test.py index 6d2058c684c..4f3097bb387 100644 --- a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget with_space/test.py +++ b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget with_space/test.py @@ -3,10 +3,12 @@ import runs_on import pytest -# Skipping the test on the ARM runners and macos-15, as we're running into trouble with Mono and nuget. +# Skipping the test on the ARM runners, macos-15 and macos-26, as we're running +# into trouble with Mono and nuget. @pytest.mark.only_if( runs_on.linux - or (runs_on.macos and runs_on.x86_64 and not runs_on.macos_15) + or (runs_on.macos and runs_on.x86_64 + and not runs_on.macos_15 and not runs_on.macos_26) ) def test(codeql, csharp): # making sure we're not doing any fallback restore: diff --git a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget/test.py b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget/test.py index 7f88196097f..9a8c9b2291e 100644 --- a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget/test.py +++ b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget/test.py @@ -2,10 +2,12 @@ import runs_on import pytest -# Skipping the test on the ARM runners and macos-15, as we're running into trouble with Mono and nuget. +# Skipping the test on the ARM runners, macos-15 and macos-26, as we're running +# into trouble with Mono and nuget. @pytest.mark.only_if( runs_on.linux - or (runs_on.macos and runs_on.x86_64 and not runs_on.macos_15) + or (runs_on.macos and runs_on.x86_64 + and not runs_on.macos_15 and not runs_on.macos_26) ) def test(codeql, csharp): codeql.database.create(build_mode="none") diff --git a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_no_sources/test.py b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_no_sources/test.py index 185fb5201f9..f095580d34d 100644 --- a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_no_sources/test.py +++ b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_no_sources/test.py @@ -2,10 +2,12 @@ import runs_on import pytest -# Skipping the test on the ARM runners, as we're running into trouble with Mono and nuget. +# Skipping the test on the ARM runners, macos-15 and macos-26, as we're running +# into trouble with Mono and nuget. @pytest.mark.only_if( runs_on.linux - or (runs_on.macos and runs_on.x86_64 and not runs_on.macos_15) + or (runs_on.macos and runs_on.x86_64 + and not runs_on.macos_15 and not runs_on.macos_26) ) def test(codeql, csharp): codeql.database.create(source_root="proj", build_mode="none") From 5cf281a1b6b9fe060e00811a56056ba7845edd78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20San=20Jos=C3=A9?= Date: Tue, 17 Feb 2026 18:16:51 +0100 Subject: [PATCH 46/56] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../posix/standalone_dependencies_no_framework/test.py | 2 +- .../posix/standalone_dependencies_nuget/test.py | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/csharp/ql/integration-tests/posix/standalone_dependencies_no_framework/test.py b/csharp/ql/integration-tests/posix/standalone_dependencies_no_framework/test.py index e0181a1ec5d..8f66ebf714e 100644 --- a/csharp/ql/integration-tests/posix/standalone_dependencies_no_framework/test.py +++ b/csharp/ql/integration-tests/posix/standalone_dependencies_no_framework/test.py @@ -3,7 +3,7 @@ import pytest import os -# Skipping the test on the ARM runners, macos-15 and macos-26, as we're running +# Skipping the test on the ARM runners, macos-15 and macos-26, as we're running # into trouble with Mono and nuget. @pytest.mark.only_if( runs_on.linux diff --git a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget/test.py b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget/test.py index 9a8c9b2291e..ebdad77e716 100644 --- a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget/test.py +++ b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget/test.py @@ -6,8 +6,14 @@ import pytest # into trouble with Mono and nuget. @pytest.mark.only_if( runs_on.linux - or (runs_on.macos and runs_on.x86_64 - and not runs_on.macos_15 and not runs_on.macos_26) + or ( + runs_on.macos + and runs_on.x86_64 + and not ( + runs_on.macos_15 + or runs_on.macos_26 + ) + ) ) def test(codeql, csharp): codeql.database.create(build_mode="none") From 9efe1120260b306d86282e8da35828581b1b9ef0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 17:16:54 +0000 Subject: [PATCH 47/56] Initial plan From 004ebd386c85c7548f39e37c0efd2fdb4d4afa71 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 17:21:50 +0000 Subject: [PATCH 48/56] Centralize mono/nuget skip predicate in conftest.py Co-authored-by: oscarsj <1410188+oscarsj@users.noreply.github.com> --- csharp/ql/integration-tests/posix/conftest.py | 20 +++++++++++++++++++ .../test.py | 10 ++-------- .../test.py | 10 ++-------- .../standalone_dependencies_nuget/test.py | 16 ++------------- .../test.py | 10 ++-------- 5 files changed, 28 insertions(+), 38 deletions(-) create mode 100644 csharp/ql/integration-tests/posix/conftest.py diff --git a/csharp/ql/integration-tests/posix/conftest.py b/csharp/ql/integration-tests/posix/conftest.py new file mode 100644 index 00000000000..0cd4ee6cfa0 --- /dev/null +++ b/csharp/ql/integration-tests/posix/conftest.py @@ -0,0 +1,20 @@ +import runs_on +import pytest + + +def _supports_mono_nuget(): + """ + Helper function to determine if the current platform supports Mono and nuget. + + Returns True if running on Linux or on macOS x86_64 (excluding macos-15 and macos-26). + macOS ARM runners (macos-15 and macos-26) are excluded due to issues with Mono and nuget. + """ + return ( + runs_on.linux + or ( + runs_on.macos + and runs_on.x86_64 + and not runs_on.macos_15 + and not runs_on.macos_26 + ) + ) diff --git a/csharp/ql/integration-tests/posix/standalone_dependencies_no_framework/test.py b/csharp/ql/integration-tests/posix/standalone_dependencies_no_framework/test.py index 8f66ebf714e..3d0d011f73d 100644 --- a/csharp/ql/integration-tests/posix/standalone_dependencies_no_framework/test.py +++ b/csharp/ql/integration-tests/posix/standalone_dependencies_no_framework/test.py @@ -1,15 +1,9 @@ -import runs_on import pytest import os +from conftest import _supports_mono_nuget -# Skipping the test on the ARM runners, macos-15 and macos-26, as we're running -# into trouble with Mono and nuget. -@pytest.mark.only_if( - runs_on.linux - or (runs_on.macos and runs_on.x86_64 - and not runs_on.macos_15 and not runs_on.macos_26) -) +@pytest.mark.only_if(_supports_mono_nuget()) def test(codeql, csharp): os.environ["CODEQL_EXTRACTOR_CSHARP_BUILDLESS_DOTNET_FRAMEWORK_REFERENCES"] = ( "/non-existent-path" diff --git a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget with_space/test.py b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget with_space/test.py index 4f3097bb387..1b111d08239 100644 --- a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget with_space/test.py +++ b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget with_space/test.py @@ -1,15 +1,9 @@ import os -import runs_on import pytest +from conftest import _supports_mono_nuget -# Skipping the test on the ARM runners, macos-15 and macos-26, as we're running -# into trouble with Mono and nuget. -@pytest.mark.only_if( - runs_on.linux - or (runs_on.macos and runs_on.x86_64 - and not runs_on.macos_15 and not runs_on.macos_26) -) +@pytest.mark.only_if(_supports_mono_nuget()) def test(codeql, csharp): # making sure we're not doing any fallback restore: os.environ["CODEQL_EXTRACTOR_CSHARP_BUILDLESS_NUGET_FEEDS_CHECK_FALLBACK_TIMEOUT"] = "1" diff --git a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget/test.py b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget/test.py index ebdad77e716..2d887a52244 100644 --- a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget/test.py +++ b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget/test.py @@ -1,19 +1,7 @@ -import runs_on import pytest +from conftest import _supports_mono_nuget -# Skipping the test on the ARM runners, macos-15 and macos-26, as we're running -# into trouble with Mono and nuget. -@pytest.mark.only_if( - runs_on.linux - or ( - runs_on.macos - and runs_on.x86_64 - and not ( - runs_on.macos_15 - or runs_on.macos_26 - ) - ) -) +@pytest.mark.only_if(_supports_mono_nuget()) def test(codeql, csharp): codeql.database.create(build_mode="none") diff --git a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_no_sources/test.py b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_no_sources/test.py index f095580d34d..a2bb13e921b 100644 --- a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_no_sources/test.py +++ b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_no_sources/test.py @@ -1,13 +1,7 @@ -import runs_on import pytest +from conftest import _supports_mono_nuget -# Skipping the test on the ARM runners, macos-15 and macos-26, as we're running -# into trouble with Mono and nuget. -@pytest.mark.only_if( - runs_on.linux - or (runs_on.macos and runs_on.x86_64 - and not runs_on.macos_15 and not runs_on.macos_26) -) +@pytest.mark.only_if(_supports_mono_nuget()) def test(codeql, csharp): codeql.database.create(source_root="proj", build_mode="none") From 60b8213fdda848af3dfc2eca803f252fcf98ede6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 17:22:27 +0000 Subject: [PATCH 49/56] Remove unused pytest import from conftest.py Co-authored-by: oscarsj <1410188+oscarsj@users.noreply.github.com> --- csharp/ql/integration-tests/posix/conftest.py | 1 - 1 file changed, 1 deletion(-) diff --git a/csharp/ql/integration-tests/posix/conftest.py b/csharp/ql/integration-tests/posix/conftest.py index 0cd4ee6cfa0..543bc046c98 100644 --- a/csharp/ql/integration-tests/posix/conftest.py +++ b/csharp/ql/integration-tests/posix/conftest.py @@ -1,5 +1,4 @@ import runs_on -import pytest def _supports_mono_nuget(): From 6760390d754b01d020cfef2652791bc715770683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20San=20Jos=C3=A9?= Date: Tue, 17 Feb 2026 18:49:11 +0100 Subject: [PATCH 50/56] Fix imports --- .../posix/standalone_dependencies_no_framework/test.py | 3 ++- .../posix/standalone_dependencies_nuget with_space/test.py | 2 +- .../posix/standalone_dependencies_nuget/test.py | 2 +- .../posix/standalone_dependencies_nuget_no_sources/test.py | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/csharp/ql/integration-tests/posix/standalone_dependencies_no_framework/test.py b/csharp/ql/integration-tests/posix/standalone_dependencies_no_framework/test.py index 3d0d011f73d..21987ed7a2d 100644 --- a/csharp/ql/integration-tests/posix/standalone_dependencies_no_framework/test.py +++ b/csharp/ql/integration-tests/posix/standalone_dependencies_no_framework/test.py @@ -1,6 +1,7 @@ import pytest import os -from conftest import _supports_mono_nuget +from ..conftest import _supports_mono_nuget + @pytest.mark.only_if(_supports_mono_nuget()) diff --git a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget with_space/test.py b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget with_space/test.py index 1b111d08239..662178aa3c0 100644 --- a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget with_space/test.py +++ b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget with_space/test.py @@ -1,6 +1,6 @@ import os import pytest -from conftest import _supports_mono_nuget +from ..conftest import _supports_mono_nuget @pytest.mark.only_if(_supports_mono_nuget()) diff --git a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget/test.py b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget/test.py index 2d887a52244..d8e8c8055ad 100644 --- a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget/test.py +++ b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget/test.py @@ -1,5 +1,5 @@ import pytest -from conftest import _supports_mono_nuget +from ..conftest import _supports_mono_nuget @pytest.mark.only_if(_supports_mono_nuget()) diff --git a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_no_sources/test.py b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_no_sources/test.py index a2bb13e921b..ccd8f61a384 100644 --- a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_no_sources/test.py +++ b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_no_sources/test.py @@ -1,5 +1,5 @@ import pytest -from conftest import _supports_mono_nuget +from ..conftest import _supports_mono_nuget @pytest.mark.only_if(_supports_mono_nuget()) From fa73cd5d5c8233a1a1633f0f8fcc5147627df1e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20San=20Jos=C3=A9?= Date: Tue, 17 Feb 2026 18:49:51 +0100 Subject: [PATCH 51/56] Remove unnecessary blank line in test.py --- .../posix/standalone_dependencies_no_framework/test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/csharp/ql/integration-tests/posix/standalone_dependencies_no_framework/test.py b/csharp/ql/integration-tests/posix/standalone_dependencies_no_framework/test.py index 21987ed7a2d..725ded1899c 100644 --- a/csharp/ql/integration-tests/posix/standalone_dependencies_no_framework/test.py +++ b/csharp/ql/integration-tests/posix/standalone_dependencies_no_framework/test.py @@ -3,7 +3,6 @@ import os from ..conftest import _supports_mono_nuget - @pytest.mark.only_if(_supports_mono_nuget()) def test(codeql, csharp): os.environ["CODEQL_EXTRACTOR_CSHARP_BUILDLESS_DOTNET_FRAMEWORK_REFERENCES"] = ( From a1eaf42cbf5fd1ee9695db7c6423e48d6bd19f0c Mon Sep 17 00:00:00 2001 From: Ben Rodes Date: Tue, 17 Feb 2026 13:05:51 -0500 Subject: [PATCH 52/56] Update python/ql/lib/change-notes/2026-02-09-ssrf_test_case_cleanup_and_new_ssrf_barriers.md Co-authored-by: Owen Mansel-Chan <62447351+owen-mc@users.noreply.github.com> --- .../2026-02-09-ssrf_test_case_cleanup_and_new_ssrf_barriers.md | 1 - 1 file changed, 1 deletion(-) diff --git a/python/ql/lib/change-notes/2026-02-09-ssrf_test_case_cleanup_and_new_ssrf_barriers.md b/python/ql/lib/change-notes/2026-02-09-ssrf_test_case_cleanup_and_new_ssrf_barriers.md index d4373eadfb2..c3b4194e7b8 100644 --- a/python/ql/lib/change-notes/2026-02-09-ssrf_test_case_cleanup_and_new_ssrf_barriers.md +++ b/python/ql/lib/change-notes/2026-02-09-ssrf_test_case_cleanup_and_new_ssrf_barriers.md @@ -1,5 +1,4 @@ --- category: minorAnalysis --- -* Modified SSRF tests to use postprocessing to more easily debug results. * Added new full SSRF sanitization barrier from the new AntiSSRF library. \ No newline at end of file From 93d417049c4196d0dddc7cd58959c5c57c5505b6 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 18 Feb 2026 08:41:13 +0100 Subject: [PATCH 53/56] C#: Add tests for `Equals` methods with nullable parameter types --- .../NullableTest.cs | 86 +++++++++++++++++++ .../IncorrectEqualsSignature/NullableTest.cs | 86 +++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 csharp/ql/test/query-tests/API Abuse/ClassDoesNotImplementEquals/NullableTest.cs create mode 100644 csharp/ql/test/query-tests/API Abuse/IncorrectEqualsSignature/NullableTest.cs diff --git a/csharp/ql/test/query-tests/API Abuse/ClassDoesNotImplementEquals/NullableTest.cs b/csharp/ql/test/query-tests/API Abuse/ClassDoesNotImplementEquals/NullableTest.cs new file mode 100644 index 00000000000..a66ffbec9a0 --- /dev/null +++ b/csharp/ql/test/query-tests/API Abuse/ClassDoesNotImplementEquals/NullableTest.cs @@ -0,0 +1,86 @@ +using System; + +#nullable enable + +namespace Test +{ + class TestClass1 : IEquatable + { + private int field1; + + public bool Equals(TestClass1? param1) + { + return param1 != null && field1 == param1.field1; + } + + public override bool Equals(object? param2) + { + return param2 is TestClass1 tc && Equals(tc); + } + + public override int GetHashCode() + { + return field1; + } + } + + class TestClass2 : IEquatable + { + private int field1; + + public bool Equals(TestClass2 param1) + { + return param1 != null && field1 == param1.field1; + } + + public override bool Equals(object? param2) + { + return param2 is TestClass2 tc && Equals(tc); + } + + public override int GetHashCode() + { + return field1; + } + } + + class TestClass3 : IEquatable + { + private int field1; + + public bool Equals(TestClass3? param1) + { + return param1 != null && field1 == param1.field1; + } + + public override bool Equals(object param2) + { + return param2 is TestClass3 tc && Equals(tc); + } + + public override int GetHashCode() + { + return field1; + } + } + + class TestClass4 : IEquatable + { + private int field1; + + public bool Equals(TestClass4 param1) + { + return param1 != null && field1 == param1.field1; + } + + public override bool Equals(object param2) + { + return param2 is TestClass4 tc && Equals(tc); + } + + public override int GetHashCode() + { + return field1; + } + } +} \ No newline at end of file diff --git a/csharp/ql/test/query-tests/API Abuse/IncorrectEqualsSignature/NullableTest.cs b/csharp/ql/test/query-tests/API Abuse/IncorrectEqualsSignature/NullableTest.cs new file mode 100644 index 00000000000..a66ffbec9a0 --- /dev/null +++ b/csharp/ql/test/query-tests/API Abuse/IncorrectEqualsSignature/NullableTest.cs @@ -0,0 +1,86 @@ +using System; + +#nullable enable + +namespace Test +{ + class TestClass1 : IEquatable + { + private int field1; + + public bool Equals(TestClass1? param1) + { + return param1 != null && field1 == param1.field1; + } + + public override bool Equals(object? param2) + { + return param2 is TestClass1 tc && Equals(tc); + } + + public override int GetHashCode() + { + return field1; + } + } + + class TestClass2 : IEquatable + { + private int field1; + + public bool Equals(TestClass2 param1) + { + return param1 != null && field1 == param1.field1; + } + + public override bool Equals(object? param2) + { + return param2 is TestClass2 tc && Equals(tc); + } + + public override int GetHashCode() + { + return field1; + } + } + + class TestClass3 : IEquatable + { + private int field1; + + public bool Equals(TestClass3? param1) + { + return param1 != null && field1 == param1.field1; + } + + public override bool Equals(object param2) + { + return param2 is TestClass3 tc && Equals(tc); + } + + public override int GetHashCode() + { + return field1; + } + } + + class TestClass4 : IEquatable + { + private int field1; + + public bool Equals(TestClass4 param1) + { + return param1 != null && field1 == param1.field1; + } + + public override bool Equals(object param2) + { + return param2 is TestClass4 tc && Equals(tc); + } + + public override int GetHashCode() + { + return field1; + } + } +} \ No newline at end of file From 116f5a253c4ce388b56f0b5a2eb7a6b16d545842 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Wed, 18 Feb 2026 12:22:01 +0100 Subject: [PATCH 54/56] Bazel: Update Rust toolchain to nightly/2026-01-22 and rules_rust to 0.68.1.codeql.1 Update the Rust nightly toolchain from nightly/2025-08-01 to nightly/2026-01-22 (rustc 1.95.0-nightly), and rules_rust from 0.66.0 to 0.68.1.codeql.1. The new nightly changed how stdlib metadata is distributed: .rlib files now contain only a metadata stub, with full metadata in separate .rmeta files. rules_rust's stdlib glob doesn't include *.rmeta, causing 'only metadata stub found' errors. This is patched via a custom registry entry (0.68.1.codeql.1). Upstream bug: https://github.com/bazelbuild/rules_rust/issues/3859 --- MODULE.bazel | 44 ++--- .../rules_rust/0.68.1.codeql.1/MODULE.bazel | 151 ++++++++++++++++++ .../patches/include_rmeta_in_stdlib.patch | 12 ++ .../rules_rust/0.68.1.codeql.1/source.json | 9 ++ .../registry/modules/rules_rust/metadata.json | 11 ++ .../src/nightly-toolchain/rust-toolchain.toml | 2 +- rust/ql/test/rust-toolchain.toml | 2 +- 7 files changed, 207 insertions(+), 24 deletions(-) create mode 100644 misc/bazel/registry/modules/rules_rust/0.68.1.codeql.1/MODULE.bazel create mode 100644 misc/bazel/registry/modules/rules_rust/0.68.1.codeql.1/patches/include_rmeta_in_stdlib.patch create mode 100644 misc/bazel/registry/modules/rules_rust/0.68.1.codeql.1/source.json create mode 100644 misc/bazel/registry/modules/rules_rust/metadata.json diff --git a/MODULE.bazel b/MODULE.bazel index 9b326127ce7..5ace37479cb 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -28,7 +28,7 @@ bazel_dep(name = "rules_kotlin", version = "2.2.0-codeql.1") bazel_dep(name = "gazelle", version = "0.40.0") bazel_dep(name = "rules_dotnet", version = "0.21.5-codeql.1") bazel_dep(name = "googletest", version = "1.14.0.bcr.1") -bazel_dep(name = "rules_rust", version = "0.66.0") +bazel_dep(name = "rules_rust", version = "0.68.1.codeql.1") bazel_dep(name = "zstd", version = "1.5.5.bcr.1") bazel_dep(name = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True) @@ -41,7 +41,7 @@ RUST_EDITION = "2024" # a nightly toolchain is required to enable experimental_use_cc_common_link, which we require internally # we prefer to run the same version as internally, even if experimental_use_cc_common_link is not really # required in this repo -RUST_VERSION = "nightly/2025-08-01" +RUST_VERSION = "nightly/2026-01-22" rust = use_extension("@rules_rust//rust:extensions.bzl", "rust") rust.toolchain( @@ -53,26 +53,26 @@ rust.toolchain( ], # generated by buildutils-internal/scripts/fill-rust-sha256s.py (internal repo) sha256s = { - "2025-08-01/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz": "9bbeaf5d3fc7247d31463a9083aa251c995cc50662c8219e7a2254d76a72a9a4", - "2025-08-01/rustc-nightly-x86_64-apple-darwin.tar.xz": "c9ea539a8eff0d5d162701f99f9e1aabe14dd0dfb420d62362817a5d09219de7", - "2025-08-01/rustc-nightly-aarch64-apple-darwin.tar.xz": "ae83feebbc39cfd982e4ecc8297731fe79c185173aee138467b334c5404b3773", - "2025-08-01/rustc-nightly-x86_64-pc-windows-msvc.tar.xz": "9f170c30d802a349be60cf52ec46260802093cb1013ad667fc0d528b7b10152f", - "2025-08-01/clippy-nightly-x86_64-unknown-linux-gnu.tar.xz": "9ae5f3cd8f557c4f6df522597c69d14398cf604cfaed2b83e767c4b77a7eaaf6", - "2025-08-01/clippy-nightly-x86_64-apple-darwin.tar.xz": "983cb9ee0b6b968188e04ab2d33743d54764b2681ce565e1b3f2b9135c696a3e", - "2025-08-01/clippy-nightly-aarch64-apple-darwin.tar.xz": "ed2219dbc49d088225e1b7c5c4390fa295066e071fddaa2714018f6bb39ddbf0", - "2025-08-01/clippy-nightly-x86_64-pc-windows-msvc.tar.xz": "911f40ab5cbdd686f40e00965271fe47c4805513a308ed01f30eafb25b448a50", - "2025-08-01/cargo-nightly-x86_64-unknown-linux-gnu.tar.xz": "106463c284e48e4904c717471eeec2be5cc83a9d2cae8d6e948b52438cad2e69", - "2025-08-01/cargo-nightly-x86_64-apple-darwin.tar.xz": "6ad35c40efc41a8c531ea43235058347b6902d98a9693bf0aed7fc16d5590cef", - "2025-08-01/cargo-nightly-aarch64-apple-darwin.tar.xz": "dd28c365e9d298abc3154c797720ad36a0058f131265c9978b4c8e4e37012c8a", - "2025-08-01/cargo-nightly-x86_64-pc-windows-msvc.tar.xz": "7b431286e12d6b3834b038f078389a00cac73f351e8c3152b2504a3c06420b3b", - "2025-08-01/llvm-tools-nightly-x86_64-unknown-linux-gnu.tar.xz": "e342e305d7927cc288d386983b2bc253cfad3776b113386e903d0b302648ef47", - "2025-08-01/llvm-tools-nightly-x86_64-apple-darwin.tar.xz": "e44dd3506524d85c37b3a54bcc91d01378fd2c590b2db5c5974d12f05c1b84d1", - "2025-08-01/llvm-tools-nightly-aarch64-apple-darwin.tar.xz": "0c1b5f46dd81be4a9227b10283a0fcaa39c14fea7e81aea6fd6d9887ff6cdc41", - "2025-08-01/llvm-tools-nightly-x86_64-pc-windows-msvc.tar.xz": "423e5fd11406adccbc31b8456ceb7375ce055cdf45e90d2c3babeb2d7f58383f", - "2025-08-01/rust-std-nightly-x86_64-unknown-linux-gnu.tar.xz": "3c0ceb46a252647a1d4c7116d9ccae684fa5e42aaf3296419febd2c962c3b41d", - "2025-08-01/rust-std-nightly-x86_64-apple-darwin.tar.xz": "3be416003cab10f767390a753d1d16ae4d26c7421c03c98992cf1943e5b0efe8", - "2025-08-01/rust-std-nightly-aarch64-apple-darwin.tar.xz": "4046ac0ef951cb056b5028a399124f60999fa37792eab69d008d8d7965f389b4", - "2025-08-01/rust-std-nightly-x86_64-pc-windows-msvc.tar.xz": "191ed9d8603c3a4fe5a7bbbc2feb72049078dae2df3d3b7d5dedf3abbf823e6e", + "2026-01-22/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz": "88db619323cc1321630d124efa51ed02fabc5e020f08cfa0eda2c0ac1afbe69a", + "2026-01-22/rustc-nightly-x86_64-apple-darwin.tar.xz": "08484da3fa38db56f93629aeabdc0ae9ff8ed9704c0792d35259cbc849b3f54c", + "2026-01-22/rustc-nightly-aarch64-apple-darwin.tar.xz": "a39c0b21b7058e364ea1bd43144e42e4bf1efade036b2e82455f2afce194ee81", + "2026-01-22/rustc-nightly-x86_64-pc-windows-msvc.tar.xz": "d00248ee9850dbb6932b2578e32ff74fc7c429854c1aa071066ca31b65385a3b", + "2026-01-22/clippy-nightly-x86_64-unknown-linux-gnu.tar.xz": "70656a0ce994ffff16d5a35a7b170a0acd41e9bb54a589c96ed45bf97b094a4d", + "2026-01-22/clippy-nightly-x86_64-apple-darwin.tar.xz": "fe242519fa961522734733009705aec3c2d9a20cc57291f2aa614e5e6262c88f", + "2026-01-22/clippy-nightly-aarch64-apple-darwin.tar.xz": "38bb226363ec97c9722edf966cd58774a683e19fd2ff2a6030094445d51e06f9", + "2026-01-22/clippy-nightly-x86_64-pc-windows-msvc.tar.xz": "6da9b4470beea67abfebf046f141eee0d2a8db7c7a9e4e2294478734fd477228", + "2026-01-22/cargo-nightly-x86_64-unknown-linux-gnu.tar.xz": "99004e9d10c43a01499642f53bb3184d41137a95d65bfb217098840a9e79e892", + "2026-01-22/cargo-nightly-x86_64-apple-darwin.tar.xz": "6e021394cf8d8400ac6cfdfcef24e4d74f988e91eb8028b36de3a64ce3502990", + "2026-01-22/cargo-nightly-aarch64-apple-darwin.tar.xz": "4b2494cb69ab64132cddbc411a38ea9f1105e54d6f986e43168d54f79510c673", + "2026-01-22/cargo-nightly-x86_64-pc-windows-msvc.tar.xz": "c36613cf57407212d10d37b76e49a60ff42336e953cdff9e177283f530a83fc1", + "2026-01-22/llvm-tools-nightly-x86_64-unknown-linux-gnu.tar.xz": "0b123c5027dbd833aae6845ffe9bd07d309bf798746a7176aadaea68fbcbd05d", + "2026-01-22/llvm-tools-nightly-x86_64-apple-darwin.tar.xz": "a47864491ad5619158c950ab7570fb6e487d5117338585c27334d45824b406d8", + "2026-01-22/llvm-tools-nightly-aarch64-apple-darwin.tar.xz": "db9bc826d6e2e7e914505d50157682e516ceb90357e83d77abddc32c2d962f41", + "2026-01-22/llvm-tools-nightly-x86_64-pc-windows-msvc.tar.xz": "ffaa406932b2fe62e01dad61cf4ed34860a5d2a6f9306ca340d79e630d930039", + "2026-01-22/rust-std-nightly-x86_64-unknown-linux-gnu.tar.xz": "e9c0d5e06e18a4b509391b3088f29293e310cdc8ccc865be8fa3f09733326925", + "2026-01-22/rust-std-nightly-x86_64-apple-darwin.tar.xz": "25d75995cee679a4828ca9fe48c5a31a67c3b0846018440ef912e5a6208f53f6", + "2026-01-22/rust-std-nightly-aarch64-apple-darwin.tar.xz": "e4132bf3f2eed4684c86756a02315bcf481c23e675e3e25630fc604c9cb4594c", + "2026-01-22/rust-std-nightly-x86_64-pc-windows-msvc.tar.xz": "961bb535ef95ae8a5fa4e224cb94aff190f155c45a9bcf7a53e184b024aa41b1", }, versions = [RUST_VERSION], ) diff --git a/misc/bazel/registry/modules/rules_rust/0.68.1.codeql.1/MODULE.bazel b/misc/bazel/registry/modules/rules_rust/0.68.1.codeql.1/MODULE.bazel new file mode 100644 index 00000000000..aedc5a8a242 --- /dev/null +++ b/misc/bazel/registry/modules/rules_rust/0.68.1.codeql.1/MODULE.bazel @@ -0,0 +1,151 @@ +"""bazelbuild/rules_rust""" + +module( + name = "rules_rust", + version = "0.68.1.codeql.1", +) + +############################################################################### +## Core +############################################################################### + +bazel_dep(name = "bazel_features", version = "1.32.0") +bazel_dep(name = "bazel_skylib", version = "1.8.2") +bazel_dep(name = "platforms", version = "1.0.0") +bazel_dep(name = "rules_cc", version = "0.2.4") +bazel_dep(name = "rules_license", version = "1.0.0") +bazel_dep(name = "rules_shell", version = "0.6.1") +bazel_dep(name = "apple_support", version = "1.24.1", repo_name = "build_bazel_apple_support") + +internal_deps = use_extension("//rust/private:internal_extensions.bzl", "i") +use_repo( + internal_deps, + "rrra", + "rrra__anyhow-1.0.71", + "rrra__camino-1.1.9", + "rrra__clap-4.3.11", + "rrra__env_logger-0.10.0", + "rrra__itertools-0.11.0", + "rrra__log-0.4.19", + "rrra__serde-1.0.171", + "rrra__serde_json-1.0.102", + "rules_rust_tinyjson", +) + +cargo_internal_deps = use_extension("//cargo/private:internal_extensions.bzl", "i") +use_repo( + cargo_internal_deps, + "rrc", + "rrc__cargo-util-schemas-0.3.1", + "rrc__cargo_toml-0.20.5", + "rrc__pathdiff-0.1.0", + "rrc__semver-1.0.25", + "rrc__toml-0.8.20", +) + +rust = use_extension("//rust:extensions.bzl", "rust") +rust.toolchain(edition = "2021") +use_repo(rust, "rust_toolchains") + +register_toolchains( + "@rust_toolchains//:all", +) + +rust_host_tools = use_extension("//rust:extensions.bzl", "rust_host_tools") +rust_host_tools.host_tools( + name = "rust_host_tools", +) +use_repo( + rust_host_tools, + "rust_host_tools", +) + +rust_test = use_extension("//test:test_extensions.bzl", "rust_test", dev_dependency = True) +use_repo( + rust_test, + "buildkite_config", + "generated_inputs_in_external_repo", + "libc", + "rtra", + "rtra__serde-1.0.228", + "rtra__serde_json-1.0.145", + "rtvsc", + "rtvsc__serde-1.0.228", + "rtvsc__serde_json-1.0.145", + "rules_rust_test_load_arbitrary_tool", + "rules_rust_toolchain_test_target_json", +) + +bazel_dep(name = "rules_python", version = "0.40.0", dev_dependency = True) +bazel_dep(name = "rules_testing", version = "0.7.0", dev_dependency = True) +bazel_dep(name = "bazel_ci_rules", version = "1.0.0", dev_dependency = True) + +############################################################################### +## Crate Universe +############################################################################### + +crate_universe_internal_deps = use_extension( + "//crate_universe/private:internal_extensions.bzl", + "cu", +) +use_repo( + crate_universe_internal_deps, + "cargo_bazel.buildifier-darwin-amd64", + "cargo_bazel.buildifier-darwin-arm64", + "cargo_bazel.buildifier-linux-amd64", + "cargo_bazel.buildifier-linux-arm64", + "cargo_bazel.buildifier-windows-amd64.exe", + "cui", + "cui__anyhow-1.0.98", + "cui__camino-1.1.9", + "cui__cargo-lock-10.1.0", + "cui__cargo-platform-0.1.9", + "cui__cargo_metadata-0.19.2", + "cui__cargo_toml-0.22.1", + "cui__cfg-expr-0.18.0", + "cui__clap-4.5.37", + "cui__crates-index-3.7.0", + "cui__glob-0.3.2", + "cui__hex-0.4.3", + "cui__indoc-2.0.6", + "cui__itertools-0.14.0", + "cui__maplit-1.0.2", + "cui__normpath-1.3.0", + "cui__once_cell-1.21.3", + "cui__pathdiff-0.2.3", + "cui__regex-1.11.1", + "cui__semver-1.0.26", + "cui__serde-1.0.219", + "cui__serde_json-1.0.140", + "cui__serde_starlark-0.1.17", + "cui__sha2-0.10.8", + "cui__spdx-0.10.8", + "cui__tempfile-3.19.1", + "cui__tera-1.20.0", + "cui__textwrap-0.16.2", + "cui__toml-0.8.21", + "cui__tracing-0.1.41", + "cui__tracing-subscriber-0.3.19", + "cui__url-2.5.4", + "cui__walkdir-2.5.0", +) + +crate_universe_internal_non_repro_deps = use_extension( + "//crate_universe/private:internal_extensions.bzl", + "cu_nr", +) +use_repo( + crate_universe_internal_non_repro_deps, + "cargo_bazel_bootstrap", +) + +crate_universe_internal_dev_deps = use_extension( + "//crate_universe/private:internal_extensions.bzl", + "cu_dev", + dev_dependency = True, +) +use_repo( + crate_universe_internal_dev_deps, + "cross_rs", + "cross_rs_host_bin", +) diff --git a/misc/bazel/registry/modules/rules_rust/0.68.1.codeql.1/patches/include_rmeta_in_stdlib.patch b/misc/bazel/registry/modules/rules_rust/0.68.1.codeql.1/patches/include_rmeta_in_stdlib.patch new file mode 100644 index 00000000000..3707cb51b63 --- /dev/null +++ b/misc/bazel/registry/modules/rules_rust/0.68.1.codeql.1/patches/include_rmeta_in_stdlib.patch @@ -0,0 +1,12 @@ +diff --git a/rust/private/repository_utils.bzl b/rust/private/repository_utils.bzl +index 05b741947..f88074af6 100644 +--- a/rust/private/repository_utils.bzl ++++ b/rust/private/repository_utils.bzl +@@ -280,6 +280,7 @@ rust_stdlib_filegroup( + srcs = glob( + [ + "lib/rustlib/{target_triple}/lib/*.rlib", ++ "lib/rustlib/{target_triple}/lib/*.rmeta", + "lib/rustlib/{target_triple}/lib/*{dylib_ext}*", + "lib/rustlib/{target_triple}/lib/*{staticlib_ext}", + "lib/rustlib/{target_triple}/lib/self-contained/**", diff --git a/misc/bazel/registry/modules/rules_rust/0.68.1.codeql.1/source.json b/misc/bazel/registry/modules/rules_rust/0.68.1.codeql.1/source.json new file mode 100644 index 00000000000..6048521ce2f --- /dev/null +++ b/misc/bazel/registry/modules/rules_rust/0.68.1.codeql.1/source.json @@ -0,0 +1,9 @@ +{ + "integrity": "sha256-yKqAbPYGZnmsI0YyQe6ArWkiZdrQRl9RERy74wuJA1I=", + "strip_prefix": "", + "url": "https://github.com/bazelbuild/rules_rust/releases/download/0.68.1/rules_rust-0.68.1.tar.gz", + "patches": { + "include_rmeta_in_stdlib.patch": "sha256-7n8XHpfkLUMEbRG6lKqdhLWydsWlRRG+Ywkxk6LvY9c=" + }, + "patch_strip": 1 +} diff --git a/misc/bazel/registry/modules/rules_rust/metadata.json b/misc/bazel/registry/modules/rules_rust/metadata.json new file mode 100644 index 00000000000..deffe6f6dfa --- /dev/null +++ b/misc/bazel/registry/modules/rules_rust/metadata.json @@ -0,0 +1,11 @@ +{ + "homepage": "https://github.com/bazelbuild/rules_rust", + "maintainers": [], + "repository": [ + "github:bazelbuild/rules_rust" + ], + "versions": [ + "0.68.1.codeql.1" + ], + "yanked_versions": {} +} diff --git a/rust/extractor/src/nightly-toolchain/rust-toolchain.toml b/rust/extractor/src/nightly-toolchain/rust-toolchain.toml index 7ed21df9121..4e2d228630e 100644 --- a/rust/extractor/src/nightly-toolchain/rust-toolchain.toml +++ b/rust/extractor/src/nightly-toolchain/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2025-06-01" +channel = "nightly-2026-01-22" components = [ "rust-src" ] diff --git a/rust/ql/test/rust-toolchain.toml b/rust/ql/test/rust-toolchain.toml index 9343bef27c6..15f54ec6b42 100644 --- a/rust/ql/test/rust-toolchain.toml +++ b/rust/ql/test/rust-toolchain.toml @@ -2,6 +2,6 @@ # IMPORTANT: this can also have an impact on QL test results [toolchain] -channel = "1.90" +channel = "1.93" profile = "minimal" components = [ "rust-src" ] From 24f3d9ede0653357329ba38e3768b319e38f2ddd Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Wed, 18 Feb 2026 13:56:48 +0100 Subject: [PATCH 55/56] Revert rust-toolchain.toml changes and update test expectations --- .../src/nightly-toolchain/rust-toolchain.toml | 2 +- .../NeverTypeRepr/NeverTypeRepr.expected | 1 - .../controlflow/BasicBlocks.expected | 4 --- .../library-tests/controlflow/Cfg.expected | 20 +++------------ .../type-inference/type-inference.expected | 25 +++++++++++++++++++ rust/ql/test/rust-toolchain.toml | 2 +- 6 files changed, 31 insertions(+), 23 deletions(-) diff --git a/rust/extractor/src/nightly-toolchain/rust-toolchain.toml b/rust/extractor/src/nightly-toolchain/rust-toolchain.toml index 4e2d228630e..7ed21df9121 100644 --- a/rust/extractor/src/nightly-toolchain/rust-toolchain.toml +++ b/rust/extractor/src/nightly-toolchain/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2026-01-22" +channel = "nightly-2025-06-01" components = [ "rust-src" ] diff --git a/rust/ql/test/extractor-tests/generated/NeverTypeRepr/NeverTypeRepr.expected b/rust/ql/test/extractor-tests/generated/NeverTypeRepr/NeverTypeRepr.expected index 7e8d7f8718b..c02b6c82354 100644 --- a/rust/ql/test/extractor-tests/generated/NeverTypeRepr/NeverTypeRepr.expected +++ b/rust/ql/test/extractor-tests/generated/NeverTypeRepr/NeverTypeRepr.expected @@ -1,2 +1 @@ | gen_never_type_repr.rs:7:17:7:17 | ! | -| gen_never_type_repr.rs:7:21:7:26 | ! | diff --git a/rust/ql/test/library-tests/controlflow/BasicBlocks.expected b/rust/ql/test/library-tests/controlflow/BasicBlocks.expected index 80357ae29f8..94760d6875e 100644 --- a/rust/ql/test/library-tests/controlflow/BasicBlocks.expected +++ b/rust/ql/test/library-tests/controlflow/BasicBlocks.expected @@ -743,7 +743,6 @@ dominates | test.rs:529:5:537:5 | enter fn const_block_assert | test.rs:533:21:533:48 | [boolean(true)] ! ... | | test.rs:529:5:537:5 | enter fn const_block_assert | test.rs:533:21:533:48 | if ... {...} | | test.rs:533:13:533:19 | ExprStmt | test.rs:533:13:533:19 | ExprStmt | -| test.rs:533:13:533:19 | enter fn panic_cold_explicit | test.rs:533:13:533:19 | enter fn panic_cold_explicit | | test.rs:533:21:533:48 | [boolean(false)] ! ... | test.rs:533:21:533:48 | [boolean(false)] ! ... | | test.rs:533:21:533:48 | [boolean(true)] ! ... | test.rs:533:13:533:19 | ExprStmt | | test.rs:533:21:533:48 | [boolean(true)] ! ... | test.rs:533:21:533:48 | [boolean(true)] ! ... | @@ -751,7 +750,6 @@ dominates | test.rs:539:5:548:5 | enter fn const_block_panic | test.rs:539:5:548:5 | enter fn const_block_panic | | test.rs:539:5:548:5 | enter fn const_block_panic | test.rs:541:9:546:9 | if false {...} | | test.rs:541:9:546:9 | if false {...} | test.rs:541:9:546:9 | if false {...} | -| test.rs:544:17:544:22 | enter fn panic_cold_explicit | test.rs:544:17:544:22 | enter fn panic_cold_explicit | | test.rs:551:1:556:1 | enter fn dead_code | test.rs:551:1:556:1 | enter fn dead_code | | test.rs:551:1:556:1 | enter fn dead_code | test.rs:553:9:553:17 | ExprStmt | | test.rs:553:9:553:17 | ExprStmt | test.rs:553:9:553:17 | ExprStmt | @@ -1424,7 +1422,6 @@ postDominance | test.rs:529:5:537:5 | enter fn const_block_assert | test.rs:529:5:537:5 | enter fn const_block_assert | | test.rs:533:13:533:19 | ExprStmt | test.rs:533:13:533:19 | ExprStmt | | test.rs:533:13:533:19 | ExprStmt | test.rs:533:21:533:48 | [boolean(true)] ! ... | -| test.rs:533:13:533:19 | enter fn panic_cold_explicit | test.rs:533:13:533:19 | enter fn panic_cold_explicit | | test.rs:533:21:533:48 | [boolean(false)] ! ... | test.rs:533:21:533:48 | [boolean(false)] ! ... | | test.rs:533:21:533:48 | [boolean(true)] ! ... | test.rs:533:21:533:48 | [boolean(true)] ! ... | | test.rs:533:21:533:48 | if ... {...} | test.rs:529:5:537:5 | enter fn const_block_assert | @@ -1435,7 +1432,6 @@ postDominance | test.rs:539:5:548:5 | enter fn const_block_panic | test.rs:539:5:548:5 | enter fn const_block_panic | | test.rs:541:9:546:9 | if false {...} | test.rs:539:5:548:5 | enter fn const_block_panic | | test.rs:541:9:546:9 | if false {...} | test.rs:541:9:546:9 | if false {...} | -| test.rs:544:17:544:22 | enter fn panic_cold_explicit | test.rs:544:17:544:22 | enter fn panic_cold_explicit | | test.rs:551:1:556:1 | enter fn dead_code | test.rs:551:1:556:1 | enter fn dead_code | | test.rs:553:9:553:17 | ExprStmt | test.rs:551:1:556:1 | enter fn dead_code | | test.rs:553:9:553:17 | ExprStmt | test.rs:553:9:553:17 | ExprStmt | diff --git a/rust/ql/test/library-tests/controlflow/Cfg.expected b/rust/ql/test/library-tests/controlflow/Cfg.expected index ef97a3b628f..25fb8f29e04 100644 --- a/rust/ql/test/library-tests/controlflow/Cfg.expected +++ b/rust/ql/test/library-tests/controlflow/Cfg.expected @@ -1288,20 +1288,13 @@ edges | test.rs:529:41:537:5 | { ... } | test.rs:529:5:537:5 | exit fn const_block_assert (normal) | | | test.rs:532:9:534:9 | ExprStmt | test.rs:533:13:533:50 | ExprStmt | | | test.rs:532:9:534:9 | { ... } | test.rs:536:9:536:10 | 42 | | +| test.rs:533:13:533:19 | "explicit panic" | test.rs:533:13:533:19 | ...::panic(...) | | +| test.rs:533:13:533:19 | ...::panic | test.rs:533:13:533:19 | "explicit panic" | | +| test.rs:533:13:533:19 | ...::panic(...) | test.rs:533:13:533:19 | { ... } | | | test.rs:533:13:533:19 | ...::panic_2021!... | test.rs:533:13:533:19 | MacroExpr | | -| test.rs:533:13:533:19 | ...::panic_explicit | test.rs:533:13:533:19 | ...::panic_explicit(...) | | -| test.rs:533:13:533:19 | ...::panic_explicit(...) | test.rs:533:13:533:19 | { ... } | | -| test.rs:533:13:533:19 | ExprStmt | test.rs:533:13:533:19 | fn panic_cold_explicit | | -| test.rs:533:13:533:19 | ExprStmt | test.rs:533:13:533:19 | panic_cold_explicit | | +| test.rs:533:13:533:19 | ExprStmt | test.rs:533:13:533:19 | ...::panic | | | test.rs:533:13:533:19 | MacroExpr | test.rs:533:13:533:19 | { ... } | | -| test.rs:533:13:533:19 | enter fn panic_cold_explicit | test.rs:533:13:533:19 | ...::panic_explicit | | -| test.rs:533:13:533:19 | exit fn panic_cold_explicit (normal) | test.rs:533:13:533:19 | exit fn panic_cold_explicit | | -| test.rs:533:13:533:19 | fn panic_cold_explicit | test.rs:533:13:533:19 | ExprStmt | | -| test.rs:533:13:533:19 | panic_cold_explicit | test.rs:533:13:533:19 | panic_cold_explicit(...) | | -| test.rs:533:13:533:19 | panic_cold_explicit(...) | test.rs:533:13:533:19 | { ... } | | | test.rs:533:13:533:19 | { ... } | test.rs:533:13:533:19 | ...::panic_2021!... | | -| test.rs:533:13:533:19 | { ... } | test.rs:533:13:533:19 | exit fn panic_cold_explicit (normal) | | -| test.rs:533:13:533:19 | { ... } | test.rs:533:13:533:19 | { ... } | | | test.rs:533:13:533:19 | { ... } | test.rs:533:21:533:48 | if ... {...} | | | test.rs:533:13:533:49 | MacroExpr | test.rs:532:9:534:9 | { ... } | | | test.rs:533:13:533:49 | assert!... | test.rs:533:13:533:49 | MacroExpr | | @@ -1324,11 +1317,6 @@ edges | test.rs:541:9:546:9 | ExprStmt | test.rs:541:12:541:16 | false | | | test.rs:541:9:546:9 | if false {...} | test.rs:547:9:547:9 | N | | | test.rs:541:12:541:16 | false | test.rs:541:9:546:9 | if false {...} | false | -| test.rs:544:17:544:22 | ...::panic_explicit | test.rs:544:17:544:22 | ...::panic_explicit(...) | | -| test.rs:544:17:544:22 | ...::panic_explicit(...) | test.rs:544:17:544:22 | { ... } | | -| test.rs:544:17:544:22 | enter fn panic_cold_explicit | test.rs:544:17:544:22 | ...::panic_explicit | | -| test.rs:544:17:544:22 | exit fn panic_cold_explicit (normal) | test.rs:544:17:544:22 | exit fn panic_cold_explicit | | -| test.rs:544:17:544:22 | { ... } | test.rs:544:17:544:22 | exit fn panic_cold_explicit (normal) | | | test.rs:547:9:547:9 | N | test.rs:539:35:548:5 | { ... } | | | test.rs:551:1:556:1 | enter fn dead_code | test.rs:552:5:554:5 | ExprStmt | | | test.rs:551:1:556:1 | exit fn dead_code (normal) | test.rs:551:1:556:1 | exit fn dead_code | | diff --git a/rust/ql/test/library-tests/type-inference/type-inference.expected b/rust/ql/test/library-tests/type-inference/type-inference.expected index 1b750fab458..a2322cdf929 100644 --- a/rust/ql/test/library-tests/type-inference/type-inference.expected +++ b/rust/ql/test/library-tests/type-inference/type-inference.expected @@ -788,12 +788,14 @@ inferCertainType | dereference.rs:116:5:116:5 | x | | dereference.rs:18:1:20:1 | MySmartPointer | | dereference.rs:143:19:151:5 | { ... } | | {EXTERNAL LOCATION} | () | | dereference.rs:144:17:144:26 | key_to_key | | {EXTERNAL LOCATION} | HashMap | +| dereference.rs:144:17:144:26 | key_to_key | A | {EXTERNAL LOCATION} | Global | | dereference.rs:144:17:144:26 | key_to_key | K | {EXTERNAL LOCATION} | & | | dereference.rs:144:17:144:26 | key_to_key | K.TRef | dereference.rs:122:5:123:21 | Key | | dereference.rs:144:17:144:26 | key_to_key | S | {EXTERNAL LOCATION} | RandomState | | dereference.rs:144:17:144:26 | key_to_key | V | {EXTERNAL LOCATION} | & | | dereference.rs:144:17:144:26 | key_to_key | V.TRef | dereference.rs:122:5:123:21 | Key | | dereference.rs:144:30:144:57 | ...::new(...) | | {EXTERNAL LOCATION} | HashMap | +| dereference.rs:144:30:144:57 | ...::new(...) | A | {EXTERNAL LOCATION} | Global | | dereference.rs:144:30:144:57 | ...::new(...) | K | {EXTERNAL LOCATION} | & | | dereference.rs:144:30:144:57 | ...::new(...) | K.TRef | dereference.rs:122:5:123:21 | Key | | dereference.rs:144:30:144:57 | ...::new(...) | S | {EXTERNAL LOCATION} | RandomState | @@ -804,6 +806,7 @@ inferCertainType | dereference.rs:145:24:145:29 | Key {...} | | dereference.rs:122:5:123:21 | Key | | dereference.rs:146:9:149:9 | if ... {...} | | {EXTERNAL LOCATION} | () | | dereference.rs:146:32:146:41 | key_to_key | | {EXTERNAL LOCATION} | HashMap | +| dereference.rs:146:32:146:41 | key_to_key | A | {EXTERNAL LOCATION} | Global | | dereference.rs:146:32:146:41 | key_to_key | K | {EXTERNAL LOCATION} | & | | dereference.rs:146:32:146:41 | key_to_key | K.TRef | dereference.rs:122:5:123:21 | Key | | dereference.rs:146:32:146:41 | key_to_key | S | {EXTERNAL LOCATION} | RandomState | @@ -813,6 +816,7 @@ inferCertainType | dereference.rs:146:52:149:9 | { ... } | | {EXTERNAL LOCATION} | () | | dereference.rs:148:13:148:15 | key | | {EXTERNAL LOCATION} | & | | dereference.rs:150:9:150:18 | key_to_key | | {EXTERNAL LOCATION} | HashMap | +| dereference.rs:150:9:150:18 | key_to_key | A | {EXTERNAL LOCATION} | Global | | dereference.rs:150:9:150:18 | key_to_key | K | {EXTERNAL LOCATION} | & | | dereference.rs:150:9:150:18 | key_to_key | K.TRef | dereference.rs:122:5:123:21 | Key | | dereference.rs:150:9:150:18 | key_to_key | S | {EXTERNAL LOCATION} | RandomState | @@ -3350,16 +3354,20 @@ inferCertainType | main.rs:2345:13:2346:13 | for ... in ... { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2345:29:2346:13 | { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2349:17:2349:20 | map1 | | {EXTERNAL LOCATION} | HashMap | +| main.rs:2349:17:2349:20 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2349:17:2349:20 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2349:24:2349:55 | ...::new(...) | | {EXTERNAL LOCATION} | HashMap | +| main.rs:2349:24:2349:55 | ...::new(...) | A | {EXTERNAL LOCATION} | Global | | main.rs:2349:24:2349:55 | ...::new(...) | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2350:9:2350:12 | map1 | | {EXTERNAL LOCATION} | HashMap | +| main.rs:2350:9:2350:12 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2350:9:2350:12 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2350:24:2350:38 | ...::new(...) | | {EXTERNAL LOCATION} | Box | | main.rs:2350:24:2350:38 | ...::new(...) | A | {EXTERNAL LOCATION} | Global | | main.rs:2350:33:2350:37 | "one" | | {EXTERNAL LOCATION} | & | | main.rs:2350:33:2350:37 | "one" | TRef | {EXTERNAL LOCATION} | str | | main.rs:2351:9:2351:12 | map1 | | {EXTERNAL LOCATION} | HashMap | +| main.rs:2351:9:2351:12 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2351:9:2351:12 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2351:24:2351:38 | ...::new(...) | | {EXTERNAL LOCATION} | Box | | main.rs:2351:24:2351:38 | ...::new(...) | A | {EXTERNAL LOCATION} | Global | @@ -3367,21 +3375,25 @@ inferCertainType | main.rs:2351:33:2351:37 | "two" | TRef | {EXTERNAL LOCATION} | str | | main.rs:2352:9:2352:33 | for ... in ... { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2352:20:2352:23 | map1 | | {EXTERNAL LOCATION} | HashMap | +| main.rs:2352:20:2352:23 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2352:20:2352:23 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2352:32:2352:33 | { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2353:9:2353:37 | for ... in ... { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2353:22:2353:25 | map1 | | {EXTERNAL LOCATION} | HashMap | +| main.rs:2353:22:2353:25 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2353:22:2353:25 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2353:36:2353:37 | { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2354:9:2354:42 | for ... in ... { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2354:13:2354:24 | TuplePat | | {EXTERNAL LOCATION} | (T_2) | | main.rs:2354:29:2354:32 | map1 | | {EXTERNAL LOCATION} | HashMap | +| main.rs:2354:29:2354:32 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2354:29:2354:32 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2354:41:2354:42 | { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2355:9:2355:36 | for ... in ... { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2355:13:2355:24 | TuplePat | | {EXTERNAL LOCATION} | (T_2) | | main.rs:2355:29:2355:33 | &map1 | | {EXTERNAL LOCATION} | & | | main.rs:2355:30:2355:33 | map1 | | {EXTERNAL LOCATION} | HashMap | +| main.rs:2355:30:2355:33 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2355:30:2355:33 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2355:35:2355:36 | { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2359:17:2359:17 | a | | {EXTERNAL LOCATION} | i64 | @@ -6452,12 +6464,14 @@ inferType | dereference.rs:116:12:116:12 | 0 | | {EXTERNAL LOCATION} | i32 | | dereference.rs:143:19:151:5 | { ... } | | {EXTERNAL LOCATION} | () | | dereference.rs:144:17:144:26 | key_to_key | | {EXTERNAL LOCATION} | HashMap | +| dereference.rs:144:17:144:26 | key_to_key | A | {EXTERNAL LOCATION} | Global | | dereference.rs:144:17:144:26 | key_to_key | K | {EXTERNAL LOCATION} | & | | dereference.rs:144:17:144:26 | key_to_key | K.TRef | dereference.rs:122:5:123:21 | Key | | dereference.rs:144:17:144:26 | key_to_key | S | {EXTERNAL LOCATION} | RandomState | | dereference.rs:144:17:144:26 | key_to_key | V | {EXTERNAL LOCATION} | & | | dereference.rs:144:17:144:26 | key_to_key | V.TRef | dereference.rs:122:5:123:21 | Key | | dereference.rs:144:30:144:57 | ...::new(...) | | {EXTERNAL LOCATION} | HashMap | +| dereference.rs:144:30:144:57 | ...::new(...) | A | {EXTERNAL LOCATION} | Global | | dereference.rs:144:30:144:57 | ...::new(...) | K | {EXTERNAL LOCATION} | & | | dereference.rs:144:30:144:57 | ...::new(...) | K.TRef | dereference.rs:122:5:123:21 | Key | | dereference.rs:144:30:144:57 | ...::new(...) | S | {EXTERNAL LOCATION} | RandomState | @@ -6483,6 +6497,7 @@ inferType | dereference.rs:146:21:146:27 | ref_key | TRef | {EXTERNAL LOCATION} | & | | dereference.rs:146:21:146:27 | ref_key | TRef.TRef | dereference.rs:122:5:123:21 | Key | | dereference.rs:146:32:146:41 | key_to_key | | {EXTERNAL LOCATION} | HashMap | +| dereference.rs:146:32:146:41 | key_to_key | A | {EXTERNAL LOCATION} | Global | | dereference.rs:146:32:146:41 | key_to_key | K | {EXTERNAL LOCATION} | & | | dereference.rs:146:32:146:41 | key_to_key | K.TRef | dereference.rs:122:5:123:21 | Key | | dereference.rs:146:32:146:41 | key_to_key | S | {EXTERNAL LOCATION} | RandomState | @@ -6508,6 +6523,7 @@ inferType | dereference.rs:148:19:148:25 | ref_key | TRef | {EXTERNAL LOCATION} | & | | dereference.rs:148:19:148:25 | ref_key | TRef.TRef | dereference.rs:122:5:123:21 | Key | | dereference.rs:150:9:150:18 | key_to_key | | {EXTERNAL LOCATION} | HashMap | +| dereference.rs:150:9:150:18 | key_to_key | A | {EXTERNAL LOCATION} | Global | | dereference.rs:150:9:150:18 | key_to_key | K | {EXTERNAL LOCATION} | & | | dereference.rs:150:9:150:18 | key_to_key | K.TRef | dereference.rs:122:5:123:21 | Key | | dereference.rs:150:9:150:18 | key_to_key | S | {EXTERNAL LOCATION} | RandomState | @@ -11262,6 +11278,7 @@ inferType | main.rs:2345:13:2346:13 | for ... in ... { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2345:29:2346:13 | { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2349:17:2349:20 | map1 | | {EXTERNAL LOCATION} | HashMap | +| main.rs:2349:17:2349:20 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2349:17:2349:20 | map1 | K | {EXTERNAL LOCATION} | i32 | | main.rs:2349:17:2349:20 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2349:17:2349:20 | map1 | V | {EXTERNAL LOCATION} | Box | @@ -11269,6 +11286,7 @@ inferType | main.rs:2349:17:2349:20 | map1 | V.T | {EXTERNAL LOCATION} | & | | main.rs:2349:17:2349:20 | map1 | V.T.TRef | {EXTERNAL LOCATION} | str | | main.rs:2349:24:2349:55 | ...::new(...) | | {EXTERNAL LOCATION} | HashMap | +| main.rs:2349:24:2349:55 | ...::new(...) | A | {EXTERNAL LOCATION} | Global | | main.rs:2349:24:2349:55 | ...::new(...) | K | {EXTERNAL LOCATION} | i32 | | main.rs:2349:24:2349:55 | ...::new(...) | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2349:24:2349:55 | ...::new(...) | V | {EXTERNAL LOCATION} | Box | @@ -11276,6 +11294,7 @@ inferType | main.rs:2349:24:2349:55 | ...::new(...) | V.T | {EXTERNAL LOCATION} | & | | main.rs:2349:24:2349:55 | ...::new(...) | V.T.TRef | {EXTERNAL LOCATION} | str | | main.rs:2350:9:2350:12 | map1 | | {EXTERNAL LOCATION} | HashMap | +| main.rs:2350:9:2350:12 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2350:9:2350:12 | map1 | K | {EXTERNAL LOCATION} | i32 | | main.rs:2350:9:2350:12 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2350:9:2350:12 | map1 | V | {EXTERNAL LOCATION} | Box | @@ -11295,6 +11314,7 @@ inferType | main.rs:2350:33:2350:37 | "one" | | {EXTERNAL LOCATION} | & | | main.rs:2350:33:2350:37 | "one" | TRef | {EXTERNAL LOCATION} | str | | main.rs:2351:9:2351:12 | map1 | | {EXTERNAL LOCATION} | HashMap | +| main.rs:2351:9:2351:12 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2351:9:2351:12 | map1 | K | {EXTERNAL LOCATION} | i32 | | main.rs:2351:9:2351:12 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2351:9:2351:12 | map1 | V | {EXTERNAL LOCATION} | Box | @@ -11317,6 +11337,7 @@ inferType | main.rs:2352:13:2352:15 | key | | {EXTERNAL LOCATION} | & | | main.rs:2352:13:2352:15 | key | TRef | {EXTERNAL LOCATION} | i32 | | main.rs:2352:20:2352:23 | map1 | | {EXTERNAL LOCATION} | HashMap | +| main.rs:2352:20:2352:23 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2352:20:2352:23 | map1 | K | {EXTERNAL LOCATION} | i32 | | main.rs:2352:20:2352:23 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2352:20:2352:23 | map1 | V | {EXTERNAL LOCATION} | Box | @@ -11337,6 +11358,7 @@ inferType | main.rs:2353:13:2353:17 | value | TRef.T | {EXTERNAL LOCATION} | & | | main.rs:2353:13:2353:17 | value | TRef.T.TRef | {EXTERNAL LOCATION} | str | | main.rs:2353:22:2353:25 | map1 | | {EXTERNAL LOCATION} | HashMap | +| main.rs:2353:22:2353:25 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2353:22:2353:25 | map1 | K | {EXTERNAL LOCATION} | i32 | | main.rs:2353:22:2353:25 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2353:22:2353:25 | map1 | V | {EXTERNAL LOCATION} | Box | @@ -11367,6 +11389,7 @@ inferType | main.rs:2354:19:2354:23 | value | TRef.T | {EXTERNAL LOCATION} | & | | main.rs:2354:19:2354:23 | value | TRef.T.TRef | {EXTERNAL LOCATION} | str | | main.rs:2354:29:2354:32 | map1 | | {EXTERNAL LOCATION} | HashMap | +| main.rs:2354:29:2354:32 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2354:29:2354:32 | map1 | K | {EXTERNAL LOCATION} | i32 | | main.rs:2354:29:2354:32 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2354:29:2354:32 | map1 | V | {EXTERNAL LOCATION} | Box | @@ -11398,6 +11421,7 @@ inferType | main.rs:2355:19:2355:23 | value | TRef.T.TRef | {EXTERNAL LOCATION} | str | | main.rs:2355:29:2355:33 | &map1 | | {EXTERNAL LOCATION} | & | | main.rs:2355:29:2355:33 | &map1 | TRef | {EXTERNAL LOCATION} | HashMap | +| main.rs:2355:29:2355:33 | &map1 | TRef.A | {EXTERNAL LOCATION} | Global | | main.rs:2355:29:2355:33 | &map1 | TRef.K | {EXTERNAL LOCATION} | i32 | | main.rs:2355:29:2355:33 | &map1 | TRef.S | {EXTERNAL LOCATION} | RandomState | | main.rs:2355:29:2355:33 | &map1 | TRef.V | {EXTERNAL LOCATION} | Box | @@ -11405,6 +11429,7 @@ inferType | main.rs:2355:29:2355:33 | &map1 | TRef.V.T | {EXTERNAL LOCATION} | & | | main.rs:2355:29:2355:33 | &map1 | TRef.V.T.TRef | {EXTERNAL LOCATION} | str | | main.rs:2355:30:2355:33 | map1 | | {EXTERNAL LOCATION} | HashMap | +| main.rs:2355:30:2355:33 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2355:30:2355:33 | map1 | K | {EXTERNAL LOCATION} | i32 | | main.rs:2355:30:2355:33 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2355:30:2355:33 | map1 | V | {EXTERNAL LOCATION} | Box | diff --git a/rust/ql/test/rust-toolchain.toml b/rust/ql/test/rust-toolchain.toml index 15f54ec6b42..9343bef27c6 100644 --- a/rust/ql/test/rust-toolchain.toml +++ b/rust/ql/test/rust-toolchain.toml @@ -2,6 +2,6 @@ # IMPORTANT: this can also have an impact on QL test results [toolchain] -channel = "1.93" +channel = "1.90" profile = "minimal" components = [ "rust-src" ] From e11363280a7a1162825da7f8e1d78dcbfce353bb Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Wed, 18 Feb 2026 16:56:28 +0100 Subject: [PATCH 56/56] Rust: accept test changes --- .../NeverTypeRepr/NeverTypeRepr.expected | 1 + .../controlflow/BasicBlocks.expected | 4 +++ .../library-tests/controlflow/Cfg.expected | 20 ++++++++++++--- .../type-inference/type-inference.expected | 25 ------------------- 4 files changed, 21 insertions(+), 29 deletions(-) diff --git a/rust/ql/test/extractor-tests/generated/NeverTypeRepr/NeverTypeRepr.expected b/rust/ql/test/extractor-tests/generated/NeverTypeRepr/NeverTypeRepr.expected index c02b6c82354..7e8d7f8718b 100644 --- a/rust/ql/test/extractor-tests/generated/NeverTypeRepr/NeverTypeRepr.expected +++ b/rust/ql/test/extractor-tests/generated/NeverTypeRepr/NeverTypeRepr.expected @@ -1 +1,2 @@ | gen_never_type_repr.rs:7:17:7:17 | ! | +| gen_never_type_repr.rs:7:21:7:26 | ! | diff --git a/rust/ql/test/library-tests/controlflow/BasicBlocks.expected b/rust/ql/test/library-tests/controlflow/BasicBlocks.expected index 94760d6875e..80357ae29f8 100644 --- a/rust/ql/test/library-tests/controlflow/BasicBlocks.expected +++ b/rust/ql/test/library-tests/controlflow/BasicBlocks.expected @@ -743,6 +743,7 @@ dominates | test.rs:529:5:537:5 | enter fn const_block_assert | test.rs:533:21:533:48 | [boolean(true)] ! ... | | test.rs:529:5:537:5 | enter fn const_block_assert | test.rs:533:21:533:48 | if ... {...} | | test.rs:533:13:533:19 | ExprStmt | test.rs:533:13:533:19 | ExprStmt | +| test.rs:533:13:533:19 | enter fn panic_cold_explicit | test.rs:533:13:533:19 | enter fn panic_cold_explicit | | test.rs:533:21:533:48 | [boolean(false)] ! ... | test.rs:533:21:533:48 | [boolean(false)] ! ... | | test.rs:533:21:533:48 | [boolean(true)] ! ... | test.rs:533:13:533:19 | ExprStmt | | test.rs:533:21:533:48 | [boolean(true)] ! ... | test.rs:533:21:533:48 | [boolean(true)] ! ... | @@ -750,6 +751,7 @@ dominates | test.rs:539:5:548:5 | enter fn const_block_panic | test.rs:539:5:548:5 | enter fn const_block_panic | | test.rs:539:5:548:5 | enter fn const_block_panic | test.rs:541:9:546:9 | if false {...} | | test.rs:541:9:546:9 | if false {...} | test.rs:541:9:546:9 | if false {...} | +| test.rs:544:17:544:22 | enter fn panic_cold_explicit | test.rs:544:17:544:22 | enter fn panic_cold_explicit | | test.rs:551:1:556:1 | enter fn dead_code | test.rs:551:1:556:1 | enter fn dead_code | | test.rs:551:1:556:1 | enter fn dead_code | test.rs:553:9:553:17 | ExprStmt | | test.rs:553:9:553:17 | ExprStmt | test.rs:553:9:553:17 | ExprStmt | @@ -1422,6 +1424,7 @@ postDominance | test.rs:529:5:537:5 | enter fn const_block_assert | test.rs:529:5:537:5 | enter fn const_block_assert | | test.rs:533:13:533:19 | ExprStmt | test.rs:533:13:533:19 | ExprStmt | | test.rs:533:13:533:19 | ExprStmt | test.rs:533:21:533:48 | [boolean(true)] ! ... | +| test.rs:533:13:533:19 | enter fn panic_cold_explicit | test.rs:533:13:533:19 | enter fn panic_cold_explicit | | test.rs:533:21:533:48 | [boolean(false)] ! ... | test.rs:533:21:533:48 | [boolean(false)] ! ... | | test.rs:533:21:533:48 | [boolean(true)] ! ... | test.rs:533:21:533:48 | [boolean(true)] ! ... | | test.rs:533:21:533:48 | if ... {...} | test.rs:529:5:537:5 | enter fn const_block_assert | @@ -1432,6 +1435,7 @@ postDominance | test.rs:539:5:548:5 | enter fn const_block_panic | test.rs:539:5:548:5 | enter fn const_block_panic | | test.rs:541:9:546:9 | if false {...} | test.rs:539:5:548:5 | enter fn const_block_panic | | test.rs:541:9:546:9 | if false {...} | test.rs:541:9:546:9 | if false {...} | +| test.rs:544:17:544:22 | enter fn panic_cold_explicit | test.rs:544:17:544:22 | enter fn panic_cold_explicit | | test.rs:551:1:556:1 | enter fn dead_code | test.rs:551:1:556:1 | enter fn dead_code | | test.rs:553:9:553:17 | ExprStmt | test.rs:551:1:556:1 | enter fn dead_code | | test.rs:553:9:553:17 | ExprStmt | test.rs:553:9:553:17 | ExprStmt | diff --git a/rust/ql/test/library-tests/controlflow/Cfg.expected b/rust/ql/test/library-tests/controlflow/Cfg.expected index 25fb8f29e04..ef97a3b628f 100644 --- a/rust/ql/test/library-tests/controlflow/Cfg.expected +++ b/rust/ql/test/library-tests/controlflow/Cfg.expected @@ -1288,13 +1288,20 @@ edges | test.rs:529:41:537:5 | { ... } | test.rs:529:5:537:5 | exit fn const_block_assert (normal) | | | test.rs:532:9:534:9 | ExprStmt | test.rs:533:13:533:50 | ExprStmt | | | test.rs:532:9:534:9 | { ... } | test.rs:536:9:536:10 | 42 | | -| test.rs:533:13:533:19 | "explicit panic" | test.rs:533:13:533:19 | ...::panic(...) | | -| test.rs:533:13:533:19 | ...::panic | test.rs:533:13:533:19 | "explicit panic" | | -| test.rs:533:13:533:19 | ...::panic(...) | test.rs:533:13:533:19 | { ... } | | | test.rs:533:13:533:19 | ...::panic_2021!... | test.rs:533:13:533:19 | MacroExpr | | -| test.rs:533:13:533:19 | ExprStmt | test.rs:533:13:533:19 | ...::panic | | +| test.rs:533:13:533:19 | ...::panic_explicit | test.rs:533:13:533:19 | ...::panic_explicit(...) | | +| test.rs:533:13:533:19 | ...::panic_explicit(...) | test.rs:533:13:533:19 | { ... } | | +| test.rs:533:13:533:19 | ExprStmt | test.rs:533:13:533:19 | fn panic_cold_explicit | | +| test.rs:533:13:533:19 | ExprStmt | test.rs:533:13:533:19 | panic_cold_explicit | | | test.rs:533:13:533:19 | MacroExpr | test.rs:533:13:533:19 | { ... } | | +| test.rs:533:13:533:19 | enter fn panic_cold_explicit | test.rs:533:13:533:19 | ...::panic_explicit | | +| test.rs:533:13:533:19 | exit fn panic_cold_explicit (normal) | test.rs:533:13:533:19 | exit fn panic_cold_explicit | | +| test.rs:533:13:533:19 | fn panic_cold_explicit | test.rs:533:13:533:19 | ExprStmt | | +| test.rs:533:13:533:19 | panic_cold_explicit | test.rs:533:13:533:19 | panic_cold_explicit(...) | | +| test.rs:533:13:533:19 | panic_cold_explicit(...) | test.rs:533:13:533:19 | { ... } | | | test.rs:533:13:533:19 | { ... } | test.rs:533:13:533:19 | ...::panic_2021!... | | +| test.rs:533:13:533:19 | { ... } | test.rs:533:13:533:19 | exit fn panic_cold_explicit (normal) | | +| test.rs:533:13:533:19 | { ... } | test.rs:533:13:533:19 | { ... } | | | test.rs:533:13:533:19 | { ... } | test.rs:533:21:533:48 | if ... {...} | | | test.rs:533:13:533:49 | MacroExpr | test.rs:532:9:534:9 | { ... } | | | test.rs:533:13:533:49 | assert!... | test.rs:533:13:533:49 | MacroExpr | | @@ -1317,6 +1324,11 @@ edges | test.rs:541:9:546:9 | ExprStmt | test.rs:541:12:541:16 | false | | | test.rs:541:9:546:9 | if false {...} | test.rs:547:9:547:9 | N | | | test.rs:541:12:541:16 | false | test.rs:541:9:546:9 | if false {...} | false | +| test.rs:544:17:544:22 | ...::panic_explicit | test.rs:544:17:544:22 | ...::panic_explicit(...) | | +| test.rs:544:17:544:22 | ...::panic_explicit(...) | test.rs:544:17:544:22 | { ... } | | +| test.rs:544:17:544:22 | enter fn panic_cold_explicit | test.rs:544:17:544:22 | ...::panic_explicit | | +| test.rs:544:17:544:22 | exit fn panic_cold_explicit (normal) | test.rs:544:17:544:22 | exit fn panic_cold_explicit | | +| test.rs:544:17:544:22 | { ... } | test.rs:544:17:544:22 | exit fn panic_cold_explicit (normal) | | | test.rs:547:9:547:9 | N | test.rs:539:35:548:5 | { ... } | | | test.rs:551:1:556:1 | enter fn dead_code | test.rs:552:5:554:5 | ExprStmt | | | test.rs:551:1:556:1 | exit fn dead_code (normal) | test.rs:551:1:556:1 | exit fn dead_code | | diff --git a/rust/ql/test/library-tests/type-inference/type-inference.expected b/rust/ql/test/library-tests/type-inference/type-inference.expected index a2322cdf929..1b750fab458 100644 --- a/rust/ql/test/library-tests/type-inference/type-inference.expected +++ b/rust/ql/test/library-tests/type-inference/type-inference.expected @@ -788,14 +788,12 @@ inferCertainType | dereference.rs:116:5:116:5 | x | | dereference.rs:18:1:20:1 | MySmartPointer | | dereference.rs:143:19:151:5 | { ... } | | {EXTERNAL LOCATION} | () | | dereference.rs:144:17:144:26 | key_to_key | | {EXTERNAL LOCATION} | HashMap | -| dereference.rs:144:17:144:26 | key_to_key | A | {EXTERNAL LOCATION} | Global | | dereference.rs:144:17:144:26 | key_to_key | K | {EXTERNAL LOCATION} | & | | dereference.rs:144:17:144:26 | key_to_key | K.TRef | dereference.rs:122:5:123:21 | Key | | dereference.rs:144:17:144:26 | key_to_key | S | {EXTERNAL LOCATION} | RandomState | | dereference.rs:144:17:144:26 | key_to_key | V | {EXTERNAL LOCATION} | & | | dereference.rs:144:17:144:26 | key_to_key | V.TRef | dereference.rs:122:5:123:21 | Key | | dereference.rs:144:30:144:57 | ...::new(...) | | {EXTERNAL LOCATION} | HashMap | -| dereference.rs:144:30:144:57 | ...::new(...) | A | {EXTERNAL LOCATION} | Global | | dereference.rs:144:30:144:57 | ...::new(...) | K | {EXTERNAL LOCATION} | & | | dereference.rs:144:30:144:57 | ...::new(...) | K.TRef | dereference.rs:122:5:123:21 | Key | | dereference.rs:144:30:144:57 | ...::new(...) | S | {EXTERNAL LOCATION} | RandomState | @@ -806,7 +804,6 @@ inferCertainType | dereference.rs:145:24:145:29 | Key {...} | | dereference.rs:122:5:123:21 | Key | | dereference.rs:146:9:149:9 | if ... {...} | | {EXTERNAL LOCATION} | () | | dereference.rs:146:32:146:41 | key_to_key | | {EXTERNAL LOCATION} | HashMap | -| dereference.rs:146:32:146:41 | key_to_key | A | {EXTERNAL LOCATION} | Global | | dereference.rs:146:32:146:41 | key_to_key | K | {EXTERNAL LOCATION} | & | | dereference.rs:146:32:146:41 | key_to_key | K.TRef | dereference.rs:122:5:123:21 | Key | | dereference.rs:146:32:146:41 | key_to_key | S | {EXTERNAL LOCATION} | RandomState | @@ -816,7 +813,6 @@ inferCertainType | dereference.rs:146:52:149:9 | { ... } | | {EXTERNAL LOCATION} | () | | dereference.rs:148:13:148:15 | key | | {EXTERNAL LOCATION} | & | | dereference.rs:150:9:150:18 | key_to_key | | {EXTERNAL LOCATION} | HashMap | -| dereference.rs:150:9:150:18 | key_to_key | A | {EXTERNAL LOCATION} | Global | | dereference.rs:150:9:150:18 | key_to_key | K | {EXTERNAL LOCATION} | & | | dereference.rs:150:9:150:18 | key_to_key | K.TRef | dereference.rs:122:5:123:21 | Key | | dereference.rs:150:9:150:18 | key_to_key | S | {EXTERNAL LOCATION} | RandomState | @@ -3354,20 +3350,16 @@ inferCertainType | main.rs:2345:13:2346:13 | for ... in ... { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2345:29:2346:13 | { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2349:17:2349:20 | map1 | | {EXTERNAL LOCATION} | HashMap | -| main.rs:2349:17:2349:20 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2349:17:2349:20 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2349:24:2349:55 | ...::new(...) | | {EXTERNAL LOCATION} | HashMap | -| main.rs:2349:24:2349:55 | ...::new(...) | A | {EXTERNAL LOCATION} | Global | | main.rs:2349:24:2349:55 | ...::new(...) | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2350:9:2350:12 | map1 | | {EXTERNAL LOCATION} | HashMap | -| main.rs:2350:9:2350:12 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2350:9:2350:12 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2350:24:2350:38 | ...::new(...) | | {EXTERNAL LOCATION} | Box | | main.rs:2350:24:2350:38 | ...::new(...) | A | {EXTERNAL LOCATION} | Global | | main.rs:2350:33:2350:37 | "one" | | {EXTERNAL LOCATION} | & | | main.rs:2350:33:2350:37 | "one" | TRef | {EXTERNAL LOCATION} | str | | main.rs:2351:9:2351:12 | map1 | | {EXTERNAL LOCATION} | HashMap | -| main.rs:2351:9:2351:12 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2351:9:2351:12 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2351:24:2351:38 | ...::new(...) | | {EXTERNAL LOCATION} | Box | | main.rs:2351:24:2351:38 | ...::new(...) | A | {EXTERNAL LOCATION} | Global | @@ -3375,25 +3367,21 @@ inferCertainType | main.rs:2351:33:2351:37 | "two" | TRef | {EXTERNAL LOCATION} | str | | main.rs:2352:9:2352:33 | for ... in ... { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2352:20:2352:23 | map1 | | {EXTERNAL LOCATION} | HashMap | -| main.rs:2352:20:2352:23 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2352:20:2352:23 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2352:32:2352:33 | { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2353:9:2353:37 | for ... in ... { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2353:22:2353:25 | map1 | | {EXTERNAL LOCATION} | HashMap | -| main.rs:2353:22:2353:25 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2353:22:2353:25 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2353:36:2353:37 | { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2354:9:2354:42 | for ... in ... { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2354:13:2354:24 | TuplePat | | {EXTERNAL LOCATION} | (T_2) | | main.rs:2354:29:2354:32 | map1 | | {EXTERNAL LOCATION} | HashMap | -| main.rs:2354:29:2354:32 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2354:29:2354:32 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2354:41:2354:42 | { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2355:9:2355:36 | for ... in ... { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2355:13:2355:24 | TuplePat | | {EXTERNAL LOCATION} | (T_2) | | main.rs:2355:29:2355:33 | &map1 | | {EXTERNAL LOCATION} | & | | main.rs:2355:30:2355:33 | map1 | | {EXTERNAL LOCATION} | HashMap | -| main.rs:2355:30:2355:33 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2355:30:2355:33 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2355:35:2355:36 | { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2359:17:2359:17 | a | | {EXTERNAL LOCATION} | i64 | @@ -6464,14 +6452,12 @@ inferType | dereference.rs:116:12:116:12 | 0 | | {EXTERNAL LOCATION} | i32 | | dereference.rs:143:19:151:5 | { ... } | | {EXTERNAL LOCATION} | () | | dereference.rs:144:17:144:26 | key_to_key | | {EXTERNAL LOCATION} | HashMap | -| dereference.rs:144:17:144:26 | key_to_key | A | {EXTERNAL LOCATION} | Global | | dereference.rs:144:17:144:26 | key_to_key | K | {EXTERNAL LOCATION} | & | | dereference.rs:144:17:144:26 | key_to_key | K.TRef | dereference.rs:122:5:123:21 | Key | | dereference.rs:144:17:144:26 | key_to_key | S | {EXTERNAL LOCATION} | RandomState | | dereference.rs:144:17:144:26 | key_to_key | V | {EXTERNAL LOCATION} | & | | dereference.rs:144:17:144:26 | key_to_key | V.TRef | dereference.rs:122:5:123:21 | Key | | dereference.rs:144:30:144:57 | ...::new(...) | | {EXTERNAL LOCATION} | HashMap | -| dereference.rs:144:30:144:57 | ...::new(...) | A | {EXTERNAL LOCATION} | Global | | dereference.rs:144:30:144:57 | ...::new(...) | K | {EXTERNAL LOCATION} | & | | dereference.rs:144:30:144:57 | ...::new(...) | K.TRef | dereference.rs:122:5:123:21 | Key | | dereference.rs:144:30:144:57 | ...::new(...) | S | {EXTERNAL LOCATION} | RandomState | @@ -6497,7 +6483,6 @@ inferType | dereference.rs:146:21:146:27 | ref_key | TRef | {EXTERNAL LOCATION} | & | | dereference.rs:146:21:146:27 | ref_key | TRef.TRef | dereference.rs:122:5:123:21 | Key | | dereference.rs:146:32:146:41 | key_to_key | | {EXTERNAL LOCATION} | HashMap | -| dereference.rs:146:32:146:41 | key_to_key | A | {EXTERNAL LOCATION} | Global | | dereference.rs:146:32:146:41 | key_to_key | K | {EXTERNAL LOCATION} | & | | dereference.rs:146:32:146:41 | key_to_key | K.TRef | dereference.rs:122:5:123:21 | Key | | dereference.rs:146:32:146:41 | key_to_key | S | {EXTERNAL LOCATION} | RandomState | @@ -6523,7 +6508,6 @@ inferType | dereference.rs:148:19:148:25 | ref_key | TRef | {EXTERNAL LOCATION} | & | | dereference.rs:148:19:148:25 | ref_key | TRef.TRef | dereference.rs:122:5:123:21 | Key | | dereference.rs:150:9:150:18 | key_to_key | | {EXTERNAL LOCATION} | HashMap | -| dereference.rs:150:9:150:18 | key_to_key | A | {EXTERNAL LOCATION} | Global | | dereference.rs:150:9:150:18 | key_to_key | K | {EXTERNAL LOCATION} | & | | dereference.rs:150:9:150:18 | key_to_key | K.TRef | dereference.rs:122:5:123:21 | Key | | dereference.rs:150:9:150:18 | key_to_key | S | {EXTERNAL LOCATION} | RandomState | @@ -11278,7 +11262,6 @@ inferType | main.rs:2345:13:2346:13 | for ... in ... { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2345:29:2346:13 | { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2349:17:2349:20 | map1 | | {EXTERNAL LOCATION} | HashMap | -| main.rs:2349:17:2349:20 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2349:17:2349:20 | map1 | K | {EXTERNAL LOCATION} | i32 | | main.rs:2349:17:2349:20 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2349:17:2349:20 | map1 | V | {EXTERNAL LOCATION} | Box | @@ -11286,7 +11269,6 @@ inferType | main.rs:2349:17:2349:20 | map1 | V.T | {EXTERNAL LOCATION} | & | | main.rs:2349:17:2349:20 | map1 | V.T.TRef | {EXTERNAL LOCATION} | str | | main.rs:2349:24:2349:55 | ...::new(...) | | {EXTERNAL LOCATION} | HashMap | -| main.rs:2349:24:2349:55 | ...::new(...) | A | {EXTERNAL LOCATION} | Global | | main.rs:2349:24:2349:55 | ...::new(...) | K | {EXTERNAL LOCATION} | i32 | | main.rs:2349:24:2349:55 | ...::new(...) | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2349:24:2349:55 | ...::new(...) | V | {EXTERNAL LOCATION} | Box | @@ -11294,7 +11276,6 @@ inferType | main.rs:2349:24:2349:55 | ...::new(...) | V.T | {EXTERNAL LOCATION} | & | | main.rs:2349:24:2349:55 | ...::new(...) | V.T.TRef | {EXTERNAL LOCATION} | str | | main.rs:2350:9:2350:12 | map1 | | {EXTERNAL LOCATION} | HashMap | -| main.rs:2350:9:2350:12 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2350:9:2350:12 | map1 | K | {EXTERNAL LOCATION} | i32 | | main.rs:2350:9:2350:12 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2350:9:2350:12 | map1 | V | {EXTERNAL LOCATION} | Box | @@ -11314,7 +11295,6 @@ inferType | main.rs:2350:33:2350:37 | "one" | | {EXTERNAL LOCATION} | & | | main.rs:2350:33:2350:37 | "one" | TRef | {EXTERNAL LOCATION} | str | | main.rs:2351:9:2351:12 | map1 | | {EXTERNAL LOCATION} | HashMap | -| main.rs:2351:9:2351:12 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2351:9:2351:12 | map1 | K | {EXTERNAL LOCATION} | i32 | | main.rs:2351:9:2351:12 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2351:9:2351:12 | map1 | V | {EXTERNAL LOCATION} | Box | @@ -11337,7 +11317,6 @@ inferType | main.rs:2352:13:2352:15 | key | | {EXTERNAL LOCATION} | & | | main.rs:2352:13:2352:15 | key | TRef | {EXTERNAL LOCATION} | i32 | | main.rs:2352:20:2352:23 | map1 | | {EXTERNAL LOCATION} | HashMap | -| main.rs:2352:20:2352:23 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2352:20:2352:23 | map1 | K | {EXTERNAL LOCATION} | i32 | | main.rs:2352:20:2352:23 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2352:20:2352:23 | map1 | V | {EXTERNAL LOCATION} | Box | @@ -11358,7 +11337,6 @@ inferType | main.rs:2353:13:2353:17 | value | TRef.T | {EXTERNAL LOCATION} | & | | main.rs:2353:13:2353:17 | value | TRef.T.TRef | {EXTERNAL LOCATION} | str | | main.rs:2353:22:2353:25 | map1 | | {EXTERNAL LOCATION} | HashMap | -| main.rs:2353:22:2353:25 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2353:22:2353:25 | map1 | K | {EXTERNAL LOCATION} | i32 | | main.rs:2353:22:2353:25 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2353:22:2353:25 | map1 | V | {EXTERNAL LOCATION} | Box | @@ -11389,7 +11367,6 @@ inferType | main.rs:2354:19:2354:23 | value | TRef.T | {EXTERNAL LOCATION} | & | | main.rs:2354:19:2354:23 | value | TRef.T.TRef | {EXTERNAL LOCATION} | str | | main.rs:2354:29:2354:32 | map1 | | {EXTERNAL LOCATION} | HashMap | -| main.rs:2354:29:2354:32 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2354:29:2354:32 | map1 | K | {EXTERNAL LOCATION} | i32 | | main.rs:2354:29:2354:32 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2354:29:2354:32 | map1 | V | {EXTERNAL LOCATION} | Box | @@ -11421,7 +11398,6 @@ inferType | main.rs:2355:19:2355:23 | value | TRef.T.TRef | {EXTERNAL LOCATION} | str | | main.rs:2355:29:2355:33 | &map1 | | {EXTERNAL LOCATION} | & | | main.rs:2355:29:2355:33 | &map1 | TRef | {EXTERNAL LOCATION} | HashMap | -| main.rs:2355:29:2355:33 | &map1 | TRef.A | {EXTERNAL LOCATION} | Global | | main.rs:2355:29:2355:33 | &map1 | TRef.K | {EXTERNAL LOCATION} | i32 | | main.rs:2355:29:2355:33 | &map1 | TRef.S | {EXTERNAL LOCATION} | RandomState | | main.rs:2355:29:2355:33 | &map1 | TRef.V | {EXTERNAL LOCATION} | Box | @@ -11429,7 +11405,6 @@ inferType | main.rs:2355:29:2355:33 | &map1 | TRef.V.T | {EXTERNAL LOCATION} | & | | main.rs:2355:29:2355:33 | &map1 | TRef.V.T.TRef | {EXTERNAL LOCATION} | str | | main.rs:2355:30:2355:33 | map1 | | {EXTERNAL LOCATION} | HashMap | -| main.rs:2355:30:2355:33 | map1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2355:30:2355:33 | map1 | K | {EXTERNAL LOCATION} | i32 | | main.rs:2355:30:2355:33 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2355:30:2355:33 | map1 | V | {EXTERNAL LOCATION} | Box |