Merge pull request #7420 from RasmusWL/ssrf-new

Approved by yoff
This commit is contained in:
CodeQL CI
2021-12-17 15:20:49 +00:00
committed by GitHub
32 changed files with 1871 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
lgtm,codescanning
* Two new queries have been added for detecting Server-side request forgery (SSRF). _Full server-side request forgery_ (`py/full-ssrf`) will only alert when the URL is fully user-controlled, and _Partial server-side request forgery_ (`py/partial-ssrf`) will alert when any part of the URL is user-controlled. Only `py/full-ssrf` will be run by default.
* To support the new SSRF queries, the PyPI package `requests` have been modeled, along with `http.client.HTTP[S]Connection` from the standard library.

View File

@@ -812,6 +812,72 @@ module HTTP {
}
}
}
/** Provides classes for modeling HTTP clients. */
module Client {
/**
* A data-flow node that makes an outgoing HTTP request.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `HTTP::Client::Request::Range` instead.
*/
class Request extends DataFlow::Node instanceof Request::Range {
/**
* Gets a data-flow node that contributes to the URL of the request.
* Depending on the framework, a request may have multiple nodes which contribute to the URL.
*/
DataFlow::Node getAUrlPart() { result = super.getAUrlPart() }
/** Gets a string that identifies the framework used for this request. */
string getFramework() { result = super.getFramework() }
/**
* Holds if this request is made using a mode that disables SSL/TLS
* certificate validation, where `disablingNode` represents the point at
* which the validation was disabled, and `argumentOrigin` represents the origin
* of the argument that disabled the validation (which could be the same node as
* `disablingNode`).
*/
predicate disablesCertificateValidation(
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
) {
super.disablesCertificateValidation(disablingNode, argumentOrigin)
}
}
/** Provides a class for modeling new HTTP requests. */
module Request {
/**
* A data-flow node that makes an outgoing HTTP request.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `HTTP::Client::Request` instead.
*/
abstract class Range extends DataFlow::Node {
/**
* Gets a data-flow node that contributes to the URL of the request.
* Depending on the framework, a request may have multiple nodes which contribute to the URL.
*/
abstract DataFlow::Node getAUrlPart();
/** Gets a string that identifies the framework used for this request. */
abstract string getFramework();
/**
* Holds if this request is made using a mode that disables SSL/TLS
* certificate validation, where `disablingNode` represents the point at
* which the validation was disabled, and `argumentOrigin` represents the origin
* of the argument that disabled the validation (which could be the same node as
* `disablingNode`).
*/
abstract predicate disablesCertificateValidation(
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
);
}
}
// TODO: investigate whether we should treat responses to client requests as
// remote-flow-sources in general.
}
}
/**

View File

@@ -30,6 +30,7 @@ private import semmle.python.frameworks.Peewee
private import semmle.python.frameworks.Psycopg2
private import semmle.python.frameworks.Pydantic
private import semmle.python.frameworks.PyMySQL
private import semmle.python.frameworks.Requests
private import semmle.python.frameworks.RestFramework
private import semmle.python.frameworks.Rsa
private import semmle.python.frameworks.RuamelYaml

View File

@@ -0,0 +1,171 @@
/**
* Provides classes modeling security-relevant aspects of the `requests` PyPI package.
*
* See
* - https://pypi.org/project/requests/
* - https://docs.python-requests.org/en/latest/
*/
private import python
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.TaintTracking
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
private import semmle.python.frameworks.Stdlib
/**
* INTERNAL: Do not use.
*
* Provides models for the `requests` PyPI package.
*
* See
* - https://pypi.org/project/requests/
* - https://docs.python-requests.org/en/latest/
*/
private module Requests {
private class OutgoingRequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode {
string methodName;
OutgoingRequestCall() {
methodName in [HTTP::httpVerbLower(), "request"] and
(
this = API::moduleImport("requests").getMember(methodName).getACall()
or
exists(API::Node moduleExporting, API::Node sessionInstance |
moduleExporting in [
API::moduleImport("requests"), //
API::moduleImport("requests").getMember("sessions")
] and
sessionInstance = moduleExporting.getMember(["Session", "session"]).getReturn()
|
this = sessionInstance.getMember(methodName).getACall()
)
)
}
override DataFlow::Node getAUrlPart() {
result = this.getArgByName("url")
or
not methodName = "request" and
result = this.getArg(0)
or
methodName = "request" and
result = this.getArg(1)
}
/** Gets the `verify` argument to this outgoing requests call. */
DataFlow::Node getVerifyArg() { result = this.getArgByName("verify") }
override predicate disablesCertificateValidation(
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
) {
disablingNode = this.getVerifyArg() and
argumentOrigin = verifyArgBacktracker(disablingNode) and
argumentOrigin.asExpr().(ImmutableLiteral).booleanValue() = false and
not argumentOrigin.asExpr() instanceof None
}
override string getFramework() { result = "requests" }
}
/**
* Extra taint propagation for outgoing requests calls,
* to ensure that responses to user-controlled URL are tainted.
*/
private class OutgoingRequestCallTaintStep extends TaintTracking::AdditionalTaintStep {
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
nodeFrom = nodeTo.(OutgoingRequestCall).getAUrlPart()
}
}
/** Gets a back-reference to the verify argument `arg`. */
private DataFlow::TypeTrackingNode verifyArgBacktracker(
DataFlow::TypeBackTracker t, DataFlow::Node arg
) {
t.start() and
arg = any(OutgoingRequestCall c).getVerifyArg() and
result = arg.getALocalSource()
or
exists(DataFlow::TypeBackTracker t2 | result = verifyArgBacktracker(t2, arg).backtrack(t2, t))
}
/** Gets a back-reference to the verify argument `arg`. */
private DataFlow::LocalSourceNode verifyArgBacktracker(DataFlow::Node arg) {
result = verifyArgBacktracker(DataFlow::TypeBackTracker::end(), arg)
}
// ---------------------------------------------------------------------------
// Response
// ---------------------------------------------------------------------------
/**
* Provides models for the `requests.models.Response` class
*
* See https://docs.python-requests.org/en/latest/api/#requests.Response.
*/
module Response {
/** Gets a reference to the `requests.models.Response` class. */
private API::Node classRef() {
result = API::moduleImport("requests").getMember("models").getMember("Response")
or
result = API::moduleImport("requests").getMember("Response")
}
/**
* A source of instances of `requests.models.Response`, extend this class to model new instances.
*
* This can include instantiations of the class, return values from function
* calls, or a special parameter that will be set when functions are called by an external
* library.
*
* Use the predicate `Response::instance()` to get references to instances of `requests.models.Response`.
*/
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
/** A direct instantiation of `requests.models.Response`. */
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
ClassInstantiation() { this = classRef().getACall() }
}
/** Return value from making a reuqest. */
private class RequestReturnValue extends InstanceSource, DataFlow::Node {
RequestReturnValue() { this = any(OutgoingRequestCall c) }
}
/** Gets a reference to an instance of `requests.models.Response`. */
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
t.start() and
result instanceof InstanceSource
or
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
}
/** Gets a reference to an instance of `requests.models.Response`. */
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
/**
* Taint propagation for `requests.models.Response`.
*/
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
InstanceTaintSteps() { this = "requests.models.Response" }
override DataFlow::Node getInstance() { result = instance() }
override string getAttributeName() {
result in ["text", "content", "raw", "links", "cookies", "headers"]
}
override string getMethodName() { result in ["json", "iter_content", "iter_lines"] }
override string getAsyncMethodName() { none() }
}
/** An attribute read that is a file-like instance. */
private class FileLikeInstances extends Stdlib::FileLikeObject::InstanceSource {
FileLikeInstances() {
this.(DataFlow::AttrRead).getObject() = instance() and
this.(DataFlow::AttrRead).getAttributeName() = "raw"
}
}
}
}

View File

