From f8fa9030bef3f75a3a5547e66d1d79d748c2bb33 Mon Sep 17 00:00:00 2001
From: Owen Mansel-Chan
Date: Thu, 22 Oct 2026 14:27:06 +0100
Subject: [PATCH] Fix URL redirection validation sanitizers
---
.../2026-10-22-url-redirect-validation.md | 4 +
.../python/frameworks/Stdlib/Urllib.qll | 277 ++++++-
.../ql/src/Security/CWE-601/UrlRedirect.qhelp | 41 +-
.../CWE-601/examples/redirect_good2.py | 14 -
.../2026-10-22-url-redirect-validation.md | 4 +
.../CWE-601-UrlRedirect/UrlRedirect.expected | 640 +++++++++++++++
.../Security/CWE-601-UrlRedirect/test.py | 751 +++++++++++++++++-
7 files changed, 1662 insertions(+), 69 deletions(-)
create mode 100644 python/ql/lib/change-notes/2026-10-22-url-redirect-validation.md
delete mode 100644 python/ql/src/Security/CWE-601/examples/redirect_good2.py
create mode 100644 python/ql/src/change-notes/2026-10-22-url-redirect-validation.md
diff --git a/python/ql/lib/change-notes/2026-10-22-url-redirect-validation.md b/python/ql/lib/change-notes/2026-10-22-url-redirect-validation.md
new file mode 100644
index 000000000000..f9d992f07853
--- /dev/null
+++ b/python/ql/lib/change-notes/2026-10-22-url-redirect-validation.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* URL redirection sanitizers based on `urllib.parse.urlparse` and `urllib.parse.urlsplit` now require checks of the scheme, leading slashes, and browser-ignored characters when the network location may be empty. Membership allowlists are trusted only when immutable or used directly as literals, and hostname attributes are treated as non-empty only when all possible receivers originate from `urlparse` or `urlsplit`.
diff --git a/python/ql/lib/semmle/python/frameworks/Stdlib/Urllib.qll b/python/ql/lib/semmle/python/frameworks/Stdlib/Urllib.qll
index 6b5764e55925..4fccf1338157 100644
--- a/python/ql/lib/semmle/python/frameworks/Stdlib/Urllib.qll
+++ b/python/ql/lib/semmle/python/frameworks/Stdlib/Urllib.qll
@@ -81,47 +81,278 @@ private module Urllib {
*/
module Parse {
/**
- * A call to `urllib.parse.urlparse`.
+ * A call to `urllib.parse.urlparse` or `urllib.parse.urlsplit`.
*/
private DataFlow::CallCfgNode getUrlParseCall() {
- result = API::moduleImport("urllib").getMember("parse").getMember("urlparse").getACall()
+ result =
+ API::moduleImport("urllib")
+ .getMember("parse")
+ .getMember(["urlparse", "urlsplit"])
+ .getACall()
+ }
+
+ /** A URL parser call whose URL argument resolves entirely to string literals. */
+ private DataFlow::CallCfgNode getConstantUrlParseCall() {
+ result = getUrlParseCall() and
+ forex(DataFlow::LocalSourceNode source |
+ source = [result.getArg(0), result.getArgByName(["url", "urlstring"])].getALocalSource()
+ |
+ source.asExpr() instanceof StringLiteral
+ )
+ }
+
+ /** Holds if `e` is a parsed URL hostname, which is either non-empty or `None`. */
+ private predicate isParsedUrlHostname(Expr e) {
+ exists(DataFlow::AttrRead read |
+ read = DataFlow::exprNode(e) and
+ read.getAttributeName() = "hostname" and
+ forex(DataFlow::LocalSourceNode source | source = read.getObject().getALocalSource() |
+ source = getConstantUrlParseCall()
+ )
+ )
+ }
+
+ /** Holds if `e` itself is known not to be `""`. */
+ private predicate sourceExcludesEmptyString(Expr e) {
+ e.(StringLiteral).getText() != ""
+ or
+ isParsedUrlHostname(e)
+ }
+
+ /** Holds if every local source of `e` excludes `""`. */
+ private predicate excludesEmptyString(Expr e) {
+ forex(DataFlow::LocalSourceNode source | source = DataFlow::exprNode(e).getALocalSource() |
+ sourceExcludesEmptyString(source.asExpr())
+ )
+ }
+
+ /** Holds if every local source of `e` is a collection literal, rather than a string. */
+ private predicate membershipChecksCollection(Expr e) {
+ forex(DataFlow::LocalSourceNode source | source = DataFlow::exprNode(e).getALocalSource() |
+ source.asCfgNode() instanceof IterableNode
+ or
+ source.asCfgNode() instanceof DictNode
+ )
}
/**
- * A read of the `netloc` attribute of a parsed URL as returned by `urllib.parse.urlparse`,
- * which is being checked in a way that is relevant for URL redirection vulnerabilities.
+ * Holds if every local source of `e` is an immutable collection literal, or a mutable
+ * collection literal used directly in the membership test, whose elements or keys all exclude
+ * `""`.
*/
- private predicate netlocCheck(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
- exists(DataFlow::CallCfgNode urlParseCall, DataFlow::AttrRead netlocRead |
+ private predicate membershipExcludesEmptyString(Expr e) {
+ forex(DataFlow::LocalSourceNode source | source = DataFlow::exprNode(e).getALocalSource() |
+ exists(Expr collection | collection = source.asExpr() |
+ (collection instanceof List or collection instanceof Tuple or collection instanceof Set) and
+ (collection instanceof Tuple or collection = e) and
+ forall(Expr elt |
+ elt =
+ [
+ collection.(List).getAnElt(), collection.(Tuple).getAnElt(),
+ collection.(Set).getAnElt()
+ ]
+ |
+ excludesEmptyString(elt)
+ )
+ or
+ collection instanceof Dict and
+ collection = e and
+ not collection.(Dict).getAnItem() instanceof DictUnpacking and
+ forall(Expr key | key = collection.(Dict).getAKey() | excludesEmptyString(key))
+ )
+ )
+ }
+
+ /** Holds if every local source of `e` is `""`. */
+ private predicate isEmptyString(Expr e) {
+ forex(DataFlow::LocalSourceNode source | source = DataFlow::exprNode(e).getALocalSource() |
+ source.asExpr().(StringLiteral).getText() = ""
+ )
+ }
+
+ /** Holds if comparing against `e` using `op` cannot accept `""`. */
+ private predicate comparisonExcludesEmptyString(Cmpop op, Expr e) {
+ op.getSymbol() in ["in", "not in"] and membershipExcludesEmptyString(e)
+ or
+ op.getSymbol() in ["==", "is", "!=", "is not"] and excludesEmptyString(e)
+ }
+
+ /** Holds if `read` reads `attribute` from the result of `urlParseCall`. */
+ private predicate parsedUrlAttributeRead(
+ DataFlow::CallCfgNode urlParseCall, DataFlow::AttrRead read, string attribute
+ ) {
+ read.getAttributeName() = attribute and
+ forex(DataFlow::LocalSourceNode source | source = read.getObject().getALocalSource() |
+ source = urlParseCall
+ )
+ }
+
+ /**
+ * Holds if `g` is a falsiness check `not parsed.` on a parsed URL `node`.
+ */
+ private predicate parsedUrlAttributeFalsiness(
+ DataFlow::GuardNode g, ControlFlowNode node, boolean branch, string attribute
+ ) {
+ exists(DataFlow::CallCfgNode urlParseCall, DataFlow::AttrRead attributeRead |
urlParseCall = getUrlParseCall() and
- netlocRead = urlParseCall.getAnAttributeRead("netloc") and
- node = urlParseCall.getArg(0).asCfgNode()
- |
- // either a simple check of the netloc attribute
- g = netlocRead.asCfgNode() and
+ parsedUrlAttributeRead(urlParseCall, attributeRead, attribute) and
+ g = attributeRead.asCfgNode() and
+ node = [urlParseCall.getArg(0), urlParseCall.getArgByName(["url", "urlstring"])].asCfgNode() and
branch = false
+ )
+ }
+
+ /**
+ * Holds if `g` compares `attribute` (`"netloc"` or `"scheme"`) of a parsed URL `node`
+ * against `other` using `op`, and the equality or collection membership holds on `branch`.
+ */
+ private predicate parsedUrlAttributeComparison(
+ DataFlow::GuardNode g, ControlFlowNode node, boolean branch, string attribute, Cmpop op,
+ Expr other
+ ) {
+ exists(
+ DataFlow::CallCfgNode urlParseCall, DataFlow::AttrRead attributeRead, CompareNode cmp,
+ ControlFlowNode otherNode
+ |
+ urlParseCall = getUrlParseCall() and
+ parsedUrlAttributeRead(urlParseCall, attributeRead, attribute) and
+ node = [urlParseCall.getArg(0), urlParseCall.getArgByName(["url", "urlstring"])].asCfgNode() and
+ cmp = g and
+ op = unique(Cmpop opp | opp = cmp.getNode().getAnOp()) and
+ other = otherNode.getNode()
+ |
+ op.getSymbol() in ["==", "is"] and
+ branch = true and
+ (
+ cmp.operands(attributeRead.asCfgNode(), op, otherNode) or
+ cmp.operands(otherNode, op, attributeRead.asCfgNode())
+ )
or
- // or a comparison (we don't care against what)
- exists(Compare cmp, string op |
- cmp = g.getNode() and
- op = unique(Cmpop opp | opp = cmp.getAnOp()).getSymbol() and
- cmp.getASubExpression() = netlocRead.asExpr()
- |
- op in ["==", "is", "in"] and branch = true
- or
- op in ["!=", "is not", "not in"] and branch = false
+ op.getSymbol() in ["!=", "is not"] and
+ branch = false and
+ (
+ cmp.operands(attributeRead.asCfgNode(), op, otherNode) or
+ cmp.operands(otherNode, op, attributeRead.asCfgNode())
)
+ or
+ op.getSymbol() = "in" and
+ membershipChecksCollection(other) and
+ branch = true and
+ cmp.operands(attributeRead.asCfgNode(), op, otherNode)
+ or
+ op.getSymbol() = "not in" and
+ membershipChecksCollection(other) and
+ branch = false and
+ cmp.operands(attributeRead.asCfgNode(), op, otherNode)
+ )
+ }
+
+ /** A `netloc` check that does not allow `""` (e.g. `parsed.netloc == request.host`). */
+ private predicate nonEmptyNetlocCheck(
+ DataFlow::GuardNode g, ControlFlowNode node, boolean branch
+ ) {
+ exists(Cmpop op, Expr other |
+ parsedUrlAttributeComparison(g, node, branch, "netloc", op, other) and
+ comparisonExcludesEmptyString(op, other)
+ )
+ }
+
+ /** A `netloc` check that allows `""` (e.g. `not parsed.netloc` or `parsed.netloc in ["", host]`). */
+ private predicate possiblyEmptyNetlocCheck(
+ DataFlow::GuardNode g, ControlFlowNode node, boolean branch
+ ) {
+ parsedUrlAttributeFalsiness(g, node, branch, "netloc")
+ or
+ exists(Cmpop op, Expr other |
+ parsedUrlAttributeComparison(g, node, branch, "netloc", op, other) and
+ not comparisonExcludesEmptyString(op, other)
+ )
+ }
+
+ /** A `scheme` check that ensures `scheme` is `""`. */
+ private predicate emptySchemeCheck(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
+ parsedUrlAttributeFalsiness(g, node, branch, "scheme")
+ or
+ exists(Cmpop op, Expr other |
+ parsedUrlAttributeComparison(g, node, branch, "scheme", op, other) and
+ op.getSymbol() in ["==", "is", "!=", "is not"] and
+ isEmptyString(other)
+ )
+ }
+
+ /** Holds if every possible callee is `methodName` bound to `object`. */
+ private predicate callsOnlyMethod(
+ DataFlow::MethodCallNode call, DataFlow::Node object, string methodName
+ ) {
+ forex(DataFlow::LocalSourceNode source | source = call.getFunction().getALocalSource() |
+ source.(DataFlow::AttrRead).getAttributeName() = methodName and
+ source.(DataFlow::AttrRead).getObject() = object
+ )
+ }
+
+ private predicate leadingSlashesCheck(
+ DataFlow::GuardNode g, ControlFlowNode node, boolean branch
+ ) {
+ exists(DataFlow::MethodCallNode call, DataFlow::Node url, Expr prefix |
+ callsOnlyMethod(call, url, "startswith") and
+ (
+ prefix = call.getArg(0).asExpr() or
+ prefix = call.getArg(0).asExpr().(Tuple).getAnElt()
+ ) and
+ prefix.(StringLiteral).getText() in ["/", "//", "///"] and
+ not exists(call.getArg(1)) and
+ not exists(call.getStarArg()) and
+ g = call.asCfgNode() and
+ node = url.asCfgNode() and
+ branch = false
+ )
+ }
+
+ /** Holds if every local source of `node` is a no-argument `strip` or `lstrip` call. */
+ private predicate isStripped(DataFlow::Node node) {
+ forex(DataFlow::LocalSourceNode source | source = node.getALocalSource() |
+ callsOnlyMethod(source, _, ["strip", "lstrip"]) and
+ not exists(source.asExpr().(Call).getAPositionalArg()) and
+ not exists(source.asExpr().(Call).getANamedArg())
+ )
+ }
+
+ /** An `isprintable` check on a stripped URL. */
+ private predicate normalizedPrintableCheck(
+ DataFlow::GuardNode g, ControlFlowNode node, boolean branch
+ ) {
+ exists(DataFlow::MethodCallNode call, DataFlow::Node url |
+ callsOnlyMethod(call, url, "isprintable") and
+ not exists(call.asExpr().(Call).getAPositionalArg()) and
+ not exists(call.asExpr().(Call).getANamedArg()) and
+ isStripped(url) and
+ g = call.asCfgNode() and
+ node = url.asCfgNode() and
+ branch = true
)
}
/**
- * A check of `urllib.parse.urlparse().netloc`, considered as a sanitizer-guard for URL redirection.
+ * A check of `urllib.parse.urlparse` or `urllib.parse.urlsplit` considered as a
+ * sanitizer-guard for URL redirection.
+ *
+ * Either `netloc` is restricted to a non-empty host, or `netloc` may be empty
+ * and `scheme`, leading slashes (`///`), and browser-ignored characters are also checked.
*/
private class NetlocCheck extends UrlRedirect::Sanitizer {
- NetlocCheck() { this = DataFlow::BarrierGuard::getABarrierNode() }
+ NetlocCheck() {
+ this = DataFlow::BarrierGuard::getABarrierNode()
+ or
+ this = DataFlow::BarrierGuard::getABarrierNode() and
+ this = DataFlow::BarrierGuard::getABarrierNode() and
+ // Browsers interpret three or more leading slashes as an authority, unlike `urlparse`.
+ this = DataFlow::BarrierGuard::getABarrierNode() and
+ // Browsers ignore leading whitespace and control characters within URLs.
+ this = DataFlow::BarrierGuard::getABarrierNode()
+ }
override predicate sanitizes(UrlRedirect::FlowState state) {
- // `urlparse` does not handle backslashes
+ // Neither parser handles backslashes as browsers do.
state instanceof UrlRedirect::NoBackslashes
}
}
diff --git a/python/ql/src/Security/CWE-601/UrlRedirect.qhelp b/python/ql/src/Security/CWE-601/UrlRedirect.qhelp
index c321ff23684d..6b3859293909 100644
--- a/python/ql/src/Security/CWE-601/UrlRedirect.qhelp
+++ b/python/ql/src/Security/CWE-601/UrlRedirect.qhelp
@@ -18,7 +18,7 @@ redirects on the server; then choose from that list based on the user input prov
If this is not possible, then the user input should be validated in some other way,
-for example, by verifying that the target URL does not include an explicit host name.
+using a maintained framework helper that accounts for how browsers interpret URLs.
@@ -38,40 +38,41 @@ and check that the user input is in that list:
-Often this is not possible, so an alternative is to check that the target URL does not
-specify an explicit host name. For example, you can use the urlparse function
-from the Python standard library to parse the URL and check that the netloc
-attribute is empty.
+If an allowlist is not practical, use a maintained framework helper to validate the
+redirect target. For Django applications, url_has_allowed_host_and_scheme
+checks whether a URL is safe to redirect to:
+
+
-Note, however, that some cases are not handled as we desire out-of-the-box by urlparse, so we need to adjust two things, as shown in the example below:
+Note that url_has_allowed_host_and_scheme handles backslashes correctly, so no
+additional processing is required.
+
+
+
+Do not rely on urlparse or urlsplit alone to validate redirect
+targets. These functions do not interpret all URLs in the same way as browsers:
-
Many browsers accept backslash characters (
\) as equivalent
- to forward slash characters (/) in URLs, but the urlparse function
- does not.
+ to forward slash characters (/) in URLs, but these parsers do not.
-
Mistyped URLs such as
https:/example.com or https:///example.com are parsed as having an empty netloc attribute, while browsers will still redirect to the correct site.
+ -
+ URLs such as
///example.com and ////example.com are parsed as
+ having both an empty netloc and an empty scheme, but browsers
+ interpret them as URLs with a host name.
+
-
-
-
-For Django application, you can use the function url_has_allowed_host_and_scheme
-to check that a URL is safe to redirect to, as shown in the following example:
-
-
-
-
-
-Note that url_has_allowed_host_and_scheme handles backslashes correctly, so no
-additional processing is required.
+Hand-written validation must also account for whitespace and control characters that
+browsers may ignore. Validate the same normalized value that is used in the redirect.
diff --git a/python/ql/src/Security/CWE-601/examples/redirect_good2.py b/python/ql/src/Security/CWE-601/examples/redirect_good2.py
deleted file mode 100644
index f9cc0313b969..000000000000
--- a/python/ql/src/Security/CWE-601/examples/redirect_good2.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from flask import Flask, request, redirect
-from urllib.parse import urlparse
-
-app = Flask(__name__)
-
-@app.route('/')
-def hello():
- target = request.args.get('target', '')
- target = target.replace('\\', '')
- if not urlparse(target).netloc and not urlparse(target).scheme:
- # relative path, safe to redirect
- return redirect(target, code=302)
- # ignore the target and redirect to the home page
- return redirect('/', code=302)
diff --git a/python/ql/src/change-notes/2026-10-22-url-redirect-validation.md b/python/ql/src/change-notes/2026-10-22-url-redirect-validation.md
new file mode 100644
index 000000000000..afe82d5f6057
--- /dev/null
+++ b/python/ql/src/change-notes/2026-10-22-url-redirect-validation.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* The `py/url-redirection` query now reports redirects guarded only by incomplete `urllib.parse` checks, including checks that accept URLs beginning with three or more slashes. The query help no longer recommends an empty network location and scheme as sufficient validation.
diff --git a/python/ql/test/query-tests/Security/CWE-601-UrlRedirect/UrlRedirect.expected b/python/ql/test/query-tests/Security/CWE-601-UrlRedirect/UrlRedirect.expected
index 3e43c112e2a3..23907337cd19 100644
--- a/python/ql/test/query-tests/Security/CWE-601-UrlRedirect/UrlRedirect.expected
+++ b/python/ql/test/query-tests/Security/CWE-601-UrlRedirect/UrlRedirect.expected
@@ -9,8 +9,56 @@
| test.py:83:21:83:26 | ControlFlowNode for unsafe | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:83:21:83:26 | ControlFlowNode for unsafe | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:93:18:93:26 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:93:18:93:26 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:114:25:114:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:114:25:114:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:124:25:124:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:124:25:124:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:132:25:132:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:132:25:132:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:140:25:140:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:140:25:140:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:148:25:148:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:148:25:148:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:156:25:156:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:156:25:156:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:165:21:165:29 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:165:21:165:29 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:300:25:300:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:300:25:300:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:314:25:314:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:314:25:314:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:328:25:328:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:328:25:328:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:342:25:342:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:342:25:342:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:355:25:355:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:355:25:355:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:370:25:370:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:370:25:370:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:382:25:382:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:382:25:382:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:391:25:391:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:391:25:391:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:404:25:404:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:404:25:404:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:417:25:417:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:417:25:417:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:431:25:431:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:431:25:431:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:444:25:444:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:444:25:444:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:458:25:458:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:458:25:458:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:470:25:470:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:470:25:470:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:483:25:483:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:483:25:483:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:497:25:497:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:497:25:497:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:514:25:514:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:514:25:514:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:527:25:527:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:527:25:527:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:539:25:539:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:539:25:539:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:553:21:553:29 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:553:21:553:29 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:566:25:566:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:566:25:566:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:582:25:582:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:582:25:582:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:597:21:597:29 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:597:21:597:29 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:612:21:612:29 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:612:21:612:29 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:629:25:629:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:629:25:629:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:644:25:644:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:644:25:644:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:702:25:702:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:702:25:702:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:720:25:720:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:720:25:720:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:729:25:729:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:729:25:729:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:739:25:739:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:739:25:739:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:751:25:751:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:751:25:751:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:760:25:760:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:760:25:760:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:768:25:768:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:768:25:768:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:776:25:776:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:776:25:776:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:784:25:784:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:784:25:784:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:792:25:792:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:792:25:792:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:800:25:800:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:800:25:800:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:810:25:810:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:810:25:810:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:827:25:827:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:827:25:827:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:835:25:835:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:835:25:835:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:849:25:849:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:849:25:849:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:863:25:863:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:863:25:863:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:877:25:877:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:877:25:877:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
+| test.py:891:25:891:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:891:25:891:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
edges
| test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:1:26:1:32 | ControlFlowNode for request | provenance | |
| test.py:1:26:1:32 | ControlFlowNode for request | test.py:7:14:7:20 | ControlFlowNode for request | provenance | |
@@ -23,8 +71,56 @@ edges
| test.py:1:26:1:32 | ControlFlowNode for request | test.py:81:17:81:23 | ControlFlowNode for request | provenance | |
| test.py:1:26:1:32 | ControlFlowNode for request | test.py:90:17:90:23 | ControlFlowNode for request | provenance | |
| test.py:1:26:1:32 | ControlFlowNode for request | test.py:111:17:111:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:121:17:121:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:129:17:129:23 | ControlFlowNode for request | provenance | |
| test.py:1:26:1:32 | ControlFlowNode for request | test.py:137:17:137:23 | ControlFlowNode for request | provenance | |
| test.py:1:26:1:32 | ControlFlowNode for request | test.py:145:17:145:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:153:17:153:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:161:17:161:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:293:17:293:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:306:17:306:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:320:17:320:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:334:17:334:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:348:17:348:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:361:17:361:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:376:17:376:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:388:17:388:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:397:17:397:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:410:17:410:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:423:17:423:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:437:17:437:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:450:17:450:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:464:17:464:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:476:17:476:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:489:17:489:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:503:17:503:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:520:17:520:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:533:17:533:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:545:17:545:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:558:17:558:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:572:17:572:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:588:17:588:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:602:17:602:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:617:17:617:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:635:17:635:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:695:17:695:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:717:17:717:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:726:17:726:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:735:17:735:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:745:17:745:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:757:17:757:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:766:17:766:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:774:17:774:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:782:17:782:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:790:17:790:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:798:17:798:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:806:17:806:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:824:17:824:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:833:17:833:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:841:17:841:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:855:17:855:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:869:17:869:23 | ControlFlowNode for request | provenance | |
+| test.py:1:26:1:32 | ControlFlowNode for request | test.py:883:17:883:23 | ControlFlowNode for request | provenance | |
| test.py:7:5:7:10 | ControlFlowNode for target | test.py:8:21:8:26 | ControlFlowNode for target | provenance | |
| test.py:7:14:7:20 | ControlFlowNode for request | test.py:7:14:7:25 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
| test.py:7:14:7:25 | ControlFlowNode for Attribute | test.py:7:14:7:43 | ControlFlowNode for Attribute() | provenance | dict.get |
@@ -72,6 +168,20 @@ edges
| test.py:111:17:111:23 | ControlFlowNode for request | test.py:111:17:111:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
| test.py:111:17:111:28 | ControlFlowNode for Attribute | test.py:111:17:111:46 | ControlFlowNode for Attribute() | provenance | dict.get |
| test.py:111:17:111:46 | ControlFlowNode for Attribute() | test.py:111:5:111:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:121:5:121:13 | ControlFlowNode for untrusted | test.py:122:17:122:25 | ControlFlowNode for untrusted | provenance | |
+| test.py:121:17:121:23 | ControlFlowNode for request | test.py:121:17:121:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:121:17:121:28 | ControlFlowNode for Attribute | test.py:121:17:121:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:121:17:121:46 | ControlFlowNode for Attribute() | test.py:121:5:121:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:122:5:122:13 | ControlFlowNode for untrusted | test.py:124:25:124:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:122:17:122:25 | ControlFlowNode for untrusted | test.py:122:17:122:44 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:122:17:122:44 | ControlFlowNode for Attribute() | test.py:122:5:122:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:129:5:129:13 | ControlFlowNode for untrusted | test.py:130:17:130:25 | ControlFlowNode for untrusted | provenance | |
+| test.py:129:17:129:23 | ControlFlowNode for request | test.py:129:17:129:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:129:17:129:28 | ControlFlowNode for Attribute | test.py:129:17:129:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:129:17:129:46 | ControlFlowNode for Attribute() | test.py:129:5:129:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:130:5:130:13 | ControlFlowNode for untrusted | test.py:132:25:132:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:130:17:130:25 | ControlFlowNode for untrusted | test.py:130:17:130:44 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:130:17:130:44 | ControlFlowNode for Attribute() | test.py:130:5:130:13 | ControlFlowNode for untrusted | provenance | |
| test.py:137:5:137:13 | ControlFlowNode for untrusted | test.py:140:25:140:33 | ControlFlowNode for untrusted | provenance | |
| test.py:137:17:137:23 | ControlFlowNode for request | test.py:137:17:137:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
| test.py:137:17:137:28 | ControlFlowNode for Attribute | test.py:137:17:137:46 | ControlFlowNode for Attribute() | provenance | dict.get |
@@ -80,6 +190,240 @@ edges
| test.py:145:17:145:23 | ControlFlowNode for request | test.py:145:17:145:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
| test.py:145:17:145:28 | ControlFlowNode for Attribute | test.py:145:17:145:46 | ControlFlowNode for Attribute() | provenance | dict.get |
| test.py:145:17:145:46 | ControlFlowNode for Attribute() | test.py:145:5:145:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:153:5:153:13 | ControlFlowNode for untrusted | test.py:154:17:154:25 | ControlFlowNode for untrusted | provenance | |
+| test.py:153:17:153:23 | ControlFlowNode for request | test.py:153:17:153:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:153:17:153:28 | ControlFlowNode for Attribute | test.py:153:17:153:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:153:17:153:46 | ControlFlowNode for Attribute() | test.py:153:5:153:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:154:5:154:13 | ControlFlowNode for untrusted | test.py:156:25:156:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:154:17:154:25 | ControlFlowNode for untrusted | test.py:154:17:154:44 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:154:17:154:44 | ControlFlowNode for Attribute() | test.py:154:5:154:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:161:5:161:13 | ControlFlowNode for untrusted | test.py:162:17:162:25 | ControlFlowNode for untrusted | provenance | |
+| test.py:161:17:161:23 | ControlFlowNode for request | test.py:161:17:161:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:161:17:161:28 | ControlFlowNode for Attribute | test.py:161:17:161:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:161:17:161:46 | ControlFlowNode for Attribute() | test.py:161:5:161:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:162:5:162:13 | ControlFlowNode for untrusted | test.py:165:21:165:29 | ControlFlowNode for untrusted | provenance | |
+| test.py:162:17:162:25 | ControlFlowNode for untrusted | test.py:162:17:162:44 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:162:17:162:44 | ControlFlowNode for Attribute() | test.py:162:5:162:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:293:5:293:13 | ControlFlowNode for untrusted | test.py:300:25:300:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:293:17:293:23 | ControlFlowNode for request | test.py:293:17:293:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:293:17:293:28 | ControlFlowNode for Attribute | test.py:293:17:293:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:293:17:293:46 | ControlFlowNode for Attribute() | test.py:293:5:293:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:306:5:306:13 | ControlFlowNode for untrusted | test.py:314:25:314:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:306:17:306:23 | ControlFlowNode for request | test.py:306:17:306:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:306:17:306:28 | ControlFlowNode for Attribute | test.py:306:17:306:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:306:17:306:46 | ControlFlowNode for Attribute() | test.py:306:17:306:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:306:17:306:65 | ControlFlowNode for Attribute() | test.py:306:5:306:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:320:5:320:13 | ControlFlowNode for untrusted | test.py:328:25:328:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:320:17:320:23 | ControlFlowNode for request | test.py:320:17:320:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:320:17:320:28 | ControlFlowNode for Attribute | test.py:320:17:320:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:320:17:320:46 | ControlFlowNode for Attribute() | test.py:320:17:320:54 | ControlFlowNode for Attribute() | provenance | |
+| test.py:320:17:320:54 | ControlFlowNode for Attribute() | test.py:320:17:320:72 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:320:17:320:72 | ControlFlowNode for Attribute() | test.py:320:5:320:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:334:5:334:13 | ControlFlowNode for untrusted | test.py:342:25:342:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:334:17:334:23 | ControlFlowNode for request | test.py:334:17:334:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:334:17:334:28 | ControlFlowNode for Attribute | test.py:334:17:334:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:334:17:334:46 | ControlFlowNode for Attribute() | test.py:334:17:334:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:334:17:334:65 | ControlFlowNode for Attribute() | test.py:334:5:334:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:348:5:348:13 | ControlFlowNode for untrusted | test.py:355:25:355:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:348:17:348:23 | ControlFlowNode for request | test.py:348:17:348:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:348:17:348:28 | ControlFlowNode for Attribute | test.py:348:17:348:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:348:17:348:46 | ControlFlowNode for Attribute() | test.py:348:17:348:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:348:17:348:65 | ControlFlowNode for Attribute() | test.py:348:5:348:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:361:5:361:13 | ControlFlowNode for untrusted | test.py:370:25:370:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:361:17:361:23 | ControlFlowNode for request | test.py:361:17:361:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:361:17:361:28 | ControlFlowNode for Attribute | test.py:361:17:361:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:361:17:361:46 | ControlFlowNode for Attribute() | test.py:361:17:361:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:361:17:361:65 | ControlFlowNode for Attribute() | test.py:361:5:361:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:376:5:376:13 | ControlFlowNode for untrusted | test.py:382:25:382:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:376:17:376:23 | ControlFlowNode for request | test.py:376:17:376:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:376:17:376:28 | ControlFlowNode for Attribute | test.py:376:17:376:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:376:17:376:46 | ControlFlowNode for Attribute() | test.py:376:17:376:64 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:376:17:376:64 | ControlFlowNode for Attribute() | test.py:376:5:376:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:388:5:388:13 | ControlFlowNode for untrusted | test.py:391:25:391:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:388:17:388:23 | ControlFlowNode for request | test.py:388:17:388:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:388:17:388:28 | ControlFlowNode for Attribute | test.py:388:17:388:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:388:17:388:46 | ControlFlowNode for Attribute() | test.py:388:17:388:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:388:17:388:65 | ControlFlowNode for Attribute() | test.py:388:5:388:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:397:5:397:13 | ControlFlowNode for untrusted | test.py:404:25:404:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:397:17:397:23 | ControlFlowNode for request | test.py:397:17:397:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:397:17:397:28 | ControlFlowNode for Attribute | test.py:397:17:397:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:397:17:397:46 | ControlFlowNode for Attribute() | test.py:397:17:397:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:397:17:397:65 | ControlFlowNode for Attribute() | test.py:397:5:397:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:410:5:410:13 | ControlFlowNode for untrusted | test.py:417:25:417:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:410:17:410:23 | ControlFlowNode for request | test.py:410:17:410:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:410:17:410:28 | ControlFlowNode for Attribute | test.py:410:17:410:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:410:17:410:46 | ControlFlowNode for Attribute() | test.py:410:17:410:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:410:17:410:65 | ControlFlowNode for Attribute() | test.py:410:5:410:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:423:5:423:13 | ControlFlowNode for untrusted | test.py:431:25:431:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:423:17:423:23 | ControlFlowNode for request | test.py:423:17:423:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:423:17:423:28 | ControlFlowNode for Attribute | test.py:423:17:423:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:423:17:423:46 | ControlFlowNode for Attribute() | test.py:423:17:423:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:423:17:423:65 | ControlFlowNode for Attribute() | test.py:423:5:423:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:437:5:437:13 | ControlFlowNode for untrusted | test.py:444:25:444:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:437:17:437:23 | ControlFlowNode for request | test.py:437:17:437:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:437:17:437:28 | ControlFlowNode for Attribute | test.py:437:17:437:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:437:17:437:46 | ControlFlowNode for Attribute() | test.py:437:17:437:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:437:17:437:65 | ControlFlowNode for Attribute() | test.py:437:5:437:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:450:5:450:13 | ControlFlowNode for untrusted | test.py:458:25:458:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:450:17:450:23 | ControlFlowNode for request | test.py:450:17:450:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:450:17:450:28 | ControlFlowNode for Attribute | test.py:450:17:450:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:450:17:450:46 | ControlFlowNode for Attribute() | test.py:450:17:450:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:450:17:450:65 | ControlFlowNode for Attribute() | test.py:450:5:450:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:464:5:464:13 | ControlFlowNode for untrusted | test.py:470:25:470:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:464:17:464:23 | ControlFlowNode for request | test.py:464:17:464:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:464:17:464:28 | ControlFlowNode for Attribute | test.py:464:17:464:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:464:17:464:46 | ControlFlowNode for Attribute() | test.py:464:17:464:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:464:17:464:65 | ControlFlowNode for Attribute() | test.py:464:5:464:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:476:5:476:13 | ControlFlowNode for untrusted | test.py:483:25:483:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:476:17:476:23 | ControlFlowNode for request | test.py:476:17:476:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:476:17:476:28 | ControlFlowNode for Attribute | test.py:476:17:476:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:476:17:476:46 | ControlFlowNode for Attribute() | test.py:476:17:476:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:476:17:476:65 | ControlFlowNode for Attribute() | test.py:476:5:476:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:489:5:489:13 | ControlFlowNode for untrusted | test.py:497:25:497:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:489:17:489:23 | ControlFlowNode for request | test.py:489:17:489:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:489:17:489:28 | ControlFlowNode for Attribute | test.py:489:17:489:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:489:17:489:46 | ControlFlowNode for Attribute() | test.py:489:17:489:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:489:17:489:65 | ControlFlowNode for Attribute() | test.py:489:5:489:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:503:5:503:13 | ControlFlowNode for untrusted | test.py:514:25:514:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:503:17:503:23 | ControlFlowNode for request | test.py:503:17:503:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:503:17:503:28 | ControlFlowNode for Attribute | test.py:503:17:503:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:503:17:503:46 | ControlFlowNode for Attribute() | test.py:503:17:503:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:503:17:503:65 | ControlFlowNode for Attribute() | test.py:503:5:503:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:520:5:520:13 | ControlFlowNode for untrusted | test.py:527:25:527:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:520:17:520:23 | ControlFlowNode for request | test.py:520:17:520:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:520:17:520:28 | ControlFlowNode for Attribute | test.py:520:17:520:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:520:17:520:46 | ControlFlowNode for Attribute() | test.py:520:17:520:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:520:17:520:65 | ControlFlowNode for Attribute() | test.py:520:5:520:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:533:5:533:13 | ControlFlowNode for untrusted | test.py:539:25:539:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:533:17:533:23 | ControlFlowNode for request | test.py:533:17:533:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:533:17:533:28 | ControlFlowNode for Attribute | test.py:533:17:533:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:533:17:533:46 | ControlFlowNode for Attribute() | test.py:533:17:533:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:533:17:533:65 | ControlFlowNode for Attribute() | test.py:533:5:533:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:545:5:545:13 | ControlFlowNode for untrusted | test.py:553:21:553:29 | ControlFlowNode for untrusted | provenance | |
+| test.py:545:17:545:23 | ControlFlowNode for request | test.py:545:17:545:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:545:17:545:28 | ControlFlowNode for Attribute | test.py:545:17:545:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:545:17:545:46 | ControlFlowNode for Attribute() | test.py:545:17:545:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:545:17:545:65 | ControlFlowNode for Attribute() | test.py:545:5:545:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:558:5:558:13 | ControlFlowNode for untrusted | test.py:566:25:566:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:558:17:558:23 | ControlFlowNode for request | test.py:558:17:558:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:558:17:558:28 | ControlFlowNode for Attribute | test.py:558:17:558:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:558:17:558:46 | ControlFlowNode for Attribute() | test.py:558:17:558:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:558:17:558:65 | ControlFlowNode for Attribute() | test.py:558:5:558:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:572:5:572:13 | ControlFlowNode for untrusted | test.py:582:25:582:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:572:17:572:23 | ControlFlowNode for request | test.py:572:17:572:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:572:17:572:28 | ControlFlowNode for Attribute | test.py:572:17:572:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:572:17:572:46 | ControlFlowNode for Attribute() | test.py:572:17:572:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:572:17:572:65 | ControlFlowNode for Attribute() | test.py:572:5:572:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:588:5:588:13 | ControlFlowNode for untrusted | test.py:597:21:597:29 | ControlFlowNode for untrusted | provenance | |
+| test.py:588:17:588:23 | ControlFlowNode for request | test.py:588:17:588:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:588:17:588:28 | ControlFlowNode for Attribute | test.py:588:17:588:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:588:17:588:46 | ControlFlowNode for Attribute() | test.py:588:17:588:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:588:17:588:65 | ControlFlowNode for Attribute() | test.py:588:5:588:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:602:5:602:13 | ControlFlowNode for untrusted | test.py:612:21:612:29 | ControlFlowNode for untrusted | provenance | |
+| test.py:602:17:602:23 | ControlFlowNode for request | test.py:602:17:602:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:602:17:602:28 | ControlFlowNode for Attribute | test.py:602:17:602:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:602:17:602:46 | ControlFlowNode for Attribute() | test.py:602:17:602:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:602:17:602:65 | ControlFlowNode for Attribute() | test.py:602:5:602:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:617:5:617:13 | ControlFlowNode for untrusted | test.py:629:25:629:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:617:17:617:23 | ControlFlowNode for request | test.py:617:17:617:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:617:17:617:28 | ControlFlowNode for Attribute | test.py:617:17:617:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:617:17:617:46 | ControlFlowNode for Attribute() | test.py:617:17:617:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:617:17:617:65 | ControlFlowNode for Attribute() | test.py:617:5:617:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:635:5:635:13 | ControlFlowNode for untrusted | test.py:644:25:644:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:635:17:635:23 | ControlFlowNode for request | test.py:635:17:635:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:635:17:635:28 | ControlFlowNode for Attribute | test.py:635:17:635:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:635:17:635:46 | ControlFlowNode for Attribute() | test.py:635:17:635:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:635:17:635:65 | ControlFlowNode for Attribute() | test.py:635:5:635:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:695:5:695:13 | ControlFlowNode for untrusted | test.py:702:25:702:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:695:17:695:23 | ControlFlowNode for request | test.py:695:17:695:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:695:17:695:28 | ControlFlowNode for Attribute | test.py:695:17:695:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:695:17:695:46 | ControlFlowNode for Attribute() | test.py:695:17:695:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:695:17:695:65 | ControlFlowNode for Attribute() | test.py:695:5:695:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:717:5:717:13 | ControlFlowNode for untrusted | test.py:720:25:720:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:717:17:717:23 | ControlFlowNode for request | test.py:717:17:717:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:717:17:717:28 | ControlFlowNode for Attribute | test.py:717:17:717:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:717:17:717:46 | ControlFlowNode for Attribute() | test.py:717:17:717:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:717:17:717:65 | ControlFlowNode for Attribute() | test.py:717:5:717:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:726:5:726:13 | ControlFlowNode for untrusted | test.py:729:25:729:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:726:17:726:23 | ControlFlowNode for request | test.py:726:17:726:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:726:17:726:28 | ControlFlowNode for Attribute | test.py:726:17:726:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:726:17:726:46 | ControlFlowNode for Attribute() | test.py:726:17:726:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:726:17:726:65 | ControlFlowNode for Attribute() | test.py:726:5:726:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:735:5:735:13 | ControlFlowNode for untrusted | test.py:739:25:739:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:735:17:735:23 | ControlFlowNode for request | test.py:735:17:735:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:735:17:735:28 | ControlFlowNode for Attribute | test.py:735:17:735:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:735:17:735:46 | ControlFlowNode for Attribute() | test.py:735:17:735:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:735:17:735:65 | ControlFlowNode for Attribute() | test.py:735:5:735:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:745:5:745:13 | ControlFlowNode for untrusted | test.py:751:25:751:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:745:17:745:23 | ControlFlowNode for request | test.py:745:17:745:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:745:17:745:28 | ControlFlowNode for Attribute | test.py:745:17:745:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:745:17:745:46 | ControlFlowNode for Attribute() | test.py:745:17:745:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:745:17:745:65 | ControlFlowNode for Attribute() | test.py:745:5:745:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:757:5:757:13 | ControlFlowNode for untrusted | test.py:760:25:760:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:757:17:757:23 | ControlFlowNode for request | test.py:757:17:757:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:757:17:757:28 | ControlFlowNode for Attribute | test.py:757:17:757:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:757:17:757:46 | ControlFlowNode for Attribute() | test.py:757:17:757:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:757:17:757:65 | ControlFlowNode for Attribute() | test.py:757:5:757:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:766:5:766:13 | ControlFlowNode for untrusted | test.py:768:25:768:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:766:17:766:23 | ControlFlowNode for request | test.py:766:17:766:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:766:17:766:28 | ControlFlowNode for Attribute | test.py:766:17:766:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:766:17:766:46 | ControlFlowNode for Attribute() | test.py:766:17:766:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:766:17:766:65 | ControlFlowNode for Attribute() | test.py:766:5:766:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:774:5:774:13 | ControlFlowNode for untrusted | test.py:776:25:776:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:774:17:774:23 | ControlFlowNode for request | test.py:774:17:774:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:774:17:774:28 | ControlFlowNode for Attribute | test.py:774:17:774:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:774:17:774:46 | ControlFlowNode for Attribute() | test.py:774:17:774:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:774:17:774:65 | ControlFlowNode for Attribute() | test.py:774:5:774:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:782:5:782:13 | ControlFlowNode for untrusted | test.py:784:25:784:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:782:17:782:23 | ControlFlowNode for request | test.py:782:17:782:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:782:17:782:28 | ControlFlowNode for Attribute | test.py:782:17:782:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:782:17:782:46 | ControlFlowNode for Attribute() | test.py:782:17:782:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:782:17:782:65 | ControlFlowNode for Attribute() | test.py:782:5:782:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:790:5:790:13 | ControlFlowNode for untrusted | test.py:792:25:792:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:790:17:790:23 | ControlFlowNode for request | test.py:790:17:790:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:790:17:790:28 | ControlFlowNode for Attribute | test.py:790:17:790:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:790:17:790:46 | ControlFlowNode for Attribute() | test.py:790:17:790:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:790:17:790:65 | ControlFlowNode for Attribute() | test.py:790:5:790:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:798:5:798:13 | ControlFlowNode for untrusted | test.py:800:25:800:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:798:17:798:23 | ControlFlowNode for request | test.py:798:17:798:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:798:17:798:28 | ControlFlowNode for Attribute | test.py:798:17:798:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:798:17:798:46 | ControlFlowNode for Attribute() | test.py:798:17:798:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:798:17:798:65 | ControlFlowNode for Attribute() | test.py:798:5:798:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:806:5:806:13 | ControlFlowNode for untrusted | test.py:810:25:810:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:806:17:806:23 | ControlFlowNode for request | test.py:806:17:806:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:806:17:806:28 | ControlFlowNode for Attribute | test.py:806:17:806:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:806:17:806:46 | ControlFlowNode for Attribute() | test.py:806:17:806:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:806:17:806:65 | ControlFlowNode for Attribute() | test.py:806:5:806:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:824:5:824:13 | ControlFlowNode for untrusted | test.py:827:25:827:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:824:17:824:23 | ControlFlowNode for request | test.py:824:17:824:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:824:17:824:28 | ControlFlowNode for Attribute | test.py:824:17:824:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:824:17:824:46 | ControlFlowNode for Attribute() | test.py:824:17:824:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:824:17:824:65 | ControlFlowNode for Attribute() | test.py:824:5:824:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:833:5:833:13 | ControlFlowNode for untrusted | test.py:835:25:835:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:833:17:833:23 | ControlFlowNode for request | test.py:833:17:833:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:833:17:833:28 | ControlFlowNode for Attribute | test.py:833:17:833:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:833:17:833:46 | ControlFlowNode for Attribute() | test.py:833:17:833:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:833:17:833:65 | ControlFlowNode for Attribute() | test.py:833:5:833:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:841:5:841:13 | ControlFlowNode for untrusted | test.py:849:25:849:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:841:17:841:23 | ControlFlowNode for request | test.py:841:17:841:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:841:17:841:28 | ControlFlowNode for Attribute | test.py:841:17:841:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:841:17:841:46 | ControlFlowNode for Attribute() | test.py:841:17:841:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:841:17:841:65 | ControlFlowNode for Attribute() | test.py:841:5:841:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:855:5:855:13 | ControlFlowNode for untrusted | test.py:863:25:863:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:855:17:855:23 | ControlFlowNode for request | test.py:855:17:855:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:855:17:855:28 | ControlFlowNode for Attribute | test.py:855:17:855:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:855:17:855:46 | ControlFlowNode for Attribute() | test.py:855:17:855:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:855:17:855:65 | ControlFlowNode for Attribute() | test.py:855:5:855:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:869:5:869:13 | ControlFlowNode for untrusted | test.py:877:25:877:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:869:17:869:23 | ControlFlowNode for request | test.py:869:17:869:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:869:17:869:28 | ControlFlowNode for Attribute | test.py:869:17:869:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:869:17:869:46 | ControlFlowNode for Attribute() | test.py:869:17:869:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:869:17:869:65 | ControlFlowNode for Attribute() | test.py:869:5:869:13 | ControlFlowNode for untrusted | provenance | |
+| test.py:883:5:883:13 | ControlFlowNode for untrusted | test.py:891:25:891:33 | ControlFlowNode for untrusted | provenance | |
+| test.py:883:17:883:23 | ControlFlowNode for request | test.py:883:17:883:28 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
+| test.py:883:17:883:28 | ControlFlowNode for Attribute | test.py:883:17:883:46 | ControlFlowNode for Attribute() | provenance | dict.get |
+| test.py:883:17:883:46 | ControlFlowNode for Attribute() | test.py:883:17:883:65 | ControlFlowNode for Attribute() | provenance | Config |
+| test.py:883:17:883:65 | ControlFlowNode for Attribute() | test.py:883:5:883:13 | ControlFlowNode for untrusted | provenance | |
nodes
| test.py:1:26:1:32 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember |
| test.py:1:26:1:32 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
@@ -140,6 +484,22 @@ nodes
| test.py:111:17:111:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| test.py:111:17:111:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| test.py:114:25:114:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:121:5:121:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:121:17:121:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:121:17:121:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:121:17:121:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:122:5:122:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:122:17:122:25 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:122:17:122:44 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:124:25:124:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:129:5:129:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:129:17:129:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:129:17:129:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:129:17:129:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:130:5:130:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:130:17:130:25 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:130:17:130:44 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:132:25:132:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
| test.py:137:5:137:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
| test.py:137:17:137:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| test.py:137:17:137:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
@@ -150,4 +510,284 @@ nodes
| test.py:145:17:145:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| test.py:145:17:145:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| test.py:148:25:148:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:153:5:153:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:153:17:153:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:153:17:153:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:153:17:153:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:154:5:154:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:154:17:154:25 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:154:17:154:44 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:156:25:156:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:161:5:161:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:161:17:161:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:161:17:161:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:161:17:161:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:162:5:162:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:162:17:162:25 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:162:17:162:44 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:165:21:165:29 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:293:5:293:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:293:17:293:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:293:17:293:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:293:17:293:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:300:25:300:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:306:5:306:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:306:17:306:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:306:17:306:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:306:17:306:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:306:17:306:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:314:25:314:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:320:5:320:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:320:17:320:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:320:17:320:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:320:17:320:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:320:17:320:54 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:320:17:320:72 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:328:25:328:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:334:5:334:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:334:17:334:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:334:17:334:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:334:17:334:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:334:17:334:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:342:25:342:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:348:5:348:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:348:17:348:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:348:17:348:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:348:17:348:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:348:17:348:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:355:25:355:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:361:5:361:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:361:17:361:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:361:17:361:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:361:17:361:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:361:17:361:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:370:25:370:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:376:5:376:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:376:17:376:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:376:17:376:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:376:17:376:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:376:17:376:64 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:382:25:382:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:388:5:388:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:388:17:388:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:388:17:388:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:388:17:388:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:388:17:388:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:391:25:391:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:397:5:397:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:397:17:397:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:397:17:397:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:397:17:397:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:397:17:397:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:404:25:404:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:410:5:410:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:410:17:410:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:410:17:410:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:410:17:410:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:410:17:410:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:417:25:417:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:423:5:423:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:423:17:423:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:423:17:423:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:423:17:423:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:423:17:423:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:431:25:431:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:437:5:437:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:437:17:437:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:437:17:437:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:437:17:437:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:437:17:437:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:444:25:444:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:450:5:450:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:450:17:450:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:450:17:450:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:450:17:450:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:450:17:450:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:458:25:458:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:464:5:464:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:464:17:464:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:464:17:464:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:464:17:464:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:464:17:464:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:470:25:470:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:476:5:476:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:476:17:476:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:476:17:476:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:476:17:476:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:476:17:476:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:483:25:483:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:489:5:489:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:489:17:489:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:489:17:489:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:489:17:489:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:489:17:489:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:497:25:497:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:503:5:503:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:503:17:503:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:503:17:503:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:503:17:503:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:503:17:503:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:514:25:514:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:520:5:520:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:520:17:520:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:520:17:520:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:520:17:520:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:520:17:520:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:527:25:527:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:533:5:533:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:533:17:533:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:533:17:533:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:533:17:533:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:533:17:533:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:539:25:539:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:545:5:545:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:545:17:545:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:545:17:545:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:545:17:545:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:545:17:545:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:553:21:553:29 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:558:5:558:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:558:17:558:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:558:17:558:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:558:17:558:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:558:17:558:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:566:25:566:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:572:5:572:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:572:17:572:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:572:17:572:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:572:17:572:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:572:17:572:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:582:25:582:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:588:5:588:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:588:17:588:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:588:17:588:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:588:17:588:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:588:17:588:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:597:21:597:29 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:602:5:602:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:602:17:602:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:602:17:602:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:602:17:602:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:602:17:602:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:612:21:612:29 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:617:5:617:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:617:17:617:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:617:17:617:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:617:17:617:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:617:17:617:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:629:25:629:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:635:5:635:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:635:17:635:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:635:17:635:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:635:17:635:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:635:17:635:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:644:25:644:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:695:5:695:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:695:17:695:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:695:17:695:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:695:17:695:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:695:17:695:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:702:25:702:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:717:5:717:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:717:17:717:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:717:17:717:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:717:17:717:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:717:17:717:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:720:25:720:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:726:5:726:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:726:17:726:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:726:17:726:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:726:17:726:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:726:17:726:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:729:25:729:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:735:5:735:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:735:17:735:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:735:17:735:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:735:17:735:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:735:17:735:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:739:25:739:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:745:5:745:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:745:17:745:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:745:17:745:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:745:17:745:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:745:17:745:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:751:25:751:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:757:5:757:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:757:17:757:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:757:17:757:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:757:17:757:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:757:17:757:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:760:25:760:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:766:5:766:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:766:17:766:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:766:17:766:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:766:17:766:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:766:17:766:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:768:25:768:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:774:5:774:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:774:17:774:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:774:17:774:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:774:17:774:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:774:17:774:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:776:25:776:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:782:5:782:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:782:17:782:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:782:17:782:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:782:17:782:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:782:17:782:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:784:25:784:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:790:5:790:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:790:17:790:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:790:17:790:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:790:17:790:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:790:17:790:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:792:25:792:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:798:5:798:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:798:17:798:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:798:17:798:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:798:17:798:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:798:17:798:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:800:25:800:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:806:5:806:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:806:17:806:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:806:17:806:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:806:17:806:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:806:17:806:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:810:25:810:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:824:5:824:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:824:17:824:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:824:17:824:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:824:17:824:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:824:17:824:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:827:25:827:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:833:5:833:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:833:17:833:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:833:17:833:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:833:17:833:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:833:17:833:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:835:25:835:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:841:5:841:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:841:17:841:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:841:17:841:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:841:17:841:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:841:17:841:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:849:25:849:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:855:5:855:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:855:17:855:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:855:17:855:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:855:17:855:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:855:17:855:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:863:25:863:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:869:5:869:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:869:17:869:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:869:17:869:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:869:17:869:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:869:17:869:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:877:25:877:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:883:5:883:13 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
+| test.py:883:17:883:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| test.py:883:17:883:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:883:17:883:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:883:17:883:65 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:891:25:891:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
subpaths
diff --git a/python/ql/test/query-tests/Security/CWE-601-UrlRedirect/test.py b/python/ql/test/query-tests/Security/CWE-601-UrlRedirect/test.py
index dd374618a9db..ce0d62503fb7 100644
--- a/python/ql/test/query-tests/Security/CWE-601-UrlRedirect/test.py
+++ b/python/ql/test/query-tests/Security/CWE-601-UrlRedirect/test.py
@@ -116,20 +116,20 @@ def not_ok5():
from urllib.parse import urlparse
-@app.route('/ok8')
-def ok8():
+@app.route('/not_ok8')
+def not_ok8():
untrusted = request.args.get('target', '')
untrusted = untrusted.replace("\\", "/")
if not urlparse(untrusted).netloc:
- return redirect(untrusted, code=302) # OK
+ return redirect(untrusted, code=302) # $ Alert
return redirect("/", code=302)
-@app.route('/ok9')
-def ok9():
+@app.route('/not_ok9')
+def not_ok9():
untrusted = request.args.get('target', '')
untrusted = untrusted.replace("\\", "/")
if urlparse(untrusted).netloc == "":
- return redirect(untrusted, code=302) # OK
+ return redirect(untrusted, code=302) # $ Alert
return redirect("/", code=302)
@app.route('/not_ok6')
@@ -148,18 +148,745 @@ def not_ok7():
return redirect(untrusted, code=302) # $ Alert # NOT OK
return redirect("/", code=302)
-@app.route('/ok10')
-def ok10():
+@app.route('/not_ok10')
+def not_ok10():
untrusted = request.args.get('target', '')
untrusted = untrusted.replace("\\", "/")
if urlparse(untrusted).netloc in ["", request.host]:
- return redirect(untrusted, code=302) # OK
+ return redirect(untrusted, code=302) # $ Alert
return redirect("/", code=302)
-@app.route('/ok11')
-def ok11():
+@app.route('/not_ok11')
+def not_ok11():
untrusted = request.args.get('target', '')
untrusted = untrusted.replace("\\", "/")
if urlparse(untrusted).netloc not in ["", request.host]:
return redirect("/", code=302) # OK
- return redirect(untrusted, code=302)
+ return redirect(untrusted, code=302) # $ Alert
+
+
+@app.route('/checked_relative_url')
+def checked_relative_url():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ if (
+ untrusted.isprintable()
+ and not untrusted.startswith("///")
+ and not urlparse(untrusted).scheme
+ and not urlparse(untrusted).netloc
+ ):
+ return redirect(untrusted, code=302) # OK
+ return redirect("/", code=302)
+
+
+from urllib.parse import urlsplit
+
+@app.route('/checked_lstripped_relative_url')
+def checked_lstripped_relative_url():
+ untrusted = request.args.get('target', '').replace("\\", "/").lstrip()
+ parsed = urlsplit(untrusted)
+ if (
+ untrusted.isprintable()
+ and not untrusted.startswith("///")
+ and not parsed.scheme
+ and not parsed.netloc
+ ):
+ return redirect(untrusted, code=302) # OK
+ return redirect("/", code=302)
+
+
+@app.route('/checked_relative_url_comparisons')
+def checked_relative_url_comparisons():
+ untrusted = request.args.get('target', '').replace("\\", "").strip()
+ parsed = urlparse(untrusted)
+ if (
+ untrusted.isprintable()
+ and not untrusted.startswith("//")
+ and parsed.scheme == ""
+ and parsed.netloc == ""
+ ):
+ return redirect(untrusted, code=302) # OK
+ return redirect("/", code=302)
+
+
+@app.route('/checked_relative_url_early_return')
+def checked_relative_url_early_return():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ parsed = urlparse(untrusted)
+ if (
+ not untrusted.isprintable()
+ or untrusted.startswith("///")
+ or parsed.scheme != ""
+ or parsed.netloc not in ["", request.host]
+ ):
+ return redirect("/", code=302) # OK
+ return redirect(untrusted, code=302) # OK
+
+
+@app.route('/checked_split_url')
+def checked_split_url():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ parsed = urlsplit(url=untrusted)
+ if (
+ untrusted.isprintable()
+ and not untrusted.startswith("///")
+ and not parsed.scheme
+ and parsed.netloc in ["", request.host]
+ ):
+ return redirect(untrusted, code=302) # OK
+ return redirect("/", code=302)
+
+
+@app.route('/checked_tuple_startswith')
+def checked_tuple_startswith():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ parsed = urlparse(untrusted)
+ if (
+ untrusted.isprintable()
+ and not untrusted.startswith(("///", "\\"))
+ and not parsed.scheme
+ and not parsed.netloc
+ ):
+ return redirect(untrusted, code=302) # OK
+ return redirect("/", code=302)
+
+
+@app.route('/named_empty_scheme')
+def named_empty_scheme():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ empty_scheme = ""
+ if (
+ untrusted.isprintable()
+ and not untrusted.startswith("///")
+ and urlparse(untrusted).scheme == empty_scheme
+ and not urlparse(untrusted).netloc
+ ):
+ return redirect(untrusted, code=302) # OK
+ return redirect("/", code=302)
+
+
+@app.route('/checked_named_host_collections')
+def checked_named_host_collections():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ kind = request.args.get('kind')
+ if kind == "list":
+ allowed_hosts = ["", request.host]
+ elif kind == "tuple":
+ allowed_hosts = ("", request.host)
+ elif kind == "set":
+ allowed_hosts = {"", request.host}
+ else:
+ allowed_hosts = {"": True, request.host: True}
+ hosts = allowed_hosts
+ parsed = urlsplit(untrusted)
+ if (
+ untrusted.isprintable()
+ and not untrusted.startswith("///")
+ and not parsed.scheme
+ and parsed.netloc in hosts
+ ):
+ return redirect(untrusted, code=302) # OK
+ return redirect("/", code=302)
+
+
+@app.route('/missing_backslash_check')
+def missing_backslash_check():
+ untrusted = request.args.get('target', '').strip()
+ if (
+ untrusted.isprintable()
+ and not untrusted.startswith("///")
+ and not urlparse(untrusted).scheme
+ and not urlparse(untrusted).netloc
+ ):
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/printable_without_strip')
+def printable_without_strip():
+ untrusted = request.args.get('target', '').replace("\\", "/")
+ parsed = urlparse(untrusted)
+ if (
+ untrusted.isprintable()
+ and not untrusted.startswith("///")
+ and not parsed.scheme
+ and not parsed.netloc
+ ):
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/strip_before_backslash_replace')
+def strip_before_backslash_replace():
+ untrusted = request.args.get('target', '').strip().replace("\\", "")
+ parsed = urlparse(untrusted)
+ if (
+ untrusted.isprintable()
+ and not untrusted.startswith("///")
+ and not parsed.scheme
+ and not parsed.netloc
+ ):
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/custom_strip_characters')
+def custom_strip_characters():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip(" ")
+ parsed = urlsplit(untrusted)
+ if (
+ untrusted.isprintable()
+ and not untrusted.startswith("///")
+ and not parsed.scheme
+ and not parsed.netloc
+ ):
+ return redirect(untrusted, code=302) # $ SPURIOUS: Alert
+ return redirect("/", code=302)
+
+
+@app.route('/stripped_without_printable_check')
+def stripped_without_printable_check():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ parsed = urlparse(untrusted)
+ if (
+ not untrusted.startswith("///")
+ and not parsed.scheme
+ and not parsed.netloc
+ ):
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/printable_check_on_other_url')
+def printable_check_on_other_url():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ other = request.args.get('other', '').replace("\\", "/").strip()
+ parsed = urlparse(untrusted)
+ if (
+ other.isprintable()
+ and not untrusted.startswith("///")
+ and not parsed.scheme
+ and not parsed.netloc
+ ):
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/missing_slash_check')
+def missing_slash_check():
+ untrusted = request.args.get('target', '').replace("\\", "").strip()
+ if (
+ untrusted.isprintable()
+ and not urlparse(untrusted).netloc
+ and not urlparse(untrusted).scheme
+ ):
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/unchecked_split_url_slashes')
+def unchecked_split_url_slashes():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ parsed = urlsplit(untrusted)
+ if untrusted.isprintable() and not parsed.scheme and not parsed.netloc:
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/insufficient_slash_check')
+def insufficient_slash_check():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ if (
+ untrusted.isprintable()
+ and not untrusted.startswith("////")
+ and not urlparse(untrusted).scheme
+ and not urlparse(untrusted).netloc
+ ):
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/slash_check_with_offset')
+def slash_check_with_offset():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ if (
+ untrusted.isprintable()
+ and not untrusted.startswith("///", 1)
+ and not urlparse(untrusted).scheme
+ and not urlparse(untrusted).netloc
+ ):
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/slash_check_with_starred_offset')
+def slash_check_with_starred_offset():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ bounds = (1,)
+ if (
+ untrusted.isprintable()
+ and not untrusted.startswith("///", *bounds)
+ and not urlparse(untrusted).scheme
+ and not urlparse(untrusted).netloc
+ ):
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/wrong_slash_branch')
+def wrong_slash_branch():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ if (
+ untrusted.isprintable()
+ and untrusted.startswith("///")
+ and not urlparse(untrusted).scheme
+ and not urlparse(untrusted).netloc
+ ):
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/checks_on_different_urls')
+def checks_on_different_urls():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ other = request.args.get('other', '').replace("\\", "/").strip()
+ if (
+ untrusted.isprintable()
+ and not other.startswith("///")
+ and not urlparse(untrusted).scheme
+ and not urlparse(untrusted).netloc
+ ):
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/missing_scheme_check')
+def missing_scheme_check():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ if (
+ untrusted.isprintable()
+ and not untrusted.startswith("///")
+ and not urlparse(untrusted).netloc
+ ):
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/nonempty_scheme')
+def nonempty_scheme():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ if (
+ untrusted.isprintable()
+ and not untrusted.startswith("///")
+ and urlparse(untrusted).scheme == "https"
+ and not urlparse(untrusted).netloc
+ ):
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/unknown_scheme')
+def unknown_scheme():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ allowed_scheme = get_allowed_scheme()
+ if (
+ untrusted.isprintable()
+ and not untrusted.startswith("///")
+ and urlparse(untrusted).scheme == allowed_scheme
+ and not urlparse(untrusted).netloc
+ ):
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/conditionally_nonempty_scheme')
+def conditionally_nonempty_scheme():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ if request.args.get('allow-https'):
+ allowed_scheme = "https"
+ else:
+ allowed_scheme = ""
+ if (
+ untrusted.isprintable()
+ and not untrusted.startswith("///")
+ and urlparse(untrusted).scheme == allowed_scheme
+ and not urlparse(untrusted).netloc
+ ):
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/reversed_scheme_membership')
+def reversed_scheme_membership():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ if (
+ untrusted.isprintable()
+ and not untrusted.startswith("///")
+ and "" in urlparse(untrusted).scheme
+ and not urlparse(untrusted).netloc
+ ):
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/missing_netloc_check')
+def missing_netloc_check():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ if (
+ untrusted.isprintable()
+ and not untrusted.startswith("///")
+ and not urlparse(untrusted).scheme
+ ):
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/unchecked_branch')
+def unchecked_branch():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ if (
+ untrusted.isprintable()
+ and not untrusted.startswith("///")
+ and not urlparse(untrusted).scheme
+ and not urlparse(untrusted).netloc
+ ):
+ return redirect(untrusted, code=302) # OK
+ return redirect(untrusted, code=302) # $ Alert
+
+
+@app.route('/host_substring_membership')
+def host_substring_membership():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ parsed = urlparse(untrusted)
+ if (
+ untrusted.isprintable()
+ and not untrusted.startswith("///")
+ and not parsed.scheme
+ and parsed.netloc in "example.com"
+ ):
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/named_host_substring_membership')
+def named_host_substring_membership():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ allowed_hosts = "example.com"
+ hosts = allowed_hosts
+ parsed = urlsplit(untrusted)
+ if (
+ untrusted.isprintable()
+ and not untrusted.startswith("///")
+ and not parsed.scheme
+ and parsed.netloc in hosts
+ ):
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/split_host_substring_membership_early_return')
+def split_host_substring_membership_early_return():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ parsed = urlsplit(untrusted)
+ if (
+ not untrusted.isprintable()
+ or untrusted.startswith("///")
+ or parsed.scheme
+ or parsed.netloc not in "example.com"
+ ):
+ return redirect("/", code=302)
+ return redirect(untrusted, code=302) # $ Alert
+
+
+@app.route('/named_host_substring_membership_early_return')
+def named_host_substring_membership_early_return():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ allowed_hosts = "example.com"
+ parsed = urlparse(untrusted)
+ if (
+ not untrusted.isprintable()
+ or untrusted.startswith("///")
+ or parsed.scheme
+ or parsed.netloc not in allowed_hosts
+ ):
+ return redirect("/", code=302)
+ return redirect(untrusted, code=302) # $ Alert
+
+
+@app.route('/conditionally_string_host_list')
+def conditionally_string_host_list():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ if request.args.get('use-list'):
+ allowed_hosts = ["", request.host]
+ else:
+ allowed_hosts = "example.com"
+ parsed = urlsplit(untrusted)
+ if (
+ untrusted.isprintable()
+ and not untrusted.startswith("///")
+ and not parsed.scheme
+ and parsed.netloc in allowed_hosts
+ ):
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/unknown_host_list')
+def unknown_host_list():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ allowed_hosts = get_allowed_hosts()
+ parsed = urlsplit(untrusted)
+ if (
+ untrusted.isprintable()
+ and not untrusted.startswith("///")
+ and not parsed.scheme
+ and parsed.netloc in allowed_hosts
+ ):
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/checked_explicit_host')
+def checked_explicit_host():
+ untrusted = request.args.get('target', '').replace("\\", "/")
+ if urlparse(untrusted).netloc == "example.com":
+ return redirect(untrusted, code=302) # OK
+ return redirect("/", code=302)
+
+
+@app.route('/checked_explicit_host_list')
+def checked_explicit_host_list():
+ untrusted = request.args.get('target', '').replace("\\", "/")
+ if urlparse(untrusted).netloc in ["example.com", "sub.example.com"]:
+ return redirect(untrusted, code=302) # OK
+ return redirect("/", code=302)
+
+
+@app.route('/named_nonempty_host_tuple')
+def named_nonempty_host_tuple():
+ untrusted = request.args.get('target', '').replace("\\", "/")
+ allowed_hosts = ("example.com", "sub.example.com")
+ if urlparse(untrusted).netloc in allowed_hosts:
+ return redirect(untrusted, code=302) # OK
+ return redirect("/", code=302)
+
+
+@app.route('/host_list_with_named_nonempty_host')
+def host_list_with_named_nonempty_host():
+ untrusted = request.args.get('target', '').replace("\\", "/")
+ allowed_host = "sub.example.com"
+ if urlparse(untrusted).netloc in [allowed_host, "example.com"]:
+ return redirect(untrusted, code=302) # OK
+ return redirect("/", code=302)
+
+
+@app.route('/parsed_hostname')
+def parsed_hostname():
+ untrusted = request.args.get('target', '').replace("\\", "/")
+ allowed_host = urlparse("https://example.com").hostname
+ if urlparse(untrusted).netloc == allowed_host:
+ return redirect(untrusted, code=302) # OK
+ return redirect("/", code=302)
+
+
+from types import SimpleNamespace
+
+@app.route('/mixed_source_parsed_hostname')
+def mixed_source_parsed_hostname():
+ untrusted = request.args.get('target', '').replace("\\", "/")
+ parts = (
+ urlsplit("https://example.com")
+ if request.args.get('use-parser')
+ else SimpleNamespace(hostname="")
+ )
+ if urlparse(untrusted).netloc == parts.hostname:
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/split_hostname_list')
+def split_hostname_list():
+ untrusted = request.args.get('target', '').replace("\\", "/")
+ allowed_host = urlsplit("https://example.com").hostname
+ if urlparse(untrusted).netloc in [allowed_host, "sub.example.com"]:
+ return redirect(untrusted, code=302) # OK
+ return redirect("/", code=302)
+
+
+@app.route('/named_empty_host')
+def named_empty_host():
+ untrusted = request.args.get('target', '').replace("\\", "/")
+ empty_host = ""
+ if urlparse(untrusted).netloc == empty_host:
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/named_possibly_empty_host_list')
+def named_possibly_empty_host_list():
+ untrusted = request.args.get('target', '').replace("\\", "/")
+ allowed_hosts = ["", "example.com"]
+ if urlparse(untrusted).netloc in allowed_hosts:
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/aliased_possibly_empty_host_list')
+def aliased_possibly_empty_host_list():
+ untrusted = request.args.get('target', '').replace("\\", "/")
+ allowed_hosts = ["", "example.com"]
+ hosts = allowed_hosts
+ if urlparse(untrusted).netloc in hosts:
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/conditionally_empty_host_list')
+def conditionally_empty_host_list():
+ untrusted = request.args.get('target', '').replace("\\", "/")
+ if request.args.get('allow-relative'):
+ allowed_hosts = ["", "example.com"]
+ else:
+ allowed_hosts = ["example.com"]
+ if urlparse(untrusted).netloc in allowed_hosts:
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/host_list_with_named_empty_host')
+def host_list_with_named_empty_host():
+ untrusted = request.args.get('target', '').replace("\\", "/")
+ empty_host = ""
+ if urlparse(untrusted).netloc in [empty_host, "example.com"]:
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/host_list_with_unknown_element')
+def host_list_with_unknown_element():
+ untrusted = request.args.get('target', '').replace("\\", "/")
+ if urlparse(untrusted).netloc in [get_allowed_host(), "example.com"]:
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/request_host_attribute')
+def request_host_attribute():
+ untrusted = request.args.get('target', '').replace("\\", "/")
+ if urlparse(untrusted).netloc == request.host:
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/request_host_list')
+def request_host_list():
+ untrusted = request.args.get('target', '').replace("\\", "/")
+ if urlparse(untrusted).netloc in [request.host, "sub.example.com"]:
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/arbitrary_attribute_host')
+def arbitrary_attribute_host():
+ untrusted = request.args.get('target', '').replace("\\", "/")
+ if urlparse(untrusted).netloc == app.redirect_host:
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/arbitrary_attribute_host_list')
+def arbitrary_attribute_host_list():
+ untrusted = request.args.get('target', '').replace("\\", "/")
+ if urlparse(untrusted).netloc in [app.redirect_host, "sub.example.com"]:
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/mutated_host_list')
+def mutated_host_list():
+ untrusted = request.args.get('target', '').replace("\\", "/")
+ allowed_hosts = ["example.com"]
+ allowed_hosts.append("")
+ if urlparse(untrusted).netloc in allowed_hosts:
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/explicit_host_dict')
+def explicit_host_dict():
+ untrusted = request.args.get('target', '').replace("\\", "/")
+ if urlparse(untrusted).netloc in {"example.com": True}:
+ return redirect(untrusted, code=302) # OK
+ return redirect("/", code=302)
+
+
+@app.route('/unpacked_host_dict')
+def unpacked_host_dict():
+ untrusted = request.args.get('target', '').replace("\\", "/")
+ allowed_hosts = get_allowed_hosts()
+ if urlparse(untrusted).netloc in {**allowed_hosts, "example.com": True}:
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/request_derived_parsed_hostname')
+def request_derived_parsed_hostname():
+ untrusted = request.args.get('target', '').replace("\\", "/")
+ if urlsplit(untrusted).netloc == urlsplit(untrusted).hostname:
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/mixed_source_parse_falsiness')
+def mixed_source_parse_falsiness():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ parts = urlsplit(untrusted) if request.args.get('parse-target') else urlsplit("/")
+ if (
+ untrusted.isprintable()
+ and not untrusted.startswith("///")
+ and not parts.scheme
+ and not parts.netloc
+ ):
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/mixed_source_parse_comparison')
+def mixed_source_parse_comparison():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ parts = urlsplit(untrusted) if request.args.get('parse-target') else urlsplit("/")
+ if (
+ untrusted.isprintable()
+ and not untrusted.startswith("///")
+ and parts.scheme == ""
+ and parts.netloc == ""
+ ):
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/mixed_prefix_method')
+def mixed_prefix_method():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ check = untrusted.startswith if request.args.get('safe-check') else untrusted.endswith
+ if (
+ untrusted.isprintable()
+ and not check("///")
+ and not urlparse(untrusted).scheme
+ and not urlparse(untrusted).netloc
+ ):
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)
+
+
+@app.route('/mixed_printable_method')
+def mixed_printable_method():
+ untrusted = request.args.get('target', '').replace("\\", "/").strip()
+ check = untrusted.isprintable if request.args.get('safe-check') else untrusted.isascii
+ if (
+ check()
+ and not untrusted.startswith("///")
+ and not urlparse(untrusted).scheme
+ and not urlparse(untrusted).netloc
+ ):
+ return redirect(untrusted, code=302) # $ Alert
+ return redirect("/", code=302)