From cb934e17b175afa8be89e9f094925cbf423cd236 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 16 Dec 2021 22:16:02 +0100 Subject: [PATCH] Python: Adjust SSRF location to request call Since that might not be the same place where the vulnerable URL part is. --- ...ServerSideRequestForgeryCustomizations.qll | 27 +++++++-- .../CWE-918/FullServerSideRequestForgery.ql | 10 ++-- .../PartialServerSideRequestForgery.ql | 7 ++- .../FullServerSideRequestForgery.expected | 23 ++++---- .../PartialServerSideRequestForgery.expected | 56 +++++++++---------- 5 files changed, 72 insertions(+), 51 deletions(-) diff --git a/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll index bd7b4e6e507..437e09bf4db 100644 --- a/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll @@ -24,7 +24,12 @@ module ServerSideRequestForgery { /** * A data flow sink for "Server-side request forgery" vulnerabilities. */ - abstract class Sink extends DataFlow::Node { } + abstract class Sink extends DataFlow::Node { + /** + * Gets the request this sink belongs to. + */ + abstract HTTP::Client::Request getRequest(); + } /** * A sanitizer for "Server-side request forgery" vulnerabilities. @@ -50,12 +55,24 @@ module ServerSideRequestForgery { /** The URL of an HTTP request, considered as a sink. */ class HttpRequestUrlAsSink extends Sink { + HTTP::Client::Request req; + HttpRequestUrlAsSink() { - exists(HTTP::Client::Request req | req.getAUrlPart() = this) and - // Since we find sinks inside stdlib, we need to exclude them manually. See - // comment for command injection sinks for more details. - not this.getScope().getEnclosingModule().getName() in ["http.client", "httplib"] + req.getAUrlPart() = this and + // if we extract the stdlib code for HTTPConnection, we will also find calls that + // make requests within the HTTPConnection implementation -- for example the + // `request` method calls the `_send_request` method internally. So without this + // extra bit of code, we would give alerts within the HTTPConnection + // implementation as well, which is just annoying. + // + // Notice that we're excluding based on the request location, and not the URL part + // location, since the URL part would be in user code for the scenario above. + // + // See comment for command injection sinks for more details. + not req.getScope().getEnclosingModule().getName() in ["http.client", "httplib"] } + + override HTTP::Client::Request getRequest() { result = req } } /** diff --git a/python/ql/src/Security/CWE-918/FullServerSideRequestForgery.ql b/python/ql/src/Security/CWE-918/FullServerSideRequestForgery.ql index 72c5ac9f70f..8ec3eb5e063 100644 --- a/python/ql/src/Security/CWE-918/FullServerSideRequestForgery.ql +++ b/python/ql/src/Security/CWE-918/FullServerSideRequestForgery.ql @@ -16,7 +16,9 @@ import DataFlow::PathGraph from FullServerSideRequestForgery::Configuration config, DataFlow::PathNode source, - DataFlow::PathNode sink -where config.hasFlowPath(source, sink) -select sink.getNode(), source, sink, "The full URL of this request depends on $@.", - source.getNode(), "a user-provided value" + DataFlow::PathNode sink, HTTP::Client::Request request +where + request = sink.getNode().(FullServerSideRequestForgery::Sink).getRequest() and + config.hasFlowPath(source, sink) +select request, source, sink, "The full URL of this request depends on $@.", source.getNode(), + "a user-provided value" diff --git a/python/ql/src/Security/CWE-918/PartialServerSideRequestForgery.ql b/python/ql/src/Security/CWE-918/PartialServerSideRequestForgery.ql index cd59a710a78..34dd9676501 100644 --- a/python/ql/src/Security/CWE-918/PartialServerSideRequestForgery.ql +++ b/python/ql/src/Security/CWE-918/PartialServerSideRequestForgery.ql @@ -17,9 +17,10 @@ import DataFlow::PathGraph from FullServerSideRequestForgery::Configuration fullConfig, PartialServerSideRequestForgery::Configuration partialConfig, DataFlow::PathNode source, - DataFlow::PathNode sink + DataFlow::PathNode sink, HTTP::Client::Request request where + request = sink.getNode().(PartialServerSideRequestForgery::Sink).getRequest() and partialConfig.hasFlowPath(source, sink) and not fullConfig.hasFlow(source.getNode(), sink.getNode()) -select sink.getNode(), source, sink, "Part of the URL of this request depends on $@.", - source.getNode(), "a user-provided value" +select request, source, sink, "Part of the URL of this request depends on $@.", source.getNode(), + "a user-provided value" 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 862d1f1304c..f44e3ad3c27 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 @@ -145,14 +145,15 @@ nodes | test_requests.py:8:18:8:27 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | subpaths #select -| full_partial_test.py:10:18:10:27 | ControlFlowNode for user_input | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:10:18:10:27 | ControlFlowNode for user_input | The full URL of this request depends on $@. | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | a user-provided value | -| test_http_client.py:13:27:13:37 | ControlFlowNode for unsafe_host | test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:13:27:13:37 | ControlFlowNode for unsafe_host | The full URL of this request depends on $@. | test_http_client.py:9:19:9:25 | ControlFlowNode for request | a user-provided value | -| test_http_client.py:14:25:14:35 | ControlFlowNode for unsafe_path | test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:14:25:14:35 | ControlFlowNode for unsafe_path | The full URL of this request depends on $@. | test_http_client.py:9:19:9:25 | ControlFlowNode for request | a user-provided value | -| test_http_client.py:14:25:14:35 | ControlFlowNode for unsafe_path | test_http_client.py:10:19:10:25 | ControlFlowNode for request | test_http_client.py:14:25:14:35 | ControlFlowNode for unsafe_path | The full URL of this request depends on $@. | test_http_client.py:10:19:10:25 | ControlFlowNode for request | a user-provided value | -| test_http_client.py:18:27:18:37 | ControlFlowNode for unsafe_host | test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:18:27:18:37 | ControlFlowNode for unsafe_host | The full URL of this request depends on $@. | test_http_client.py:9:19:9:25 | ControlFlowNode for request | a user-provided value | -| test_http_client.py:19:25:19:35 | ControlFlowNode for unsafe_path | test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:19:25:19:35 | ControlFlowNode for unsafe_path | The full URL of this request depends on $@. | test_http_client.py:9:19:9:25 | ControlFlowNode for request | a user-provided value | -| test_http_client.py:19:25:19:35 | ControlFlowNode for unsafe_path | test_http_client.py:10:19:10:25 | ControlFlowNode for request | test_http_client.py:19:25:19:35 | ControlFlowNode for unsafe_path | The full URL of this request depends on $@. | test_http_client.py:10:19:10:25 | ControlFlowNode for request | a user-provided value | -| test_http_client.py:25:27:25:37 | ControlFlowNode for unsafe_host | test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:25:27:25:37 | ControlFlowNode for unsafe_host | The full URL of this request depends on $@. | test_http_client.py:9:19:9:25 | ControlFlowNode for request | a user-provided value | -| test_http_client.py:29:25:29:35 | ControlFlowNode for unsafe_path | test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:29:25:29:35 | ControlFlowNode for unsafe_path | The full URL of this request depends on $@. | test_http_client.py:9:19:9:25 | ControlFlowNode for request | a user-provided value | -| test_http_client.py:29:25:29:35 | ControlFlowNode for unsafe_path | test_http_client.py:10:19:10:25 | ControlFlowNode for request | test_http_client.py:29:25:29:35 | ControlFlowNode for unsafe_path | The full URL of this request depends on $@. | test_http_client.py:10:19:10:25 | ControlFlowNode for request | a user-provided value | -| test_requests.py:8:18:8:27 | ControlFlowNode for user_input | test_requests.py:6:18:6:24 | ControlFlowNode for request | test_requests.py:8:18:8:27 | ControlFlowNode for user_input | The full URL of this request depends on $@. | test_requests.py:6:18:6:24 | ControlFlowNode for request | a user-provided value | +| full_partial_test.py:10:5:10:28 | ControlFlowNode for Attribute() | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:10:18:10:27 | ControlFlowNode for user_input | The full URL of this request depends on $@. | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | a user-provided value | +| test_http_client.py:14:5:14:36 | ControlFlowNode for Attribute() | test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:13:27:13:37 | ControlFlowNode for unsafe_host | The full URL of this request depends on $@. | test_http_client.py:9:19:9:25 | ControlFlowNode for request | a user-provided value | +| test_http_client.py:14:5:14:36 | ControlFlowNode for Attribute() | test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:14:25:14:35 | ControlFlowNode for unsafe_path | The full URL of this request depends on $@. | test_http_client.py:9:19:9:25 | ControlFlowNode for request | a user-provided value | +| test_http_client.py:14:5:14:36 | ControlFlowNode for Attribute() | test_http_client.py:10:19:10:25 | ControlFlowNode for request | test_http_client.py:14:25:14:35 | ControlFlowNode for unsafe_path | The full URL of this request depends on $@. | test_http_client.py:10:19:10:25 | ControlFlowNode for request | a user-provided value | +| test_http_client.py:19:5:19:36 | ControlFlowNode for Attribute() | test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:18:27:18:37 | ControlFlowNode for unsafe_host | The full URL of this request depends on $@. | test_http_client.py:9:19:9:25 | ControlFlowNode for request | a user-provided value | +| test_http_client.py:19:5:19:36 | ControlFlowNode for Attribute() | test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:19:25:19:35 | ControlFlowNode for unsafe_path | The full URL of this request depends on $@. | test_http_client.py:9:19:9:25 | ControlFlowNode for request | a user-provided value | +| test_http_client.py:19:5:19:36 | ControlFlowNode for Attribute() | test_http_client.py:10:19:10:25 | ControlFlowNode for request | test_http_client.py:19:25:19:35 | ControlFlowNode for unsafe_path | The full URL of this request depends on $@. | test_http_client.py:10:19:10:25 | ControlFlowNode for request | a user-provided value | +| test_http_client.py:22:5:22:31 | ControlFlowNode for Attribute() | test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:18:27:18:37 | ControlFlowNode for unsafe_host | The full URL of this request depends on $@. | test_http_client.py:9:19:9:25 | ControlFlowNode for request | a user-provided value | +| test_http_client.py:26:5:26:31 | ControlFlowNode for Attribute() | test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:25:27:25:37 | ControlFlowNode for unsafe_host | The full URL of this request depends on $@. | test_http_client.py:9:19:9:25 | ControlFlowNode for request | a user-provided value | +| test_http_client.py:29:5:29:36 | ControlFlowNode for Attribute() | test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:29:25:29:35 | ControlFlowNode for unsafe_path | The full URL of this request depends on $@. | test_http_client.py:9:19:9:25 | ControlFlowNode for request | a user-provided value | +| test_http_client.py:29:5:29:36 | ControlFlowNode for Attribute() | test_http_client.py:10:19:10:25 | ControlFlowNode for request | test_http_client.py:29:25:29:35 | ControlFlowNode for unsafe_path | The full URL of this request depends on $@. | test_http_client.py:10:19:10:25 | ControlFlowNode for request | a user-provided value | +| test_requests.py:8:5:8:28 | ControlFlowNode for Attribute() | test_requests.py:6:18:6:24 | ControlFlowNode for request | test_requests.py:8:18:8:27 | ControlFlowNode for user_input | The full URL of this request depends on $@. | test_requests.py:6:18:6:24 | ControlFlowNode for request | a 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 ec66fff0eeb..cf3eade5da1 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 @@ -145,31 +145,31 @@ nodes | test_requests.py:8:18:8:27 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | subpaths #select -| full_partial_test.py:13:18:13:20 | ControlFlowNode for url | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:13:18:13:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | a user-provided value | -| full_partial_test.py:19:18:19:20 | ControlFlowNode for url | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:19:18:19:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | a user-provided value | -| full_partial_test.py:23:18:23:20 | ControlFlowNode for url | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:23:18:23:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | a user-provided value | -| full_partial_test.py:23:18:23:20 | ControlFlowNode for url | full_partial_test.py:8:17:8:23 | ControlFlowNode for request | full_partial_test.py:23:18:23:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:8:17:8:23 | ControlFlowNode for request | a user-provided value | -| full_partial_test.py:28:18:28:20 | ControlFlowNode for url | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:28:18:28:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | a user-provided value | -| full_partial_test.py:31:18:31:20 | ControlFlowNode for url | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:31:18:31:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | a user-provided value | -| full_partial_test.py:34:18:34:20 | ControlFlowNode for url | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:34:18:34:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | a user-provided value | -| full_partial_test.py:34:18:34:20 | ControlFlowNode for url | full_partial_test.py:8:17:8:23 | ControlFlowNode for request | full_partial_test.py:34:18:34:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:8:17:8:23 | ControlFlowNode for request | a user-provided value | -| full_partial_test.py:39:18:39:20 | ControlFlowNode for url | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:39:18:39:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | a user-provided value | -| full_partial_test.py:42:18:42:20 | ControlFlowNode for url | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:42:18:42:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | a user-provided value | -| full_partial_test.py:45:18:45:20 | ControlFlowNode for url | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:45:18:45:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | a user-provided value | -| full_partial_test.py:45:18:45:20 | ControlFlowNode for url | full_partial_test.py:8:17:8:23 | ControlFlowNode for request | full_partial_test.py:45:18:45:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:8:17:8:23 | ControlFlowNode for request | a user-provided value | -| full_partial_test.py:50:18:50:20 | ControlFlowNode for url | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:50:18:50:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | a user-provided value | -| full_partial_test.py:53:18:53:20 | ControlFlowNode for url | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:53:18:53:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | a user-provided value | -| full_partial_test.py:56:18:56:20 | ControlFlowNode for url | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:56:18:56:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | a user-provided value | -| full_partial_test.py:56:18:56:20 | ControlFlowNode for url | full_partial_test.py:8:17:8:23 | ControlFlowNode for request | full_partial_test.py:56:18:56:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:8:17:8:23 | ControlFlowNode for request | a user-provided value | -| full_partial_test.py:63:18:63:20 | ControlFlowNode for url | full_partial_test.py:60:18:60:24 | ControlFlowNode for request | full_partial_test.py:63:18:63:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:60:18:60:24 | ControlFlowNode for request | a user-provided value | -| full_partial_test.py:66:18:66:20 | ControlFlowNode for url | full_partial_test.py:60:18:60:24 | ControlFlowNode for request | full_partial_test.py:66:18:66:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:60:18:60:24 | ControlFlowNode for request | a user-provided value | -| full_partial_test.py:69:18:69:20 | ControlFlowNode for url | full_partial_test.py:60:18:60:24 | ControlFlowNode for request | full_partial_test.py:69:18:69:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:60:18:60:24 | ControlFlowNode for request | a user-provided value | -| full_partial_test.py:72:18:72:20 | ControlFlowNode for url | full_partial_test.py:60:18:60:24 | ControlFlowNode for request | full_partial_test.py:72:18:72:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:60:18:60:24 | ControlFlowNode for request | a user-provided value | -| full_partial_test.py:78:18:78:20 | ControlFlowNode for url | full_partial_test.py:60:18:60:24 | ControlFlowNode for request | full_partial_test.py:78:18:78:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:60:18:60:24 | ControlFlowNode for request | a user-provided value | -| full_partial_test.py:81:18:81:20 | ControlFlowNode for url | full_partial_test.py:60:18:60:24 | ControlFlowNode for request | full_partial_test.py:81:18:81:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:60:18:60:24 | ControlFlowNode for request | a user-provided value | -| test_http_client.py:33:25:33:28 | ControlFlowNode for path | test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:33:25:33:28 | ControlFlowNode for path | Part of the URL of this request depends on $@. | test_http_client.py:9:19:9:25 | ControlFlowNode for request | a user-provided value | -| test_http_client.py:33:25:33:28 | ControlFlowNode for path | test_http_client.py:10:19:10:25 | ControlFlowNode for request | test_http_client.py:33:25:33:28 | ControlFlowNode for path | Part of the URL of this request depends on $@. | test_http_client.py:10:19:10:25 | ControlFlowNode for request | a user-provided value | -| test_http_client.py:33:25:33:28 | ControlFlowNode for path | test_http_client.py:11:18:11:24 | ControlFlowNode for request | test_http_client.py:33:25:33:28 | ControlFlowNode for path | Part of the URL of this request depends on $@. | test_http_client.py:11:18:11:24 | ControlFlowNode for request | a user-provided value | -| test_http_client.py:37:25:37:28 | ControlFlowNode for path | test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:37:25:37:28 | ControlFlowNode for path | Part of the URL of this request depends on $@. | test_http_client.py:9:19:9:25 | ControlFlowNode for request | a user-provided value | -| test_http_client.py:37:25:37:28 | ControlFlowNode for path | test_http_client.py:10:19:10:25 | ControlFlowNode for request | test_http_client.py:37:25:37:28 | ControlFlowNode for path | Part of the URL of this request depends on $@. | test_http_client.py:10:19:10:25 | ControlFlowNode for request | a user-provided value | -| test_http_client.py:37:25:37:28 | ControlFlowNode for path | test_http_client.py:11:18:11:24 | ControlFlowNode for request | test_http_client.py:37:25:37:28 | ControlFlowNode for path | Part of the URL of this request depends on $@. | test_http_client.py:11:18:11:24 | ControlFlowNode for request | a user-provided value | +| full_partial_test.py:13:5:13:21 | ControlFlowNode for Attribute() | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:13:18:13:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | a user-provided value | +| full_partial_test.py:19:5:19:21 | ControlFlowNode for Attribute() | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:19:18:19:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | a user-provided value | +| full_partial_test.py:23:5:23:21 | ControlFlowNode for Attribute() | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:23:18:23:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | a user-provided value | +| full_partial_test.py:23:5:23:21 | ControlFlowNode for Attribute() | full_partial_test.py:8:17:8:23 | ControlFlowNode for request | full_partial_test.py:23:18:23:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:8:17:8:23 | ControlFlowNode for request | a user-provided value | +| full_partial_test.py:28:5:28:21 | ControlFlowNode for Attribute() | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:28:18:28:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | a user-provided value | +| full_partial_test.py:31:5:31:21 | ControlFlowNode for Attribute() | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:31:18:31:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | a user-provided value | +| full_partial_test.py:34:5:34:21 | ControlFlowNode for Attribute() | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:34:18:34:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | a user-provided value | +| full_partial_test.py:34:5:34:21 | ControlFlowNode for Attribute() | full_partial_test.py:8:17:8:23 | ControlFlowNode for request | full_partial_test.py:34:18:34:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:8:17:8:23 | ControlFlowNode for request | a user-provided value | +| full_partial_test.py:39:5:39:21 | ControlFlowNode for Attribute() | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:39:18:39:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | a user-provided value | +| full_partial_test.py:42:5:42:21 | ControlFlowNode for Attribute() | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:42:18:42:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | a user-provided value | +| full_partial_test.py:45:5:45:21 | ControlFlowNode for Attribute() | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:45:18:45:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | a user-provided value | +| full_partial_test.py:45:5:45:21 | ControlFlowNode for Attribute() | full_partial_test.py:8:17:8:23 | ControlFlowNode for request | full_partial_test.py:45:18:45:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:8:17:8:23 | ControlFlowNode for request | a user-provided value | +| full_partial_test.py:50:5:50:21 | ControlFlowNode for Attribute() | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:50:18:50:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | a user-provided value | +| full_partial_test.py:53:5:53:21 | ControlFlowNode for Attribute() | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:53:18:53:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | a user-provided value | +| full_partial_test.py:56:5:56:21 | ControlFlowNode for Attribute() | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:56:18:56:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:7:18:7:24 | ControlFlowNode for request | a user-provided value | +| full_partial_test.py:56:5:56:21 | ControlFlowNode for Attribute() | full_partial_test.py:8:17:8:23 | ControlFlowNode for request | full_partial_test.py:56:18:56:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:8:17:8:23 | ControlFlowNode for request | a user-provided value | +| full_partial_test.py:63:5:63:21 | ControlFlowNode for Attribute() | full_partial_test.py:60:18:60:24 | ControlFlowNode for request | full_partial_test.py:63:18:63:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:60:18:60:24 | ControlFlowNode for request | a user-provided value | +| full_partial_test.py:66:5:66:21 | ControlFlowNode for Attribute() | full_partial_test.py:60:18:60:24 | ControlFlowNode for request | full_partial_test.py:66:18:66:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:60:18:60:24 | ControlFlowNode for request | a user-provided value | +| full_partial_test.py:69:5:69:21 | ControlFlowNode for Attribute() | full_partial_test.py:60:18:60:24 | ControlFlowNode for request | full_partial_test.py:69:18:69:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:60:18:60:24 | ControlFlowNode for request | a user-provided value | +| full_partial_test.py:72:5:72:21 | ControlFlowNode for Attribute() | full_partial_test.py:60:18:60:24 | ControlFlowNode for request | full_partial_test.py:72:18:72:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:60:18:60:24 | ControlFlowNode for request | a user-provided value | +| full_partial_test.py:78:5:78:21 | ControlFlowNode for Attribute() | full_partial_test.py:60:18:60:24 | ControlFlowNode for request | full_partial_test.py:78:18:78:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:60:18:60:24 | ControlFlowNode for request | a user-provided value | +| full_partial_test.py:81:5:81:21 | ControlFlowNode for Attribute() | full_partial_test.py:60:18:60:24 | ControlFlowNode for request | full_partial_test.py:81:18:81:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:60:18:60:24 | ControlFlowNode for request | a user-provided value | +| test_http_client.py:33:5:33:29 | ControlFlowNode for Attribute() | test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:33:25:33:28 | ControlFlowNode for path | Part of the URL of this request depends on $@. | test_http_client.py:9:19:9:25 | ControlFlowNode for request | a user-provided value | +| test_http_client.py:33:5:33:29 | ControlFlowNode for Attribute() | test_http_client.py:10:19:10:25 | ControlFlowNode for request | test_http_client.py:33:25:33:28 | ControlFlowNode for path | Part of the URL of this request depends on $@. | test_http_client.py:10:19:10:25 | ControlFlowNode for request | a user-provided value | +| test_http_client.py:33:5:33:29 | ControlFlowNode for Attribute() | test_http_client.py:11:18:11:24 | ControlFlowNode for request | test_http_client.py:33:25:33:28 | ControlFlowNode for path | Part of the URL of this request depends on $@. | test_http_client.py:11:18:11:24 | ControlFlowNode for request | a user-provided value | +| test_http_client.py:37:5:37:29 | ControlFlowNode for Attribute() | test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:37:25:37:28 | ControlFlowNode for path | Part of the URL of this request depends on $@. | test_http_client.py:9:19:9:25 | ControlFlowNode for request | a user-provided value | +| test_http_client.py:37:5:37:29 | ControlFlowNode for Attribute() | test_http_client.py:10:19:10:25 | ControlFlowNode for request | test_http_client.py:37:25:37:28 | ControlFlowNode for path | Part of the URL of this request depends on $@. | test_http_client.py:10:19:10:25 | ControlFlowNode for request | a user-provided value | +| test_http_client.py:37:5:37:29 | ControlFlowNode for Attribute() | test_http_client.py:11:18:11:24 | ControlFlowNode for request | test_http_client.py:37:25:37:28 | ControlFlowNode for path | Part of the URL of this request depends on $@. | test_http_client.py:11:18:11:24 | ControlFlowNode for request | a user-provided value |