@@ -2091,6 +2091,201 @@ private module StdlibPrivate {
}
}
// ---------------------------------------------------------------------------
// http.client (Python 3)
// httplib (Python 2)
// ---------------------------------------------------------------------------
/**
* Provides models for the `http.client.HTTPConnection` and `HTTPSConnection` classes
*
* See
* - https://docs.python.org/3.10/library/http.client.html#http.client.HTTPConnection
* - https://docs.python.org/3.10/library/http.client.html#http.client.HTTPSConnection
* - https://docs.python.org/2.7/library/httplib.html#httplib.HTTPConnection
* - https://docs.python.org/2.7/library/httplib.html#httplib.HTTPSConnection
*/
module HTTPConnection {
/** Gets a reference to the `http.client.HTTPConnection` class. */
private API::Node classRef() {
exists(string className | className in ["HTTPConnection", "HTTPSConnection"] |
// Python 3
result = API::moduleImport("http").getMember("client").getMember(className)
or
// Python 2
result = API::moduleImport("httplib").getMember(className)
or
result =
API::moduleImport("six").getMember("moves").getMember("http_client").getMember(className)
)
}
/**
* A source of instances of `http.client.HTTPConnection`, extend this class to model new instances.
*
* This can include instantiations of the class, return values from function
* calls, or a special parameter that will be set when functions are called by an external
* library.
*
* Use the predicate `HTTPConnection::instance()` to get references to instances of `http.client.HTTPConnection`.
*/
abstract class InstanceSource extends DataFlow::LocalSourceNode {
/** Gets the argument that specified the host, if any. */
abstract DataFlow::Node getHostArgument();
}
/** A direct instantiation of `http.client.HTTPConnection`. */
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
ClassInstantiation() { this = classRef().getACall() }
override DataFlow::Node getHostArgument() {
result in [this.getArg(0), this.getArgByName("host")]
}
}
/**
* Gets a reference to an instance of `http.client.HTTPConnection`,
* that was instantiated with host argument `hostArg`.
*/
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t, DataFlow::Node hostArg) {
t.start() and
hostArg = result.(InstanceSource).getHostArgument()
or
exists(DataFlow::TypeTracker t2 | result = instance(t2, hostArg).track(t2, t))
}
/**
* Gets a reference to an instance of `http.client.HTTPConnection`,
* that was instantiated with host argument `hostArg`.
*/
DataFlow::Node instance(DataFlow::Node hostArg) {
instance(DataFlow::TypeTracker::end(), hostArg).flowsTo(result)
}
/** A method call on a HTTPConnection that sends off a request */
private class RequestCall extends HTTP::Client::Request::Range, DataFlow::MethodCallNode {
RequestCall() { this.calls(instance(_), ["request", "_send_request", "putrequest"]) }
DataFlow::Node getUrlArg() { result in [this.getArg(1), this.getArgByName("url")] }
override DataFlow::Node getAUrlPart() {
result = this.getUrlArg()
or
this.getObject() = instance(result)
}
override string getFramework() { result = "http.client.HTTP[S]Connection" }
override predicate disablesCertificateValidation(
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
) {
// TODO: Proper alerting of insecure verification settings on SSLContext.
// Because that is not restricted to HTTP[S]Connection usage, we need something
// more general, and I would like to tackle that in future PR.
none()
}
}
/** A call to the `getresponse` method. */
private class HttpConnectionGetResponseCall extends DataFlow::MethodCallNode,
HTTPResponse::InstanceSource {
HttpConnectionGetResponseCall() { this.calls(instance(_), "getresponse") }
}
/**
* Extra taint propagation for `http.client.HTTPConnection`,
* to ensure that responses to user-controlled URL are tainted.
*/
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// constructor
exists(InstanceSource instanceSource |
nodeFrom = instanceSource.getHostArgument() and
nodeTo = instanceSource
)
or
// a request method
exists(RequestCall call |
nodeFrom = call.getUrlArg() and
nodeTo.(DataFlow::PostUpdateNode).getPreUpdateNode() = call.getObject()
)
or
// `getresponse` call
exists(HttpConnectionGetResponseCall call |
nodeFrom = call.getObject() and
nodeTo = call
)
}
}
}
/**
* Provides models for the `http.client.HTTPResponse` class
*
* See
* - https://docs.python.org/3.10/library/http.client.html#httpresponse-objects
* - https://docs.python.org/3/library/http.client.html#http.client.HTTPResponse.
*/
module HTTPResponse {
/** Gets a reference to the `http.client.HTTPResponse` class. */
private API::Node classRef() {
result = API::moduleImport("http").getMember("client").getMember("HTTPResponse")
}
/**
* A source of instances of `http.client.HTTPResponse`, extend this class to model new instances.
*
* A `http.client.HTTPResponse` is itself a file-like object.
*
* This can include instantiations of the class, return values from function
* calls, or a special parameter that will be set when functions are called by an external
* library.
*
* Use the predicate `HTTPResponse::instance()` to get references to instances of `http.client.HTTPResponse`.
*/
abstract class InstanceSource extends Stdlib::FileLikeObject::InstanceSource,
DataFlow::LocalSourceNode { }
/** A direct instantiation of `http.client.HTTPResponse`. */
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
ClassInstantiation() { this = classRef().getACall() }
}
/** Gets a reference to an instance of `http.client.HTTPResponse`. */
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
t.start() and
result instanceof InstanceSource
or
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
}
/** Gets a reference to an instance of `http.client.HTTPResponse`. */
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
/**
* Taint propagation for `http.client.HTTPResponse`.
*/
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
InstanceTaintSteps() { this = "http.client.HTTPResponse" }
override DataFlow::Node getInstance() { result = instance() }
override string getAttributeName() { result in ["headers", "msg", "reason", "url"] }
override string getMethodName() { result in ["getheader", "getheaders", "info", "geturl",] }
override string getAsyncMethodName() { none() }
}
/** An attribute read that is a HTTPMessage instance. */
private class HTTPMessageInstances extends Stdlib::HTTPMessage::InstanceSource {
HTTPMessageInstances() {
this.(DataFlow::AttrRead).accesses(instance(), ["headers", "msg"])
or
this.(DataFlow::MethodCallNode).calls(instance(), "info")
}
}
}
// ---------------------------------------------------------------------------
// sqlite3
// ---------------------------------------------------------------------------

View File

@@ -0,0 +1,84 @@
/**
* Provides a taint-tracking configuration for detecting "Server-side request forgery" vulnerabilities.
*
* Note, for performance reasons: only import this file if
* `ServerSideRequestForgery::Configuration` is needed, otherwise
* `ServerSideRequestForgeryCustomizations` should be imported instead.
*/
private import python
import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.TaintTracking
import semmle.python.Concepts
/**
* Provides a taint-tracking configuration for detecting "Server-side request forgery" vulnerabilities.
*
* This configuration has a sanitizer to limit results to cases where attacker has full control of URL.
* See `PartialServerSideRequestForgery` for a variant without this requirement.
*
* You should use the `partOfFullyControlledRequest` to only select results where all
* URL parts are fully controlled.
*/
module FullServerSideRequestForgery {
import ServerSideRequestForgeryCustomizations::ServerSideRequestForgery
/**
* A taint-tracking configuration for detecting "Server-side request forgery" vulnerabilities.
*/
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "FullServerSideRequestForgery" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
override predicate isSanitizer(DataFlow::Node node) {
node instanceof Sanitizer
or
node instanceof FullUrlControlSanitizer
}
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
guard instanceof SanitizerGuard
}
}
}
/**
* Holds if all URL parts of `request` is fully user controlled.
*/
predicate fullyControlledRequest(HTTP::Client::Request request) {
exists(FullServerSideRequestForgery::Configuration fullConfig |
forall(DataFlow::Node urlPart | urlPart = request.getAUrlPart() |
fullConfig.hasFlow(_, urlPart)
)
)
}
/**
* Provides a taint-tracking configuration for detecting "Server-side request forgery" vulnerabilities.
*
* This configuration has results, even when the attacker does not have full control over the URL.
* See `FullServerSideRequestForgery` for variant that has this requirement.
*/
module PartialServerSideRequestForgery {
import ServerSideRequestForgeryCustomizations::ServerSideRequestForgery
/**
* A taint-tracking configuration for detecting "Server-side request forgery" vulnerabilities.
*/
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "PartialServerSideRequestForgery" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
guard instanceof SanitizerGuard
}
}
}

View File

@@ -0,0 +1,143 @@
/**
* Provides default sources, sinks and sanitizers for detecting
* "Server-side request forgery"
* vulnerabilities, as well as extension points for adding your own.
*/
private import python
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.Concepts
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.dataflow.new.BarrierGuards
/**
* Provides default sources, sinks and sanitizers for detecting
* "Server-side request forgery"
* vulnerabilities, as well as extension points for adding your own.
*/
module ServerSideRequestForgery {
/**
* A data flow source for "Server-side request forgery" vulnerabilities.
*/
abstract class Source extends DataFlow::Node { }
/**
* A data flow sink for "Server-side request forgery" vulnerabilities.
*/
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.
*/
abstract class Sanitizer extends DataFlow::Node { }
/**
* A sanitizer for "Server-side request forgery" vulnerabilities,
* that ensures the attacker does not have full control of the URL. (that is, might
* still be able to control path or query parameters).
*/
abstract class FullUrlControlSanitizer extends DataFlow::Node { }
/**
* A sanitizer guard for "Server-side request forgery" vulnerabilities.
*/
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
/**
* A source of remote user input, considered as a flow source.
*/
class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { }
/** The URL of an HTTP request, considered as a sink. */
class HttpRequestUrlAsSink extends Sink {
HTTP::Client::Request req;
HttpRequestUrlAsSink() {
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 }
}
/**
* A comparison with a constant string, considered as a sanitizer-guard.
*/
class StringConstCompareAsSanitizerGuard extends SanitizerGuard, StringConstCompare { }
/**
* A string construction (concat, format, f-string) where the left side is not
* user-controlled.
*
* For all of these cases, we try to allow `http://` or `https://` on the left side
* since that will still allow full URL control.
*/
class StringConstructionAsFullUrlControlSanitizer extends FullUrlControlSanitizer {
StringConstructionAsFullUrlControlSanitizer() {
// string concat
exists(BinaryExprNode add |
add.getOp() instanceof Add and
add.getRight() = this.asCfgNode() and
not add.getLeft().getNode().(StrConst).getText().toLowerCase() in ["http://", "https://"]
)
or
// % formatting
exists(BinaryExprNode fmt |
fmt.getOp() instanceof Mod and
fmt.getRight() = this.asCfgNode() and
// detecting %-formatting is not super easy, so we simplify it to only handle
// when there is a **single** substitution going on.
not fmt.getLeft().getNode().(StrConst).getText().regexpMatch("^(?i)https?://%s[^%]*$")
)
or
// arguments to a format call
exists(DataFlow::MethodCallNode call, string httpPrefixRe |
httpPrefixRe = "^(?i)https?://(?:(\\{\\})|\\{([0-9]+)\\}|\\{([^0-9].*)\\}).*$"
|
call.getMethodName() = "format" and
(
if call.getObject().asExpr().(StrConst).getText().regexpMatch(httpPrefixRe)
then
exists(string text | text = call.getObject().asExpr().(StrConst).getText() |
// `http://{}...`
exists(text.regexpCapture(httpPrefixRe, 1)) and
this in [call.getArg(any(int i | i >= 1)), call.getArgByName(_)]
or
// `http://{123}...`
exists(int safeArgIndex | safeArgIndex = text.regexpCapture(httpPrefixRe, 2).toInt() |
this in [call.getArg(any(int i | i != safeArgIndex)), call.getArgByName(_)]
)
or
// `http://{abc}...`
exists(string safeArgName | safeArgName = text.regexpCapture(httpPrefixRe, 3) |
this in [call.getArg(_), call.getArgByName(any(string s | s != safeArgName))]
)
)
else this in [call.getArg(_), call.getArgByName(_)]
)
)
or
// f-string
exists(Fstring fstring |
if fstring.getValue(0).(StrConst).getText().toLowerCase() in ["http://", "https://"]
then fstring.getValue(any(int i | i >= 2)) = this.asExpr()
else fstring.getValue(any(int i | i >= 1)) = this.asExpr()
)
}
}
}

View File

@@ -0,0 +1,11 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<overview>
<include src="ServerSideRequestForgery-start.inc.qhelp" />
<!-- query specific -->
<p>This query covers full SSRF, to find partial SSRF use the <code>py/partial-ssrf</code> query.</p>
</overview>
<include src="ServerSideRequestForgery-end.inc.qhelp" />
</qhelp>

View File

@@ -0,0 +1,25 @@
/**
* @name Full server-side request forgery
* @description Making a network request to a URL that is fully user-controlled allows for request forgery attacks.
* @kind path-problem
* @problem.severity error
* @security-severity 9.1
* @precision high
* @id py/full-ssrf
* @tags security
* external/cwe/cwe-918
*/
import python
import semmle.python.security.dataflow.ServerSideRequestForgery
import DataFlow::PathGraph
from
FullServerSideRequestForgery::Configuration fullConfig, DataFlow::PathNode source,
DataFlow::PathNode sink, HTTP::Client::Request request
where
request = sink.getNode().(FullServerSideRequestForgery::Sink).getRequest() and
fullConfig.hasFlowPath(source, sink) and
fullyControlledRequest(request)
select request, source, sink, "The full URL of this request depends on $@.", source.getNode(),
"a user-provided value"

View File

@@ -0,0 +1,11 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<overview>
<include src="ServerSideRequestForgery-start.inc.qhelp" />
<!-- query specific -->
<p>This query covers partial SSRF, to find full SSRF use the <code>py/full-ssrf</code> query.</p>
</overview>
<include src="ServerSideRequestForgery-end.inc.qhelp" />
</qhelp>

View File

@@ -0,0 +1,25 @@
/**
* @name Partial server-side request forgery
* @description Making a network request to a URL that is partially user-controlled allows for request forgery attacks.
* @kind path-problem
* @problem.severity error
* @security-severity 9.1
* @precision medium
* @id py/partial-ssrf
* @tags security
* external/cwe/cwe-918
*/
import python
import semmle.python.security.dataflow.ServerSideRequestForgery
import DataFlow::PathGraph
from
PartialServerSideRequestForgery::Configuration partialConfig, DataFlow::PathNode source,
DataFlow::PathNode sink, HTTP::Client::Request request
where
request = sink.getNode().(PartialServerSideRequestForgery::Sink).getRequest() and
partialConfig.hasFlowPath(source, sink) and
not fullyControlledRequest(request)
select request, source, sink, "Part of the URL of this request depends on $@.", source.getNode(),
"a user-provided value"

View File

@@ -0,0 +1,47 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<recommendation>
<p>To guard against SSRF attacks you should avoid putting user-provided input directly
into a request URL. Instead, either maintain a list of authorized URLs on the server and choose
from that list based on the input provided, or perform proper validation of the input.
</p>
</recommendation>
<example>
<p>The following example shows code vulnerable to a full SSRF attack, because it
uses untrusted input (HTTP request parameter) directly to construct a URL. By using
<code>evil.com#</code> as the <code>target</code> value, the requested URL will be
<code>https://evil.com#.example.com/data/</code>. It also shows how to remedy the
problem by using the user input select a known fixed string.
</p>
<sample src="examples/ServerSideRequestForgery_full.py" />
</example>
<example>
<p>
The following example shows code vulnerable to a partial SSRF attack, because it
uses untrusted input (HTTP request parameter) directly to construct a URL. By
using <code>../transfer-funds-to/123?amount=456</code> as the
<code>user_id</code> value, the requested URL will be
<code>https://api.example.com/transfer-funds-to/123?amount=456</code>. It also
shows how to remedy the problem by validating the input.
</p>
<sample src="examples/ServerSideRequestForgery_partial.py" />
</example>
<references>
<li>
<a href="https://owasp.org/www-community/attacks/Server_Side_Request_Forgery">OWASP SSRF article</a>
</li>
<li>
<a href="https://portswigger.net/web-security/ssrf">PortSwigger SSRF article</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,31 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<fragment>
<p>Directly incorporating user input into an HTTP request without validating the input
can facilitate server-side request forgery (SSRF) attacks. In these attacks, the
request may be changed, directed at a different server, or via a different
protocol. This can allow the attacker to obtain sensitive information or perform
actions with escalated privilege.
</p>
<p>
We make a distinctions between how much of the URL an attacker can control:
</p>
<ul>
<li><b>Full SSRF</b>: where the full URL can be controlled.</li>
<li><b>Partial SSRF</b>: where only part of the URL can be controlled, such as the
path component of a URL to a hardcoded domain.</li>
</ul>
<p></p>
<p>
Partial control of a URL is often much harder to exploit. Therefore we have created a
separate query for each of these.
</p>
</fragment>
</qhelp>

View File

@@ -0,0 +1,15 @@
import requests
from flask import Flask, request
app = Flask(__name__)
@app.route("/full_ssrf")
def full_ssrf():
target = request.args["target"]
# BAD: user has full control of URL
resp = request.get("https://" + target + ".example.com/data/")
# GOOD: `subdomain` is controlled by the server.
subdomain = "europe" if target == "EU" else "world"
resp = request.get("https://" + subdomain + ".example.com/data/")

View File

@@ -0,0 +1,15 @@
import requests
from flask import Flask, request
app = Flask(__name__)
@app.route("/partial_ssrf")
def partial_ssrf():
user_id = request.args["user_id"]
# BAD: user can fully control the path component of the URL
resp = requests.get("https://api.example.com/user_info/" + user_id)
if user_id.isalnum():
# GOOD: user_id is restricted to be alpha-numeric, and cannot alter path component of URL
resp = requests.get("https://api.example.com/user_info/" + user_id)

View File

@@ -475,3 +475,31 @@ class CryptographicOperationTest extends InlineExpectationsTest {
)
}
}
class HttpClientRequestTest extends InlineExpectationsTest {
HttpClientRequestTest() { this = "HttpClientRequestTest" }
override string getARelevantTag() {
result in ["clientRequestUrlPart", "clientRequestCertValidationDisabled"]
}
override predicate hasActualResult(Location location, string element, string tag, string value) {
exists(location.getFile().getRelativePath()) and
exists(HTTP::Client::Request req, DataFlow::Node url |
url = req.getAUrlPart() and
location = url.getLocation() and
element = url.toString() and
value = prettyNodeForInlineTest(url) and
tag = "clientRequestUrlPart"
)
or
exists(location.getFile().getRelativePath()) and
exists(HTTP::Client::Request req |
req.disablesCertificateValidation(_, _) and
location = req.getLocation() and
element = req.toString() and
value = "" and
tag = "clientRequestCertValidationDisabled"
)
}
}

View File

@@ -0,0 +1,2 @@
import python
import experimental.meta.ConceptsTest

View File

@@ -0,0 +1,3 @@
argumentToEnsureNotTaintedNotMarkedAsSpurious
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
failures

View File

@@ -0,0 +1 @@
import experimental.meta.InlineTaintTest

View File

@@ -0,0 +1,55 @@
import requests
from flask import Flask, request
app = Flask(__name__)
@app.route("/taint_test") # $ routeSetup="/taint_test"
def test_taint(): # $ requestHandler
url = request.args['untrusted_input']
# response from a request to a user-controlled URL should be considered
# user-controlled as well.
resp = requests.get(url) # $ clientRequestUrlPart=url
requests.Response
requests.models.Response
ensure_tainted(
url, # $ tainted
# see https://docs.python-requests.org/en/latest/api/#requests.Response
resp, # $ tainted
resp.text, # $ tainted
resp.content, # $ tainted
resp.json(), # $ tainted
# file-like
resp.raw, # $ tainted
resp.raw.read(), # $ tainted
resp.links, # $ tainted
resp.links['key'], # $ tainted
resp.links.get('key'), # $ tainted
resp.cookies, # $ tainted
resp.cookies['key'], # $ tainted
resp.cookies.get('key'), # $ tainted
resp.headers, # $ tainted
resp.headers['key'], # $ tainted
resp.headers.get('key'), # $ tainted
)
for content_chunk in resp.iter_content():
ensure_tainted(content_chunk) # $ tainted
for line in resp.iter_lines():
ensure_tainted(line) # $ tainted
# for now, we don't assume that the response to ANY outgoing request is a remote
# flow source, since this could lead to FPs.
# TODO: investigate whether we should consider this a remote flow source.
trusted_url = "https://internal-api-that-i-trust.com"
resp = requests.get(trusted_url) # $ clientRequestUrlPart=trusted_url
ensure__not_tainted(resp)

View File

@@ -0,0 +1,50 @@
import requests
resp = requests.get("url") # $ clientRequestUrlPart="url"
resp = requests.get(url="url") # $ clientRequestUrlPart="url"
resp = requests.request("GET", "url") # $ clientRequestUrlPart="url"
with requests.Session() as session:
resp = session.get("url") # $ clientRequestUrlPart="url"
resp = session.request(method="GET", url="url") # $ clientRequestUrlPart="url"
s = requests.Session()
resp = s.get("url") # $ clientRequestUrlPart="url"
s = requests.session()
resp = s.get("url") # $ clientRequestUrlPart="url"
# test full import path for Session
with requests.sessions.Session() as session:
resp = session.get("url") # $ clientRequestUrlPart="url"
# Low level access
req = requests.Request("GET", "url") # $ MISSING: clientRequestUrlPart="url"
resp = s.send(req.prepare())
# other methods than GET
resp = requests.post("url") # $ clientRequestUrlPart="url"
resp = requests.patch("url") # $ clientRequestUrlPart="url"
resp = requests.options("url") # $ clientRequestUrlPart="url"
# ==============================================================================
# Disabling certificate validation
# ==============================================================================
resp = requests.get("url", verify=False) # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
def make_get(verify_arg):
resp = requests.get("url", verify=verify_arg) # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
make_get(False)
with requests.Session() as session:
# see https://github.com/psf/requests/blob/39d0fdd9096f7dceccbc8f82e1eda7dd64717a8e/requests/sessions.py#L621
session.verify = False
resp = session.get("url") # $ clientRequestUrlPart="url" MISSING: clientRequestCertValidationDisabled
resp = session.get("url", verify=True) # $ clientRequestUrlPart="url"
req = requests.Request("GET", "url") # $ MISSING: clientRequestUrlPart="url"
resp = session.send(req.prepare()) # $ MISSING: clientRequestCertValidationDisabled

View File

@@ -0,0 +1,170 @@
import sys
import ssl
PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] == 3
if PY2:
from httplib import HTTPConnection, HTTPSConnection
if PY3:
from http.client import HTTPConnection, HTTPSConnection
# NOTE: the URL may be relative to host, or may be full URL.
conn = HTTPConnection("example.com") # $ clientRequestUrlPart="example.com"
conn.request("GET", "/") # $ clientRequestUrlPart="/"
url = "http://example.com/"
conn.request("GET", url) # $ clientRequestUrlPart=url
# kwargs
conn = HTTPConnection(host="example.com") # $ clientRequestUrlPart="example.com"
conn.request(method="GET", url="/") # $ clientRequestUrlPart="/"
# using internal method... you shouldn't but you can
conn._send_request("GET", "url", body=None, headers={}, encode_chunked=False) # $ clientRequestUrlPart="url"
# low level sending of request
conn.putrequest("GET", "url") # $ clientRequestUrlPart="url"
conn.putheader("X-Foo", "value")
conn.endheaders(message_body=None)
# HTTPS
conn = HTTPSConnection("host") # $ clientRequestUrlPart="host"
conn.request("GET", "url") # $ clientRequestUrlPart="url"
# six aliases
import six
conn = six.moves.http_client.HTTPConnection("host") # $ clientRequestUrlPart="host"
conn.request("GET", "url") # $ clientRequestUrlPart="url"
conn = six.moves.http_client.HTTPSConnection("host") # $ clientRequestUrlPart="host"
conn.request("GET", "url") # $ clientRequestUrlPart="url"
# ==============================================================================
# Certificate validation disabled
# ==============================================================================
# default SSL context is the one given by `_create_default_https_context`
context = ssl._create_default_https_context()
assert context.check_hostname == True
assert context.verify_mode == ssl.CERT_REQUIRED
conn = HTTPSConnection("host", context=context) # $ clientRequestUrlPart="host"
conn.request("GET", "url") # $ clientRequestUrlPart="url"
# `_create_default_https_context` is currently just an alias for `create_default_context`
# which creates a context for SERVER_AUTH purpose.
context = ssl.create_default_context()
assert context.check_hostname == True
assert context.verify_mode == ssl.CERT_REQUIRED
conn = HTTPSConnection("host", context=context) # $ clientRequestUrlPart="host"
conn.request("GET", "url") # $ clientRequestUrlPart="url"
# however, if you supply your own SSLContext, you need to set it manually
context = ssl.SSLContext()
assert context.check_hostname == False
assert context.verify_mode == ssl.CERT_NONE
conn = HTTPSConnection("host", context=context) # $ clientRequestUrlPart="host"
conn.request("GET", "url") # $ clientRequestUrlPart="url" MISSING: clientRequestCertValidationDisabled
# and if you misunderstood whether to use server/client in the purpose, you will also
# get a context without hostname verification.
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
assert context.check_hostname == False
assert context.verify_mode == ssl.CERT_NONE
conn = HTTPSConnection("host", context=context) # $ clientRequestUrlPart="host"
conn.request("GET", "url") # $ clientRequestUrlPart="url" MISSING: clientRequestCertValidationDisabled
# NOTICE that current documentation says
#
# > Enabling hostname checking automatically sets verify_mode from CERT_NONE to
# > CERT_REQUIRED. It cannot be set back to CERT_NONE as long as hostname checking is
# > enabled.
# - https://docs.python.org/3.10/library/ssl.html#ssl.SSLContext.check_hostname
context = ssl.SSLContext()
context.check_hostname = True
assert context.verify_mode == ssl.CERT_REQUIRED
conn = HTTPSConnection("host", context=context) # $ clientRequestUrlPart="host"
conn.request("GET", "url") # $ clientRequestUrlPart="url"
# only setting verify_mode is not enough, since check_hostname is not enabled
context = ssl.SSLContext()
context.verify_mode = ssl.CERT_REQUIRED
assert context.check_hostname == False
conn = HTTPSConnection("host", context=context) # $ clientRequestUrlPart="host"
conn.request("GET", "url") # $ clientRequestUrlPart="url" MISSING: clientRequestCertValidationDisabled
# ==============================================================================
# taint test
# ==============================================================================
from flask import request
def taint_test():
host = request.args['host']
url = request.args['url']
conn = HTTPConnection(host) # $ clientRequestUrlPart=host
conn.request("GET", url) # $ clientRequestUrlPart=url
resp = conn.getresponse()
ensure_tainted(
# see
# https://docs.python.org/3.10/library/http.client.html#httpresponse-objects
# https://docs.python.org/3/library/http.client.html#http.client.HTTPResponse
# a HTTPResponse itself is file-like
resp, # $ tainted
resp.read(), # $ tainted
resp.getheader("name"), # $ tainted
resp.getheaders(), # $ tainted
# http.client.HTTPMessage
resp.headers, # $ tainted
resp.headers.get_all(), # $ tainted
# Alias for .headers
# http.client.HTTPMessage
resp.msg, # $ tainted
resp.msg.get_all(), # $ tainted
# Alias for .headers
resp.info(), # $ tainted
resp.info().get_all(), # $ tainted
# although this would usually be the textual version of the status
# ("OK" for 200), it is possible to put your own evil data in here.
resp.reason, # $ tainted
# the URL of the recourse that was visited, if redirects were followed.
# I don't see any reason this could not contain evil data.
resp.url, # $ tainted
resp.geturl(), # $ tainted
)
ensure_not_tainted(
resp.status,
resp.code,
resp.getcode(),
)
# check that only setting either host/url is enough to propagate taint
conn = HTTPConnection("host") # $ clientRequestUrlPart="host"
conn.request("GET", url) # $ clientRequestUrlPart=url
resp = conn.getresponse()
ensure_tainted(resp) # $ tainted
conn = HTTPConnection(host) # $ clientRequestUrlPart=host
conn.request("GET", "url") # $ clientRequestUrlPart="url"
resp = conn.getresponse()
ensure_tainted(resp) # $ tainted

View File

@@ -0,0 +1,274 @@
edges
| full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:7:18:7:29 | ControlFlowNode for Attribute |
| full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:7:18:7:29 | ControlFlowNode for Attribute |
| full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:8:17:8:23 | ControlFlowNode for request |
| full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:8:17:8:28 | ControlFlowNode for Attribute |
| full_partial_test.py:7:18:7:29 | ControlFlowNode for Attribute | full_partial_test.py:7:18:7:48 | ControlFlowNode for Subscript |
| full_partial_test.py:7:18:7:29 | ControlFlowNode for Attribute | full_partial_test.py:7:18:7:48 | ControlFlowNode for Subscript |
| full_partial_test.py:7:18:7:48 | ControlFlowNode for Subscript | full_partial_test.py:10:18:10:27 | ControlFlowNode for user_input |
| full_partial_test.py:7:18:7:48 | ControlFlowNode for Subscript | full_partial_test.py:10:18:10:27 | ControlFlowNode for user_input |
| full_partial_test.py:7:18:7:48 | ControlFlowNode for Subscript | full_partial_test.py:13:18:13:20 | ControlFlowNode for url |
| full_partial_test.py:7:18:7:48 | ControlFlowNode for Subscript | full_partial_test.py:13:18:13:20 | ControlFlowNode for url |
| full_partial_test.py:7:18:7:48 | ControlFlowNode for Subscript | full_partial_test.py:19:18:19:20 | ControlFlowNode for url |
| full_partial_test.py:7:18:7:48 | ControlFlowNode for Subscript | full_partial_test.py:19:18:19:20 | ControlFlowNode for url |
| full_partial_test.py:7:18:7:48 | ControlFlowNode for Subscript | full_partial_test.py:23:18:23:20 | ControlFlowNode for url |
| full_partial_test.py:7:18:7:48 | ControlFlowNode for Subscript | 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:8:17:8:28 | ControlFlowNode for Attribute |
| full_partial_test.py:8:17:8:28 | ControlFlowNode for Attribute | full_partial_test.py:8:17:8:41 | ControlFlowNode for Subscript |
| full_partial_test.py:8:17:8:41 | ControlFlowNode for Subscript | full_partial_test.py:23:18:23:20 | ControlFlowNode for url |
| full_partial_test.py:37:18:37:24 | ControlFlowNode for request | full_partial_test.py:37:18:37:29 | ControlFlowNode for Attribute |
| full_partial_test.py:37:18:37:24 | ControlFlowNode for request | full_partial_test.py:37:18:37:29 | ControlFlowNode for Attribute |
| full_partial_test.py:37:18:37:24 | ControlFlowNode for request | full_partial_test.py:38:17:38:23 | ControlFlowNode for request |
| full_partial_test.py:37:18:37:24 | ControlFlowNode for request | full_partial_test.py:38:17:38:28 | ControlFlowNode for Attribute |
| full_partial_test.py:37:18:37:29 | ControlFlowNode for Attribute | full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript |
| full_partial_test.py:37:18:37:29 | ControlFlowNode for Attribute | full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript |
| full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript | full_partial_test.py:42:18:42:20 | ControlFlowNode for url |
| full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript | full_partial_test.py:42:18:42:20 | ControlFlowNode for url |
| full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript | full_partial_test.py:45:18:45:20 | ControlFlowNode for url |
| full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript | full_partial_test.py:45:18:45:20 | ControlFlowNode for url |
| full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript | full_partial_test.py:48:18:48:20 | ControlFlowNode for url |
| full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript | full_partial_test.py:48:18:48:20 | ControlFlowNode for url |
| full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript | full_partial_test.py:51:18:51:20 | ControlFlowNode for url |
| full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript | full_partial_test.py:51:18:51:20 | ControlFlowNode for url |
| full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript | full_partial_test.py:54:18:54:20 | ControlFlowNode for url |
| full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript | full_partial_test.py:54:18:54:20 | ControlFlowNode for url |
| full_partial_test.py:38:17:38:23 | ControlFlowNode for request | full_partial_test.py:38:17:38:28 | ControlFlowNode for Attribute |
| full_partial_test.py:38:17:38:28 | ControlFlowNode for Attribute | full_partial_test.py:38:17:38:41 | ControlFlowNode for Subscript |
| full_partial_test.py:38:17:38:41 | ControlFlowNode for Subscript | full_partial_test.py:48:18:48:20 | ControlFlowNode for url |
| full_partial_test.py:57:18:57:24 | ControlFlowNode for request | full_partial_test.py:57:18:57:29 | ControlFlowNode for Attribute |
| full_partial_test.py:57:18:57:24 | ControlFlowNode for request | full_partial_test.py:57:18:57:29 | ControlFlowNode for Attribute |
| full_partial_test.py:57:18:57:24 | ControlFlowNode for request | full_partial_test.py:58:17:58:23 | ControlFlowNode for request |
| full_partial_test.py:57:18:57:24 | ControlFlowNode for request | full_partial_test.py:58:17:58:28 | ControlFlowNode for Attribute |
| full_partial_test.py:57:18:57:29 | ControlFlowNode for Attribute | full_partial_test.py:57:18:57:48 | ControlFlowNode for Subscript |
| full_partial_test.py:57:18:57:29 | ControlFlowNode for Attribute | full_partial_test.py:57:18:57:48 | ControlFlowNode for Subscript |
| full_partial_test.py:57:18:57:48 | ControlFlowNode for Subscript | full_partial_test.py:62:18:62:20 | ControlFlowNode for url |
| full_partial_test.py:57:18:57:48 | ControlFlowNode for Subscript | full_partial_test.py:62:18:62:20 | ControlFlowNode for url |
| full_partial_test.py:57:18:57:48 | ControlFlowNode for Subscript | full_partial_test.py:65:18:65:20 | ControlFlowNode for url |
| full_partial_test.py:57:18:57:48 | ControlFlowNode for Subscript | full_partial_test.py:65:18:65:20 | ControlFlowNode for url |
| full_partial_test.py:57:18:57:48 | ControlFlowNode for Subscript | full_partial_test.py:67:38:67:58 | ControlFlowNode for Tuple |
| full_partial_test.py:58:17:58:23 | ControlFlowNode for request | full_partial_test.py:58:17:58:28 | ControlFlowNode for Attribute |
| full_partial_test.py:58:17:58:28 | ControlFlowNode for Attribute | full_partial_test.py:58:17:58:41 | ControlFlowNode for Subscript |
| full_partial_test.py:58:17:58:41 | ControlFlowNode for Subscript | full_partial_test.py:67:38:67:58 | ControlFlowNode for Tuple |
| full_partial_test.py:67:38:67:58 | ControlFlowNode for Tuple | full_partial_test.py:68:18:68:20 | ControlFlowNode for url |
| full_partial_test.py:71:18:71:24 | ControlFlowNode for request | full_partial_test.py:71:18:71:29 | ControlFlowNode for Attribute |
| full_partial_test.py:71:18:71:24 | ControlFlowNode for request | full_partial_test.py:71:18:71:29 | ControlFlowNode for Attribute |
| full_partial_test.py:71:18:71:24 | ControlFlowNode for request | full_partial_test.py:72:17:72:23 | ControlFlowNode for request |
| full_partial_test.py:71:18:71:24 | ControlFlowNode for request | full_partial_test.py:72:17:72:28 | ControlFlowNode for Attribute |
| full_partial_test.py:71:18:71:29 | ControlFlowNode for Attribute | full_partial_test.py:71:18:71:48 | ControlFlowNode for Subscript |
| full_partial_test.py:71:18:71:29 | ControlFlowNode for Attribute | full_partial_test.py:71:18:71:48 | ControlFlowNode for Subscript |
| full_partial_test.py:71:18:71:48 | ControlFlowNode for Subscript | full_partial_test.py:76:18:76:20 | ControlFlowNode for url |
| full_partial_test.py:71:18:71:48 | ControlFlowNode for Subscript | full_partial_test.py:76:18:76:20 | ControlFlowNode for url |
| full_partial_test.py:71:18:71:48 | ControlFlowNode for Subscript | full_partial_test.py:79:18:79:20 | ControlFlowNode for url |
| full_partial_test.py:71:18:71:48 | ControlFlowNode for Subscript | full_partial_test.py:79:18:79:20 | ControlFlowNode for url |
| full_partial_test.py:71:18:71:48 | ControlFlowNode for Subscript | full_partial_test.py:82:18:82:20 | ControlFlowNode for url |
| full_partial_test.py:71:18:71:48 | ControlFlowNode for Subscript | full_partial_test.py:82:18:82:20 | ControlFlowNode for url |
| full_partial_test.py:72:17:72:23 | ControlFlowNode for request | full_partial_test.py:72:17:72:28 | ControlFlowNode for Attribute |
| full_partial_test.py:72:17:72:28 | ControlFlowNode for Attribute | full_partial_test.py:72:17:72:41 | ControlFlowNode for Subscript |
| full_partial_test.py:72:17:72:41 | ControlFlowNode for Subscript | full_partial_test.py:82:18:82:20 | ControlFlowNode for url |
| full_partial_test.py:86:18:86:24 | ControlFlowNode for request | full_partial_test.py:86:18:86:29 | ControlFlowNode for Attribute |
| full_partial_test.py:86:18:86:29 | ControlFlowNode for Attribute | full_partial_test.py:86:18:86:48 | ControlFlowNode for Subscript |
| full_partial_test.py:86:18:86:48 | ControlFlowNode for Subscript | full_partial_test.py:89:18:89:20 | ControlFlowNode for url |
| full_partial_test.py:92:18:92:24 | ControlFlowNode for request | full_partial_test.py:92:18:92:29 | ControlFlowNode for Attribute |
| full_partial_test.py:92:18:92:29 | ControlFlowNode for Attribute | full_partial_test.py:92:18:92:48 | ControlFlowNode for Subscript |
| full_partial_test.py:92:18:92:48 | ControlFlowNode for Subscript | full_partial_test.py:95:18:95:20 | ControlFlowNode for url |
| full_partial_test.py:98:18:98:24 | ControlFlowNode for request | full_partial_test.py:98:18:98:29 | ControlFlowNode for Attribute |
| full_partial_test.py:98:18:98:29 | ControlFlowNode for Attribute | full_partial_test.py:98:18:98:48 | ControlFlowNode for Subscript |
| full_partial_test.py:98:18:98:48 | ControlFlowNode for Subscript | full_partial_test.py:101:18:101:20 | ControlFlowNode for url |
| full_partial_test.py:104:18:104:24 | ControlFlowNode for request | full_partial_test.py:104:18:104:29 | ControlFlowNode for Attribute |
| full_partial_test.py:104:18:104:29 | ControlFlowNode for Attribute | full_partial_test.py:104:18:104:48 | ControlFlowNode for Subscript |
| full_partial_test.py:104:18:104:48 | ControlFlowNode for Subscript | full_partial_test.py:107:18:107:20 | ControlFlowNode for url |
| full_partial_test.py:110:18:110:24 | ControlFlowNode for request | full_partial_test.py:110:18:110:29 | ControlFlowNode for Attribute |
| full_partial_test.py:110:18:110:29 | ControlFlowNode for Attribute | full_partial_test.py:110:18:110:48 | ControlFlowNode for Subscript |
| full_partial_test.py:110:18:110:48 | ControlFlowNode for Subscript | full_partial_test.py:116:18:116:20 | ControlFlowNode for url |
| full_partial_test.py:119:18:119:24 | ControlFlowNode for request | full_partial_test.py:119:18:119:29 | ControlFlowNode for Attribute |
| full_partial_test.py:119:18:119:29 | ControlFlowNode for Attribute | full_partial_test.py:119:18:119:48 | ControlFlowNode for Subscript |
| full_partial_test.py:119:18:119:48 | ControlFlowNode for Subscript | full_partial_test.py:122:18:122:20 | ControlFlowNode for url |
| test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:9:19:9:30 | ControlFlowNode for Attribute |
| test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:9:19:9:30 | ControlFlowNode for Attribute |
| test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:10:19:10:25 | ControlFlowNode for request |
| test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:10:19:10:25 | ControlFlowNode for request |
| test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:10:19:10:30 | ControlFlowNode for Attribute |
| test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:10:19:10:30 | ControlFlowNode for Attribute |
| test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:11:18:11:24 | ControlFlowNode for request |
| test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:11:18:11:29 | ControlFlowNode for Attribute |
| test_http_client.py:9:19:9:30 | ControlFlowNode for Attribute | test_http_client.py:9:19:9:38 | ControlFlowNode for Subscript |
| test_http_client.py:9:19:9:30 | ControlFlowNode for Attribute | test_http_client.py:9:19:9:38 | ControlFlowNode for Subscript |
| test_http_client.py:9:19:9:38 | ControlFlowNode for Subscript | test_http_client.py:13:27:13:37 | ControlFlowNode for unsafe_host |
| test_http_client.py:9:19:9:38 | ControlFlowNode for Subscript | test_http_client.py:13:27:13:37 | ControlFlowNode for unsafe_host |
| test_http_client.py:9:19:9:38 | ControlFlowNode for Subscript | test_http_client.py:18:27:18:37 | ControlFlowNode for unsafe_host |
| test_http_client.py:9:19:9:38 | ControlFlowNode for Subscript | test_http_client.py:18:27:18:37 | ControlFlowNode for unsafe_host |
| test_http_client.py:9:19:9:38 | ControlFlowNode for Subscript | test_http_client.py:25:27:25:37 | ControlFlowNode for unsafe_host |
| test_http_client.py:9:19:9:38 | ControlFlowNode for Subscript | test_http_client.py:25:27:25:37 | ControlFlowNode for unsafe_host |
| test_http_client.py:10:19:10:25 | ControlFlowNode for request | test_http_client.py:10:19:10:30 | ControlFlowNode for Attribute |
| test_http_client.py:10:19:10:25 | ControlFlowNode for request | test_http_client.py:10:19:10:30 | ControlFlowNode for Attribute |
| test_http_client.py:10:19:10:25 | ControlFlowNode for request | test_http_client.py:11:18:11:24 | ControlFlowNode for request |
| test_http_client.py:10:19:10:25 | ControlFlowNode for request | test_http_client.py:11:18:11:29 | ControlFlowNode for Attribute |
| test_http_client.py:10:19:10:30 | ControlFlowNode for Attribute | test_http_client.py:10:19:10:38 | ControlFlowNode for Subscript |
| test_http_client.py:10:19:10:30 | ControlFlowNode for Attribute | test_http_client.py:10:19:10:38 | ControlFlowNode for Subscript |
| test_http_client.py:10:19:10:38 | ControlFlowNode for Subscript | test_http_client.py:14:25:14:35 | ControlFlowNode for unsafe_path |
| test_http_client.py:10:19:10:38 | ControlFlowNode for Subscript | test_http_client.py:14:25:14:35 | ControlFlowNode for unsafe_path |
| test_http_client.py:10:19:10:38 | ControlFlowNode for Subscript | test_http_client.py:19:25:19:35 | ControlFlowNode for unsafe_path |
| test_http_client.py:10:19:10:38 | ControlFlowNode for Subscript | test_http_client.py:19:25:19:35 | ControlFlowNode for unsafe_path |
| test_http_client.py:10:19:10:38 | ControlFlowNode for Subscript | test_http_client.py:29:25:29:35 | ControlFlowNode for unsafe_path |
| test_http_client.py:10:19:10:38 | ControlFlowNode for Subscript | test_http_client.py:29:25:29:35 | ControlFlowNode for unsafe_path |
| test_http_client.py:11:18:11:24 | ControlFlowNode for request | test_http_client.py:11:18:11:29 | ControlFlowNode for Attribute |
| test_http_client.py:11:18:11:29 | ControlFlowNode for Attribute | test_http_client.py:11:18:11:48 | ControlFlowNode for Subscript |
| test_http_client.py:11:18:11:48 | ControlFlowNode for Subscript | test_http_client.py:33:25:33:28 | ControlFlowNode for path |
| test_http_client.py:11:18:11:48 | ControlFlowNode for Subscript | test_http_client.py:37:25:37:28 | ControlFlowNode for path |
| test_requests.py:6:18:6:24 | ControlFlowNode for request | test_requests.py:6:18:6:29 | ControlFlowNode for Attribute |
| test_requests.py:6:18:6:24 | ControlFlowNode for request | test_requests.py:6:18:6:29 | ControlFlowNode for Attribute |
| test_requests.py:6:18:6:29 | ControlFlowNode for Attribute | test_requests.py:6:18:6:48 | ControlFlowNode for Subscript |
| test_requests.py:6:18:6:29 | ControlFlowNode for Attribute | test_requests.py:6:18:6:48 | ControlFlowNode for Subscript |
| test_requests.py:6:18:6:48 | ControlFlowNode for Subscript | test_requests.py:8:18:8:27 | ControlFlowNode for user_input |
| test_requests.py:6:18:6:48 | ControlFlowNode for Subscript | test_requests.py:8:18:8:27 | ControlFlowNode for user_input |
nodes
| full_partial_test.py:7:18:7:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:7:18:7:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:7:18:7:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:7:18:7:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:7:18:7:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:7:18:7:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:8:17:8:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:8:17:8:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:8:17:8:41 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:10:18:10:27 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input |
| full_partial_test.py:10:18:10:27 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input |
| full_partial_test.py:13:18:13:20 | 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:19:18:19:20 | 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:23:18:23:20 | 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:18:37:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:37:18:37:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:37:18:37:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:37:18:37:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:38:17:38:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:38:17:38:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:38:17:38:41 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:42:18:42:20 | 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:45:18:45:20 | 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:48:18:48:20 | 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:51:18:51:20 | 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:54:18:54:20 | 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:18:57:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:57:18:57:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:57:18:57:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:57:18:57:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:57:18:57:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:57:18:57:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:58:17:58:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:58:17:58:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:58:17:58:41 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:62:18:62:20 | 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:65:18:65:20 | 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:38:67:58 | ControlFlowNode for Tuple | semmle.label | ControlFlowNode for Tuple |
| full_partial_test.py:68:18:68:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url |
| full_partial_test.py:71:18:71:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:71:18:71:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:71:18:71:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:71:18:71:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:71:18:71:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:71:18:71:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:72:17:72:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:72:17:72:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:72:17:72:41 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:76:18:76:20 | 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:79:18:79:20 | 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:82:18:82:20 | 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:18:86:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:86:18:86:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:86:18:86:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:89:18:89:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url |
| full_partial_test.py:92:18:92:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:92:18:92:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:92:18:92:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:95:18:95:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url |
| full_partial_test.py:98:18:98:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:98:18:98:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:98:18:98:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:101:18:101:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url |
| full_partial_test.py:104:18:104:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:104:18:104:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:104:18:104:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:107:18:107:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url |
| full_partial_test.py:110:18:110:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:110:18:110:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:110:18:110:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:116:18:116:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url |
| full_partial_test.py:119:18:119:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:119:18:119:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:119:18:119:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:122:18:122:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url |
| test_http_client.py:9:19:9:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| test_http_client.py:9:19:9:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| test_http_client.py:9:19:9:30 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| test_http_client.py:9:19:9:30 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| test_http_client.py:9:19:9:38 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test_http_client.py:9:19:9:38 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test_http_client.py:10:19:10:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| test_http_client.py:10:19:10:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| test_http_client.py:10:19:10:30 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| test_http_client.py:10:19:10:30 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| test_http_client.py:10:19:10:38 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test_http_client.py:10:19:10:38 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test_http_client.py:11:18:11:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| test_http_client.py:11:18:11:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| test_http_client.py:11:18:11:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test_http_client.py:13:27:13:37 | ControlFlowNode for unsafe_host | semmle.label | ControlFlowNode for unsafe_host |
| 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: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: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: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: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:29:25:29:35 | ControlFlowNode for unsafe_path | semmle.label | ControlFlowNode for unsafe_path |
| test_http_client.py:33:25:33:28 | 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_requests.py:6:18:6:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| test_requests.py:6:18:6:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| test_requests.py:6:18:6:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| test_requests.py:6:18:6:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| test_requests.py:6:18:6:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test_requests.py:6:18:6:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test_requests.py:8:18:8:27 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input |
| test_requests.py:8:18:8:27 | 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: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 |
| 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 | The full 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 | The full 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 | The full 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:37:18:37:24 | ControlFlowNode for request | full_partial_test.py:42:18:42:20 | ControlFlowNode for url | The full URL of this request depends on $@. | full_partial_test.py:37:18:37:24 | ControlFlowNode for request | a user-provided value |
| full_partial_test.py:45:5:45:21 | ControlFlowNode for Attribute() | full_partial_test.py:37:18:37:24 | ControlFlowNode for request | full_partial_test.py:45:18:45:20 | ControlFlowNode for url | The full URL of this request depends on $@. | full_partial_test.py:37:18:37:24 | ControlFlowNode for request | a user-provided value |
| full_partial_test.py:48:5:48:21 | ControlFlowNode for Attribute() | full_partial_test.py:37:18:37:24 | ControlFlowNode for request | full_partial_test.py:48:18:48:20 | ControlFlowNode for url | The full URL of this request depends on $@. | full_partial_test.py:37:18:37:24 | ControlFlowNode for request | a user-provided value |
| full_partial_test.py:51:5:51:21 | ControlFlowNode for Attribute() | full_partial_test.py:37:18:37:24 | ControlFlowNode for request | full_partial_test.py:51:18:51:20 | ControlFlowNode for url | The full URL of this request depends on $@. | full_partial_test.py:37:18:37:24 | ControlFlowNode for request | a user-provided value |
| full_partial_test.py:54:5:54:21 | ControlFlowNode for Attribute() | full_partial_test.py:37:18:37:24 | ControlFlowNode for request | full_partial_test.py:54:18:54:20 | ControlFlowNode for url | The full URL of this request depends on $@. | full_partial_test.py:37:18:37:24 | ControlFlowNode for request | a user-provided value |
| full_partial_test.py:62:5:62:21 | ControlFlowNode for Attribute() | full_partial_test.py:57:18:57:24 | ControlFlowNode for request | full_partial_test.py:62:18:62:20 | ControlFlowNode for url | The full URL of this request depends on $@. | full_partial_test.py:57:18:57:24 | ControlFlowNode for request | a user-provided value |
| full_partial_test.py:65:5:65:21 | ControlFlowNode for Attribute() | full_partial_test.py:57:18:57:24 | ControlFlowNode for request | full_partial_test.py:65:18:65:20 | ControlFlowNode for url | The full URL of this request depends on $@. | full_partial_test.py:57:18:57:24 | ControlFlowNode for request | a user-provided value |
| full_partial_test.py:76:5:76:21 | ControlFlowNode for Attribute() | full_partial_test.py:71:18:71:24 | ControlFlowNode for request | full_partial_test.py:76:18:76:20 | ControlFlowNode for url | The full URL of this request depends on $@. | full_partial_test.py:71:18:71:24 | ControlFlowNode for request | a user-provided value |
| full_partial_test.py:79:5:79:21 | ControlFlowNode for Attribute() | full_partial_test.py:71:18:71:24 | ControlFlowNode for request | full_partial_test.py:79:18:79:20 | ControlFlowNode for url | The full URL of this request depends on $@. | full_partial_test.py:71:18:71:24 | ControlFlowNode for request | a user-provided value |
| full_partial_test.py:82:5:82:21 | ControlFlowNode for Attribute() | full_partial_test.py:71:18:71:24 | ControlFlowNode for request | full_partial_test.py:82:18:82:20 | ControlFlowNode for url | The full URL of this request depends on $@. | full_partial_test.py:71:18:71: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_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 |

View File

@@ -0,0 +1 @@
Security/CWE-918/FullServerSideRequestForgery.ql

View File

@@ -0,0 +1,271 @@
edges
| full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:7:18:7:29 | ControlFlowNode for Attribute |
| full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:7:18:7:29 | ControlFlowNode for Attribute |
| full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:8:17:8:23 | ControlFlowNode for request |
| full_partial_test.py:7:18:7:24 | ControlFlowNode for request | full_partial_test.py:8:17:8:28 | ControlFlowNode for Attribute |
| full_partial_test.py:7:18:7:29 | ControlFlowNode for Attribute | full_partial_test.py:7:18:7:48 | ControlFlowNode for Subscript |
| full_partial_test.py:7:18:7:29 | ControlFlowNode for Attribute | full_partial_test.py:7:18:7:48 | ControlFlowNode for Subscript |
| full_partial_test.py:7:18:7:48 | ControlFlowNode for Subscript | full_partial_test.py:10:18:10:27 | ControlFlowNode for user_input |
| full_partial_test.py:7:18:7:48 | ControlFlowNode for Subscript | full_partial_test.py:10:18:10:27 | ControlFlowNode for user_input |
| full_partial_test.py:7:18:7:48 | ControlFlowNode for Subscript | full_partial_test.py:13:18:13:20 | ControlFlowNode for url |
| full_partial_test.py:7:18:7:48 | ControlFlowNode for Subscript | full_partial_test.py:13:18:13:20 | ControlFlowNode for url |
| full_partial_test.py:7:18:7:48 | ControlFlowNode for Subscript | full_partial_test.py:19:18:19:20 | ControlFlowNode for url |
| full_partial_test.py:7:18:7:48 | ControlFlowNode for Subscript | full_partial_test.py:19:18:19:20 | ControlFlowNode for url |
| full_partial_test.py:7:18:7:48 | ControlFlowNode for Subscript | full_partial_test.py:23:18:23:20 | ControlFlowNode for url |
| full_partial_test.py:7:18:7:48 | ControlFlowNode for Subscript | 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:8:17:8:28 | ControlFlowNode for Attribute |
| full_partial_test.py:8:17:8:28 | ControlFlowNode for Attribute | full_partial_test.py:8:17:8:41 | ControlFlowNode for Subscript |
| full_partial_test.py:8:17:8:41 | ControlFlowNode for Subscript | full_partial_test.py:23:18:23:20 | ControlFlowNode for url |
| full_partial_test.py:37:18:37:24 | ControlFlowNode for request | full_partial_test.py:37:18:37:29 | ControlFlowNode for Attribute |
| full_partial_test.py:37:18:37:24 | ControlFlowNode for request | full_partial_test.py:37:18:37:29 | ControlFlowNode for Attribute |
| full_partial_test.py:37:18:37:24 | ControlFlowNode for request | full_partial_test.py:38:17:38:23 | ControlFlowNode for request |
| full_partial_test.py:37:18:37:24 | ControlFlowNode for request | full_partial_test.py:38:17:38:28 | ControlFlowNode for Attribute |
| full_partial_test.py:37:18:37:29 | ControlFlowNode for Attribute | full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript |
| full_partial_test.py:37:18:37:29 | ControlFlowNode for Attribute | full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript |
| full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript | full_partial_test.py:42:18:42:20 | ControlFlowNode for url |
| full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript | full_partial_test.py:42:18:42:20 | ControlFlowNode for url |
| full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript | full_partial_test.py:45:18:45:20 | ControlFlowNode for url |
| full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript | full_partial_test.py:45:18:45:20 | ControlFlowNode for url |
| full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript | full_partial_test.py:48:18:48:20 | ControlFlowNode for url |
| full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript | full_partial_test.py:48:18:48:20 | ControlFlowNode for url |
| full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript | full_partial_test.py:51:18:51:20 | ControlFlowNode for url |
| full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript | full_partial_test.py:51:18:51:20 | ControlFlowNode for url |
| full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript | full_partial_test.py:54:18:54:20 | ControlFlowNode for url |
| full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript | full_partial_test.py:54:18:54:20 | ControlFlowNode for url |
| full_partial_test.py:38:17:38:23 | ControlFlowNode for request | full_partial_test.py:38:17:38:28 | ControlFlowNode for Attribute |
| full_partial_test.py:38:17:38:28 | ControlFlowNode for Attribute | full_partial_test.py:38:17:38:41 | ControlFlowNode for Subscript |
| full_partial_test.py:38:17:38:41 | ControlFlowNode for Subscript | full_partial_test.py:48:18:48:20 | ControlFlowNode for url |
| full_partial_test.py:57:18:57:24 | ControlFlowNode for request | full_partial_test.py:57:18:57:29 | ControlFlowNode for Attribute |
| full_partial_test.py:57:18:57:24 | ControlFlowNode for request | full_partial_test.py:57:18:57:29 | ControlFlowNode for Attribute |
| full_partial_test.py:57:18:57:24 | ControlFlowNode for request | full_partial_test.py:58:17:58:23 | ControlFlowNode for request |
| full_partial_test.py:57:18:57:24 | ControlFlowNode for request | full_partial_test.py:58:17:58:28 | ControlFlowNode for Attribute |
| full_partial_test.py:57:18:57:29 | ControlFlowNode for Attribute | full_partial_test.py:57:18:57:48 | ControlFlowNode for Subscript |
| full_partial_test.py:57:18:57:29 | ControlFlowNode for Attribute | full_partial_test.py:57:18:57:48 | ControlFlowNode for Subscript |
| full_partial_test.py:57:18:57:48 | ControlFlowNode for Subscript | full_partial_test.py:62:18:62:20 | ControlFlowNode for url |
| full_partial_test.py:57:18:57:48 | ControlFlowNode for Subscript | full_partial_test.py:62:18:62:20 | ControlFlowNode for url |
| full_partial_test.py:57:18:57:48 | ControlFlowNode for Subscript | full_partial_test.py:65:18:65:20 | ControlFlowNode for url |
| full_partial_test.py:57:18:57:48 | ControlFlowNode for Subscript | full_partial_test.py:65:18:65:20 | ControlFlowNode for url |
| full_partial_test.py:57:18:57:48 | ControlFlowNode for Subscript | full_partial_test.py:67:38:67:58 | ControlFlowNode for Tuple |
| full_partial_test.py:58:17:58:23 | ControlFlowNode for request | full_partial_test.py:58:17:58:28 | ControlFlowNode for Attribute |
| full_partial_test.py:58:17:58:28 | ControlFlowNode for Attribute | full_partial_test.py:58:17:58:41 | ControlFlowNode for Subscript |
| full_partial_test.py:58:17:58:41 | ControlFlowNode for Subscript | full_partial_test.py:67:38:67:58 | ControlFlowNode for Tuple |
| full_partial_test.py:67:38:67:58 | ControlFlowNode for Tuple | full_partial_test.py:68:18:68:20 | ControlFlowNode for url |
| full_partial_test.py:71:18:71:24 | ControlFlowNode for request | full_partial_test.py:71:18:71:29 | ControlFlowNode for Attribute |
| full_partial_test.py:71:18:71:24 | ControlFlowNode for request | full_partial_test.py:71:18:71:29 | ControlFlowNode for Attribute |
| full_partial_test.py:71:18:71:24 | ControlFlowNode for request | full_partial_test.py:72:17:72:23 | ControlFlowNode for request |
| full_partial_test.py:71:18:71:24 | ControlFlowNode for request | full_partial_test.py:72:17:72:28 | ControlFlowNode for Attribute |
| full_partial_test.py:71:18:71:29 | ControlFlowNode for Attribute | full_partial_test.py:71:18:71:48 | ControlFlowNode for Subscript |
| full_partial_test.py:71:18:71:29 | ControlFlowNode for Attribute | full_partial_test.py:71:18:71:48 | ControlFlowNode for Subscript |
| full_partial_test.py:71:18:71:48 | ControlFlowNode for Subscript | full_partial_test.py:76:18:76:20 | ControlFlowNode for url |
| full_partial_test.py:71:18:71:48 | ControlFlowNode for Subscript | full_partial_test.py:76:18:76:20 | ControlFlowNode for url |
| full_partial_test.py:71:18:71:48 | ControlFlowNode for Subscript | full_partial_test.py:79:18:79:20 | ControlFlowNode for url |
| full_partial_test.py:71:18:71:48 | ControlFlowNode for Subscript | full_partial_test.py:79:18:79:20 | ControlFlowNode for url |
| full_partial_test.py:71:18:71:48 | ControlFlowNode for Subscript | full_partial_test.py:82:18:82:20 | ControlFlowNode for url |
| full_partial_test.py:71:18:71:48 | ControlFlowNode for Subscript | full_partial_test.py:82:18:82:20 | ControlFlowNode for url |
| full_partial_test.py:72:17:72:23 | ControlFlowNode for request | full_partial_test.py:72:17:72:28 | ControlFlowNode for Attribute |
| full_partial_test.py:72:17:72:28 | ControlFlowNode for Attribute | full_partial_test.py:72:17:72:41 | ControlFlowNode for Subscript |
| full_partial_test.py:72:17:72:41 | ControlFlowNode for Subscript | full_partial_test.py:82:18:82:20 | ControlFlowNode for url |
| full_partial_test.py:86:18:86:24 | ControlFlowNode for request | full_partial_test.py:86:18:86:29 | ControlFlowNode for Attribute |
| full_partial_test.py:86:18:86:29 | ControlFlowNode for Attribute | full_partial_test.py:86:18:86:48 | ControlFlowNode for Subscript |
| full_partial_test.py:86:18:86:48 | ControlFlowNode for Subscript | full_partial_test.py:89:18:89:20 | ControlFlowNode for url |
| full_partial_test.py:92:18:92:24 | ControlFlowNode for request | full_partial_test.py:92:18:92:29 | ControlFlowNode for Attribute |
| full_partial_test.py:92:18:92:29 | ControlFlowNode for Attribute | full_partial_test.py:92:18:92:48 | ControlFlowNode for Subscript |
| full_partial_test.py:92:18:92:48 | ControlFlowNode for Subscript | full_partial_test.py:95:18:95:20 | ControlFlowNode for url |
| full_partial_test.py:98:18:98:24 | ControlFlowNode for request | full_partial_test.py:98:18:98:29 | ControlFlowNode for Attribute |
| full_partial_test.py:98:18:98:29 | ControlFlowNode for Attribute | full_partial_test.py:98:18:98:48 | ControlFlowNode for Subscript |
| full_partial_test.py:98:18:98:48 | ControlFlowNode for Subscript | full_partial_test.py:101:18:101:20 | ControlFlowNode for url |
| full_partial_test.py:104:18:104:24 | ControlFlowNode for request | full_partial_test.py:104:18:104:29 | ControlFlowNode for Attribute |
| full_partial_test.py:104:18:104:29 | ControlFlowNode for Attribute | full_partial_test.py:104:18:104:48 | ControlFlowNode for Subscript |
| full_partial_test.py:104:18:104:48 | ControlFlowNode for Subscript | full_partial_test.py:107:18:107:20 | ControlFlowNode for url |
| full_partial_test.py:110:18:110:24 | ControlFlowNode for request | full_partial_test.py:110:18:110:29 | ControlFlowNode for Attribute |
| full_partial_test.py:110:18:110:29 | ControlFlowNode for Attribute | full_partial_test.py:110:18:110:48 | ControlFlowNode for Subscript |
| full_partial_test.py:110:18:110:48 | ControlFlowNode for Subscript | full_partial_test.py:116:18:116:20 | ControlFlowNode for url |
| full_partial_test.py:119:18:119:24 | ControlFlowNode for request | full_partial_test.py:119:18:119:29 | ControlFlowNode for Attribute |
| full_partial_test.py:119:18:119:29 | ControlFlowNode for Attribute | full_partial_test.py:119:18:119:48 | ControlFlowNode for Subscript |
| full_partial_test.py:119:18:119:48 | ControlFlowNode for Subscript | full_partial_test.py:122:18:122:20 | ControlFlowNode for url |
| test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:9:19:9:30 | ControlFlowNode for Attribute |
| test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:9:19:9:30 | ControlFlowNode for Attribute |
| test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:10:19:10:25 | ControlFlowNode for request |
| test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:10:19:10:25 | ControlFlowNode for request |
| test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:10:19:10:30 | ControlFlowNode for Attribute |
| test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:10:19:10:30 | ControlFlowNode for Attribute |
| test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:11:18:11:24 | ControlFlowNode for request |
| test_http_client.py:9:19:9:25 | ControlFlowNode for request | test_http_client.py:11:18:11:29 | ControlFlowNode for Attribute |
| test_http_client.py:9:19:9:30 | ControlFlowNode for Attribute | test_http_client.py:9:19:9:38 | ControlFlowNode for Subscript |
| test_http_client.py:9:19:9:30 | ControlFlowNode for Attribute | test_http_client.py:9:19:9:38 | ControlFlowNode for Subscript |
| test_http_client.py:9:19:9:38 | ControlFlowNode for Subscript | test_http_client.py:13:27:13:37 | ControlFlowNode for unsafe_host |
| test_http_client.py:9:19:9:38 | ControlFlowNode for Subscript | test_http_client.py:13:27:13:37 | ControlFlowNode for unsafe_host |
| test_http_client.py:9:19:9:38 | ControlFlowNode for Subscript | test_http_client.py:18:27:18:37 | ControlFlowNode for unsafe_host |
| test_http_client.py:9:19:9:38 | ControlFlowNode for Subscript | test_http_client.py:18:27:18:37 | ControlFlowNode for unsafe_host |
| test_http_client.py:9:19:9:38 | ControlFlowNode for Subscript | test_http_client.py:25:27:25:37 | ControlFlowNode for unsafe_host |
| test_http_client.py:9:19:9:38 | ControlFlowNode for Subscript | test_http_client.py:25:27:25:37 | ControlFlowNode for unsafe_host |
| test_http_client.py:10:19:10:25 | ControlFlowNode for request | test_http_client.py:10:19:10:30 | ControlFlowNode for Attribute |
| test_http_client.py:10:19:10:25 | ControlFlowNode for request | test_http_client.py:10:19:10:30 | ControlFlowNode for Attribute |
| test_http_client.py:10:19:10:25 | ControlFlowNode for request | test_http_client.py:11:18:11:24 | ControlFlowNode for request |
| test_http_client.py:10:19:10:25 | ControlFlowNode for request | test_http_client.py:11:18:11:29 | ControlFlowNode for Attribute |
| test_http_client.py:10:19:10:30 | ControlFlowNode for Attribute | test_http_client.py:10:19:10:38 | ControlFlowNode for Subscript |
| test_http_client.py:10:19:10:30 | ControlFlowNode for Attribute | test_http_client.py:10:19:10:38 | ControlFlowNode for Subscript |
| test_http_client.py:10:19:10:38 | ControlFlowNode for Subscript | test_http_client.py:14:25:14:35 | ControlFlowNode for unsafe_path |
| test_http_client.py:10:19:10:38 | ControlFlowNode for Subscript | test_http_client.py:14:25:14:35 | ControlFlowNode for unsafe_path |
| test_http_client.py:10:19:10:38 | ControlFlowNode for Subscript | test_http_client.py:19:25:19:35 | ControlFlowNode for unsafe_path |
| test_http_client.py:10:19:10:38 | ControlFlowNode for Subscript | test_http_client.py:19:25:19:35 | ControlFlowNode for unsafe_path |
| test_http_client.py:10:19:10:38 | ControlFlowNode for Subscript | test_http_client.py:29:25:29:35 | ControlFlowNode for unsafe_path |
| test_http_client.py:10:19:10:38 | ControlFlowNode for Subscript | test_http_client.py:29:25:29:35 | ControlFlowNode for unsafe_path |
| test_http_client.py:11:18:11:24 | ControlFlowNode for request | test_http_client.py:11:18:11:29 | ControlFlowNode for Attribute |
| test_http_client.py:11:18:11:29 | ControlFlowNode for Attribute | test_http_client.py:11:18:11:48 | ControlFlowNode for Subscript |
| test_http_client.py:11:18:11:48 | ControlFlowNode for Subscript | test_http_client.py:33:25:33:28 | ControlFlowNode for path |
| test_http_client.py:11:18:11:48 | ControlFlowNode for Subscript | test_http_client.py:37:25:37:28 | ControlFlowNode for path |
| test_requests.py:6:18:6:24 | ControlFlowNode for request | test_requests.py:6:18:6:29 | ControlFlowNode for Attribute |
| test_requests.py:6:18:6:24 | ControlFlowNode for request | test_requests.py:6:18:6:29 | ControlFlowNode for Attribute |
| test_requests.py:6:18:6:29 | ControlFlowNode for Attribute | test_requests.py:6:18:6:48 | ControlFlowNode for Subscript |
| test_requests.py:6:18:6:29 | ControlFlowNode for Attribute | test_requests.py:6:18:6:48 | ControlFlowNode for Subscript |
| test_requests.py:6:18:6:48 | ControlFlowNode for Subscript | test_requests.py:8:18:8:27 | ControlFlowNode for user_input |
| test_requests.py:6:18:6:48 | ControlFlowNode for Subscript | test_requests.py:8:18:8:27 | ControlFlowNode for user_input |
nodes
| full_partial_test.py:7:18:7:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:7:18:7:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:7:18:7:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:7:18:7:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:7:18:7:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:7:18:7:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:8:17:8:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:8:17:8:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:8:17:8:41 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:10:18:10:27 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input |
| full_partial_test.py:10:18:10:27 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input |
| full_partial_test.py:13:18:13:20 | 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:19:18:19:20 | 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:23:18:23:20 | 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:18:37:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:37:18:37:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:37:18:37:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:37:18:37:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:37:18:37:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:38:17:38:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:38:17:38:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:38:17:38:41 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:42:18:42:20 | 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:45:18:45:20 | 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:48:18:48:20 | 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:51:18:51:20 | 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:54:18:54:20 | 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:18:57:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:57:18:57:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:57:18:57:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:57:18:57:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:57:18:57:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:57:18:57:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:58:17:58:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:58:17:58:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:58:17:58:41 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:62:18:62:20 | 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:65:18:65:20 | 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:38:67:58 | ControlFlowNode for Tuple | semmle.label | ControlFlowNode for Tuple |
| full_partial_test.py:68:18:68:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url |
| full_partial_test.py:71:18:71:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:71:18:71:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:71:18:71:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:71:18:71:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:71:18:71:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:71:18:71:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:72:17:72:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:72:17:72:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:72:17:72:41 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:76:18:76:20 | 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:79:18:79:20 | 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:82:18:82:20 | 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:18:86:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:86:18:86:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:86:18:86:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:89:18:89:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url |
| full_partial_test.py:92:18:92:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:92:18:92:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:92:18:92:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:95:18:95:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url |
| full_partial_test.py:98:18:98:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:98:18:98:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:98:18:98:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:101:18:101:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url |
| full_partial_test.py:104:18:104:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:104:18:104:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:104:18:104:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:107:18:107:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url |
| full_partial_test.py:110:18:110:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:110:18:110:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:110:18:110:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:116:18:116:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url |
| full_partial_test.py:119:18:119:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| full_partial_test.py:119:18:119:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| full_partial_test.py:119:18:119:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| full_partial_test.py:122:18:122:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url |
| test_http_client.py:9:19:9:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| test_http_client.py:9:19:9:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| test_http_client.py:9:19:9:30 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| test_http_client.py:9:19:9:30 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| test_http_client.py:9:19:9:38 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test_http_client.py:9:19:9:38 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test_http_client.py:10:19:10:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| test_http_client.py:10:19:10:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| test_http_client.py:10:19:10:30 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| test_http_client.py:10:19:10:30 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| test_http_client.py:10:19:10:38 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test_http_client.py:10:19:10:38 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test_http_client.py:11:18:11:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| test_http_client.py:11:18:11:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| test_http_client.py:11:18:11:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test_http_client.py:13:27:13:37 | ControlFlowNode for unsafe_host | semmle.label | ControlFlowNode for unsafe_host |
| 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: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: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: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: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:29:25:29:35 | ControlFlowNode for unsafe_path | semmle.label | ControlFlowNode for unsafe_path |
| test_http_client.py:33:25:33:28 | 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_requests.py:6:18:6:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| test_requests.py:6:18:6:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| test_requests.py:6:18:6:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| test_requests.py:6:18:6:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| test_requests.py:6:18:6:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test_requests.py:6:18:6:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test_requests.py:8:18:8:27 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input |
| test_requests.py:8:18:8:27 | 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:57:18:57:24 | ControlFlowNode for request | full_partial_test.py:68:18:68:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:57:18:57:24 | ControlFlowNode for request | a user-provided value |
| full_partial_test.py:68:5:68:21 | ControlFlowNode for Attribute() | full_partial_test.py:58:17:58:23 | ControlFlowNode for request | full_partial_test.py:68:18:68:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:58:17:58:23 | ControlFlowNode for request | a user-provided value |
| full_partial_test.py:89:5:89:21 | ControlFlowNode for Attribute() | full_partial_test.py:86:18:86:24 | ControlFlowNode for request | full_partial_test.py:89:18:89:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:86:18:86:24 | ControlFlowNode for request | a user-provided value |
| full_partial_test.py:95:5:95:21 | ControlFlowNode for Attribute() | full_partial_test.py:92:18:92:24 | ControlFlowNode for request | full_partial_test.py:95:18:95:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:92:18:92:24 | ControlFlowNode for request | a user-provided value |
| full_partial_test.py:101:5:101:21 | ControlFlowNode for Attribute() | full_partial_test.py:98:18:98:24 | ControlFlowNode for request | full_partial_test.py:101:18:101:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:98:18:98:24 | ControlFlowNode for request | a user-provided value |
| full_partial_test.py:107:5:107:21 | ControlFlowNode for Attribute() | full_partial_test.py:104:18:104:24 | ControlFlowNode for request | full_partial_test.py:107:18:107:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:104:18:104:24 | ControlFlowNode for request | a user-provided value |
| full_partial_test.py:116:5:116:21 | ControlFlowNode for Attribute() | full_partial_test.py:110:18:110:24 | ControlFlowNode for request | full_partial_test.py:116:18:116:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:110:18:110:24 | ControlFlowNode for request | a user-provided value |
| full_partial_test.py:122:5:122:21 | ControlFlowNode for Attribute() | full_partial_test.py:119:18:119:24 | ControlFlowNode for request | full_partial_test.py:122:18:122:20 | ControlFlowNode for url | Part of the URL of this request depends on $@. | full_partial_test.py:119:18:119:24 | 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 | 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: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 | 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: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 | 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: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 | 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: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 |

View File

@@ -0,0 +1 @@
Security/CWE-918/PartialServerSideRequestForgery.ql

View File

@@ -0,0 +1,122 @@
from flask import request
import requests
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
url = "https://" + user_input
requests.get(url) # NOT OK -- user has full control
# 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
# 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
# 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
# and out, or just the edges in or out). The sanitizers for full URL control is applied
# on the `fromNode`, since for `"https://{}/{}".format(user_input1, user_input2)` there
# is still a valid taint-step for `user_input1` -- if we made `toNode` a sanitizer that
# would also remove this flow that we actually want. When coupled with use-use flow,
# this means that later uses of a sanitized value will no longer be tainted, so
# `requests.get(user_input2)` would no longer give an alert. To overcome this problem,
# we split these tests into multiple functions, so we do not get this use-use flow, and
# therefore know we are able to see where the sanitizers are applied.
def full_ssrf_format():
user_input = request.args['untrusted_input']
query_val = request.args['query_val']
# using .format
url = "https://{}".format(user_input)
requests.get(url) # NOT OK -- user has full control
url = "https://{}/foo".format(user_input)
requests.get(url) # NOT OK -- user has full control
url = "https://{}/foo?key={}".format(user_input, query_val)
requests.get(url) # NOT OK -- user has full control
url = "https://{x}".format(x=user_input)
requests.get(url) # NOT OK -- user has full control
url = "https://{1}".format(0, user_input)
requests.get(url) # NOT OK -- user has full control
def full_ssrf_percent_format():
user_input = request.args['untrusted_input']
query_val = request.args['query_val']
# using %-formatting
url = "https://%s" % user_input
requests.get(url) # NOT OK -- user has full control
url = "https://%s/foo" % user_input
requests.get(url) # NOT OK -- user has full control
url = "https://%s/foo/key=%s" % (user_input, query_val)
requests.get(url) # NOT OK -- user has full control
def full_ssrf_f_strings():
user_input = request.args['untrusted_input']
query_val = request.args['query_val']
# using f-strings
url = f"https://{user_input}"
requests.get(url) # NOT OK -- user has full control
url = f"https://{user_input}/foo"
requests.get(url) # NOT OK -- user has full control
url = f"https://{user_input}/foo?key={query_val}"
requests.get(url) # NOT OK -- user has full control
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
def partial_ssrf_2():
user_input = request.args['untrusted_input']
url = "https://example.com/" + user_input
requests.get(url) # NOT OK -- user controls path
def partial_ssrf_3():
user_input = request.args['untrusted_input']
url = "https://example.com/" + user_input
requests.get(url) # NOT OK -- user controls path
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
def partial_ssrf_5():
user_input = request.args['untrusted_input']
# this is probably the least interesting one, since it's only the fragment that is
# controlled
url = "https://example.com/foo#%s" % user_input
requests.get(url) # NOT OK -- user contollred fragment
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

View File

@@ -0,0 +1 @@
semmle-extractor-options: --lang=3 --max-import-depth=1

View File

@@ -0,0 +1,37 @@
from flask import Flask, request
from http.client import HTTPConnection
app = Flask(__name__)
@app.route("/ssrf_test") # $ routeSetup="/ssrf_test"
def ssrf_test():
unsafe_host = request.args["host"]
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
# 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
# partial SSRF on SAME connection
conn.request("GET", "/foo") # NOT OK -- user has control of host
# the rest are partial SSRF
conn = HTTPConnection(unsafe_host)
conn.request("GET", "/foo") # NOT OK -- user controlled domain
conn = HTTPConnection("example.com")
conn.request("GET", unsafe_path) # NOT OK -- user controlled path
path = "foo?" + user_input
conn = HTTPConnection("example.com")
conn.request("GET", path) # NOT OK -- user controlled query parameters
path = "foo#" + user_input
conn = HTTPConnection("example.com")
conn.request("GET", path) # NOT OK -- user controlled fragment

View File

@@ -0,0 +1,11 @@
from flask import request
import requests
def ssrf_test():
user_input = request.args['untrusted_input']
requests.get(user_input) # NOT OK -- user has full control
# 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.