From dabccd8686b0d77debd6993edd7101de48a978b4 Mon Sep 17 00:00:00 2001
From: intrigus
Date: Tue, 16 Aug 2022 23:56:50 +0200
Subject: [PATCH 001/991] Add query for tainted `wordexp` calls.
---
.../Security/CWE/CWE-078/WordexpTainted.c | 19 +++++++
.../Security/CWE/CWE-078/WordexpTainted.qhelp | 39 ++++++++++++++
.../Security/CWE/CWE-078/WordexpTainted.ql | 53 +++++++++++++++++++
.../CWE/CWE-078/WordexpTainted.expected | 11 ++++
.../Security/CWE/CWE-078/WordexpTainted.qlref | 1 +
.../query-tests/Security/CWE/CWE-078/test.cpp | 45 ++++++++++++++++
6 files changed, 168 insertions(+)
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-078/WordexpTainted.c
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-078/WordexpTainted.qhelp
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-078/WordexpTainted.ql
create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-078/WordexpTainted.expected
create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-078/WordexpTainted.qlref
create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-078/test.cpp
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-078/WordexpTainted.c b/cpp/ql/src/experimental/Security/CWE/CWE-078/WordexpTainted.c
new file mode 100644
index 00000000000..63cd5488f44
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-078/WordexpTainted.c
@@ -0,0 +1,19 @@
+
+int main(int argc, char** argv) {
+ char *filePath = argv[2];
+
+ {
+ // BAD: the user-controlled string is injected
+ // directly into `wordexp` which performs command substitution
+
+ wordexp_t we;
+ wordexp(filePath, &we, 0);
+ }
+
+ {
+ // GOOD: command substitution is disabled
+
+ wordexp_t we;
+ wordexp(filePath, &we, WRDE_NOCMD);
+ }
+}
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-078/WordexpTainted.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-078/WordexpTainted.qhelp
new file mode 100644
index 00000000000..a9e4b6b98b2
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-078/WordexpTainted.qhelp
@@ -0,0 +1,39 @@
+
+
+
+The code passes user input to wordexp. This leaves the code
+vulnerable to attack by command injection, because wordexp performs command substitution.
+
+
+
+
+When calling wordexp, pass the WRDE_NOCMD flag to to prevent command substitution.
+
+
+
+The following example passes a user-supplied file path to wordexp in two ways. The
+first way uses wordexp with no specified flags. As such, it is vulnerable to command
+injection.
+The second way uses wordexp with the WRDE_NOCMD flag. As such, no command substitution
+is performed, making this safe from command injection.
+
+
+
+
+
+CERT C Coding Standard:
+STR02-C.
+Sanitize data passed to complex subsystems.
+
+OWASP:
+Command Injection.
+
+
+
+
+
+
+
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-078/WordexpTainted.ql b/cpp/ql/src/experimental/Security/CWE/CWE-078/WordexpTainted.ql
new file mode 100644
index 00000000000..43922b79110
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-078/WordexpTainted.ql
@@ -0,0 +1,53 @@
+/**
+ * @name Uncontrolled data used in `wordexp` command
+ * @description Using user-supplied data in a `wordexp` command, without
+ * disabling command substitution, can make code vulnerable
+ * to command injection.
+ * @kind path-problem
+ * @problem.severity error
+ * @precision high
+ * @id cpp/wordexp-injection
+ * @tags security
+ * external/cwe/cwe-078
+ */
+
+import cpp
+import semmle.code.cpp.ir.dataflow.TaintTracking
+import semmle.code.cpp.security.FlowSources
+import DataFlow::PathGraph
+
+/**
+ * The `wordexp` function, which can perform command substitution.
+ */
+private class WordexpFunction extends Function {
+ WordexpFunction() { hasGlobalName("wordexp") }
+}
+
+/**
+ * Holds if `fc` disables command substitution by containing `WRDE_NOCMD` as a flag argument.
+ */
+private predicate isCommandSubstitutionDisabled(FunctionCall fc) {
+ fc.getArgument(2).getValue().toInt().bitAnd(4) = 4
+ /* 4 = WRDE_NOCMD. Check whether the flag is set. */
+}
+
+/**
+ * A configuration to track user-supplied data to the `wordexp` function.
+ */
+class WordexpTaintConfiguration extends TaintTracking::Configuration {
+ WordexpTaintConfiguration() { this = "WordexpTaintConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) { source instanceof FlowSource }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(FunctionCall fc | fc.getTarget() instanceof WordexpFunction |
+ fc.getArgument(0) = sink.asExpr() and
+ not isCommandSubstitutionDisabled(fc)
+ )
+ }
+}
+
+from WordexpTaintConfiguration conf, DataFlow::PathNode sourceNode, DataFlow::PathNode sinkNode
+where conf.hasFlowPath(sourceNode, sinkNode)
+select sinkNode.getNode(), sourceNode, sinkNode,
+ "Using user-supplied data in a `wordexp` command, without disabling command substitution, can make code vulnerable to command injection."
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-078/WordexpTainted.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-078/WordexpTainted.expected
new file mode 100644
index 00000000000..a8d7a480c81
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-078/WordexpTainted.expected
@@ -0,0 +1,11 @@
+edges
+| test.cpp:23:20:23:23 | argv | test.cpp:29:13:29:20 | (const char *)... |
+| test.cpp:23:20:23:23 | argv | test.cpp:29:13:29:20 | filePath |
+nodes
+| test.cpp:23:20:23:23 | argv | semmle.label | argv |
+| test.cpp:29:13:29:20 | (const char *)... | semmle.label | (const char *)... |
+| test.cpp:29:13:29:20 | filePath | semmle.label | filePath |
+subpaths
+#select
+| test.cpp:29:13:29:20 | (const char *)... | test.cpp:23:20:23:23 | argv | test.cpp:29:13:29:20 | (const char *)... | Using user-supplied data in a `wordexp` command, without disabling command substitution, can make code vulnerable to command injection. |
+| test.cpp:29:13:29:20 | filePath | test.cpp:23:20:23:23 | argv | test.cpp:29:13:29:20 | filePath | Using user-supplied data in a `wordexp` command, without disabling command substitution, can make code vulnerable to command injection. |
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-078/WordexpTainted.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-078/WordexpTainted.qlref
new file mode 100644
index 00000000000..ecff539f3e6
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-078/WordexpTainted.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-078/WordexpTainted.ql
\ No newline at end of file
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-078/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-078/test.cpp
new file mode 100644
index 00000000000..0ae98b8f163
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-078/test.cpp
@@ -0,0 +1,45 @@
+#ifdef _MSC_VER
+#define restrict __restrict
+#else
+#define restrict __restrict__
+#endif
+
+typedef unsigned long size_t;
+
+typedef struct {
+ size_t we_wordc;
+ char **we_wordv;
+ size_t we_offs;
+} wordexp_t;
+
+enum {
+ WRDE_APPEND = (1 << 1),
+ WRDE_NOCMD = (1 << 2)
+};
+
+int wordexp(const char *restrict s, wordexp_t *restrict p, int flags);
+
+int main(int argc, char** argv) {
+ char *filePath = argv[2];
+
+ {
+ // BAD: the user string is injected directly into `wordexp` which performs command substitution
+
+ wordexp_t we;
+ wordexp(filePath, &we, 0);
+ }
+
+ {
+ // GOOD: command substitution is disabled
+
+ wordexp_t we;
+ wordexp(filePath, &we, WRDE_NOCMD);
+ }
+
+ {
+ // GOOD: command substitution is disabled
+
+ wordexp_t we;
+ wordexp(filePath, &we, WRDE_NOCMD | WRDE_APPEND);
+ }
+}
From 45f708bb586b1e5bb8f0d6e1ebc015f1fa192253 Mon Sep 17 00:00:00 2001
From: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com>
Date: Wed, 17 Aug 2022 00:00:32 +0200
Subject: [PATCH 002/991] Fix typo.
---
.../src/experimental/Security/CWE/CWE-078/WordexpTainted.qhelp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-078/WordexpTainted.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-078/WordexpTainted.qhelp
index a9e4b6b98b2..d4f77ef1e3d 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-078/WordexpTainted.qhelp
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-078/WordexpTainted.qhelp
@@ -9,7 +9,7 @@ vulnerable to attack by command injection, because wordexp performs
-When calling wordexp, pass the WRDE_NOCMD flag to to prevent command substitution.
+When calling wordexp, pass the WRDE_NOCMD flag to prevent command substitution.
From f978951cbe119735f5b2be0f2cec66bfd45a08ad Mon Sep 17 00:00:00 2001
From: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com>
Date: Wed, 17 Aug 2022 22:30:43 +0200
Subject: [PATCH 003/991] Explain command substitution
---
.../experimental/Security/CWE/CWE-078/WordexpTainted.qhelp | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-078/WordexpTainted.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-078/WordexpTainted.qhelp
index d4f77ef1e3d..6dd9662c57d 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-078/WordexpTainted.qhelp
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-078/WordexpTainted.qhelp
@@ -4,7 +4,10 @@
The code passes user input to wordexp. This leaves the code
-vulnerable to attack by command injection, because wordexp performs command substitution.
+vulnerable to attack by command injection, because wordexp performs command substitution.
+Command substitution is a feature that replaces $(command) or `command` with the
+output of the given command, allowing the user to run arbitrary code on the system.
+
From f84035a65cfcf05a7f014becf0ce8053ef82b3b7 Mon Sep 17 00:00:00 2001
From: Alex Ford
Date: Fri, 9 Sep 2022 17:44:33 +0100
Subject: [PATCH 004/991] Ruby: add `rb/sensitive-get-query` query
---
ruby/ql/lib/codeql/ruby/Concepts.qll | 24 ++++
.../ruby/frameworks/ActionController.qll | 2 +
.../ql/lib/codeql/ruby/frameworks/GraphQL.qll | 9 ++
.../codeql/ruby/security/SensitiveActions.qll | 124 ++++++++++++++++++
.../2022-09-10-sensitive-get-query.md | 4 +
.../security/cwe-598/SensitiveGetQuery.qhelp | 43 ++++++
.../security/cwe-598/SensitiveGetQuery.ql | 44 +++++++
.../security/cwe-598/examples/routes.rb | 4 +
.../cwe-598/examples/users_controller.rb | 16 +++
.../cwe-598/SensitiveGetQuery.expected | 2 +
.../security/cwe-598/SensitiveGetQuery.qlref | 1 +
.../app/controllers/users_controller.rb | 17 +++
.../security/cwe-598/config/routes.rb | 5 +
13 files changed, 295 insertions(+)
create mode 100644 ruby/ql/src/change-notes/2022-09-10-sensitive-get-query.md
create mode 100644 ruby/ql/src/queries/security/cwe-598/SensitiveGetQuery.qhelp
create mode 100644 ruby/ql/src/queries/security/cwe-598/SensitiveGetQuery.ql
create mode 100644 ruby/ql/src/queries/security/cwe-598/examples/routes.rb
create mode 100644 ruby/ql/src/queries/security/cwe-598/examples/users_controller.rb
create mode 100644 ruby/ql/test/query-tests/security/cwe-598/SensitiveGetQuery.expected
create mode 100644 ruby/ql/test/query-tests/security/cwe-598/SensitiveGetQuery.qlref
create mode 100644 ruby/ql/test/query-tests/security/cwe-598/app/controllers/users_controller.rb
create mode 100644 ruby/ql/test/query-tests/security/cwe-598/config/routes.rb
diff --git a/ruby/ql/lib/codeql/ruby/Concepts.qll b/ruby/ql/lib/codeql/ruby/Concepts.qll
index 214f6734473..582b3bb7fdb 100644
--- a/ruby/ql/lib/codeql/ruby/Concepts.qll
+++ b/ruby/ql/lib/codeql/ruby/Concepts.qll
@@ -250,6 +250,11 @@ module HTTP {
/** Gets a string that identifies the framework used for this route setup. */
string getFramework() { result = super.getFramework() }
+
+ /**
+ * Gets the HTTP method name, in lowercase, that this handler will respond to.
+ */
+ string getHttpMethod() { result = super.getHttpMethod() }
}
/** Provides a class for modeling new HTTP routing APIs. */
@@ -287,6 +292,11 @@ module HTTP {
/** Gets a string that identifies the framework used for this route setup. */
abstract string getFramework();
+
+ /**
+ * Gets the HTTP method name, in all caps, that this handler will respond to.
+ */
+ abstract string getHttpMethod();
}
}
@@ -343,6 +353,12 @@ module HTTP {
/** Gets a string that identifies the framework used for this route setup. */
string getFramework() { result = super.getFramework() }
+
+ /**
+ * Gets an HTTP method name, in all caps, that this handler will respond to.
+ * Handlers can potentially respond to multiple HTTP methods.
+ */
+ string getAnHttpMethod() { result = super.getAnHttpMethod() }
}
/** Provides a class for modeling new HTTP request handlers. */
@@ -364,6 +380,12 @@ module HTTP {
/** Gets a string that identifies the framework used for this request handler. */
abstract string getFramework();
+
+ /**
+ * Gets an HTTP method name, in all caps, that this handler will respond to.
+ * Handlers can potentially respond to multiple HTTP methods.
+ */
+ abstract string getAnHttpMethod();
}
}
@@ -378,6 +400,8 @@ module HTTP {
}
override string getFramework() { result = rs.getFramework() }
+
+ override string getAnHttpMethod() { result = rs.getHttpMethod() }
}
/** A parameter that will receive parts of the url when handling an incoming request. */
diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll
index d607612364f..36414d0b84b 100644
--- a/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll
+++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll
@@ -67,6 +67,8 @@ class ActionControllerActionMethod extends Method, HTTP::Server::RequestHandler:
override string getFramework() { result = "ActionController" }
+ override string getAnHttpMethod() { result = this.getARoute().getHttpMethod() }
+
/** Gets a call to render from within this method. */
RenderCall getARenderCall() { result.getParent+() = this }
diff --git a/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll b/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll
index ede99069213..f17e96a8d83 100644
--- a/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll
+++ b/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll
@@ -85,6 +85,9 @@ private class GraphqlSchemaResolverClass extends ClassDeclaration {
}
}
+/** Gets an HTTP method that is supported for querying a GraphQL server. */
+private string getASupportedHTTPMethod() { result = ["get", "post"] }
+
/**
* A `ClassDeclaration` for a class that extends `GraphQL::Schema::Object`.
* For example,
@@ -173,6 +176,8 @@ class GraphqlResolveMethod extends Method, HTTP::Server::RequestHandler::Range {
override string getFramework() { result = "GraphQL" }
+ override string getAnHttpMethod() { result = getASupportedHTTPMethod() }
+
/** Gets the mutation class containing this method. */
GraphqlResolvableClass getMutationClass() { result = resolvableClass }
}
@@ -220,6 +225,8 @@ class GraphqlLoadMethod extends Method, HTTP::Server::RequestHandler::Range {
override string getFramework() { result = "GraphQL" }
+ override string getAnHttpMethod() { result = getASupportedHTTPMethod() }
+
/** Gets the mutation class containing this method. */
GraphqlResolvableClass getMutationClass() { result = resolvableClass }
}
@@ -389,6 +396,8 @@ class GraphqlFieldResolutionMethod extends Method, HTTP::Server::RequestHandler:
override string getFramework() { result = "GraphQL" }
+ override string getAnHttpMethod() { result = getASupportedHTTPMethod() }
+
/** Gets the class containing this method. */
GraphqlSchemaObjectClass getGraphqlClass() { result = schemaObjectClass }
}
diff --git a/ruby/ql/lib/codeql/ruby/security/SensitiveActions.qll b/ruby/ql/lib/codeql/ruby/security/SensitiveActions.qll
index dcdd0989e14..483dcad8d76 100644
--- a/ruby/ql/lib/codeql/ruby/security/SensitiveActions.qll
+++ b/ruby/ql/lib/codeql/ruby/security/SensitiveActions.qll
@@ -11,6 +11,130 @@
private import codeql.ruby.AST
private import codeql.ruby.DataFlow
+import codeql.ruby.security.internal.SensitiveDataHeuristics
+private import HeuristicNames
+
+/** An expression that might contain sensitive data. */
+cached
+abstract class SensitiveExpr extends Expr {
+ /** Gets a human-readable description of this expression for use in alert messages. */
+ cached
+ abstract string describe();
+
+ /** Gets a classification of the kind of sensitive data this expression might contain. */
+ cached
+ abstract SensitiveDataClassification getClassification();
+}
+
+/** A method call that might produce sensitive data. */
+class SensitiveCall extends SensitiveExpr, MethodCall {
+ SensitiveDataClassification classification;
+
+ SensitiveCall() {
+ classification = this.getMethodName().(SensitiveDataMethodName).getClassification()
+ or
+ // This is particularly to pick up methods with an argument like "password", which
+ // may indicate a lookup.
+ exists(string s | this.getAnArgument().getConstantValue().isStringlikeValue(s) |
+ nameIndicatesSensitiveData(s, classification)
+ )
+ }
+
+ override string describe() { result = "a call to " + this.getMethodName() }
+
+ override SensitiveDataClassification getClassification() { result = classification }
+}
+
+/** An access to a variable or hash value that might contain sensitive data. */
+abstract class SensitiveVariableAccess extends SensitiveExpr {
+ string name;
+
+ SensitiveVariableAccess() {
+ this.(VariableAccess).getVariable().hasName(name)
+ or
+ this.(ElementReference).getAnArgument().getConstantValue().isStringlikeValue(name)
+ }
+
+ override string describe() { result = "an access to " + name }
+}
+
+/** A write to a location that might contain sensitive data. */
+abstract class SensitiveWrite extends DataFlow::Node { }
+
+/**
+ * Holds if `node` is a write to a variable or hash value named `name`.
+ *
+ * Helper predicate factored out for performance,
+ * to filter `name` as much as possible before using it in
+ * regex matching.
+ */
+pragma[nomagic]
+private predicate writesProperty(DataFlow::Node node, string name) {
+ exists(VariableWriteAccess vwa | vwa.getVariable().getName() = name |
+ node.asExpr().getExpr() = vwa
+ )
+ or
+ // hash value assignment
+ node.(DataFlow::CallNode).getMethodName() = "[]=" and
+ node.(DataFlow::CallNode).getArgument(0).asExpr().getConstantValue().isStringlikeValue(name)
+}
+
+/** A write to a variable or property that might contain sensitive data. */
+private class BasicSensitiveWrite extends SensitiveWrite {
+ SensitiveDataClassification classification;
+
+ BasicSensitiveWrite() {
+ exists(string name |
+ /*
+ * PERFORMANCE OPTIMISATION:
+ * `nameIndicatesSensitiveData` performs a `regexpMatch` on `name`.
+ * To carry out a regex match, we must first compute the Cartesian product
+ * of all possible `name`s and regexes, then match.
+ * To keep this product as small as possible,
+ * we want to filter `name` as much as possible before the product.
+ *
+ * Do this by factoring out a helper predicate containing the filtering
+ * logic that restricts `name`. This helper predicate will get picked first
+ * in the join order, since it is the only call here that binds `name`.
+ */
+
+ writesProperty(this, name) and
+ nameIndicatesSensitiveData(name, classification)
+ )
+ }
+
+ /** Gets a classification of the kind of sensitive data the write might handle. */
+ SensitiveDataClassification getClassification() { result = classification }
+}
+
+/** An access to a variable or hash value that might contain sensitive data. */
+private class BasicSensitiveVariableAccess extends SensitiveVariableAccess {
+ SensitiveDataClassification classification;
+
+ BasicSensitiveVariableAccess() { nameIndicatesSensitiveData(name, classification) }
+
+ override SensitiveDataClassification getClassification() { result = classification }
+}
+
+/** A method name that suggests it may be sensitive. */
+abstract class SensitiveMethodName extends string {
+ SensitiveMethodName() { this = any(MethodBase m).getName() }
+}
+
+/** A method name that suggests it may produce sensitive data. */
+abstract class SensitiveDataMethodName extends SensitiveMethodName {
+ /** Gets a classification of the kind of sensitive data this method may produce. */
+ abstract SensitiveDataClassification getClassification();
+}
+
+/** A method name that might return sensitive credential data. */
+class CredentialsMethodName extends SensitiveDataMethodName {
+ SensitiveDataClassification classification;
+
+ CredentialsMethodName() { nameIndicatesSensitiveData(this, classification) }
+
+ override SensitiveDataClassification getClassification() { result = classification }
+}
/**
* A sensitive action, such as transfer of sensitive data.
diff --git a/ruby/ql/src/change-notes/2022-09-10-sensitive-get-query.md b/ruby/ql/src/change-notes/2022-09-10-sensitive-get-query.md
new file mode 100644
index 00000000000..9d51950d1e4
--- /dev/null
+++ b/ruby/ql/src/change-notes/2022-09-10-sensitive-get-query.md
@@ -0,0 +1,4 @@
+---
+category: newQuery
+---
+* Added a new query, `rb/sensitive-get-query`, to detect cases where sensitive data is read from the query parameters of an HTTP `GET` request.
diff --git a/ruby/ql/src/queries/security/cwe-598/SensitiveGetQuery.qhelp b/ruby/ql/src/queries/security/cwe-598/SensitiveGetQuery.qhelp
new file mode 100644
index 00000000000..cbd8f0730f6
--- /dev/null
+++ b/ruby/ql/src/queries/security/cwe-598/SensitiveGetQuery.qhelp
@@ -0,0 +1,43 @@
+
+
+
+
+Sensitive information such as user passwords should not be transmitted within the query string of the requested URL.
+Sensitive information within URLs may be logged in various locations, including the user's browser, the web server,
+and any forward or reverse proxy servers between the two endpoints. URLs may also be displayed on-screen, bookmarked
+or emailed around by users. They may be disclosed to third parties via the Referer header when any off-site links are
+followed. Placing sensitive information into the URL therefore increases the risk that it will be captured by an attacker.
+
+
+
+
+
+Use HTTP POST to send sensitive information as part of the request body; for example, as form data.
+
+
+
+
+
+The following example shows two route handlers that both receive a username and a password.
+The first receives this sensitive information from the query parameters of a GET request, which is
+transmitted in the URL. The second receives this sensitive information from the request body of a POST request.
+
+
+
+
+
+
+
+ CWE:
+ CWE-598: Use of GET Request Method with Sensitive Query Strings
+
+
+ PortSwigger (Burp):
+ Password Submitted using GET Method
+
+
+ OWASP:
+ Information Exposure through Query Strings in URL
+
+
+
diff --git a/ruby/ql/src/queries/security/cwe-598/SensitiveGetQuery.ql b/ruby/ql/src/queries/security/cwe-598/SensitiveGetQuery.ql
new file mode 100644
index 00000000000..fc1ed0bea9c
--- /dev/null
+++ b/ruby/ql/src/queries/security/cwe-598/SensitiveGetQuery.ql
@@ -0,0 +1,44 @@
+/**
+ * @name Sensitive data read from GET request
+ * @description Placing sensitive data in a GET request increases the risk of
+ * the data being exposed to an attacker.
+ * @kind problem
+ * @problem.severity warning
+ * @security-severity 6.5
+ * @precision high
+ * @id rb/sensitive-get-query
+ * @tags security
+ * external/cwe/cwe-598
+ */
+
+import ruby
+private import codeql.ruby.DataFlow
+private import codeql.ruby.security.SensitiveActions
+private import codeql.ruby.Concepts
+private import codeql.ruby.frameworks.ActionDispatch
+private import codeql.ruby.frameworks.ActionController
+private import codeql.ruby.frameworks.core.Array
+
+// Local flow augmented with flow through element references
+private predicate localFlowWithElementReference(DataFlow::LocalSourceNode src, DataFlow::Node to) {
+ src.flowsTo(to)
+ or
+ exists(DataFlow::Node midRecv, DataFlow::LocalSourceNode mid, ElementReference ref |
+ src.flowsTo(midRecv) and
+ midRecv.asExpr().getExpr() = ref.getReceiver() and
+ mid.asExpr().getExpr() = ref
+ |
+ localFlowWithElementReference(mid, to)
+ )
+}
+
+from
+ HTTP::Server::RequestHandler handler, HTTP::Server::RequestInputAccess input,
+ DataFlow::Node sensitive
+where
+ handler.getAnHttpMethod() = "get" and
+ input.asExpr().getExpr().getEnclosingMethod() = handler and
+ sensitive.asExpr().getExpr() instanceof SensitiveExpr and
+ localFlowWithElementReference(input, sensitive)
+select input, "$@ for GET requests uses query parameter as sensitive data.", handler,
+ "Request handler"
diff --git a/ruby/ql/src/queries/security/cwe-598/examples/routes.rb b/ruby/ql/src/queries/security/cwe-598/examples/routes.rb
new file mode 100644
index 00000000000..74370fe3bbb
--- /dev/null
+++ b/ruby/ql/src/queries/security/cwe-598/examples/routes.rb
@@ -0,0 +1,4 @@
+Rails.application.routes.draw do
+ get "users/login", to: "#login_get" # BAD: sensitive data transmitted through query parameters
+ post "users/login", to: "users#login_post" # GOOD: sensitive data transmitted in the request body
+end
diff --git a/ruby/ql/src/queries/security/cwe-598/examples/users_controller.rb b/ruby/ql/src/queries/security/cwe-598/examples/users_controller.rb
new file mode 100644
index 00000000000..ab720ba174d
--- /dev/null
+++ b/ruby/ql/src/queries/security/cwe-598/examples/users_controller.rb
@@ -0,0 +1,16 @@
+class UsersController < ActionController::Base
+ def login_get
+ password = params[:password]
+ authenticate_user(params[:username], password)
+ end
+
+ def login_post
+ password = params[:password]
+ authenticate_user(params[:username], password)
+ end
+
+ private
+ def authenticate_user(username, password)
+ # ... authenticate the user here
+ end
+end
diff --git a/ruby/ql/test/query-tests/security/cwe-598/SensitiveGetQuery.expected b/ruby/ql/test/query-tests/security/cwe-598/SensitiveGetQuery.expected
new file mode 100644
index 00000000000..2df6239372d
--- /dev/null
+++ b/ruby/ql/test/query-tests/security/cwe-598/SensitiveGetQuery.expected
@@ -0,0 +1,2 @@
+| app/controllers/users_controller.rb:4:16:4:21 | call to params | $@ for GET requests uses query parameter as sensitive data. | app/controllers/users_controller.rb:3:3:6:5 | login_get | Request handler |
+| app/controllers/users_controller.rb:5:23:5:28 | call to params | $@ for GET requests uses query parameter as sensitive data. | app/controllers/users_controller.rb:3:3:6:5 | login_get | Request handler |
diff --git a/ruby/ql/test/query-tests/security/cwe-598/SensitiveGetQuery.qlref b/ruby/ql/test/query-tests/security/cwe-598/SensitiveGetQuery.qlref
new file mode 100644
index 00000000000..98d0d8e6be7
--- /dev/null
+++ b/ruby/ql/test/query-tests/security/cwe-598/SensitiveGetQuery.qlref
@@ -0,0 +1 @@
+queries/security/cwe-598/SensitiveGetQuery.ql
\ No newline at end of file
diff --git a/ruby/ql/test/query-tests/security/cwe-598/app/controllers/users_controller.rb b/ruby/ql/test/query-tests/security/cwe-598/app/controllers/users_controller.rb
new file mode 100644
index 00000000000..7788894f98a
--- /dev/null
+++ b/ruby/ql/test/query-tests/security/cwe-598/app/controllers/users_controller.rb
@@ -0,0 +1,17 @@
+class UsersController < ApplicationController
+
+ def login_get
+ password = params[:password] # BAD: route handler uses GET query parameters to receive sensitive data
+ authenticate_user(params[:username], password) # BAD: route handler uses GET query parameters to receive sensitive data
+ end
+
+ def login_post
+ password = params[:password] # GOOD: handler uses POST form parameters to receive sensitive data
+ authenticate_user(params[:username], password) # GOOD: handler uses POST form parameters to receive sensitive data
+ end
+
+ private
+ def authenticate_user(username, password)
+ # ... authenticate the user here
+ end
+end
diff --git a/ruby/ql/test/query-tests/security/cwe-598/config/routes.rb b/ruby/ql/test/query-tests/security/cwe-598/config/routes.rb
new file mode 100644
index 00000000000..8853aad320b
--- /dev/null
+++ b/ruby/ql/test/query-tests/security/cwe-598/config/routes.rb
@@ -0,0 +1,5 @@
+Rails.application.routes.draw do
+ match "users/login1", to: "users#login_get", via: :get
+ get "users/login2", to: "users#login_get"
+ post "users/login3", to: "users#login_post"
+end
From 0da367f6e57428a7bf165782cbcd5eb6dcc0ee01 Mon Sep 17 00:00:00 2001
From: Alex Ford
Date: Mon, 12 Sep 2022 08:52:44 +0100
Subject: [PATCH 005/991] Ruby: address QL4QL alerts for rb/sensitive-get-query
---
ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll | 8 ++++----
ruby/ql/src/queries/security/cwe-598/SensitiveGetQuery.ql | 2 +-
.../security/cwe-598/SensitiveGetQuery.expected | 4 ++--
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll b/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll
index f17e96a8d83..2f98853ebbf 100644
--- a/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll
+++ b/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll
@@ -86,7 +86,7 @@ private class GraphqlSchemaResolverClass extends ClassDeclaration {
}
/** Gets an HTTP method that is supported for querying a GraphQL server. */
-private string getASupportedHTTPMethod() { result = ["get", "post"] }
+private string getASupportedHttpMethod() { result = ["get", "post"] }
/**
* A `ClassDeclaration` for a class that extends `GraphQL::Schema::Object`.
@@ -176,7 +176,7 @@ class GraphqlResolveMethod extends Method, HTTP::Server::RequestHandler::Range {
override string getFramework() { result = "GraphQL" }
- override string getAnHttpMethod() { result = getASupportedHTTPMethod() }
+ override string getAnHttpMethod() { result = getASupportedHttpMethod() }
/** Gets the mutation class containing this method. */
GraphqlResolvableClass getMutationClass() { result = resolvableClass }
@@ -225,7 +225,7 @@ class GraphqlLoadMethod extends Method, HTTP::Server::RequestHandler::Range {
override string getFramework() { result = "GraphQL" }
- override string getAnHttpMethod() { result = getASupportedHTTPMethod() }
+ override string getAnHttpMethod() { result = getASupportedHttpMethod() }
/** Gets the mutation class containing this method. */
GraphqlResolvableClass getMutationClass() { result = resolvableClass }
@@ -396,7 +396,7 @@ class GraphqlFieldResolutionMethod extends Method, HTTP::Server::RequestHandler:
override string getFramework() { result = "GraphQL" }
- override string getAnHttpMethod() { result = getASupportedHTTPMethod() }
+ override string getAnHttpMethod() { result = getASupportedHttpMethod() }
/** Gets the class containing this method. */
GraphqlSchemaObjectClass getGraphqlClass() { result = schemaObjectClass }
diff --git a/ruby/ql/src/queries/security/cwe-598/SensitiveGetQuery.ql b/ruby/ql/src/queries/security/cwe-598/SensitiveGetQuery.ql
index fc1ed0bea9c..7177668b06b 100644
--- a/ruby/ql/src/queries/security/cwe-598/SensitiveGetQuery.ql
+++ b/ruby/ql/src/queries/security/cwe-598/SensitiveGetQuery.ql
@@ -41,4 +41,4 @@ where
sensitive.asExpr().getExpr() instanceof SensitiveExpr and
localFlowWithElementReference(input, sensitive)
select input, "$@ for GET requests uses query parameter as sensitive data.", handler,
- "Request handler"
+ "Route handler"
diff --git a/ruby/ql/test/query-tests/security/cwe-598/SensitiveGetQuery.expected b/ruby/ql/test/query-tests/security/cwe-598/SensitiveGetQuery.expected
index 2df6239372d..a851b810cb6 100644
--- a/ruby/ql/test/query-tests/security/cwe-598/SensitiveGetQuery.expected
+++ b/ruby/ql/test/query-tests/security/cwe-598/SensitiveGetQuery.expected
@@ -1,2 +1,2 @@
-| app/controllers/users_controller.rb:4:16:4:21 | call to params | $@ for GET requests uses query parameter as sensitive data. | app/controllers/users_controller.rb:3:3:6:5 | login_get | Request handler |
-| app/controllers/users_controller.rb:5:23:5:28 | call to params | $@ for GET requests uses query parameter as sensitive data. | app/controllers/users_controller.rb:3:3:6:5 | login_get | Request handler |
+| app/controllers/users_controller.rb:4:16:4:21 | call to params | $@ for GET requests uses query parameter as sensitive data. | app/controllers/users_controller.rb:3:3:6:5 | login_get | Route handler |
+| app/controllers/users_controller.rb:5:23:5:28 | call to params | $@ for GET requests uses query parameter as sensitive data. | app/controllers/users_controller.rb:3:3:6:5 | login_get | Route handler |
From 894a0f1c3b7083a5db426c6ba3d4243ef98f27da Mon Sep 17 00:00:00 2001
From: intrigus
Date: Mon, 12 Sep 2022 21:02:18 +0200
Subject: [PATCH 006/991] Add string to int sanitizer.
---
.../src/experimental/Security/CWE/CWE-078/WordexpTainted.ql | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-078/WordexpTainted.ql b/cpp/ql/src/experimental/Security/CWE/CWE-078/WordexpTainted.ql
index 43922b79110..40b61ff60f6 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-078/WordexpTainted.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-078/WordexpTainted.ql
@@ -45,6 +45,10 @@ class WordexpTaintConfiguration extends TaintTracking::Configuration {
not isCommandSubstitutionDisabled(fc)
)
}
+
+ override predicate isSanitizer(DataFlow::Node node) {
+ node.asExpr().getUnspecifiedType() instanceof IntegralType
+ }
}
from WordexpTaintConfiguration conf, DataFlow::PathNode sourceNode, DataFlow::PathNode sinkNode
From 79ad7d293f1c0cbd2b21f5320cad697da3324137 Mon Sep 17 00:00:00 2001
From: Alex Ford
Date: Wed, 14 Sep 2022 17:05:58 +0100
Subject: [PATCH 007/991] Ruby: make SensitiveExpr a dataflow node rather than
an Expr
---
.../lib/codeql/ruby/controlflow/CfgNodes.qll | 9 +++++++++
.../codeql/ruby/security/SensitiveActions.qll | 19 ++++++++++++-------
.../security/cwe-598/SensitiveGetQuery.ql | 3 +--
3 files changed, 22 insertions(+), 9 deletions(-)
diff --git a/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll
index 67288094072..b8284cfef80 100644
--- a/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll
+++ b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll
@@ -684,6 +684,15 @@ module ExprNodes {
final ExprCfgNode getValue() { e.hasCfgChild(e.getValue(), this, result) }
}
+ /** A control-flow node that wraps a `VariableAccess` AST expression. */
+ class VariableAccessCfgNode extends ExprCfgNode {
+ override string getAPrimaryQlClass() { result = "VariableAccessCfgNode" }
+
+ override VariableAccess e;
+
+ final override VariableAccess getExpr() { result = ExprCfgNode.super.getExpr() }
+ }
+
/** A control-flow node that wraps a `VariableReadAccess` AST expression. */
class VariableReadAccessCfgNode extends ExprCfgNode {
override string getAPrimaryQlClass() { result = "VariableReadAccessCfgNode" }
diff --git a/ruby/ql/lib/codeql/ruby/security/SensitiveActions.qll b/ruby/ql/lib/codeql/ruby/security/SensitiveActions.qll
index 483dcad8d76..13317a8684d 100644
--- a/ruby/ql/lib/codeql/ruby/security/SensitiveActions.qll
+++ b/ruby/ql/lib/codeql/ruby/security/SensitiveActions.qll
@@ -13,10 +13,11 @@ private import codeql.ruby.AST
private import codeql.ruby.DataFlow
import codeql.ruby.security.internal.SensitiveDataHeuristics
private import HeuristicNames
+private import codeql.ruby.CFG
/** An expression that might contain sensitive data. */
cached
-abstract class SensitiveExpr extends Expr {
+abstract class SensitiveNode extends DataFlow::Node {
/** Gets a human-readable description of this expression for use in alert messages. */
cached
abstract string describe();
@@ -27,7 +28,7 @@ abstract class SensitiveExpr extends Expr {
}
/** A method call that might produce sensitive data. */
-class SensitiveCall extends SensitiveExpr, MethodCall {
+class SensitiveCall extends SensitiveNode instanceof DataFlow::CallNode {
SensitiveDataClassification classification;
SensitiveCall() {
@@ -35,24 +36,28 @@ class SensitiveCall extends SensitiveExpr, MethodCall {
or
// This is particularly to pick up methods with an argument like "password", which
// may indicate a lookup.
- exists(string s | this.getAnArgument().getConstantValue().isStringlikeValue(s) |
+ exists(string s | super.getArgument(_).asExpr().getConstantValue().isStringlikeValue(s) |
nameIndicatesSensitiveData(s, classification)
)
}
- override string describe() { result = "a call to " + this.getMethodName() }
+ override string describe() { result = "a call to " + super.getMethodName() }
override SensitiveDataClassification getClassification() { result = classification }
}
/** An access to a variable or hash value that might contain sensitive data. */
-abstract class SensitiveVariableAccess extends SensitiveExpr {
+abstract class SensitiveVariableAccess extends SensitiveNode {
string name;
SensitiveVariableAccess() {
- this.(VariableAccess).getVariable().hasName(name)
+ this.asExpr().(CfgNodes::ExprNodes::VariableAccessCfgNode).getExpr().getVariable().hasName(name)
or
- this.(ElementReference).getAnArgument().getConstantValue().isStringlikeValue(name)
+ this.asExpr()
+ .(CfgNodes::ExprNodes::ElementReferenceCfgNode)
+ .getAnArgument()
+ .getConstantValue()
+ .isStringlikeValue(name)
}
override string describe() { result = "an access to " + name }
diff --git a/ruby/ql/src/queries/security/cwe-598/SensitiveGetQuery.ql b/ruby/ql/src/queries/security/cwe-598/SensitiveGetQuery.ql
index 7177668b06b..848f84c7fff 100644
--- a/ruby/ql/src/queries/security/cwe-598/SensitiveGetQuery.ql
+++ b/ruby/ql/src/queries/security/cwe-598/SensitiveGetQuery.ql
@@ -34,11 +34,10 @@ private predicate localFlowWithElementReference(DataFlow::LocalSourceNode src, D
from
HTTP::Server::RequestHandler handler, HTTP::Server::RequestInputAccess input,
- DataFlow::Node sensitive
+ SensitiveNode sensitive
where
handler.getAnHttpMethod() = "get" and
input.asExpr().getExpr().getEnclosingMethod() = handler and
- sensitive.asExpr().getExpr() instanceof SensitiveExpr and
localFlowWithElementReference(input, sensitive)
select input, "$@ for GET requests uses query parameter as sensitive data.", handler,
"Route handler"
From 08c8db89374be9bc7105035e7ce23726847d9737 Mon Sep 17 00:00:00 2001
From: Alex Ford
Date: Fri, 16 Sep 2022 15:36:57 +0100
Subject: [PATCH 008/991] Ruby: stop rb/sensitive-get-query from considering ID
type data as sensitive
---
ruby/ql/src/queries/security/cwe-598/SensitiveGetQuery.ql | 3 ++-
.../query-tests/security/cwe-598/SensitiveGetQuery.expected | 1 -
.../security/cwe-598/app/controllers/users_controller.rb | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/ruby/ql/src/queries/security/cwe-598/SensitiveGetQuery.ql b/ruby/ql/src/queries/security/cwe-598/SensitiveGetQuery.ql
index 848f84c7fff..5fc271dd991 100644
--- a/ruby/ql/src/queries/security/cwe-598/SensitiveGetQuery.ql
+++ b/ruby/ql/src/queries/security/cwe-598/SensitiveGetQuery.ql
@@ -38,6 +38,7 @@ from
where
handler.getAnHttpMethod() = "get" and
input.asExpr().getExpr().getEnclosingMethod() = handler and
- localFlowWithElementReference(input, sensitive)
+ localFlowWithElementReference(input, sensitive) and
+ not sensitive.getClassification() = SensitiveDataClassification::id()
select input, "$@ for GET requests uses query parameter as sensitive data.", handler,
"Route handler"
diff --git a/ruby/ql/test/query-tests/security/cwe-598/SensitiveGetQuery.expected b/ruby/ql/test/query-tests/security/cwe-598/SensitiveGetQuery.expected
index a851b810cb6..9563e1eefae 100644
--- a/ruby/ql/test/query-tests/security/cwe-598/SensitiveGetQuery.expected
+++ b/ruby/ql/test/query-tests/security/cwe-598/SensitiveGetQuery.expected
@@ -1,2 +1 @@
| app/controllers/users_controller.rb:4:16:4:21 | call to params | $@ for GET requests uses query parameter as sensitive data. | app/controllers/users_controller.rb:3:3:6:5 | login_get | Route handler |
-| app/controllers/users_controller.rb:5:23:5:28 | call to params | $@ for GET requests uses query parameter as sensitive data. | app/controllers/users_controller.rb:3:3:6:5 | login_get | Route handler |
diff --git a/ruby/ql/test/query-tests/security/cwe-598/app/controllers/users_controller.rb b/ruby/ql/test/query-tests/security/cwe-598/app/controllers/users_controller.rb
index 7788894f98a..80a13184737 100644
--- a/ruby/ql/test/query-tests/security/cwe-598/app/controllers/users_controller.rb
+++ b/ruby/ql/test/query-tests/security/cwe-598/app/controllers/users_controller.rb
@@ -2,12 +2,12 @@ class UsersController < ApplicationController
def login_get
password = params[:password] # BAD: route handler uses GET query parameters to receive sensitive data
- authenticate_user(params[:username], password) # BAD: route handler uses GET query parameters to receive sensitive data
+ authenticate_user(params[:username], password)
end
def login_post
password = params[:password] # GOOD: handler uses POST form parameters to receive sensitive data
- authenticate_user(params[:username], password) # GOOD: handler uses POST form parameters to receive sensitive data
+ authenticate_user(params[:username], password)
end
private
From 00891fa45518e2ad0b2c969868c5bccc4bd0a2b8 Mon Sep 17 00:00:00 2001
From: Ed Minnix
Date: Mon, 19 Sep 2022 10:31:02 -0400
Subject: [PATCH 009/991] Android Manifest Incomplete provider permissions
initial commit
Initial work on checking provider elements in Android manifests for
complete permissions.
---
.../ContentProviderIncompletePermissions.ql | 23 +++++++++++++++++++
1 file changed, 23 insertions(+)
create mode 100644 java/ql/src/Security/CWE/CWE-276/ContentProviderIncompletePermissions.ql
diff --git a/java/ql/src/Security/CWE/CWE-276/ContentProviderIncompletePermissions.ql b/java/ql/src/Security/CWE/CWE-276/ContentProviderIncompletePermissions.ql
new file mode 100644
index 00000000000..647b3b707c2
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-276/ContentProviderIncompletePermissions.ql
@@ -0,0 +1,23 @@
+/**
+ * @name Missing read or write permission configuration
+ * @description Defining an incomplete set of permissions
+ * @kind problem
+ * @problem.severity warning
+ * @security-severity 7.8
+ * @id java/android/incomplete-provider-permissions
+ * @tags security
+ * external/cwe/cwe-276
+ * @precision medium
+ */
+
+import java
+import semmle.code.xml.AndroidManifest
+
+from AndroidProviderXmlElement provider
+where
+ (
+ provider.getAnAttribute().(AndroidPermissionXmlAttribute).isWrite() or
+ provider.getAnAttribute().(AndroidPermissionXmlAttribute).isRead()
+ ) and
+ not provider.requiresPermissions()
+select provider, "Incomplete permissions"
From e37f62bb5eb7aff3036c39169def3d50111ecc60 Mon Sep 17 00:00:00 2001
From: Ed Minnix
Date: Mon, 19 Sep 2022 10:32:02 -0400
Subject: [PATCH 010/991] Android ContentProvider.openFile does not check
`mode` initital commit
Initial commit for work on a query finding instances where the `mode`
parameter of an override of the `openFile` method of the
`android.content.ContentProvider` class
---
.../CWE-276/MisconfiguedContentProviderUse.ql | 27 +++++++++++++++++++
1 file changed, 27 insertions(+)
create mode 100644 java/ql/src/Security/CWE/CWE-276/MisconfiguedContentProviderUse.ql
diff --git a/java/ql/src/Security/CWE/CWE-276/MisconfiguedContentProviderUse.ql b/java/ql/src/Security/CWE/CWE-276/MisconfiguedContentProviderUse.ql
new file mode 100644
index 00000000000..06c68403d53
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-276/MisconfiguedContentProviderUse.ql
@@ -0,0 +1,27 @@
+/**
+ * @name Misconfigured ContentProvider use
+ * @description ContentProvider#openFile override which does not use `mode` argument.
+ * @kind problem
+ * @id java/android/misconfigured-content-provider
+ * @problem.severity warning
+ * @security-severity 7.8
+ * @tags security external/cwe/cwe-276
+ * @precision medium
+ */
+
+import java
+
+class ContentProviderOpenFileMethod extends Method {
+ ContentProviderOpenFileMethod() {
+ this.hasName("openFile") and
+ this.getDeclaringType().getASupertype*().hasQualifiedName("android.content", "ContentProvider")
+ }
+
+ predicate doesNotCheckMode() {
+ exists(Parameter p | p = this.getParameter(1) | not exists(p.getAnAccess()))
+ }
+}
+
+from ContentProviderOpenFileMethod ofm
+where ofm.doesNotCheckMode()
+select ofm, "Open file"
From 7720d85c98d4fc03e43881c51d32ce0e2d99d05c Mon Sep 17 00:00:00 2001
From: Alex Ford
Date: Tue, 20 Sep 2022 08:58:35 +0100
Subject: [PATCH 011/991] Ruby: use camelcase verion of Http module
---
ruby/ql/src/queries/security/cwe-598/SensitiveGetQuery.ql | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ruby/ql/src/queries/security/cwe-598/SensitiveGetQuery.ql b/ruby/ql/src/queries/security/cwe-598/SensitiveGetQuery.ql
index 5fc271dd991..0011ec51408 100644
--- a/ruby/ql/src/queries/security/cwe-598/SensitiveGetQuery.ql
+++ b/ruby/ql/src/queries/security/cwe-598/SensitiveGetQuery.ql
@@ -33,7 +33,7 @@ private predicate localFlowWithElementReference(DataFlow::LocalSourceNode src, D
}
from
- HTTP::Server::RequestHandler handler, HTTP::Server::RequestInputAccess input,
+ Http::Server::RequestHandler handler, Http::Server::RequestInputAccess input,
SensitiveNode sensitive
where
handler.getAnHttpMethod() = "get" and
From c599b02e98e5c8625d606dfe0f02f6e1480eb007 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Tue, 20 Sep 2022 15:23:13 +0100
Subject: [PATCH 012/991] C++: Add test case.
---
.../UnusedStaticFunctions.expected | 2 ++
.../UnusedStaticFunctions/extraction_error.c | 15 +++++++++++++++
2 files changed, 17 insertions(+)
create mode 100644 cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticFunctions/extraction_error.c
diff --git a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticFunctions/UnusedStaticFunctions.expected b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticFunctions/UnusedStaticFunctions.expected
index 46a5698c8c6..6409e4adece 100644
--- a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticFunctions/UnusedStaticFunctions.expected
+++ b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticFunctions/UnusedStaticFunctions.expected
@@ -1,3 +1,5 @@
+| extraction_error.c:4:13:4:43 | my_function2_called_after_error | Static function my_function2_called_after_error is unreachable | extraction_error.c:4:13:4:43 | my_function2_called_after_error | my_function2_called_after_error |
+| extraction_error.c:5:13:5:35 | my_function3_not_called | Static function my_function3_not_called is unreachable | extraction_error.c:5:13:5:35 | my_function3_not_called | my_function3_not_called |
| unused_functions.c:16:13:16:27 | unused_function | Static function unused_function is unreachable | unused_functions.c:16:13:16:27 | unused_function | unused_function |
| unused_functions.c:20:13:20:28 | unused_function2 | Static function unused_function2 is unreachable ($@ must be removed at the same time) | unused_functions.c:24:13:24:28 | unused_function3 | unused_function3 |
| unused_functions.c:24:13:24:28 | unused_function3 | Static function unused_function3 is unreachable | unused_functions.c:24:13:24:28 | unused_function3 | unused_function3 |
diff --git a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticFunctions/extraction_error.c b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticFunctions/extraction_error.c
new file mode 100644
index 00000000000..a5ef25a6921
--- /dev/null
+++ b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticFunctions/extraction_error.c
@@ -0,0 +1,15 @@
+// semmle-extractor-options: --expect_errors
+
+static void my_function1_called() {} // GOOD
+static void my_function2_called_after_error() {} // GOOD [FALSE POSITIVE]
+static void my_function3_not_called() {} // BAD
+
+int main(void) {
+ my_function1_called();
+
+--- compilation stops here because this line is not valid C code ---
+
+ my_function2_called_after_error();
+
+ return 0;
+}
From 2756c0e7afc496079d1051d9a7c3a5d1b31139da Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Tue, 20 Sep 2022 16:24:28 +0100
Subject: [PATCH 013/991] C++: Don't report results in files with compilation
errors.
---
.../Unused Entities/UnusedStaticFunctions.ql | 29 ++++++++++++++-----
.../UnusedStaticFunctions.expected | 2 --
.../UnusedStaticFunctions/extraction_error.c | 4 +--
3 files changed, 23 insertions(+), 12 deletions(-)
diff --git a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.ql b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.ql
index 418250d15ac..cfca5bad544 100644
--- a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.ql
+++ b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.ql
@@ -14,15 +14,28 @@
import cpp
predicate immediatelyReachableFunction(Function f) {
- not f.isStatic() or
- exists(BlockExpr be | be.getFunction() = f) or
- f instanceof MemberFunction or
- f instanceof TemplateFunction or
- f.getFile() instanceof HeaderFile or
- f.getAnAttribute().hasName("constructor") or
- f.getAnAttribute().hasName("destructor") or
- f.getAnAttribute().hasName("used") or
+ not f.isStatic()
+ or
+ exists(BlockExpr be | be.getFunction() = f)
+ or
+ f instanceof MemberFunction
+ or
+ f instanceof TemplateFunction
+ or
+ f.getFile() instanceof HeaderFile
+ or
+ f.getAnAttribute().hasName("constructor")
+ or
+ f.getAnAttribute().hasName("destructor")
+ or
+ f.getAnAttribute().hasName("used")
+ or
f.getAnAttribute().hasName("unused")
+ or
+ // a compiler error in the same file suggests we may be missing data
+ exists(Diagnostic d | d.getFile() = f.getFile() and d.getSeverity() >= 3)
+ or
+ exists(ErrorExpr ee | ee.getFile() = f.getFile())
}
predicate immediatelyReachableVariable(Variable v) {
diff --git a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticFunctions/UnusedStaticFunctions.expected b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticFunctions/UnusedStaticFunctions.expected
index 6409e4adece..46a5698c8c6 100644
--- a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticFunctions/UnusedStaticFunctions.expected
+++ b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticFunctions/UnusedStaticFunctions.expected
@@ -1,5 +1,3 @@
-| extraction_error.c:4:13:4:43 | my_function2_called_after_error | Static function my_function2_called_after_error is unreachable | extraction_error.c:4:13:4:43 | my_function2_called_after_error | my_function2_called_after_error |
-| extraction_error.c:5:13:5:35 | my_function3_not_called | Static function my_function3_not_called is unreachable | extraction_error.c:5:13:5:35 | my_function3_not_called | my_function3_not_called |
| unused_functions.c:16:13:16:27 | unused_function | Static function unused_function is unreachable | unused_functions.c:16:13:16:27 | unused_function | unused_function |
| unused_functions.c:20:13:20:28 | unused_function2 | Static function unused_function2 is unreachable ($@ must be removed at the same time) | unused_functions.c:24:13:24:28 | unused_function3 | unused_function3 |
| unused_functions.c:24:13:24:28 | unused_function3 | Static function unused_function3 is unreachable | unused_functions.c:24:13:24:28 | unused_function3 | unused_function3 |
diff --git a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticFunctions/extraction_error.c b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticFunctions/extraction_error.c
index a5ef25a6921..66eedf743fb 100644
--- a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticFunctions/extraction_error.c
+++ b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticFunctions/extraction_error.c
@@ -1,8 +1,8 @@
// semmle-extractor-options: --expect_errors
static void my_function1_called() {} // GOOD
-static void my_function2_called_after_error() {} // GOOD [FALSE POSITIVE]
-static void my_function3_not_called() {} // BAD
+static void my_function2_called_after_error() {} // GOOD
+static void my_function3_not_called() {} // BAD [NOT DETECTED]
int main(void) {
my_function1_called();
From e319c1773e48304610c1aa330121a4e45afc39a1 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Wed, 21 Sep 2022 10:34:36 +0100
Subject: [PATCH 014/991] C++: Change note.
---
cpp/ql/src/change-notes/2022-09-21-unused-static-function.md | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 cpp/ql/src/change-notes/2022-09-21-unused-static-function.md
diff --git a/cpp/ql/src/change-notes/2022-09-21-unused-static-function.md b/cpp/ql/src/change-notes/2022-09-21-unused-static-function.md
new file mode 100644
index 00000000000..80bd25b7179
--- /dev/null
+++ b/cpp/ql/src/change-notes/2022-09-21-unused-static-function.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* Fixed false positives from the "Unused static function" (`cpp/unused-static-function`) query in files that had errors during compilation.
From 1cdaaf7882a563ae7dc2f5986d4a2e7b5a195652 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Wed, 21 Sep 2022 11:11:11 +0100
Subject: [PATCH 015/991] C++: Performance fix.
---
.../Unused Entities/UnusedStaticFunctions.ql | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.ql b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.ql
index cfca5bad544..242b0cd0683 100644
--- a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.ql
+++ b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.ql
@@ -13,6 +13,12 @@
import cpp
+predicate possiblyIncompleteFile(File f) {
+ exists(Diagnostic d | d.getFile() = f and d.getSeverity() >= 3)
+ or
+ exists(ErrorExpr ee | ee.getFile() = f)
+}
+
predicate immediatelyReachableFunction(Function f) {
not f.isStatic()
or
@@ -33,9 +39,7 @@ predicate immediatelyReachableFunction(Function f) {
f.getAnAttribute().hasName("unused")
or
// a compiler error in the same file suggests we may be missing data
- exists(Diagnostic d | d.getFile() = f.getFile() and d.getSeverity() >= 3)
- or
- exists(ErrorExpr ee | ee.getFile() = f.getFile())
+ possiblyIncompleteFile(f.getFile())
}
predicate immediatelyReachableVariable(Variable v) {
From 0584191b6c349763e8a79513e43c1be71cdb65c7 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Wed, 21 Sep 2022 11:49:28 +0100
Subject: [PATCH 016/991] C++: Add pragma[noinline].
---
.../src/Best Practices/Unused Entities/UnusedStaticFunctions.ql | 1 +
1 file changed, 1 insertion(+)
diff --git a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.ql b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.ql
index 242b0cd0683..8d5c969f7b7 100644
--- a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.ql
+++ b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.ql
@@ -13,6 +13,7 @@
import cpp
+pragma[noinline]
predicate possiblyIncompleteFile(File f) {
exists(Diagnostic d | d.getFile() = f and d.getSeverity() >= 3)
or
From 518b45bc8e8c16516f3fb15cf8019f25992911c5 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Wed, 21 Sep 2022 15:41:27 +0100
Subject: [PATCH 017/991] C++: Add two more test cases.
---
.../unused_static_functions.cpp | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticFunctions/unused_static_functions.cpp b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticFunctions/unused_static_functions.cpp
index 2984d8f0b1a..c0d83b52a57 100644
--- a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticFunctions/unused_static_functions.cpp
+++ b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticFunctions/unused_static_functions.cpp
@@ -33,3 +33,16 @@ static void f6(void);
static void f5(void) { f6(); }
static void f6(void) { f5(); }
+// f7 and f8 are reachable from `function_caller`
+static int f7() { return 1; } // GOOD
+static void f8() { } // GOOD
+
+void function_caller()
+{
+ auto my_lambda = []() {
+ return f7();
+ }();
+
+ f8();
+}
+
From f1efc76e8c127dcdbaf4e0682800f9c642503caa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?=
Date: Thu, 22 Sep 2022 17:06:04 +0200
Subject: [PATCH 018/991] C++: Initial commit of
`cpp/comma-before-missing-indentation`
---
.../CommaBeforeMisleadingIndentation.cpp | 0
.../CommaBeforeMisleadingIndentation.qhelp | 18 ++++++++++++++++++
.../CommaBeforeMisleadingIndentation.ql | 1 +
.../CommaBeforeMisleadingIndentation.expected | 1 +
.../CommaBeforeMisleadingIndentation.qlref | 1 +
.../CommaBeforeMisleadingIndentation/test.cpp | 19 +++++++++++++++++++
6 files changed, 40 insertions(+)
create mode 100644 cpp/ql/src/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation.cpp
create mode 100644 cpp/ql/src/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation.qhelp
create mode 100644 cpp/ql/src/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation.ql
create mode 100644 cpp/ql/test/query-tests/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation/CommaBeforeMisleadingIndentation.expected
create mode 100644 cpp/ql/test/query-tests/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation/CommaBeforeMisleadingIndentation.qlref
create mode 100644 cpp/ql/test/query-tests/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation/test.cpp
diff --git a/cpp/ql/src/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation.cpp b/cpp/ql/src/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation.cpp
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/cpp/ql/src/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation.qhelp b/cpp/ql/src/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation.qhelp
new file mode 100644
index 00000000000..5483ddfe921
--- /dev/null
+++ b/cpp/ql/src/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation.qhelp
@@ -0,0 +1,18 @@
+
+
+
+
+...
+
+
+
+...
+
+
+
+
+
+
+
diff --git a/cpp/ql/src/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation.ql b/cpp/ql/src/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation.ql
new file mode 100644
index 00000000000..82198eaf87b
--- /dev/null
+++ b/cpp/ql/src/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation.ql
@@ -0,0 +1 @@
+select 1
diff --git a/cpp/ql/test/query-tests/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation/CommaBeforeMisleadingIndentation.expected b/cpp/ql/test/query-tests/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation/CommaBeforeMisleadingIndentation.expected
new file mode 100644
index 00000000000..2a4f078a25f
--- /dev/null
+++ b/cpp/ql/test/query-tests/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation/CommaBeforeMisleadingIndentation.expected
@@ -0,0 +1 @@
+| 1 |
diff --git a/cpp/ql/test/query-tests/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation/CommaBeforeMisleadingIndentation.qlref b/cpp/ql/test/query-tests/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation/CommaBeforeMisleadingIndentation.qlref
new file mode 100644
index 00000000000..02b5f38e358
--- /dev/null
+++ b/cpp/ql/test/query-tests/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation/CommaBeforeMisleadingIndentation.qlref
@@ -0,0 +1 @@
+Best Practices/Likely Errors/CommaBeforeMisleadingIndentation.ql
diff --git a/cpp/ql/test/query-tests/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation/test.cpp b/cpp/ql/test/query-tests/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation/test.cpp
new file mode 100644
index 00000000000..ed35190fee9
--- /dev/null
+++ b/cpp/ql/test/query-tests/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation/test.cpp
@@ -0,0 +1,19 @@
+
+void test(int i, int j, int (*foo)(int))
+{
+ if (i)
+ (void)i, // GOOD
+ (void)j;
+
+ if (i)
+ (void)i, // BAD
+ (void)j;
+
+ foo((i++, j++)); // GOOD
+ foo((i++, // GOOD
+ j++));
+ foo((i++
+ , j++)); // GOOD
+ foo((i++,
+ j++)); // BAD (?)
+}
From dca13f5c89905977e1ddecb6b05e2f8afa27b7c1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?=
Date: Thu, 22 Sep 2022 17:42:28 +0200
Subject: [PATCH 019/991] C++: Initial
`cpp/comma-before-misleading-indentation`
MRVA top 1000 run at: https://github.com/github/semmle-code/actions/runs/3106828111
---
.../CommaBeforeMisleadingIndentation.ql | 20 ++++++++++++++++++-
.../CommaBeforeMisleadingIndentation.expected | 3 ++-
2 files changed, 21 insertions(+), 2 deletions(-)
diff --git a/cpp/ql/src/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation.ql b/cpp/ql/src/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation.ql
index 82198eaf87b..12bb7d551f8 100644
--- a/cpp/ql/src/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation.ql
+++ b/cpp/ql/src/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation.ql
@@ -1 +1,19 @@
-select 1
+/**
+ * @name Comma before misleading indentation
+ * @description The expressions before and after the comma operator can be misread because of an unusual difference in start columns.
+ * @kind problem
+ * @id cpp/comma-before-misleading-indentation
+ * @problem.severity recommendation
+ * @tags maintainability
+ * readability
+ */
+
+import cpp
+
+from CommaExpr ce
+where
+ ce.fromSource() and
+ not exists(MacroInvocation me | ce = me.getAnAffectedElement()) and
+ ce.getLeftOperand().getLocation().getStartColumn() >
+ ce.getRightOperand().getLocation().getStartColumn()
+select ce, "Comma before misleading indentation."
diff --git a/cpp/ql/test/query-tests/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation/CommaBeforeMisleadingIndentation.expected b/cpp/ql/test/query-tests/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation/CommaBeforeMisleadingIndentation.expected
index 2a4f078a25f..a977b9ce72f 100644
--- a/cpp/ql/test/query-tests/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation/CommaBeforeMisleadingIndentation.expected
+++ b/cpp/ql/test/query-tests/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation/CommaBeforeMisleadingIndentation.expected
@@ -1 +1,2 @@
-| 1 |
+| test.cpp:9:3:10:8 | ... , ... | Comma before misleading indentation. |
+| test.cpp:17:7:18:4 | ... , ... | Comma before misleading indentation. |
From 0e9b77e7c314509beea26479cb10b47a4044d5e0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?=
Date: Fri, 23 Sep 2022 11:46:31 +0200
Subject: [PATCH 020/991] C++: Initial .qhelp file
---
.../CommaBeforeMisleadingIndentation.cpp | 32 +++++++++++++++++++
.../CommaBeforeMisleadingIndentation.qhelp | 10 ++++--
2 files changed, 40 insertions(+), 2 deletions(-)
diff --git a/cpp/ql/src/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation.cpp b/cpp/ql/src/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation.cpp
index e69de29bb2d..2b30b6ba1f1 100644
--- a/cpp/ql/src/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation.cpp
+++ b/cpp/ql/src/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation.cpp
@@ -0,0 +1,32 @@
+/*
+ * Here, the comma should have been a semicolon:
+ */
+
+enum privileges entitlements = NONE;
+
+if (is_admin)
+ entitlements = FULL, // BAD
+
+restrict_privileges(entitlements);
+
+/*
+ * This is misleading, because the code is unexpectedly equivalent to:
+ */
+
+enum privileges entitlements = NONE;
+
+if (is_admin) {
+ entitlements = FULL;
+ restrict_privileges(entitlements);
+}
+
+/*
+ * Whereas the following code was probably intended:
+ */
+
+enum privileges entitlements = NONE;
+
+if (is_admin)
+ entitlements = FULL; // GOOD
+
+restrict_privileges(entitlements);
diff --git a/cpp/ql/src/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation.qhelp b/cpp/ql/src/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation.qhelp
index 5483ddfe921..b54a0e4800a 100644
--- a/cpp/ql/src/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation.qhelp
+++ b/cpp/ql/src/Best Practices/Likely Errors/CommaBeforeMisleadingIndentation.qhelp
@@ -4,11 +4,17 @@
-...
+
+If the expression to the right of a comma operator starts at an earlier column than the expression to the left, then
+this suspicious indentation likely indicates a logic error caused by a typo that may escape visual inspection.
+
-...
+
+Use standard indentation around the comma operator: begin the right-hand-side operand at the same level of
+indentation as the left-hand-side operand.
+
From d60a829569d4d7f6ac893c4a4cddad69978ef4c5 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Fri, 23 Sep 2022 12:17:09 +0100
Subject: [PATCH 021/991] C++: Remove ErrorExpr case.
---
.../src/Best Practices/Unused Entities/UnusedStaticFunctions.ql | 2 --
1 file changed, 2 deletions(-)
diff --git a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.ql b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.ql
index 8d5c969f7b7..514cfac9a81 100644
--- a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.ql
+++ b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.ql
@@ -16,8 +16,6 @@ import cpp
pragma[noinline]
predicate possiblyIncompleteFile(File f) {
exists(Diagnostic d | d.getFile() = f and d.getSeverity() >= 3)
- or
- exists(ErrorExpr ee | ee.getFile() = f)
}
predicate immediatelyReachableFunction(Function f) {
From 364bc883ba3056c19f29002958046cab84492523 Mon Sep 17 00:00:00 2001
From: Alex Ford
Date: Fri, 23 Sep 2022 15:54:15 +0100
Subject: [PATCH 022/991] Ruby: add YAML.load_file as an unsafe deserialization
sink
---
.../change-notes/2022-09-23-yaml-load-file.md | 4 ++++
.../UnsafeDeserializationCustomizations.qll | 9 ++++++---
.../UnsafeDeserialization.expected | 18 ++++++++++++++++++
.../UnsafeDeserialization.rb | 18 ++++++++++++++++++
4 files changed, 46 insertions(+), 3 deletions(-)
create mode 100644 ruby/ql/lib/change-notes/2022-09-23-yaml-load-file.md
diff --git a/ruby/ql/lib/change-notes/2022-09-23-yaml-load-file.md b/ruby/ql/lib/change-notes/2022-09-23-yaml-load-file.md
new file mode 100644
index 00000000000..3bb4345a3fc
--- /dev/null
+++ b/ruby/ql/lib/change-notes/2022-09-23-yaml-load-file.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* Added `YAML.load_file` as a potential sink for unsafe deserialization.
diff --git a/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll b/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll
index 91b3dd80606..f56544a8d8b 100644
--- a/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll
+++ b/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll
@@ -48,12 +48,15 @@ module UnsafeDeserialization {
}
/**
- * An argument in a call to `YAML.load`, considered a sink for unsafe
- * deserialization.
+ * An argument in a call to `YAML.load` or `YAML.load_file`, considered a sink
+ * for unsafe deserialization. As the `YAML` module is an alias of `Psych` in
*/
class YamlLoadArgument extends Sink {
YamlLoadArgument() {
- this = API::getTopLevelMember("YAML").getAMethodCall("load").getArgument(0)
+ this =
+ API::getTopLevelMember(["YAML", "Psych"])
+ .getAMethodCall(["load", "load_file"])
+ .getArgument(0)
}
}
diff --git a/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.expected b/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.expected
index f81f114ae8f..4cdab3af4dc 100644
--- a/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.expected
+++ b/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.expected
@@ -16,6 +16,12 @@ edges
| UnsafeDeserialization.rb:58:17:58:28 | ...[...] : | UnsafeDeserialization.rb:68:23:68:31 | json_data |
| UnsafeDeserialization.rb:80:11:80:16 | call to params : | UnsafeDeserialization.rb:80:11:80:22 | ...[...] : |
| UnsafeDeserialization.rb:80:11:80:22 | ...[...] : | UnsafeDeserialization.rb:81:34:81:36 | xml |
+| UnsafeDeserialization.rb:86:17:86:22 | call to params : | UnsafeDeserialization.rb:86:17:86:28 | ...[...] : |
+| UnsafeDeserialization.rb:86:17:86:28 | ...[...] : | UnsafeDeserialization.rb:87:29:87:37 | yaml_path |
+| UnsafeDeserialization.rb:92:17:92:22 | call to params : | UnsafeDeserialization.rb:92:17:92:28 | ...[...] : |
+| UnsafeDeserialization.rb:92:17:92:28 | ...[...] : | UnsafeDeserialization.rb:93:25:93:33 | yaml_data |
+| UnsafeDeserialization.rb:98:17:98:22 | call to params : | UnsafeDeserialization.rb:98:17:98:28 | ...[...] : |
+| UnsafeDeserialization.rb:98:17:98:28 | ...[...] : | UnsafeDeserialization.rb:99:30:99:38 | yaml_path |
nodes
| UnsafeDeserialization.rb:9:39:9:44 | call to params : | semmle.label | call to params : |
| UnsafeDeserialization.rb:9:39:9:50 | ...[...] : | semmle.label | ...[...] : |
@@ -42,6 +48,15 @@ nodes
| UnsafeDeserialization.rb:80:11:80:16 | call to params : | semmle.label | call to params : |
| UnsafeDeserialization.rb:80:11:80:22 | ...[...] : | semmle.label | ...[...] : |
| UnsafeDeserialization.rb:81:34:81:36 | xml | semmle.label | xml |
+| UnsafeDeserialization.rb:86:17:86:22 | call to params : | semmle.label | call to params : |
+| UnsafeDeserialization.rb:86:17:86:28 | ...[...] : | semmle.label | ...[...] : |
+| UnsafeDeserialization.rb:87:29:87:37 | yaml_path | semmle.label | yaml_path |
+| UnsafeDeserialization.rb:92:17:92:22 | call to params : | semmle.label | call to params : |
+| UnsafeDeserialization.rb:92:17:92:28 | ...[...] : | semmle.label | ...[...] : |
+| UnsafeDeserialization.rb:93:25:93:33 | yaml_data | semmle.label | yaml_data |
+| UnsafeDeserialization.rb:98:17:98:22 | call to params : | semmle.label | call to params : |
+| UnsafeDeserialization.rb:98:17:98:28 | ...[...] : | semmle.label | ...[...] : |
+| UnsafeDeserialization.rb:99:30:99:38 | yaml_path | semmle.label | yaml_path |
subpaths
#select
| UnsafeDeserialization.rb:10:27:10:41 | serialized_data | UnsafeDeserialization.rb:9:39:9:44 | call to params : | UnsafeDeserialization.rb:10:27:10:41 | serialized_data | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:9:39:9:44 | call to params | user-provided value |
@@ -53,3 +68,6 @@ subpaths
| UnsafeDeserialization.rb:53:22:53:30 | json_data | UnsafeDeserialization.rb:51:17:51:22 | call to params : | UnsafeDeserialization.rb:53:22:53:30 | json_data | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:51:17:51:22 | call to params | user-provided value |
| UnsafeDeserialization.rb:68:23:68:31 | json_data | UnsafeDeserialization.rb:58:17:58:22 | call to params : | UnsafeDeserialization.rb:68:23:68:31 | json_data | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:58:17:58:22 | call to params | user-provided value |
| UnsafeDeserialization.rb:81:34:81:36 | xml | UnsafeDeserialization.rb:80:11:80:16 | call to params : | UnsafeDeserialization.rb:81:34:81:36 | xml | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:80:11:80:16 | call to params | user-provided value |
+| UnsafeDeserialization.rb:87:29:87:37 | yaml_path | UnsafeDeserialization.rb:86:17:86:22 | call to params : | UnsafeDeserialization.rb:87:29:87:37 | yaml_path | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:86:17:86:22 | call to params | user-provided value |
+| UnsafeDeserialization.rb:93:25:93:33 | yaml_data | UnsafeDeserialization.rb:92:17:92:22 | call to params : | UnsafeDeserialization.rb:93:25:93:33 | yaml_data | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:92:17:92:22 | call to params | user-provided value |
+| UnsafeDeserialization.rb:99:30:99:38 | yaml_path | UnsafeDeserialization.rb:98:17:98:22 | call to params : | UnsafeDeserialization.rb:99:30:99:38 | yaml_path | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:98:17:98:22 | call to params | user-provided value |
diff --git a/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.rb b/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.rb
index fc6bc198d41..27bb1c35f34 100644
--- a/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.rb
+++ b/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.rb
@@ -80,4 +80,22 @@ class UsersController < ActionController::Base
xml = params[:key]
hash = Hash.from_trusted_xml(xml)
end
+
+ # BAD
+ def route11
+ yaml_path = params[:key]
+ object = YAML.load_file yaml_path
+ end
+
+ # BAD
+ def route12
+ yaml_data = params[:key]
+ object = Psych.load yaml_data
+ end
+
+ # BAD
+ def route13
+ yaml_path = params[:key]
+ object = Psych.load_file yaml_path
+ end
end
From d94b196843d73930fc8fe85aca0cc4a2deac397f Mon Sep 17 00:00:00 2001
From: Alex Ford
Date: Fri, 23 Sep 2022 16:56:33 +0100
Subject: [PATCH 023/991] Ruby: fix documentation
---
.../ruby/security/UnsafeDeserializationCustomizations.qll | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll b/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll
index f56544a8d8b..dfe448e7b5b 100644
--- a/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll
+++ b/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll
@@ -49,7 +49,8 @@ module UnsafeDeserialization {
/**
* An argument in a call to `YAML.load` or `YAML.load_file`, considered a sink
- * for unsafe deserialization. As the `YAML` module is an alias of `Psych` in
+ * for unsafe deserialization. The `YAML` module is an alias of `Psych` in
+ * recent versions of Ruby.
*/
class YamlLoadArgument extends Sink {
YamlLoadArgument() {
From b93a2b06bf11346a2b3e74e3c2134124cc25ad50 Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Fri, 23 Sep 2022 14:38:06 -0400
Subject: [PATCH 024/991] C++: prototype for off-by-one in array-typed field
---
.../Likely Bugs/ConstantSizeArrayOffByOne.ql | 24 +++++++++++++++++++
1 file changed, 24 insertions(+)
create mode 100644 cpp/ql/src/experimental/Likely Bugs/ConstantSizeArrayOffByOne.ql
diff --git a/cpp/ql/src/experimental/Likely Bugs/ConstantSizeArrayOffByOne.ql b/cpp/ql/src/experimental/Likely Bugs/ConstantSizeArrayOffByOne.ql
new file mode 100644
index 00000000000..a21cc43f4a2
--- /dev/null
+++ b/cpp/ql/src/experimental/Likely Bugs/ConstantSizeArrayOffByOne.ql
@@ -0,0 +1,24 @@
+/**
+ * @id cpp/constant-size-array-off-by-one
+ * @kind path-problem
+ */
+
+import experimental.semmle.code.cpp.semantic.analysis.RangeAnalysis
+import experimental.semmle.code.cpp.rangeanalysis.Bound
+import experimental.semmle.code.cpp.semantic.SemanticExprSpecific
+import semmle.code.cpp.ir.IR
+import experimental.semmle.code.cpp.ir.dataflow.DataFlow
+
+from
+ FieldAddressInstruction fai, PointerArithmeticInstruction pai, AddressOperand ao, ZeroBound b,
+ int delta, int size
+where
+ size = fai.getField().getUnspecifiedType().(ArrayType).getArraySize() and
+ DataFlow::localInstructionFlow(fai, pai.getLeft()) and
+ DataFlow::localInstructionFlow(pai, ao.getAnyDef()) and
+ semBounded(getSemanticExpr(pai.getRight()), b, delta, true, _) and
+ delta >= size and
+ size != 0 and // sometimes 0 or 1 is used for a variable-size array
+ size != 1
+select pai, "This pointer may have an off-by-" + (delta - size) + "error allowing it to overrun $@",
+ fai.getField(), fai.getField().toString()
From ed0c85e3afd375d5f13bdeadf9c58726223c085d Mon Sep 17 00:00:00 2001
From: Harry Maclean
Date: Tue, 6 Sep 2022 12:35:25 +0100
Subject: [PATCH 025/991] Ruby: Model ActionView helper XSS sinks
---
.../lib/codeql/ruby/frameworks/ActionView.qll | 112 ++++++++++++++++++
ruby/ql/lib/codeql/ruby/security/XSS.qll | 12 ++
.../frameworks/ActionView.expected | 9 ++
.../library-tests/frameworks/ActionView.ql | 5 +
.../security/cwe-079/ReflectedXSS.expected | 4 +
.../cwe-079/app/views/foo/bars/show.html.erb | 3 +
6 files changed, 145 insertions(+)
diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionView.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionView.qll
index ef5b0a0204e..b7dc988205b 100644
--- a/ruby/ql/lib/codeql/ruby/frameworks/ActionView.qll
+++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionView.qll
@@ -216,4 +216,116 @@ class FileSystemResolverAccess extends DataFlow::CallNode, FileSystemAccess::Ran
override DataFlow::Node getAPathArgument() { result = this.getArgument(0) }
}
+
// TODO: model flow in/out of template files properly,
+//
+/**
+ * Action view helper methods which are XSS sinks.
+ */
+module ActionViewHelpers {
+ /**
+ * Calls to ActionView helpers which render their argument without escaping.
+ * These arguments should be treated as XSS sinks.
+ * In the documentation for classes in this module, the vulnerable argument is
+ * named `x`.
+ */
+ abstract class RawHelperCall extends MethodCall {
+ abstract Expr getRawArgument();
+ }
+
+ /**
+ * `ActionView::Helpers::TextHelper#simple_format`.
+ *
+ * `simple_format(x, y, sanitize: false)`.
+ */
+ private class SimpleFormat extends ActionViewContextCall, RawHelperCall {
+ SimpleFormat() {
+ this.getMethodName() = "simple_format" and
+ this.getKeywordArgument("sanitize").getConstantValue().isBoolean(false)
+ }
+
+ override Expr getRawArgument() { result = this.getArgument(0) }
+ }
+
+ /**
+ * `ActionView::Helpers::TextHelper#truncate`.
+ *
+ * `truncate(x, escape: false)`.
+ */
+ private class Truncate extends ActionViewContextCall, RawHelperCall {
+ Truncate() {
+ this.getMethodName() = "truncate" and
+ this.getKeywordArgument("escape").getConstantValue().isBoolean(false)
+ }
+
+ override Expr getRawArgument() { result = this.getArgument(0) }
+ }
+
+ /**
+ * `ActionView::Helpers::TextHelper#highlight`.
+ *
+ * `truncate(x, y, sanitize: false)`.
+ */
+ private class Highlight extends ActionViewContextCall, RawHelperCall {
+ Highlight() {
+ this.getMethodName() = "highlight" and
+ this.getKeywordArgument("sanitize").getConstantValue().isBoolean(false)
+ }
+
+ override Expr getRawArgument() { result = this.getArgument(0) }
+ }
+
+ /**
+ * `ActionView::Helpers::JavascriptHelper#javascript_tag`.
+ *
+ * `javascript_tag(x)`.
+ */
+ private class JavascriptTag extends ActionViewContextCall, RawHelperCall {
+ JavascriptTag() { this.getMethodName() = "javascript_tag" }
+
+ override Expr getRawArgument() { result = this.getArgument(0) }
+ }
+
+ /**
+ * `ActionView::Helpers::TagHelper#tag`.
+ *
+ * `tag(x, x, y, false)`.
+ */
+ private class ContentTag extends ActionViewContextCall, RawHelperCall {
+ ContentTag() {
+ this.getMethodName() = "content_tag" and
+ this.getArgument(3).getConstantValue().isBoolean(false)
+ }
+
+ override Expr getRawArgument() { result = this.getArgument(1) }
+ }
+
+ /**
+ * `ActionView::Helpers::TagHelper#tag`.
+ *
+ * `tag(x, x, y, false)`.
+ */
+ private class Tag extends ActionViewContextCall, RawHelperCall {
+ Tag() {
+ this.getMethodName() = "tag" and
+ this.getArgument(3).getConstantValue().isBoolean(false)
+ }
+
+ override Expr getRawArgument() { result = this.getArgument(0) }
+ }
+
+ /**
+ * `ActionView::Helpers::TagHelper#tag.`.
+ *
+ * `tag.h1(x, escape: false)`.
+ */
+ private class TagMethod extends MethodCall, RawHelperCall {
+ TagMethod() {
+ inActionViewContext(this) and
+ this.getReceiver().(MethodCall).getMethodName() = "tag" and
+ this.getKeywordArgument("escape").getConstantValue().isBoolean(false)
+ }
+
+ override Expr getRawArgument() { result = this.getArgument(0) }
+ }
+}
diff --git a/ruby/ql/lib/codeql/ruby/security/XSS.qll b/ruby/ql/lib/codeql/ruby/security/XSS.qll
index 28877b15ed8..21a504fd8d0 100644
--- a/ruby/ql/lib/codeql/ruby/security/XSS.qll
+++ b/ruby/ql/lib/codeql/ruby/security/XSS.qll
@@ -75,6 +75,18 @@ private module Shared {
RawCallArgumentAsSink() { this.getCall() instanceof RawCall }
}
+ /**
+ * An argument to an ActionView helper method which is not escaped,
+ * considered as a flow sink.
+ */
+ class RawHelperCallArgumentAsSink extends Sink, ErbOutputMethodCallArgumentNode {
+ RawHelperCallArgumentAsSink() {
+ exists(ErbOutputDirective d, ActionViewHelpers::RawHelperCall c |
+ d.getTerminalStmt() = c and this.asExpr().getExpr() = c.getRawArgument()
+ )
+ }
+ }
+
/**
* A argument to a call to the `link_to` method, which does not expect
* unsanitized user-input, considered as a flow sink.
diff --git a/ruby/ql/test/library-tests/frameworks/ActionView.expected b/ruby/ql/test/library-tests/frameworks/ActionView.expected
index 913183ead4f..2f525d2be25 100644
--- a/ruby/ql/test/library-tests/frameworks/ActionView.expected
+++ b/ruby/ql/test/library-tests/frameworks/ActionView.expected
@@ -30,3 +30,12 @@ httpResponses
| app/controllers/foo/bars_controller.rb:36:12:36:67 | call to render_to_string | app/controllers/foo/bars_controller.rb:36:29:36:33 | @user | application/json |
| app/controllers/foo/bars_controller.rb:38:5:38:50 | call to render | app/controllers/foo/bars_controller.rb:38:12:38:22 | call to backtrace | text/plain |
| app/controllers/foo/bars_controller.rb:44:5:44:17 | call to render | app/controllers/foo/bars_controller.rb:44:12:44:17 | "show" | text/html |
+rawHelperCalls
+| action_view/helpers.erb:4:1:4:36 | call to simple_format | action_view/helpers.erb:4:15:4:15 | call to x |
+| action_view/helpers.erb:7:1:7:26 | call to truncate | action_view/helpers.erb:7:10:7:10 | call to x |
+| action_view/helpers.erb:10:1:10:29 | call to highlight | action_view/helpers.erb:10:11:10:11 | call to x |
+| action_view/helpers.erb:12:1:12:17 | call to javascript_tag | action_view/helpers.erb:12:16:12:16 | call to x |
+| action_view/helpers.erb:15:1:15:27 | call to content_tag | action_view/helpers.erb:15:16:15:16 | call to y |
+| action_view/helpers.erb:18:1:18:19 | call to tag | action_view/helpers.erb:18:5:18:5 | call to x |
+| action_view/helpers.erb:21:1:21:24 | call to h1 | action_view/helpers.erb:21:8:21:8 | call to x |
+| action_view/helpers.erb:24:1:24:23 | call to p | action_view/helpers.erb:24:7:24:7 | call to x |
diff --git a/ruby/ql/test/library-tests/frameworks/ActionView.ql b/ruby/ql/test/library-tests/frameworks/ActionView.ql
index da5f03467a1..a1018386a04 100644
--- a/ruby/ql/test/library-tests/frameworks/ActionView.ql
+++ b/ruby/ql/test/library-tests/frameworks/ActionView.ql
@@ -1,3 +1,4 @@
+private import ruby
private import codeql.ruby.frameworks.ActionController
private import codeql.ruby.frameworks.ActionView
private import codeql.ruby.Concepts
@@ -16,3 +17,7 @@ query predicate linkToCalls(LinkToCall c) { any() }
query predicate httpResponses(Http::Server::HttpResponse r, DataFlow::Node body, string mimeType) {
r.getBody() = body and r.getMimetype() = mimeType
}
+
+query predicate rawHelperCalls(ActionViewHelpers::RawHelperCall c, Expr arg) {
+ arg = c.getRawArgument()
+}
diff --git a/ruby/ql/test/query-tests/security/cwe-079/ReflectedXSS.expected b/ruby/ql/test/query-tests/security/cwe-079/ReflectedXSS.expected
index 21b436b28bc..ce5a7826b44 100644
--- a/ruby/ql/test/query-tests/security/cwe-079/ReflectedXSS.expected
+++ b/ruby/ql/test/query-tests/security/cwe-079/ReflectedXSS.expected
@@ -22,6 +22,7 @@ edges
| app/views/foo/bars/show.html.erb:44:76:44:87 | call to display_text : | app/views/foo/bars/show.html.erb:44:64:44:87 | ... + ... : |
| app/views/foo/bars/show.html.erb:54:29:54:34 | call to params : | app/views/foo/bars/show.html.erb:54:29:54:44 | ...[...] |
| app/views/foo/bars/show.html.erb:57:13:57:18 | call to params : | app/views/foo/bars/show.html.erb:57:13:57:28 | ...[...] |
+| app/views/foo/bars/show.html.erb:74:19:74:24 | call to params : | app/views/foo/bars/show.html.erb:74:19:74:34 | ...[...] |
nodes
| app/controllers/foo/bars_controller.rb:9:12:9:17 | call to params : | semmle.label | call to params : |
| app/controllers/foo/bars_controller.rb:9:12:9:29 | ...[...] : | semmle.label | ...[...] : |
@@ -50,6 +51,8 @@ nodes
| app/views/foo/bars/show.html.erb:54:29:54:44 | ...[...] | semmle.label | ...[...] |
| app/views/foo/bars/show.html.erb:57:13:57:18 | call to params : | semmle.label | call to params : |
| app/views/foo/bars/show.html.erb:57:13:57:28 | ...[...] | semmle.label | ...[...] |
+| app/views/foo/bars/show.html.erb:74:19:74:24 | call to params : | semmle.label | call to params : |
+| app/views/foo/bars/show.html.erb:74:19:74:34 | ...[...] | semmle.label | ...[...] |
subpaths
#select
| app/views/foo/bars/_widget.html.erb:5:9:5:20 | call to display_text | app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params : | app/views/foo/bars/_widget.html.erb:5:9:5:20 | call to display_text | Cross-site scripting vulnerability due to a $@. | app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params | user-provided value |
@@ -64,3 +67,4 @@ subpaths
| app/views/foo/bars/show.html.erb:51:5:51:18 | call to user_name_memo | app/controllers/foo/bars_controller.rb:13:20:13:25 | call to params : | app/views/foo/bars/show.html.erb:51:5:51:18 | call to user_name_memo | Cross-site scripting vulnerability due to a $@. | app/controllers/foo/bars_controller.rb:13:20:13:25 | call to params | user-provided value |
| app/views/foo/bars/show.html.erb:54:29:54:44 | ...[...] | app/views/foo/bars/show.html.erb:54:29:54:34 | call to params : | app/views/foo/bars/show.html.erb:54:29:54:44 | ...[...] | Cross-site scripting vulnerability due to a $@. | app/views/foo/bars/show.html.erb:54:29:54:34 | call to params | user-provided value |
| app/views/foo/bars/show.html.erb:57:13:57:28 | ...[...] | app/views/foo/bars/show.html.erb:57:13:57:18 | call to params : | app/views/foo/bars/show.html.erb:57:13:57:28 | ...[...] | Cross-site scripting vulnerability due to a $@. | app/views/foo/bars/show.html.erb:57:13:57:18 | call to params | user-provided value |
+| app/views/foo/bars/show.html.erb:74:19:74:34 | ...[...] | app/views/foo/bars/show.html.erb:74:19:74:24 | call to params : | app/views/foo/bars/show.html.erb:74:19:74:34 | ...[...] | Cross-site scripting vulnerability due to a $@. | app/views/foo/bars/show.html.erb:74:19:74:24 | call to params | user-provided value |
diff --git a/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/bars/show.html.erb b/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/bars/show.html.erb
index 0df7af11039..20c7c8eb345 100644
--- a/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/bars/show.html.erb
+++ b/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/bars/show.html.erb
@@ -69,3 +69,6 @@
html_escaped_in_template = h params[:text]
html_escaped_in_template.html_safe
%>
+
+<%# BAD: simple_format called with sanitize: false %>
+<%= simple_format(params[:comment], sanitize: false) %>
From 35a05f6deac51b85b19fe8bb4d178ab5c5929c11 Mon Sep 17 00:00:00 2001
From: Harry Maclean
Date: Wed, 7 Sep 2022 14:31:49 +0100
Subject: [PATCH 026/991] Ruby: Add summaries for ActiveSupport::SafeBuffer
---
.../codeql/ruby/frameworks/ActiveSupport.qll | 25 ++
.../active_support/ActiveSupportFlow.expected | 339 ++++++++++++++++++
.../active_support/ActiveSupportFlow.ql | 11 +
.../frameworks/active_support/flow.rb | 65 ++++
4 files changed, 440 insertions(+)
create mode 100644 ruby/ql/test/library-tests/frameworks/active_support/ActiveSupportFlow.expected
create mode 100644 ruby/ql/test/library-tests/frameworks/active_support/ActiveSupportFlow.ql
create mode 100644 ruby/ql/test/library-tests/frameworks/active_support/flow.rb
diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActiveSupport.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActiveSupport.qll
index 7e8c09a407e..d21877fe49d 100644
--- a/ruby/ql/lib/codeql/ruby/frameworks/ActiveSupport.qll
+++ b/ruby/ql/lib/codeql/ruby/frameworks/ActiveSupport.qll
@@ -9,6 +9,7 @@ private import codeql.ruby.DataFlow
private import codeql.ruby.dataflow.FlowSummary
private import codeql.ruby.ApiGraphs
private import codeql.ruby.frameworks.stdlib.Logger::Logger as StdlibLogger
+private import codeql.ruby.frameworks.data.ModelsAsData
/**
* Modeling for `ActiveSupport`.
@@ -138,4 +139,28 @@ module ActiveSupport {
}
}
}
+
+ /**
+ * `ActiveSupport::SafeBuffer` wraps a string, providing HTML-safe methods
+ * for concatenation.
+ * It is possible to insert tainted data into `SafeBuffer` that won't get
+ * sanitized, and this taint is then propagated via most of the methods.
+ */
+ private class SafeBufferSummary extends ModelInput::SummaryModelCsv {
+ // TODO: SafeBuffer also reponds to all String methods.
+ // Can we model this without repeating all the existing summaries we have
+ // for String?
+ override predicate row(string row) {
+ row =
+ [
+ // SafeBuffer.new(x) does not sanitize x
+ "activesupport;;Member[ActionView].Member[SafeBuffer].Method[new];Argument[0];ReturnValue;taint",
+ // SafeBuffer#safe_concat(x) does not sanitize x
+ "activesupport;;Member[ActionView].Member[SafeBuffer].Instance.Method[safe_concat];Argument[0];ReturnValue;taint",
+ "activesupport;;Member[ActionView].Member[SafeBuffer].Instance.Method[safe_concat];Argument[0];Argument[self];taint",
+ // These methods preserve taint in self
+ "activesupport;;Member[ActionView].Member[SafeBuffer].Instance.Method[concat,insert,prepend,to_s,to_param];Argument[self];ReturnValue;taint",
+ ]
+ }
+ }
}
diff --git a/ruby/ql/test/library-tests/frameworks/active_support/ActiveSupportFlow.expected b/ruby/ql/test/library-tests/frameworks/active_support/ActiveSupportFlow.expected
new file mode 100644
index 00000000000..6ed0766aa28
--- /dev/null
+++ b/ruby/ql/test/library-tests/frameworks/active_support/ActiveSupportFlow.expected
@@ -0,0 +1,339 @@
+failures
+edges
+| active_support.rb:9:9:9:18 | call to source : | active_support.rb:10:10:10:10 | x : |
+| active_support.rb:10:10:10:10 | x : | active_support.rb:10:10:10:19 | call to camelize |
+| active_support.rb:14:9:14:18 | call to source : | active_support.rb:15:10:15:10 | x : |
+| active_support.rb:15:10:15:10 | x : | active_support.rb:15:10:15:20 | call to camelcase |
+| active_support.rb:19:9:19:18 | call to source : | active_support.rb:20:10:20:10 | x : |
+| active_support.rb:20:10:20:10 | x : | active_support.rb:20:10:20:19 | call to classify |
+| active_support.rb:24:9:24:18 | call to source : | active_support.rb:25:10:25:10 | x : |
+| active_support.rb:25:10:25:10 | x : | active_support.rb:25:10:25:20 | call to dasherize |
+| active_support.rb:29:9:29:18 | call to source : | active_support.rb:30:10:30:10 | x : |
+| active_support.rb:30:10:30:10 | x : | active_support.rb:30:10:30:24 | call to deconstantize |
+| active_support.rb:34:9:34:18 | call to source : | active_support.rb:35:10:35:10 | x : |
+| active_support.rb:35:10:35:10 | x : | active_support.rb:35:10:35:21 | call to demodulize |
+| active_support.rb:39:9:39:18 | call to source : | active_support.rb:40:10:40:10 | x : |
+| active_support.rb:40:10:40:10 | x : | active_support.rb:40:10:40:22 | call to foreign_key |
+| active_support.rb:44:9:44:18 | call to source : | active_support.rb:45:10:45:10 | x : |
+| active_support.rb:45:10:45:10 | x : | active_support.rb:45:10:45:19 | call to humanize |
+| active_support.rb:49:9:49:18 | call to source : | active_support.rb:50:10:50:10 | x : |
+| active_support.rb:50:10:50:10 | x : | active_support.rb:50:10:50:20 | call to indent |
+| active_support.rb:54:9:54:18 | call to source : | active_support.rb:55:10:55:10 | x : |
+| active_support.rb:55:10:55:10 | x : | active_support.rb:55:10:55:23 | call to parameterize |
+| active_support.rb:59:9:59:18 | call to source : | active_support.rb:60:10:60:10 | x : |
+| active_support.rb:60:10:60:10 | x : | active_support.rb:60:10:60:20 | call to pluralize |
+| active_support.rb:64:9:64:18 | call to source : | active_support.rb:65:10:65:10 | x : |
+| active_support.rb:65:10:65:10 | x : | active_support.rb:65:10:65:22 | call to singularize |
+| active_support.rb:69:9:69:18 | call to source : | active_support.rb:70:10:70:10 | x : |
+| active_support.rb:70:10:70:10 | x : | active_support.rb:70:10:70:17 | call to squish |
+| active_support.rb:74:9:74:18 | call to source : | active_support.rb:75:10:75:10 | x : |
+| active_support.rb:75:10:75:10 | x : | active_support.rb:75:10:75:24 | call to strip_heredoc |
+| active_support.rb:79:9:79:18 | call to source : | active_support.rb:80:10:80:10 | x : |
+| active_support.rb:80:10:80:10 | x : | active_support.rb:80:10:80:19 | call to tableize |
+| active_support.rb:84:9:84:18 | call to source : | active_support.rb:85:10:85:10 | x : |
+| active_support.rb:85:10:85:10 | x : | active_support.rb:85:10:85:20 | call to titlecase |
+| active_support.rb:89:9:89:18 | call to source : | active_support.rb:90:10:90:10 | x : |
+| active_support.rb:90:10:90:10 | x : | active_support.rb:90:10:90:19 | call to titleize |
+| active_support.rb:94:9:94:18 | call to source : | active_support.rb:95:10:95:10 | x : |
+| active_support.rb:95:10:95:10 | x : | active_support.rb:95:10:95:21 | call to underscore |
+| active_support.rb:99:9:99:18 | call to source : | active_support.rb:100:10:100:10 | x : |
+| active_support.rb:100:10:100:10 | x : | active_support.rb:100:10:100:23 | call to upcase_first |
+| active_support.rb:104:10:104:17 | call to source : | active_support.rb:105:9:105:9 | x [element 0] : |
+| active_support.rb:104:10:104:17 | call to source : | active_support.rb:105:9:105:9 | x [element 0] : |
+| active_support.rb:105:9:105:9 | x [element 0] : | active_support.rb:105:9:105:23 | call to compact_blank [element] : |
+| active_support.rb:105:9:105:9 | x [element 0] : | active_support.rb:105:9:105:23 | call to compact_blank [element] : |
+| active_support.rb:105:9:105:23 | call to compact_blank [element] : | active_support.rb:106:10:106:10 | y [element] : |
+| active_support.rb:105:9:105:23 | call to compact_blank [element] : | active_support.rb:106:10:106:10 | y [element] : |
+| active_support.rb:106:10:106:10 | y [element] : | active_support.rb:106:10:106:13 | ...[...] |
+| active_support.rb:106:10:106:10 | y [element] : | active_support.rb:106:10:106:13 | ...[...] |
+| active_support.rb:110:10:110:18 | call to source : | active_support.rb:111:9:111:9 | x [element 0] : |
+| active_support.rb:110:10:110:18 | call to source : | active_support.rb:111:9:111:9 | x [element 0] : |
+| active_support.rb:111:9:111:9 | x [element 0] : | active_support.rb:111:9:111:21 | call to excluding [element] : |
+| active_support.rb:111:9:111:9 | x [element 0] : | active_support.rb:111:9:111:21 | call to excluding [element] : |
+| active_support.rb:111:9:111:21 | call to excluding [element] : | active_support.rb:112:10:112:10 | y [element] : |
+| active_support.rb:111:9:111:21 | call to excluding [element] : | active_support.rb:112:10:112:10 | y [element] : |
+| active_support.rb:112:10:112:10 | y [element] : | active_support.rb:112:10:112:13 | ...[...] |
+| active_support.rb:112:10:112:10 | y [element] : | active_support.rb:112:10:112:13 | ...[...] |
+| active_support.rb:116:10:116:18 | call to source : | active_support.rb:117:9:117:9 | x [element 0] : |
+| active_support.rb:116:10:116:18 | call to source : | active_support.rb:117:9:117:9 | x [element 0] : |
+| active_support.rb:117:9:117:9 | x [element 0] : | active_support.rb:117:9:117:19 | call to without [element] : |
+| active_support.rb:117:9:117:9 | x [element 0] : | active_support.rb:117:9:117:19 | call to without [element] : |
+| active_support.rb:117:9:117:19 | call to without [element] : | active_support.rb:118:10:118:10 | y [element] : |
+| active_support.rb:117:9:117:19 | call to without [element] : | active_support.rb:118:10:118:10 | y [element] : |
+| active_support.rb:118:10:118:10 | y [element] : | active_support.rb:118:10:118:13 | ...[...] |
+| active_support.rb:118:10:118:10 | y [element] : | active_support.rb:118:10:118:13 | ...[...] |
+| active_support.rb:122:10:122:18 | call to source : | active_support.rb:123:9:123:9 | x [element 0] : |
+| active_support.rb:122:10:122:18 | call to source : | active_support.rb:123:9:123:9 | x [element 0] : |
+| active_support.rb:123:9:123:9 | x [element 0] : | active_support.rb:123:9:123:37 | call to in_order_of [element] : |
+| active_support.rb:123:9:123:9 | x [element 0] : | active_support.rb:123:9:123:37 | call to in_order_of [element] : |
+| active_support.rb:123:9:123:37 | call to in_order_of [element] : | active_support.rb:124:10:124:10 | y [element] : |
+| active_support.rb:123:9:123:37 | call to in_order_of [element] : | active_support.rb:124:10:124:10 | y [element] : |
+| active_support.rb:124:10:124:10 | y [element] : | active_support.rb:124:10:124:13 | ...[...] |
+| active_support.rb:124:10:124:10 | y [element] : | active_support.rb:124:10:124:13 | ...[...] |
+| active_support.rb:128:10:128:18 | call to source : | active_support.rb:129:9:129:9 | a [element 0] : |
+| active_support.rb:128:10:128:18 | call to source : | active_support.rb:129:9:129:9 | a [element 0] : |
+| active_support.rb:128:10:128:18 | call to source : | active_support.rb:130:10:130:10 | a [element 0] : |
+| active_support.rb:128:10:128:18 | call to source : | active_support.rb:130:10:130:10 | a [element 0] : |
+| active_support.rb:129:9:129:9 | a [element 0] : | active_support.rb:129:9:129:41 | call to including [element 0] : |
+| active_support.rb:129:9:129:9 | a [element 0] : | active_support.rb:129:9:129:41 | call to including [element 0] : |
+| active_support.rb:129:9:129:41 | call to including [element 0] : | active_support.rb:132:10:132:10 | b [element 0] : |
+| active_support.rb:129:9:129:41 | call to including [element 0] : | active_support.rb:132:10:132:10 | b [element 0] : |
+| active_support.rb:129:9:129:41 | call to including [element] : | active_support.rb:132:10:132:10 | b [element] : |
+| active_support.rb:129:9:129:41 | call to including [element] : | active_support.rb:132:10:132:10 | b [element] : |
+| active_support.rb:129:9:129:41 | call to including [element] : | active_support.rb:133:10:133:10 | b [element] : |
+| active_support.rb:129:9:129:41 | call to including [element] : | active_support.rb:133:10:133:10 | b [element] : |
+| active_support.rb:129:9:129:41 | call to including [element] : | active_support.rb:134:10:134:10 | b [element] : |
+| active_support.rb:129:9:129:41 | call to including [element] : | active_support.rb:134:10:134:10 | b [element] : |
+| active_support.rb:129:9:129:41 | call to including [element] : | active_support.rb:135:10:135:10 | b [element] : |
+| active_support.rb:129:9:129:41 | call to including [element] : | active_support.rb:135:10:135:10 | b [element] : |
+| active_support.rb:129:21:129:29 | call to source : | active_support.rb:129:9:129:41 | call to including [element] : |
+| active_support.rb:129:21:129:29 | call to source : | active_support.rb:129:9:129:41 | call to including [element] : |
+| active_support.rb:129:32:129:40 | call to source : | active_support.rb:129:9:129:41 | call to including [element] : |
+| active_support.rb:129:32:129:40 | call to source : | active_support.rb:129:9:129:41 | call to including [element] : |
+| active_support.rb:130:10:130:10 | a [element 0] : | active_support.rb:130:10:130:13 | ...[...] |
+| active_support.rb:130:10:130:10 | a [element 0] : | active_support.rb:130:10:130:13 | ...[...] |
+| active_support.rb:132:10:132:10 | b [element 0] : | active_support.rb:132:10:132:13 | ...[...] |
+| active_support.rb:132:10:132:10 | b [element 0] : | active_support.rb:132:10:132:13 | ...[...] |
+| active_support.rb:132:10:132:10 | b [element] : | active_support.rb:132:10:132:13 | ...[...] |
+| active_support.rb:132:10:132:10 | b [element] : | active_support.rb:132:10:132:13 | ...[...] |
+| active_support.rb:133:10:133:10 | b [element] : | active_support.rb:133:10:133:13 | ...[...] |
+| active_support.rb:133:10:133:10 | b [element] : | active_support.rb:133:10:133:13 | ...[...] |
+| active_support.rb:134:10:134:10 | b [element] : | active_support.rb:134:10:134:13 | ...[...] |
+| active_support.rb:134:10:134:10 | b [element] : | active_support.rb:134:10:134:13 | ...[...] |
+| active_support.rb:135:10:135:10 | b [element] : | active_support.rb:135:10:135:13 | ...[...] |
+| active_support.rb:135:10:135:10 | b [element] : | active_support.rb:135:10:135:13 | ...[...] |
+| flow.rb:2:7:2:16 | call to source : | flow.rb:3:34:3:34 | x : |
+| flow.rb:3:7:3:35 | call to new : | flow.rb:4:8:4:8 | y |
+| flow.rb:3:34:3:34 | x : | flow.rb:3:7:3:35 | call to new : |
+| flow.rb:9:7:9:16 | call to source : | flow.rb:10:21:10:21 | b : |
+| flow.rb:10:7:10:22 | call to safe_concat : | flow.rb:11:8:11:8 | y |
+| flow.rb:10:21:10:21 | b : | flow.rb:10:7:10:22 | call to safe_concat : |
+| flow.rb:16:7:16:16 | call to source : | flow.rb:17:17:17:17 | b : |
+| flow.rb:17:3:17:3 | [post] x : | flow.rb:18:8:18:8 | x |
+| flow.rb:17:17:17:17 | b : | flow.rb:17:3:17:3 | [post] x : |
+| flow.rb:22:7:22:16 | call to source : | flow.rb:24:34:24:34 | a : |
+| flow.rb:24:7:24:35 | call to new : | flow.rb:25:7:25:7 | x : |
+| flow.rb:24:34:24:34 | a : | flow.rb:24:7:24:35 | call to new : |
+| flow.rb:25:7:25:7 | x : | flow.rb:25:7:25:17 | call to concat : |
+| flow.rb:25:7:25:17 | call to concat : | flow.rb:26:8:26:8 | y |
+| flow.rb:30:7:30:16 | call to source : | flow.rb:32:34:32:34 | a : |
+| flow.rb:32:7:32:35 | call to new : | flow.rb:33:7:33:7 | x : |
+| flow.rb:32:34:32:34 | a : | flow.rb:32:7:32:35 | call to new : |
+| flow.rb:33:7:33:7 | x : | flow.rb:33:7:33:20 | call to insert : |
+| flow.rb:33:7:33:20 | call to insert : | flow.rb:34:8:34:8 | y |
+| flow.rb:38:7:38:16 | call to source : | flow.rb:40:34:40:34 | a : |
+| flow.rb:40:7:40:35 | call to new : | flow.rb:41:7:41:7 | x : |
+| flow.rb:40:34:40:34 | a : | flow.rb:40:7:40:35 | call to new : |
+| flow.rb:41:7:41:7 | x : | flow.rb:41:7:41:18 | call to prepend : |
+| flow.rb:41:7:41:18 | call to prepend : | flow.rb:42:8:42:8 | y |
+| flow.rb:46:7:46:16 | call to source : | flow.rb:48:34:48:34 | a : |
+| flow.rb:48:7:48:35 | call to new : | flow.rb:49:7:49:7 | x : |
+| flow.rb:48:34:48:34 | a : | flow.rb:48:7:48:35 | call to new : |
+| flow.rb:49:7:49:7 | x : | flow.rb:49:7:49:18 | call to prepend : |
+| flow.rb:49:7:49:18 | call to prepend : | flow.rb:50:8:50:8 | y |
+| flow.rb:54:7:54:16 | call to source : | flow.rb:55:34:55:34 | a : |
+| flow.rb:55:7:55:35 | call to new : | flow.rb:56:7:56:7 | x : |
+| flow.rb:55:34:55:34 | a : | flow.rb:55:7:55:35 | call to new : |
+| flow.rb:56:7:56:7 | x : | flow.rb:56:7:56:12 | call to to_s : |
+| flow.rb:56:7:56:12 | call to to_s : | flow.rb:57:8:57:8 | y |
+| flow.rb:61:7:61:16 | call to source : | flow.rb:62:34:62:34 | a : |
+| flow.rb:62:7:62:35 | call to new : | flow.rb:63:7:63:7 | x : |
+| flow.rb:62:34:62:34 | a : | flow.rb:62:7:62:35 | call to new : |
+| flow.rb:63:7:63:7 | x : | flow.rb:63:7:63:16 | call to to_param : |
+| flow.rb:63:7:63:16 | call to to_param : | flow.rb:64:8:64:8 | y |
+nodes
+| active_support.rb:9:9:9:18 | call to source : | semmle.label | call to source : |
+| active_support.rb:10:10:10:10 | x : | semmle.label | x : |
+| active_support.rb:10:10:10:19 | call to camelize | semmle.label | call to camelize |
+| active_support.rb:14:9:14:18 | call to source : | semmle.label | call to source : |
+| active_support.rb:15:10:15:10 | x : | semmle.label | x : |
+| active_support.rb:15:10:15:20 | call to camelcase | semmle.label | call to camelcase |
+| active_support.rb:19:9:19:18 | call to source : | semmle.label | call to source : |
+| active_support.rb:20:10:20:10 | x : | semmle.label | x : |
+| active_support.rb:20:10:20:19 | call to classify | semmle.label | call to classify |
+| active_support.rb:24:9:24:18 | call to source : | semmle.label | call to source : |
+| active_support.rb:25:10:25:10 | x : | semmle.label | x : |
+| active_support.rb:25:10:25:20 | call to dasherize | semmle.label | call to dasherize |
+| active_support.rb:29:9:29:18 | call to source : | semmle.label | call to source : |
+| active_support.rb:30:10:30:10 | x : | semmle.label | x : |
+| active_support.rb:30:10:30:24 | call to deconstantize | semmle.label | call to deconstantize |
+| active_support.rb:34:9:34:18 | call to source : | semmle.label | call to source : |
+| active_support.rb:35:10:35:10 | x : | semmle.label | x : |
+| active_support.rb:35:10:35:21 | call to demodulize | semmle.label | call to demodulize |
+| active_support.rb:39:9:39:18 | call to source : | semmle.label | call to source : |
+| active_support.rb:40:10:40:10 | x : | semmle.label | x : |
+| active_support.rb:40:10:40:22 | call to foreign_key | semmle.label | call to foreign_key |
+| active_support.rb:44:9:44:18 | call to source : | semmle.label | call to source : |
+| active_support.rb:45:10:45:10 | x : | semmle.label | x : |
+| active_support.rb:45:10:45:19 | call to humanize | semmle.label | call to humanize |
+| active_support.rb:49:9:49:18 | call to source : | semmle.label | call to source : |
+| active_support.rb:50:10:50:10 | x : | semmle.label | x : |
+| active_support.rb:50:10:50:20 | call to indent | semmle.label | call to indent |
+| active_support.rb:54:9:54:18 | call to source : | semmle.label | call to source : |
+| active_support.rb:55:10:55:10 | x : | semmle.label | x : |
+| active_support.rb:55:10:55:23 | call to parameterize | semmle.label | call to parameterize |
+| active_support.rb:59:9:59:18 | call to source : | semmle.label | call to source : |
+| active_support.rb:60:10:60:10 | x : | semmle.label | x : |
+| active_support.rb:60:10:60:20 | call to pluralize | semmle.label | call to pluralize |
+| active_support.rb:64:9:64:18 | call to source : | semmle.label | call to source : |
+| active_support.rb:65:10:65:10 | x : | semmle.label | x : |
+| active_support.rb:65:10:65:22 | call to singularize | semmle.label | call to singularize |
+| active_support.rb:69:9:69:18 | call to source : | semmle.label | call to source : |
+| active_support.rb:70:10:70:10 | x : | semmle.label | x : |
+| active_support.rb:70:10:70:17 | call to squish | semmle.label | call to squish |
+| active_support.rb:74:9:74:18 | call to source : | semmle.label | call to source : |
+| active_support.rb:75:10:75:10 | x : | semmle.label | x : |
+| active_support.rb:75:10:75:24 | call to strip_heredoc | semmle.label | call to strip_heredoc |
+| active_support.rb:79:9:79:18 | call to source : | semmle.label | call to source : |
+| active_support.rb:80:10:80:10 | x : | semmle.label | x : |
+| active_support.rb:80:10:80:19 | call to tableize | semmle.label | call to tableize |
+| active_support.rb:84:9:84:18 | call to source : | semmle.label | call to source : |
+| active_support.rb:85:10:85:10 | x : | semmle.label | x : |
+| active_support.rb:85:10:85:20 | call to titlecase | semmle.label | call to titlecase |
+| active_support.rb:89:9:89:18 | call to source : | semmle.label | call to source : |
+| active_support.rb:90:10:90:10 | x : | semmle.label | x : |
+| active_support.rb:90:10:90:19 | call to titleize | semmle.label | call to titleize |
+| active_support.rb:94:9:94:18 | call to source : | semmle.label | call to source : |
+| active_support.rb:95:10:95:10 | x : | semmle.label | x : |
+| active_support.rb:95:10:95:21 | call to underscore | semmle.label | call to underscore |
+| active_support.rb:99:9:99:18 | call to source : | semmle.label | call to source : |
+| active_support.rb:100:10:100:10 | x : | semmle.label | x : |
+| active_support.rb:100:10:100:23 | call to upcase_first | semmle.label | call to upcase_first |
+| active_support.rb:104:10:104:17 | call to source : | semmle.label | call to source : |
+| active_support.rb:104:10:104:17 | call to source : | semmle.label | call to source : |
+| active_support.rb:105:9:105:9 | x [element 0] : | semmle.label | x [element 0] : |
+| active_support.rb:105:9:105:9 | x [element 0] : | semmle.label | x [element 0] : |
+| active_support.rb:105:9:105:23 | call to compact_blank [element] : | semmle.label | call to compact_blank [element] : |
+| active_support.rb:105:9:105:23 | call to compact_blank [element] : | semmle.label | call to compact_blank [element] : |
+| active_support.rb:106:10:106:10 | y [element] : | semmle.label | y [element] : |
+| active_support.rb:106:10:106:10 | y [element] : | semmle.label | y [element] : |
+| active_support.rb:106:10:106:13 | ...[...] | semmle.label | ...[...] |
+| active_support.rb:106:10:106:13 | ...[...] | semmle.label | ...[...] |
+| active_support.rb:110:10:110:18 | call to source : | semmle.label | call to source : |
+| active_support.rb:110:10:110:18 | call to source : | semmle.label | call to source : |
+| active_support.rb:111:9:111:9 | x [element 0] : | semmle.label | x [element 0] : |
+| active_support.rb:111:9:111:9 | x [element 0] : | semmle.label | x [element 0] : |
+| active_support.rb:111:9:111:21 | call to excluding [element] : | semmle.label | call to excluding [element] : |
+| active_support.rb:111:9:111:21 | call to excluding [element] : | semmle.label | call to excluding [element] : |
+| active_support.rb:112:10:112:10 | y [element] : | semmle.label | y [element] : |
+| active_support.rb:112:10:112:10 | y [element] : | semmle.label | y [element] : |
+| active_support.rb:112:10:112:13 | ...[...] | semmle.label | ...[...] |
+| active_support.rb:112:10:112:13 | ...[...] | semmle.label | ...[...] |
+| active_support.rb:116:10:116:18 | call to source : | semmle.label | call to source : |
+| active_support.rb:116:10:116:18 | call to source : | semmle.label | call to source : |
+| active_support.rb:117:9:117:9 | x [element 0] : | semmle.label | x [element 0] : |
+| active_support.rb:117:9:117:9 | x [element 0] : | semmle.label | x [element 0] : |
+| active_support.rb:117:9:117:19 | call to without [element] : | semmle.label | call to without [element] : |
+| active_support.rb:117:9:117:19 | call to without [element] : | semmle.label | call to without [element] : |
+| active_support.rb:118:10:118:10 | y [element] : | semmle.label | y [element] : |
+| active_support.rb:118:10:118:10 | y [element] : | semmle.label | y [element] : |
+| active_support.rb:118:10:118:13 | ...[...] | semmle.label | ...[...] |
+| active_support.rb:118:10:118:13 | ...[...] | semmle.label | ...[...] |
+| active_support.rb:122:10:122:18 | call to source : | semmle.label | call to source : |
+| active_support.rb:122:10:122:18 | call to source : | semmle.label | call to source : |
+| active_support.rb:123:9:123:9 | x [element 0] : | semmle.label | x [element 0] : |
+| active_support.rb:123:9:123:9 | x [element 0] : | semmle.label | x [element 0] : |
+| active_support.rb:123:9:123:37 | call to in_order_of [element] : | semmle.label | call to in_order_of [element] : |
+| active_support.rb:123:9:123:37 | call to in_order_of [element] : | semmle.label | call to in_order_of [element] : |
+| active_support.rb:124:10:124:10 | y [element] : | semmle.label | y [element] : |
+| active_support.rb:124:10:124:10 | y [element] : | semmle.label | y [element] : |
+| active_support.rb:124:10:124:13 | ...[...] | semmle.label | ...[...] |
+| active_support.rb:124:10:124:13 | ...[...] | semmle.label | ...[...] |
+| active_support.rb:128:10:128:18 | call to source : | semmle.label | call to source : |
+| active_support.rb:128:10:128:18 | call to source : | semmle.label | call to source : |
+| active_support.rb:129:9:129:9 | a [element 0] : | semmle.label | a [element 0] : |
+| active_support.rb:129:9:129:9 | a [element 0] : | semmle.label | a [element 0] : |
+| active_support.rb:129:9:129:41 | call to including [element 0] : | semmle.label | call to including [element 0] : |
+| active_support.rb:129:9:129:41 | call to including [element 0] : | semmle.label | call to including [element 0] : |
+| active_support.rb:129:9:129:41 | call to including [element] : | semmle.label | call to including [element] : |
+| active_support.rb:129:9:129:41 | call to including [element] : | semmle.label | call to including [element] : |
+| active_support.rb:129:21:129:29 | call to source : | semmle.label | call to source : |
+| active_support.rb:129:21:129:29 | call to source : | semmle.label | call to source : |
+| active_support.rb:129:32:129:40 | call to source : | semmle.label | call to source : |
+| active_support.rb:129:32:129:40 | call to source : | semmle.label | call to source : |
+| active_support.rb:130:10:130:10 | a [element 0] : | semmle.label | a [element 0] : |
+| active_support.rb:130:10:130:10 | a [element 0] : | semmle.label | a [element 0] : |
+| active_support.rb:130:10:130:13 | ...[...] | semmle.label | ...[...] |
+| active_support.rb:130:10:130:13 | ...[...] | semmle.label | ...[...] |
+| active_support.rb:132:10:132:10 | b [element 0] : | semmle.label | b [element 0] : |
+| active_support.rb:132:10:132:10 | b [element 0] : | semmle.label | b [element 0] : |
+| active_support.rb:132:10:132:10 | b [element] : | semmle.label | b [element] : |
+| active_support.rb:132:10:132:10 | b [element] : | semmle.label | b [element] : |
+| active_support.rb:132:10:132:13 | ...[...] | semmle.label | ...[...] |
+| active_support.rb:132:10:132:13 | ...[...] | semmle.label | ...[...] |
+| active_support.rb:133:10:133:10 | b [element] : | semmle.label | b [element] : |
+| active_support.rb:133:10:133:10 | b [element] : | semmle.label | b [element] : |
+| active_support.rb:133:10:133:13 | ...[...] | semmle.label | ...[...] |
+| active_support.rb:133:10:133:13 | ...[...] | semmle.label | ...[...] |
+| active_support.rb:134:10:134:10 | b [element] : | semmle.label | b [element] : |
+| active_support.rb:134:10:134:10 | b [element] : | semmle.label | b [element] : |
+| active_support.rb:134:10:134:13 | ...[...] | semmle.label | ...[...] |
+| active_support.rb:134:10:134:13 | ...[...] | semmle.label | ...[...] |
+| active_support.rb:135:10:135:10 | b [element] : | semmle.label | b [element] : |
+| active_support.rb:135:10:135:10 | b [element] : | semmle.label | b [element] : |
+| active_support.rb:135:10:135:13 | ...[...] | semmle.label | ...[...] |
+| active_support.rb:135:10:135:13 | ...[...] | semmle.label | ...[...] |
+| flow.rb:2:7:2:16 | call to source : | semmle.label | call to source : |
+| flow.rb:3:7:3:35 | call to new : | semmle.label | call to new : |
+| flow.rb:3:34:3:34 | x : | semmle.label | x : |
+| flow.rb:4:8:4:8 | y | semmle.label | y |
+| flow.rb:9:7:9:16 | call to source : | semmle.label | call to source : |
+| flow.rb:10:7:10:22 | call to safe_concat : | semmle.label | call to safe_concat : |
+| flow.rb:10:21:10:21 | b : | semmle.label | b : |
+| flow.rb:11:8:11:8 | y | semmle.label | y |
+| flow.rb:16:7:16:16 | call to source : | semmle.label | call to source : |
+| flow.rb:17:3:17:3 | [post] x : | semmle.label | [post] x : |
+| flow.rb:17:17:17:17 | b : | semmle.label | b : |
+| flow.rb:18:8:18:8 | x | semmle.label | x |
+| flow.rb:22:7:22:16 | call to source : | semmle.label | call to source : |
+| flow.rb:24:7:24:35 | call to new : | semmle.label | call to new : |
+| flow.rb:24:34:24:34 | a : | semmle.label | a : |
+| flow.rb:25:7:25:7 | x : | semmle.label | x : |
+| flow.rb:25:7:25:17 | call to concat : | semmle.label | call to concat : |
+| flow.rb:26:8:26:8 | y | semmle.label | y |
+| flow.rb:30:7:30:16 | call to source : | semmle.label | call to source : |
+| flow.rb:32:7:32:35 | call to new : | semmle.label | call to new : |
+| flow.rb:32:34:32:34 | a : | semmle.label | a : |
+| flow.rb:33:7:33:7 | x : | semmle.label | x : |
+| flow.rb:33:7:33:20 | call to insert : | semmle.label | call to insert : |
+| flow.rb:34:8:34:8 | y | semmle.label | y |
+| flow.rb:38:7:38:16 | call to source : | semmle.label | call to source : |
+| flow.rb:40:7:40:35 | call to new : | semmle.label | call to new : |
+| flow.rb:40:34:40:34 | a : | semmle.label | a : |
+| flow.rb:41:7:41:7 | x : | semmle.label | x : |
+| flow.rb:41:7:41:18 | call to prepend : | semmle.label | call to prepend : |
+| flow.rb:42:8:42:8 | y | semmle.label | y |
+| flow.rb:46:7:46:16 | call to source : | semmle.label | call to source : |
+| flow.rb:48:7:48:35 | call to new : | semmle.label | call to new : |
+| flow.rb:48:34:48:34 | a : | semmle.label | a : |
+| flow.rb:49:7:49:7 | x : | semmle.label | x : |
+| flow.rb:49:7:49:18 | call to prepend : | semmle.label | call to prepend : |
+| flow.rb:50:8:50:8 | y | semmle.label | y |
+| flow.rb:54:7:54:16 | call to source : | semmle.label | call to source : |
+| flow.rb:55:7:55:35 | call to new : | semmle.label | call to new : |
+| flow.rb:55:34:55:34 | a : | semmle.label | a : |
+| flow.rb:56:7:56:7 | x : | semmle.label | x : |
+| flow.rb:56:7:56:12 | call to to_s : | semmle.label | call to to_s : |
+| flow.rb:57:8:57:8 | y | semmle.label | y |
+| flow.rb:61:7:61:16 | call to source : | semmle.label | call to source : |
+| flow.rb:62:7:62:35 | call to new : | semmle.label | call to new : |
+| flow.rb:62:34:62:34 | a : | semmle.label | a : |
+| flow.rb:63:7:63:7 | x : | semmle.label | x : |
+| flow.rb:63:7:63:16 | call to to_param : | semmle.label | call to to_param : |
+| flow.rb:64:8:64:8 | y | semmle.label | y |
+subpaths
+#select
+| active_support.rb:106:10:106:13 | ...[...] | active_support.rb:104:10:104:17 | call to source : | active_support.rb:106:10:106:13 | ...[...] | $@ | active_support.rb:104:10:104:17 | call to source : | call to source : |
+| active_support.rb:112:10:112:13 | ...[...] | active_support.rb:110:10:110:18 | call to source : | active_support.rb:112:10:112:13 | ...[...] | $@ | active_support.rb:110:10:110:18 | call to source : | call to source : |
+| active_support.rb:118:10:118:13 | ...[...] | active_support.rb:116:10:116:18 | call to source : | active_support.rb:118:10:118:13 | ...[...] | $@ | active_support.rb:116:10:116:18 | call to source : | call to source : |
+| active_support.rb:124:10:124:13 | ...[...] | active_support.rb:122:10:122:18 | call to source : | active_support.rb:124:10:124:13 | ...[...] | $@ | active_support.rb:122:10:122:18 | call to source : | call to source : |
+| active_support.rb:130:10:130:13 | ...[...] | active_support.rb:128:10:128:18 | call to source : | active_support.rb:130:10:130:13 | ...[...] | $@ | active_support.rb:128:10:128:18 | call to source : | call to source : |
+| active_support.rb:132:10:132:13 | ...[...] | active_support.rb:128:10:128:18 | call to source : | active_support.rb:132:10:132:13 | ...[...] | $@ | active_support.rb:128:10:128:18 | call to source : | call to source : |
+| active_support.rb:132:10:132:13 | ...[...] | active_support.rb:129:21:129:29 | call to source : | active_support.rb:132:10:132:13 | ...[...] | $@ | active_support.rb:129:21:129:29 | call to source : | call to source : |
+| active_support.rb:132:10:132:13 | ...[...] | active_support.rb:129:32:129:40 | call to source : | active_support.rb:132:10:132:13 | ...[...] | $@ | active_support.rb:129:32:129:40 | call to source : | call to source : |
+| active_support.rb:133:10:133:13 | ...[...] | active_support.rb:129:21:129:29 | call to source : | active_support.rb:133:10:133:13 | ...[...] | $@ | active_support.rb:129:21:129:29 | call to source : | call to source : |
+| active_support.rb:133:10:133:13 | ...[...] | active_support.rb:129:32:129:40 | call to source : | active_support.rb:133:10:133:13 | ...[...] | $@ | active_support.rb:129:32:129:40 | call to source : | call to source : |
+| active_support.rb:134:10:134:13 | ...[...] | active_support.rb:129:21:129:29 | call to source : | active_support.rb:134:10:134:13 | ...[...] | $@ | active_support.rb:129:21:129:29 | call to source : | call to source : |
+| active_support.rb:134:10:134:13 | ...[...] | active_support.rb:129:32:129:40 | call to source : | active_support.rb:134:10:134:13 | ...[...] | $@ | active_support.rb:129:32:129:40 | call to source : | call to source : |
+| active_support.rb:135:10:135:13 | ...[...] | active_support.rb:129:21:129:29 | call to source : | active_support.rb:135:10:135:13 | ...[...] | $@ | active_support.rb:129:21:129:29 | call to source : | call to source : |
+| active_support.rb:135:10:135:13 | ...[...] | active_support.rb:129:32:129:40 | call to source : | active_support.rb:135:10:135:13 | ...[...] | $@ | active_support.rb:129:32:129:40 | call to source : | call to source : |
diff --git a/ruby/ql/test/library-tests/frameworks/active_support/ActiveSupportFlow.ql b/ruby/ql/test/library-tests/frameworks/active_support/ActiveSupportFlow.ql
new file mode 100644
index 00000000000..4e812d32daa
--- /dev/null
+++ b/ruby/ql/test/library-tests/frameworks/active_support/ActiveSupportFlow.ql
@@ -0,0 +1,11 @@
+/**
+ * @kind path-problem
+ */
+
+import ruby
+import TestUtilities.InlineFlowTest
+import PathGraph
+
+from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultValueFlowConf conf
+where conf.hasFlowPath(source, sink)
+select sink, source, sink, "$@", source, source.toString()
diff --git a/ruby/ql/test/library-tests/frameworks/active_support/flow.rb b/ruby/ql/test/library-tests/frameworks/active_support/flow.rb
new file mode 100644
index 00000000000..7d8dcee6f31
--- /dev/null
+++ b/ruby/ql/test/library-tests/frameworks/active_support/flow.rb
@@ -0,0 +1,65 @@
+def m1
+ x = source "a"
+ y = ActionView::SafeBuffer.new(x)
+ sink y # $hasTaintFlow=a
+end
+
+def m2
+ x = ActionView::SafeBuffer.new("a")
+ b = source "b"
+ y = x.safe_concat(b)
+ sink y # $hasTaintFlow=b
+end
+
+def m3
+ x = ActionView::SafeBuffer.new("a")
+ b = source "b"
+ x.safe_concat(b)
+ sink x # $hasTaintFlow=b
+end
+
+def m4
+ a = source "a"
+ b = source "b"
+ x = ActionView::SafeBuffer.new(a)
+ y = x.concat(b)
+ sink y # $hasTaintFlow=a
+end
+
+def m5
+ a = source "a"
+ b = source "b"
+ x = ActionView::SafeBuffer.new(a)
+ y = x.insert(i, b)
+ sink y # $hasTaintFlow=a
+end
+
+def m6
+ a = source "a"
+ b = source "b"
+ x = ActionView::SafeBuffer.new(a)
+ y = x.prepend(b)
+ sink y # $hasTaintFlow=a
+end
+
+def m7
+ a = source "a"
+ b = source "b"
+ x = ActionView::SafeBuffer.new(a)
+ y = x.prepend(b)
+ sink y # $hasTaintFlow=a
+end
+
+def m7
+ a = source "a"
+ x = ActionView::SafeBuffer.new(a)
+ y = x.to_s
+ sink y # $hasTaintFlow=a
+end
+
+def m8
+ a = source "a"
+ x = ActionView::SafeBuffer.new(a)
+ y = x.to_param
+ sink y # $hasTaintFlow=a
+end
From 1d693d336fe7adc1eed55c851e2405680864ec26 Mon Sep 17 00:00:00 2001
From: Harry Maclean
Date: Wed, 7 Sep 2022 14:47:24 +0100
Subject: [PATCH 027/991] Ruby: Model javascript_include_tag and friends
---
.../lib/codeql/ruby/frameworks/ActionView.qll | 202 ++++++++++--------
ruby/ql/lib/codeql/ruby/security/XSS.qll | 11 +-
.../library-tests/frameworks/ActionView.ql | 2 +-
.../security/cwe-079/ReflectedXSS.expected | 4 +
.../cwe-079/app/views/foo/bars/show.html.erb | 3 +
5 files changed, 128 insertions(+), 94 deletions(-)
diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionView.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionView.qll
index b7dc988205b..3f9089fa2fb 100644
--- a/ruby/ql/lib/codeql/ruby/frameworks/ActionView.qll
+++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionView.qll
@@ -218,114 +218,134 @@ class FileSystemResolverAccess extends DataFlow::CallNode, FileSystemAccess::Ran
}
// TODO: model flow in/out of template files properly,
-//
-/**
- * Action view helper methods which are XSS sinks.
- */
-module ActionViewHelpers {
+// TODO: Move the classes and predicates above inside this module.
+/** Modeling for `ActionView`. */
+module ActionView {
/**
- * Calls to ActionView helpers which render their argument without escaping.
- * These arguments should be treated as XSS sinks.
- * In the documentation for classes in this module, the vulnerable argument is
- * named `x`.
+ * Action view helper methods which are XSS sinks.
*/
- abstract class RawHelperCall extends MethodCall {
- abstract Expr getRawArgument();
- }
-
- /**
- * `ActionView::Helpers::TextHelper#simple_format`.
- *
- * `simple_format(x, y, sanitize: false)`.
- */
- private class SimpleFormat extends ActionViewContextCall, RawHelperCall {
- SimpleFormat() {
- this.getMethodName() = "simple_format" and
- this.getKeywordArgument("sanitize").getConstantValue().isBoolean(false)
+ module Helpers {
+ /**
+ * Calls to ActionView helpers which render their argument without escaping.
+ * These arguments should be treated as XSS sinks.
+ * In the documentation for classes in this module, the vulnerable argument is
+ * named `x`.
+ */
+ abstract class RawHelperCall extends MethodCall {
+ abstract Expr getRawArgument();
}
- override Expr getRawArgument() { result = this.getArgument(0) }
- }
+ /**
+ * `ActionView::Helpers::TextHelper#simple_format`.
+ *
+ * `simple_format(x, y, sanitize: false)`.
+ */
+ private class SimpleFormat extends ActionViewContextCall, RawHelperCall {
+ SimpleFormat() {
+ this.getMethodName() = "simple_format" and
+ this.getKeywordArgument("sanitize").getConstantValue().isBoolean(false)
+ }
- /**
- * `ActionView::Helpers::TextHelper#truncate`.
- *
- * `truncate(x, escape: false)`.
- */
- private class Truncate extends ActionViewContextCall, RawHelperCall {
- Truncate() {
- this.getMethodName() = "truncate" and
- this.getKeywordArgument("escape").getConstantValue().isBoolean(false)
+ override Expr getRawArgument() { result = this.getArgument(0) }
}
- override Expr getRawArgument() { result = this.getArgument(0) }
- }
+ /**
+ * `ActionView::Helpers::TextHelper#truncate`.
+ *
+ * `truncate(x, escape: false)`.
+ */
+ private class Truncate extends ActionViewContextCall, RawHelperCall {
+ Truncate() {
+ this.getMethodName() = "truncate" and
+ this.getKeywordArgument("escape").getConstantValue().isBoolean(false)
+ }
- /**
- * `ActionView::Helpers::TextHelper#highlight`.
- *
- * `truncate(x, y, sanitize: false)`.
- */
- private class Highlight extends ActionViewContextCall, RawHelperCall {
- Highlight() {
- this.getMethodName() = "highlight" and
- this.getKeywordArgument("sanitize").getConstantValue().isBoolean(false)
+ override Expr getRawArgument() { result = this.getArgument(0) }
}
- override Expr getRawArgument() { result = this.getArgument(0) }
- }
+ /**
+ * `ActionView::Helpers::TextHelper#highlight`.
+ *
+ * `truncate(x, y, sanitize: false)`.
+ */
+ private class Highlight extends ActionViewContextCall, RawHelperCall {
+ Highlight() {
+ this.getMethodName() = "highlight" and
+ this.getKeywordArgument("sanitize").getConstantValue().isBoolean(false)
+ }
- /**
- * `ActionView::Helpers::JavascriptHelper#javascript_tag`.
- *
- * `javascript_tag(x)`.
- */
- private class JavascriptTag extends ActionViewContextCall, RawHelperCall {
- JavascriptTag() { this.getMethodName() = "javascript_tag" }
-
- override Expr getRawArgument() { result = this.getArgument(0) }
- }
-
- /**
- * `ActionView::Helpers::TagHelper#tag`.
- *
- * `tag(x, x, y, false)`.
- */
- private class ContentTag extends ActionViewContextCall, RawHelperCall {
- ContentTag() {
- this.getMethodName() = "content_tag" and
- this.getArgument(3).getConstantValue().isBoolean(false)
+ override Expr getRawArgument() { result = this.getArgument(0) }
}
- override Expr getRawArgument() { result = this.getArgument(1) }
+ /**
+ * `ActionView::Helpers::JavascriptHelper#javascript_tag`.
+ *
+ * `javascript_tag(x)`.
+ */
+ private class JavascriptTag extends ActionViewContextCall, RawHelperCall {
+ JavascriptTag() { this.getMethodName() = "javascript_tag" }
+
+ override Expr getRawArgument() { result = this.getArgument(0) }
+ }
+
+ /**
+ * `ActionView::Helpers::TagHelper#tag`.
+ *
+ * `tag(x, x, y, false)`.
+ */
+ private class ContentTag extends ActionViewContextCall, RawHelperCall {
+ ContentTag() {
+ this.getMethodName() = "content_tag" and
+ this.getArgument(3).getConstantValue().isBoolean(false)
+ }
+
+ override Expr getRawArgument() { result = this.getArgument(1) }
+ }
+
+ /**
+ * `ActionView::Helpers::TagHelper#tag`.
+ *
+ * `tag(x, x, y, false)`.
+ */
+ private class Tag extends ActionViewContextCall, RawHelperCall {
+ Tag() {
+ this.getMethodName() = "tag" and
+ this.getArgument(3).getConstantValue().isBoolean(false)
+ }
+
+ override Expr getRawArgument() { result = this.getArgument(0) }
+ }
+
+ /**
+ * `ActionView::Helpers::TagHelper#tag.`.
+ *
+ * `tag.h1(x, escape: false)`.
+ */
+ private class TagMethod extends MethodCall, RawHelperCall {
+ TagMethod() {
+ inActionViewContext(this) and
+ this.getReceiver().(MethodCall).getMethodName() = "tag" and
+ this.getKeywordArgument("escape").getConstantValue().isBoolean(false)
+ }
+
+ override Expr getRawArgument() { result = this.getArgument(0) }
+ }
}
/**
- * `ActionView::Helpers::TagHelper#tag`.
- *
- * `tag(x, x, y, false)`.
+ * An argument to a method call which constructs a script tag, interpreting the
+ * argument as a URL. Remote input flowing to this argument may allow loading of
+ * arbitrary javascript.
*/
- private class Tag extends ActionViewContextCall, RawHelperCall {
- Tag() {
- this.getMethodName() = "tag" and
- this.getArgument(3).getConstantValue().isBoolean(false)
+ class ArgumentInterpretedAsUrl extends DataFlow::Node {
+ ArgumentInterpretedAsUrl() {
+ exists(DataFlow::CallNode call |
+ call.getMethodName() = ["javascript_include_tag", "javascript_path", "path_to_javascript"] and
+ this = call.getArgument(0)
+ or
+ call.getMethodName() = "javascript_url" and
+ this = call.getKeywordArgument("host")
+ )
}
-
- override Expr getRawArgument() { result = this.getArgument(0) }
- }
-
- /**
- * `ActionView::Helpers::TagHelper#tag.`.
- *
- * `tag.h1(x, escape: false)`.
- */
- private class TagMethod extends MethodCall, RawHelperCall {
- TagMethod() {
- inActionViewContext(this) and
- this.getReceiver().(MethodCall).getMethodName() = "tag" and
- this.getKeywordArgument("escape").getConstantValue().isBoolean(false)
- }
-
- override Expr getRawArgument() { result = this.getArgument(0) }
}
}
diff --git a/ruby/ql/lib/codeql/ruby/security/XSS.qll b/ruby/ql/lib/codeql/ruby/security/XSS.qll
index 21a504fd8d0..40c8110ccf8 100644
--- a/ruby/ql/lib/codeql/ruby/security/XSS.qll
+++ b/ruby/ql/lib/codeql/ruby/security/XSS.qll
@@ -79,14 +79,21 @@ private module Shared {
* An argument to an ActionView helper method which is not escaped,
* considered as a flow sink.
*/
- class RawHelperCallArgumentAsSink extends Sink, ErbOutputMethodCallArgumentNode {
+ class RawHelperCallArgumentAsSink extends Sink {
RawHelperCallArgumentAsSink() {
- exists(ErbOutputDirective d, ActionViewHelpers::RawHelperCall c |
+ exists(ErbOutputDirective d, ActionView::Helpers::RawHelperCall c |
d.getTerminalStmt() = c and this.asExpr().getExpr() = c.getRawArgument()
)
}
}
+ /**
+ * An argument that is used to construct the `src` attribute of a ` // OK
});
From bba70a70fbfda95a16c1593036b08ddb5d202236 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Thu, 6 Oct 2022 00:35:04 +0100
Subject: [PATCH 399/991] Swift: Support selecting fields in Swift MaD.
---
.../codeql/swift/dataflow/ExternalFlow.qll | 36 +++++++++++++++---
.../internal/FlowSummaryImplSpecific.qll | 26 ++++++++++---
.../codeql/swift/elements/decl/TypeDecl.qll | 20 ++++++++++
.../swift/elements/type/NominalType.qll | 14 +++++++
.../swift/frameworks/StandardLibrary/Url.qll | 38 +++++++++----------
5 files changed, 102 insertions(+), 32 deletions(-)
diff --git a/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll b/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll
index b1c25900385..01d4de5f0c6 100644
--- a/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll
+++ b/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll
@@ -410,8 +410,10 @@ pragma[nomagic]
private Element interpretElement0(
string namespace, string type, boolean subtypes, string name, string signature
) {
+ elementSpec(namespace, type, subtypes, name, signature, _) and
namespace = "" and // TODO: Fill out when we properly extract modules.
(
+ // Non-member functions
exists(AbstractFunctionDecl func |
func.getName() = name and
type = "" and
@@ -421,10 +423,11 @@ private Element interpretElement0(
result = func
)
or
+ // Member functions
exists(NominalType nomType, IterableDeclContext decl, MethodDecl method |
method.getName() = name and
method = decl.getAMember() and
- nomType.getName() = type and
+ nomType.getFullName() = type and
matchesSignature(method, signature) and
result = method
|
@@ -434,6 +437,20 @@ private Element interpretElement0(
subtypes = false and
getDeclType(decl) = nomType
)
+ or
+ signature = "" and
+ exists(NominalType nomType, IterableDeclContext decl, FieldDecl field |
+ field.getName() = name and
+ field = decl.getAMember() and
+ nomType.getFullName() = type and
+ result = field
+ |
+ subtypes = true and
+ getDeclType(decl) = nomType.getADerivedType*()
+ or
+ subtypes = false and
+ getDeclType(decl) = nomType
+ )
)
}
@@ -447,11 +464,18 @@ Element interpretElement(
)
}
-/**
- * Holds if `c` has a `generated` summary.
- */
-predicate hasSummary(SummarizedCallable c, boolean generated) {
- summaryElement(c, _, _, _, generated)
+private predicate parseField(AccessPathToken c, Content::FieldContent f) {
+ exists(string fieldRegex, string name |
+ c.getName() = "Field" and
+ fieldRegex = "^([^.]+)$" and
+ name = c.getAnArgument().regexpCapture(fieldRegex, 1) and
+ f.getField().getName() = name
+ )
+}
+
+/** Holds if the specification component parses as a `Content`. */
+predicate parseContent(AccessPathToken component, Content content) {
+ parseField(component, content)
}
cached
diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImplSpecific.qll b/swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImplSpecific.qll
index fc831054231..fddedcbbf85 100644
--- a/swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImplSpecific.qll
+++ b/swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImplSpecific.qll
@@ -105,12 +105,19 @@ predicate sinkElement(Element e, string input, string kind, boolean generated) {
/** Gets the summary component for specification component `c`, if any. */
bindingset[c]
SummaryComponent interpretComponentSpecific(AccessPathToken c) {
- none() // TODO once we have field flow
+ exists(ContentSet cs, Content content |
+ cs.isSingleton(content) and
+ parseContent(c, content) and
+ result = SummaryComponent::content(cs)
+ )
}
/** Gets the textual representation of the content in the format used for flow summaries. */
-private string getContentSpecificCsv(ContentSet c) {
- none() // TODO once we have field flow
+private string getContentSpecificCsv(ContentSet cs) {
+ exists(Content::FieldContent c |
+ cs.isSingleton(c) and
+ result = "Field[" + c.getField().getName() + "]"
+ )
}
/** Gets the textual representation of a summary component in the format used for flow summaries. */
@@ -182,10 +189,17 @@ class InterpretNode extends TInterpretNode {
}
}
-/** Provides additional sink specification logic required for attributes. */
-predicate interpretOutputSpecific(string c, InterpretNode mid, InterpretNode node) { none() }
+predicate interpretOutputSpecific(string c, InterpretNode mid, InterpretNode node) {
+ // Allow fields to be picked as output nodes.
+ exists(Node n, AstNode ast |
+ n = node.asNode() and
+ ast = mid.asElement()
+ |
+ c = "" and
+ n.asExpr().(MemberRefExpr).getMember() = ast
+ )
+}
-/** Provides additional sink specification logic required for attributes. */
predicate interpretInputSpecific(string c, InterpretNode mid, InterpretNode n) { none() }
/** Gets the argument position obtained by parsing `X` in `Parameter[X]`. */
diff --git a/swift/ql/lib/codeql/swift/elements/decl/TypeDecl.qll b/swift/ql/lib/codeql/swift/elements/decl/TypeDecl.qll
index 967a3a81294..e1cbc88fb44 100644
--- a/swift/ql/lib/codeql/swift/elements/decl/TypeDecl.qll
+++ b/swift/ql/lib/codeql/swift/elements/decl/TypeDecl.qll
@@ -1,6 +1,7 @@
private import codeql.swift.generated.decl.TypeDecl
private import codeql.swift.generated.type.Type
private import codeql.swift.elements.type.AnyGenericType
+private import swift
class TypeDecl extends TypeDeclBase {
override string toString() { result = this.getName() }
@@ -12,4 +13,23 @@ class TypeDecl extends TypeDeclBase {
TypeDecl getDerivedTypeDecl(int i) { result.getBaseTypeDecl(i) = this }
TypeDecl getADerivedTypeDecl() { result = this.getDerivedTypeDecl(_) }
+
+ /**
+ * Gets the full name of this `TypeDecl`. For example in:
+ * ```swift
+ * struct A {
+ * struct B {
+ * // ...
+ * }
+ * }
+ * ```
+ * The name and full name of `A` is `A`. The name of `B` is `B`, but the
+ * full name of `B` is `A.B`.
+ */
+ string getFullName() {
+ not this.getEnclosingDecl() instanceof TypeDecl and
+ result = this.getName()
+ or
+ result = this.getEnclosingDecl().(TypeDecl).getFullName() + "." + this.getName()
+ }
}
diff --git a/swift/ql/lib/codeql/swift/elements/type/NominalType.qll b/swift/ql/lib/codeql/swift/elements/type/NominalType.qll
index 391b09996b0..d3de83a83f9 100644
--- a/swift/ql/lib/codeql/swift/elements/type/NominalType.qll
+++ b/swift/ql/lib/codeql/swift/elements/type/NominalType.qll
@@ -5,4 +5,18 @@ class NominalType extends NominalTypeBase {
NominalType getABaseType() { result = this.getDeclaration().(NominalTypeDecl).getABaseType() }
NominalType getADerivedType() { result.getABaseType() = this }
+
+ /**
+ * Gets the full name of this `TypeDecl`. For example in:
+ * ```swift
+ * struct A {
+ * struct B {
+ * // ...
+ * }
+ * }
+ * ```
+ * The name and full name of `A` is `A`. The name of `B` is `B`, but the
+ * full name of `B` is `A.B`.
+ */
+ string getFullName() { result = this.getDeclaration().(NominalTypeDecl).getFullName() }
}
diff --git a/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/Url.qll b/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/Url.qll
index 04d4b9d211d..34a95c6586c 100644
--- a/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/Url.qll
+++ b/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/Url.qll
@@ -1,27 +1,25 @@
import swift
-private import codeql.swift.dataflow.FlowSources
+private import codeql.swift.dataflow.ExternalFlow
/**
* A model for `URL` members that are sources of remote flow.
*/
-class UrlRemoteFlowSource extends RemoteFlowSource {
- UrlRemoteFlowSource() {
- exists(StructDecl urlClass, ConcreteVarDecl memberDecl |
- urlClass.getName() = "URL" and
- (
- urlClass.getAMember() = memberDecl and
- memberDecl.getName() = ["resourceBytes", "lines"]
- or
- exists(StructDecl asyncBytesClass |
- urlClass.getAMember() = asyncBytesClass and
- asyncBytesClass.getName() = "AsyncBytes" and
- asyncBytesClass.getAMember() = memberDecl and
- memberDecl.getName() = "lines"
- )
- ) and
- this.asExpr().(MemberRefExpr).getMember() = memberDecl
- )
+private class UrlRemoteFlowSource extends SourceModelCsv {
+ override predicate row(string row) {
+ row =
+ [
+ ";URL;true;resourceBytes;;;;remote", ";URL;true;lines;;;;remote",
+ ";URL.AsyncBytes;true;lines;;;;remote"
+ ]
+ }
+}
+
+private class UrlSummaries extends SummaryModelCsv {
+ override predicate row(string row) {
+ row =
+ [
+ ";URL;true;init(string:);(String);;Argument[0];ReturnValue;taint",
+ ";URL;true;init(string:relativeTo:);(String,URL?);;Argument[0,1];ReturnValue;taint"
+ ]
}
-
- override string getSourceType() { result = "external" }
}
From 0b6ea703ea8b704bc8a1d008edbdb310234a0e0c Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Thu, 6 Oct 2022 00:38:03 +0100
Subject: [PATCH 400/991] Swift: Create explicit parameter nodes for source
parameters and MaD parameters.
---
.../dataflow/internal/DataFlowPrivate.qll | 56 +++++++++--
.../dataflow/internal/DataFlowPublic.qll | 10 +-
.../dataflow/dataflow/LocalFlow.expected | 92 +++++++++++++------
3 files changed, 123 insertions(+), 35 deletions(-)
diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll
index 7cab7c5eee6..8c822861788 100644
--- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll
+++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll
@@ -64,7 +64,13 @@ private module Cached {
TExprNode(CfgNode n, Expr e) { hasExprNode(n, e) } or
TSsaDefinitionNode(Ssa::Definition def) or
TInoutReturnNode(ParamDecl param) { modifiableParam(param) } or
- TSummaryNode(FlowSummary::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNodeState state) or
+ TSummaryNode(FlowSummary::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNodeState state) {
+ FlowSummaryImpl::Private::summaryNodeRange(c, state)
+ } or
+ TSourceParameterNode(ParamDecl param) or
+ TSummaryParameterNode(FlowSummary::SummarizedCallable c, ParameterPosition pos) {
+ FlowSummaryImpl::Private::summaryParameterNodeRange(c, pos)
+ } or
TExprPostUpdateNode(CfgNode n) {
// Obviously, the base of setters needs a post-update node
n = any(PropertySetterCfgNode setter).getBase()
@@ -93,6 +99,20 @@ private module Cached {
)
}
+ private SsaDefinitionNode getParameterDefNode(ParamDecl p) {
+ exists(BasicBlock bb, int i |
+ bb.getNode(i).getNode().asAstNode() = p and
+ result.asDefinition().definesAt(_, bb, i)
+ )
+ }
+
+ /**
+ * Holds if `nodeFrom` is a parameter node, and `nodeTo` is a corresponding SSA node.
+ */
+ private predicate localFlowSsaParamInput(Node nodeFrom, Node nodeTo) {
+ nodeTo = getParameterDefNode(nodeFrom.(ParameterNode).getParameter())
+ }
+
private predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) {
exists(Ssa::Definition def |
// Step from assignment RHS to def
@@ -117,6 +137,8 @@ private module Cached {
localFlowSsaInput(nodeFrom, def, nodeTo.asDefinition())
)
or
+ localFlowSsaParamInput(nodeFrom, nodeTo)
+ or
// flow through `&` (inout argument)
nodeFrom.asExpr() = nodeTo.asExpr().(InOutExpr).getSubExpr()
or
@@ -181,17 +203,15 @@ predicate nodeIsHidden(Node n) { none() }
private module ParameterNodes {
abstract class ParameterNodeImpl extends NodeImpl {
predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { none() }
+
+ /** Gets the parameter associated with this node, if any. */
+ ParamDecl getParameter() { none() }
}
- class NormalParameterNode extends ParameterNodeImpl, SsaDefinitionNodeImpl {
+ class SourceParameterNode extends ParameterNodeImpl, TSourceParameterNode {
ParamDecl param;
- NormalParameterNode() {
- exists(BasicBlock bb, int i |
- super.asDefinition().definesAt(param, bb, i) and
- bb.getNode(i).getNode().asAstNode() = param
- )
- }
+ SourceParameterNode() { this = TSourceParameterNode(param) }
override Location getLocationImpl() { result = param.getLocation() }
@@ -206,6 +226,26 @@ private module ParameterNodes {
}
override DataFlowCallable getEnclosingCallable() { this.isParameterOf(result, _) }
+
+ override ParamDecl getParameter() { result = param }
+ }
+
+ class SummaryParameterNode extends ParameterNodeImpl, TSummaryParameterNode {
+ FlowSummary::SummarizedCallable sc;
+ ParameterPosition pos;
+
+ SummaryParameterNode() { this = TSummaryParameterNode(sc, pos) }
+
+ override predicate isParameterOf(DataFlowCallable c, ParameterPosition p) {
+ c.getUnderlyingCallable() = sc and
+ p = pos
+ }
+
+ override Location getLocationImpl() { result = sc.getLocation() }
+
+ override string toStringImpl() { result = "[summary param] " + pos + " in " + sc }
+
+ override DataFlowCallable getEnclosingCallable() { this.isParameterOf(result, _) }
}
}
diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll
index 22743df2230..2cabfd6b96c 100644
--- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll
+++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll
@@ -70,7 +70,15 @@ class ExprNode extends Node, TExprNode {
* The value of a parameter at function entry, viewed as a node in a data
* flow graph.
*/
-class ParameterNode extends Node, SsaDefinitionNode instanceof ParameterNodeImpl { }
+class ParameterNode extends Node instanceof ParameterNodeImpl {
+ override ControlFlowNode getCfgNode() { result = this.(ParameterNodeImpl).getCfgNode() }
+
+ DataFlowCallable getDeclaringFunction() {
+ result = this.(ParameterNodeImpl).getEnclosingCallable()
+ }
+
+ ParamDecl getParameter() { result = this.(ParameterNodeImpl).getParameter() }
+}
/**
*/
diff --git a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected
index f26a3f61f19..7dc50d8f0f7 100644
--- a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected
+++ b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected
@@ -10,9 +10,12 @@
| test.swift:15:5:15:5 | Phi | test.swift:15:15:15:15 | t2 |
| test.swift:17:5:17:10 | WriteDef | test.swift:21:15:21:15 | t1 |
| test.swift:17:10:17:10 | 0 | test.swift:17:5:17:10 | WriteDef |
-| test.swift:29:18:29:21 | x | test.swift:30:15:30:15 | x |
-| test.swift:29:26:29:29 | y | test.swift:31:15:31:15 | y |
-| test.swift:42:16:42:19 | b | test.swift:45:8:45:8 | b |
+| test.swift:29:18:29:21 | WriteDef | test.swift:30:15:30:15 | x |
+| test.swift:29:18:29:21 | x | test.swift:29:18:29:21 | WriteDef |
+| test.swift:29:26:29:29 | WriteDef | test.swift:31:15:31:15 | y |
+| test.swift:29:26:29:29 | y | test.swift:29:26:29:29 | WriteDef |
+| test.swift:42:16:42:19 | WriteDef | test.swift:45:8:45:8 | b |
+| test.swift:42:16:42:19 | b | test.swift:42:16:42:19 | WriteDef |
| test.swift:43:9:43:13 | WriteDef | test.swift:46:13:46:13 | t1 |
| test.swift:43:19:43:26 | call to source() | test.swift:43:9:43:13 | WriteDef |
| test.swift:46:9:46:13 | WriteDef | test.swift:50:5:50:5 | Phi |
@@ -28,8 +31,10 @@
| test.swift:61:22:61:23 | &... | test.swift:62:15:62:15 | x |
| test.swift:61:22:61:23 | [post] &... | test.swift:62:15:62:15 | x |
| test.swift:61:23:61:23 | x | test.swift:61:22:61:23 | &... |
-| test.swift:65:16:65:28 | arg1 | test.swift:66:21:66:21 | arg1 |
-| test.swift:65:33:65:45 | arg2 | test.swift:67:12:67:12 | arg2 |
+| test.swift:65:16:65:28 | WriteDef | test.swift:66:21:66:21 | arg1 |
+| test.swift:65:16:65:28 | arg1 | test.swift:65:16:65:28 | WriteDef |
+| test.swift:65:33:65:45 | WriteDef | test.swift:67:12:67:12 | arg2 |
+| test.swift:65:33:65:45 | arg2 | test.swift:65:33:65:45 | WriteDef |
| test.swift:66:9:66:15 | WriteDef | test.swift:68:12:68:12 | temp |
| test.swift:66:21:66:21 | arg1 | test.swift:66:9:66:15 | WriteDef |
| test.swift:67:5:67:12 | WriteDef | test.swift:65:1:70:1 | arg1[return] |
@@ -49,12 +54,14 @@
| test.swift:81:5:81:18 | WriteDef | test.swift:80:1:82:1 | arg[return] |
| test.swift:81:11:81:18 | call to source() | test.swift:81:5:81:18 | WriteDef |
| test.swift:84:1:91:1 | Phi | test.swift:84:1:91:1 | arg[return] |
-| test.swift:84:48:84:54 | bool | test.swift:85:8:85:8 | bool |
+| test.swift:84:48:84:54 | WriteDef | test.swift:85:8:85:8 | bool |
+| test.swift:84:48:84:54 | bool | test.swift:84:48:84:54 | WriteDef |
| test.swift:86:9:86:22 | WriteDef | test.swift:84:1:91:1 | Phi |
| test.swift:86:15:86:22 | call to source() | test.swift:86:9:86:22 | WriteDef |
| test.swift:89:9:89:22 | WriteDef | test.swift:84:1:91:1 | Phi |
| test.swift:89:15:89:22 | call to source() | test.swift:89:9:89:22 | WriteDef |
-| test.swift:93:17:93:23 | bool | test.swift:104:50:104:50 | bool |
+| test.swift:93:17:93:23 | WriteDef | test.swift:104:50:104:50 | bool |
+| test.swift:93:17:93:23 | bool | test.swift:93:17:93:23 | WriteDef |
| test.swift:95:13:95:16 | WriteDef | test.swift:96:19:96:19 | x |
| test.swift:95:22:95:22 | 0 | test.swift:95:13:95:16 | WriteDef |
| test.swift:96:19:96:19 | x | test.swift:97:40:97:40 | x |
@@ -67,45 +74,66 @@
| test.swift:104:40:104:41 | &... | test.swift:105:19:105:19 | x |
| test.swift:104:40:104:41 | [post] &... | test.swift:105:19:105:19 | x |
| test.swift:104:41:104:41 | x | test.swift:104:40:104:41 | &... |
-| test.swift:109:9:109:14 | arg | test.swift:110:12:110:12 | arg |
-| test.swift:113:14:113:19 | arg | test.swift:114:19:114:19 | arg |
-| test.swift:113:24:113:41 | lambda | test.swift:114:12:114:12 | lambda |
+| test.swift:109:9:109:14 | WriteDef | test.swift:110:12:110:12 | arg |
+| test.swift:109:9:109:14 | arg | test.swift:109:9:109:14 | WriteDef |
+| test.swift:113:14:113:19 | WriteDef | test.swift:114:19:114:19 | arg |
+| test.swift:113:14:113:19 | arg | test.swift:113:14:113:19 | WriteDef |
+| test.swift:113:24:113:41 | WriteDef | test.swift:114:12:114:12 | lambda |
+| test.swift:113:24:113:41 | lambda | test.swift:113:24:113:41 | WriteDef |
| test.swift:118:9:118:12 | WriteDef | test.swift:119:31:119:31 | x |
| test.swift:118:18:118:25 | call to source() | test.swift:118:9:118:12 | WriteDef |
| test.swift:119:9:119:12 | WriteDef | test.swift:120:15:120:15 | y |
| test.swift:119:18:119:44 | call to forward(arg:lambda:) | test.swift:119:9:119:12 | WriteDef |
| test.swift:122:9:122:12 | WriteDef | test.swift:126:15:126:15 | z |
| test.swift:122:18:125:6 | call to forward(arg:lambda:) | test.swift:122:9:122:12 | WriteDef |
-| test.swift:123:10:123:13 | i | test.swift:124:16:124:16 | i |
+| test.swift:123:10:123:13 | WriteDef | test.swift:124:16:124:16 | i |
+| test.swift:123:10:123:13 | i | test.swift:123:10:123:13 | WriteDef |
| test.swift:128:9:128:16 | WriteDef | test.swift:132:15:132:15 | clean |
| test.swift:128:22:131:6 | call to forward(arg:lambda:) | test.swift:128:9:128:16 | WriteDef |
| test.swift:141:9:141:9 | WriteDef | test.swift:145:15:145:15 | lambda2 |
| test.swift:141:19:144:5 | { ... } | test.swift:141:9:141:9 | WriteDef |
-| test.swift:142:10:142:13 | i | test.swift:143:16:143:16 | i |
+| test.swift:142:10:142:13 | WriteDef | test.swift:143:16:143:16 | i |
+| test.swift:142:10:142:13 | i | test.swift:142:10:142:13 | WriteDef |
| test.swift:147:9:147:9 | WriteDef | test.swift:151:15:151:15 | lambdaSource |
| test.swift:147:24:150:5 | { ... } | test.swift:147:9:147:9 | WriteDef |
| test.swift:151:15:151:15 | lambdaSource | test.swift:159:16:159:16 | lambdaSource |
| test.swift:153:9:153:9 | WriteDef | test.swift:157:5:157:5 | lambdaSink |
| test.swift:153:22:156:5 | { ... } | test.swift:153:9:153:9 | WriteDef |
-| test.swift:154:10:154:13 | i | test.swift:155:19:155:19 | i |
+| test.swift:154:10:154:13 | WriteDef | test.swift:155:19:155:19 | i |
+| test.swift:154:10:154:13 | i | test.swift:154:10:154:13 | WriteDef |
| test.swift:157:5:157:5 | lambdaSink | test.swift:159:5:159:5 | lambdaSink |
-| test.swift:162:7:162:7 | self | test.swift:162:7:162:7 | self[return] |
-| test.swift:165:3:165:3 | self | test.swift:166:5:166:5 | self |
+| test.swift:162:7:162:7 | WriteDef | test.swift:162:7:162:7 | self[return] |
+| test.swift:162:7:162:7 | self | test.swift:162:7:162:7 | WriteDef |
+| test.swift:163:7:163:7 | self | test.swift:163:7:163:7 | WriteDef |
+| test.swift:163:7:163:7 | self | test.swift:163:7:163:7 | WriteDef |
+| test.swift:163:7:163:7 | self | test.swift:163:7:163:7 | WriteDef |
+| test.swift:163:7:163:7 | value | test.swift:163:7:163:7 | WriteDef |
+| test.swift:165:3:165:3 | WriteDef | test.swift:166:5:166:5 | self |
+| test.swift:165:3:165:3 | self | test.swift:165:3:165:3 | WriteDef |
| test.swift:166:5:166:5 | [post] self | test.swift:165:3:167:3 | self[return] |
| test.swift:166:5:166:5 | self | test.swift:165:3:167:3 | self[return] |
-| test.swift:169:8:169:8 | self | test.swift:170:5:170:5 | self |
-| test.swift:169:12:169:22 | value | test.swift:170:9:170:9 | value |
+| test.swift:169:8:169:8 | WriteDef | test.swift:170:5:170:5 | self |
+| test.swift:169:8:169:8 | self | test.swift:169:8:169:8 | WriteDef |
+| test.swift:169:12:169:22 | WriteDef | test.swift:170:9:170:9 | value |
+| test.swift:169:12:169:22 | value | test.swift:169:12:169:22 | WriteDef |
| test.swift:170:5:170:5 | [post] self | test.swift:169:3:171:3 | self[return] |
| test.swift:170:5:170:5 | self | test.swift:169:3:171:3 | self[return] |
-| test.swift:173:8:173:8 | self | test.swift:174:12:174:12 | self |
+| test.swift:173:8:173:8 | WriteDef | test.swift:174:12:174:12 | self |
+| test.swift:173:8:173:8 | self | test.swift:173:8:173:8 | WriteDef |
| test.swift:174:12:174:12 | [post] self | test.swift:173:3:175:3 | self[return] |
| test.swift:174:12:174:12 | self | test.swift:173:3:175:3 | self[return] |
| test.swift:179:7:179:7 | WriteDef | test.swift:180:3:180:3 | a |
| test.swift:179:11:179:13 | call to init() | test.swift:179:7:179:7 | WriteDef |
| test.swift:180:3:180:3 | [post] a | test.swift:181:13:181:13 | a |
| test.swift:180:3:180:3 | a | test.swift:181:13:181:13 | a |
-| test.swift:184:7:184:7 | self | test.swift:184:7:184:7 | self[return] |
-| test.swift:187:3:187:3 | self | test.swift:188:5:188:5 | self |
+| test.swift:184:7:184:7 | WriteDef | test.swift:184:7:184:7 | self[return] |
+| test.swift:184:7:184:7 | self | test.swift:184:7:184:7 | WriteDef |
+| test.swift:185:7:185:7 | self | test.swift:185:7:185:7 | WriteDef |
+| test.swift:185:7:185:7 | self | test.swift:185:7:185:7 | WriteDef |
+| test.swift:185:7:185:7 | self | test.swift:185:7:185:7 | WriteDef |
+| test.swift:185:7:185:7 | value | test.swift:185:7:185:7 | WriteDef |
+| test.swift:187:3:187:3 | WriteDef | test.swift:188:5:188:5 | self |
+| test.swift:187:3:187:3 | self | test.swift:187:3:187:3 | WriteDef |
| test.swift:188:5:188:5 | [post] self | test.swift:187:3:189:3 | self[return] |
| test.swift:188:5:188:5 | self | test.swift:187:3:189:3 | self[return] |
| test.swift:193:7:193:7 | WriteDef | test.swift:194:3:194:3 | b |
@@ -128,19 +156,31 @@
| test.swift:217:11:217:13 | call to init() | test.swift:217:7:217:7 | WriteDef |
| test.swift:218:3:218:3 | [post] b | test.swift:219:13:219:13 | b |
| test.swift:218:3:218:3 | b | test.swift:219:13:219:13 | b |
-| test.swift:222:7:222:7 | self | test.swift:222:7:222:7 | self[return] |
-| test.swift:222:7:222:7 | self | test.swift:222:7:222:7 | self[return] |
-| test.swift:224:5:224:5 | self | test.swift:224:5:226:5 | self[return] |
-| test.swift:227:5:227:5 | self | test.swift:227:5:229:5 | self[return] |
+| test.swift:222:7:222:7 | WriteDef | test.swift:222:7:222:7 | self[return] |
+| test.swift:222:7:222:7 | WriteDef | test.swift:222:7:222:7 | self[return] |
+| test.swift:222:7:222:7 | self | test.swift:222:7:222:7 | WriteDef |
+| test.swift:222:7:222:7 | self | test.swift:222:7:222:7 | WriteDef |
+| test.swift:223:7:223:7 | self | test.swift:223:7:223:7 | WriteDef |
+| test.swift:224:5:224:5 | WriteDef | test.swift:224:5:226:5 | self[return] |
+| test.swift:224:5:224:5 | self | test.swift:224:5:224:5 | WriteDef |
+| test.swift:227:5:227:5 | WriteDef | test.swift:227:5:229:5 | self[return] |
+| test.swift:227:5:227:5 | self | test.swift:227:5:227:5 | WriteDef |
| test.swift:234:7:234:7 | WriteDef | test.swift:235:13:235:13 | a |
| test.swift:234:11:234:31 | call to init() | test.swift:234:7:234:7 | WriteDef |
| test.swift:235:13:235:13 | [post] a | test.swift:237:3:237:3 | a |
| test.swift:235:13:235:13 | a | test.swift:237:3:237:3 | a |
| test.swift:237:3:237:3 | [post] a | test.swift:238:13:238:13 | a |
| test.swift:237:3:237:3 | a | test.swift:238:13:238:13 | a |
-| test.swift:243:9:243:9 | self | test.swift:243:18:243:18 | self |
+| test.swift:242:9:242:9 | self | test.swift:242:9:242:9 | WriteDef |
+| test.swift:242:9:242:9 | self | test.swift:242:9:242:9 | WriteDef |
+| test.swift:242:9:242:9 | self | test.swift:242:9:242:9 | WriteDef |
+| test.swift:242:9:242:9 | value | test.swift:242:9:242:9 | WriteDef |
+| test.swift:243:9:243:9 | WriteDef | test.swift:243:18:243:18 | self |
+| test.swift:243:9:243:9 | self | test.swift:243:9:243:9 | WriteDef |
| test.swift:243:18:243:18 | [post] self | test.swift:243:9:243:42 | self[return] |
| test.swift:243:18:243:18 | self | test.swift:243:9:243:42 | self[return] |
-| test.swift:246:5:246:5 | self | test.swift:247:9:247:9 | self |
+| test.swift:246:5:246:5 | WriteDef | test.swift:247:9:247:9 | self |
+| test.swift:246:5:246:5 | self | test.swift:246:5:246:5 | WriteDef |
| test.swift:247:9:247:9 | [post] self | test.swift:246:5:248:5 | self[return] |
| test.swift:247:9:247:9 | self | test.swift:246:5:248:5 | self[return] |
+| test.swift:252:23:252:23 | value | test.swift:252:23:252:23 | WriteDef |
From 9d069b32b0e61b5eef1f8c94e453e3dd759f8ba4 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Thu, 6 Oct 2022 00:39:21 +0100
Subject: [PATCH 401/991] Swift: Create ArgumentNodes and OutNodes for MaD.
---
.../dataflow/internal/DataFlowDispatch.qll | 6 +++++-
.../swift/dataflow/internal/DataFlowPrivate.qll | 17 +++++++++++++++++
2 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowDispatch.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowDispatch.qll
index 75d4d20bcf3..f4073c1b503 100644
--- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowDispatch.qll
+++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowDispatch.qll
@@ -229,7 +229,9 @@ class SummaryCall extends DataFlowCall, TSummaryCall {
cached
private module Cached {
cached
- newtype TDataFlowCallable = TDataFlowFunc(CfgScope scope)
+ newtype TDataFlowCallable =
+ TDataFlowFunc(CfgScope scope) or
+ TSummarizedCallable(FlowSummary::SummarizedCallable c)
/** Gets a viable run-time target for the call `call`. */
cached
@@ -241,6 +243,8 @@ private module Cached {
result = TDataFlowFunc(call.(PropertySetterCall).getAccessorDecl())
or
result = TDataFlowFunc(call.(PropertyObserverCall).getAccessorDecl())
+ or
+ result = TSummarizedCallable(call.asCall().getStaticTarget())
}
cached
diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll
index 8c822861788..67f388b2bc3 100644
--- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll
+++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll
@@ -340,6 +340,14 @@ private module ArgumentNodes {
)
}
}
+
+ class SummaryArgumentNode extends SummaryNode, ArgumentNode {
+ SummaryArgumentNode() { FlowSummaryImpl::Private::summaryArgumentNode(_, this, _) }
+
+ override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
+ FlowSummaryImpl::Private::summaryArgumentNode(call, this, pos)
+ }
+ }
}
import ArgumentNodes
@@ -410,6 +418,12 @@ private module OutNodes {
}
}
+ class SummaryOutNode extends OutNode, SummaryNode {
+ override DataFlowCall getCall(ReturnKind kind) {
+ FlowSummaryImpl::Private::summaryOutNode(result, this, kind)
+ }
+ }
+
class InOutUpdateArgNode extends OutNode, ExprPostUpdateNode {
Argument arg;
@@ -596,6 +610,9 @@ predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c)
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
kind = TLambdaCallKind() and
receiver.asExpr() = call.asCall().getExpr().(ApplyExpr).getFunction()
+ or
+ kind = TLambdaCallKind() and
+ receiver = call.(SummaryCall).getReceiver()
}
/** Extra data-flow steps needed for lambda flow analysis. */
From 197f036797d5fbdbd628a9aa2319bca4ba88aad0 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Thu, 6 Oct 2022 00:41:34 +0100
Subject: [PATCH 402/991] Swift: Support local MaD steps in both dataflow and
taintflow.
---
.../codeql/swift/dataflow/internal/DataFlowPrivate.qll | 9 ++++++++-
.../swift/dataflow/internal/TaintTrackingPrivate.qll | 3 +++
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll
index 67f388b2bc3..8bca6664467 100644
--- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll
+++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll
@@ -147,6 +147,8 @@ private module Cached {
or
// flow through `!`
nodeFrom.asExpr() = nodeTo.asExpr().(ForceValueExpr).getSubExpr()
+ or
+ FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, true)
}
/**
@@ -160,7 +162,10 @@ private module Cached {
/** This is the local flow predicate that is exposed. */
cached
- predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) { localFlowStepCommon(nodeFrom, nodeTo) }
+ predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) {
+ localFlowStepCommon(nodeFrom, nodeTo) or
+ FlowSummaryImpl::Private::Steps::summaryThroughStepValue(nodeFrom, nodeTo, _)
+ }
cached
newtype TContentSet = TSingletonContent(Content c)
@@ -489,6 +494,8 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
node2.(PostUpdateNode).getPreUpdateNode().asExpr() = ref.getBase() and
c.isSingleton(any(Content::FieldContent ct | ct.getField() = ref.getMember()))
)
+ or
+ FlowSummaryImpl::Private::Steps::summaryStoreStep(node1, c, node2)
}
predicate isLValue(Expr e) { any(AssignExpr assign).getDest() = e }
diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll
index 411feb3742e..6c8642ccdd9 100644
--- a/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll
+++ b/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll
@@ -4,6 +4,7 @@ private import TaintTrackingPublic
private import codeql.swift.dataflow.DataFlow
private import codeql.swift.dataflow.Ssa
private import codeql.swift.controlflow.CfgNodes
+private import FlowSummaryImpl as FlowSummaryImpl
/**
* Holds if `node` should be a sanitizer in all global taint flow configurations
@@ -57,6 +58,8 @@ private module Cached {
nodeFrom.asExpr() = call.getAnArgument().getExpr() and
nodeTo.asExpr() = call
)
+ or
+ FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, false)
}
/**
From 1edd4d855a8517f218f0ae6b1d43cedcdd008ce7 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Thu, 6 Oct 2022 00:43:07 +0100
Subject: [PATCH 403/991] Swift: Add an example with flow through a callback
function.
---
.../codeql/swift/dataflow/ExternalFlow.qll | 1 +
.../frameworks/StandardLibrary/UrlSession.qll | 8 +++
.../dataflow/taint/LocalTaint.expected | 28 ++++----
.../dataflow/taint/Taint.expected | 66 +++++++++++++++----
.../library-tests/dataflow/taint/url.swift | 40 +++++++++--
5 files changed, 111 insertions(+), 32 deletions(-)
create mode 100644 swift/ql/lib/codeql/swift/frameworks/StandardLibrary/UrlSession.qll
diff --git a/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll b/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll
index 01d4de5f0c6..32e4cd8f5ff 100644
--- a/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll
+++ b/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll
@@ -80,6 +80,7 @@ private import internal.FlowSummaryImplSpecific
private module Frameworks {
private import codeql.swift.frameworks.StandardLibrary.String
private import codeql.swift.frameworks.StandardLibrary.Url
+ private import codeql.swift.frameworks.StandardLibrary.UrlSession
}
/**
diff --git a/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/UrlSession.qll b/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/UrlSession.qll
new file mode 100644
index 00000000000..b469744e4ff
--- /dev/null
+++ b/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/UrlSession.qll
@@ -0,0 +1,8 @@
+private import codeql.swift.dataflow.ExternalFlow
+
+private class UrlSessionSummaries extends SummaryModelCsv {
+ override predicate row(string row) {
+ row =
+ ";URLSession;true;dataTask(with:completionHandler:);;;Argument[0];Argument[1].Parameter[0];taint"
+ }
+}
diff --git a/swift/ql/test/library-tests/dataflow/taint/LocalTaint.expected b/swift/ql/test/library-tests/dataflow/taint/LocalTaint.expected
index f454e5e4445..6f7098f10a0 100644
--- a/swift/ql/test/library-tests/dataflow/taint/LocalTaint.expected
+++ b/swift/ql/test/library-tests/dataflow/taint/LocalTaint.expected
@@ -123,17 +123,17 @@
| string.swift:39:13:39:19 | ... .+(_:_:) ... | string.swift:39:13:39:29 | ... .+(_:_:) ... |
| string.swift:39:19:39:19 | tainted | string.swift:39:13:39:19 | ... .+(_:_:) ... |
| string.swift:39:29:39:29 | < | string.swift:39:13:39:29 | ... .+(_:_:) ... |
-| url.swift:14:29:14:29 | clean | url.swift:14:17:14:34 | call to init(string:) |
-| url.swift:15:31:15:31 | tainted | url.swift:15:19:15:38 | call to init(string:) |
-| url.swift:20:24:20:24 | clean | url.swift:20:12:20:46 | call to init(string:relativeTo:) |
-| url.swift:20:43:20:43 | nil | url.swift:20:12:20:46 | call to init(string:relativeTo:) |
-| url.swift:21:24:21:24 | tainted | url.swift:21:12:21:48 | call to init(string:relativeTo:) |
-| url.swift:21:45:21:45 | nil | url.swift:21:12:21:48 | call to init(string:relativeTo:) |
-| url.swift:22:24:22:24 | clean | url.swift:22:12:22:51 | call to init(string:relativeTo:) |
-| url.swift:22:43:22:43 | urlClean | url.swift:22:12:22:51 | call to init(string:relativeTo:) |
-| url.swift:23:24:23:24 | clean | url.swift:23:12:23:53 | call to init(string:relativeTo:) |
-| url.swift:23:43:23:43 | urlTainted | url.swift:23:12:23:53 | call to init(string:relativeTo:) |
-| url.swift:25:25:25:25 | clean | url.swift:25:13:25:30 | call to init(string:) |
-| url.swift:29:25:29:25 | tainted | url.swift:29:13:29:32 | call to init(string:) |
-| url.swift:34:26:34:26 | clean | url.swift:34:14:34:31 | call to init(string:) |
-| url.swift:38:28:38:28 | tainted | url.swift:38:16:38:35 | call to init(string:) |
+| url.swift:40:29:40:29 | clean | url.swift:40:17:40:34 | call to init(string:) |
+| url.swift:41:31:41:31 | tainted | url.swift:41:19:41:38 | call to init(string:) |
+| url.swift:46:24:46:24 | clean | url.swift:46:12:46:46 | call to init(string:relativeTo:) |
+| url.swift:46:43:46:43 | nil | url.swift:46:12:46:46 | call to init(string:relativeTo:) |
+| url.swift:47:24:47:24 | tainted | url.swift:47:12:47:48 | call to init(string:relativeTo:) |
+| url.swift:47:45:47:45 | nil | url.swift:47:12:47:48 | call to init(string:relativeTo:) |
+| url.swift:48:24:48:24 | clean | url.swift:48:12:48:51 | call to init(string:relativeTo:) |
+| url.swift:48:43:48:43 | urlClean | url.swift:48:12:48:51 | call to init(string:relativeTo:) |
+| url.swift:49:24:49:24 | clean | url.swift:49:12:49:53 | call to init(string:relativeTo:) |
+| url.swift:49:43:49:43 | urlTainted | url.swift:49:12:49:53 | call to init(string:relativeTo:) |
+| url.swift:51:25:51:25 | clean | url.swift:51:13:51:30 | call to init(string:) |
+| url.swift:55:25:55:25 | tainted | url.swift:55:13:55:32 | call to init(string:) |
+| url.swift:60:26:60:26 | clean | url.swift:60:14:60:31 | call to init(string:) |
+| url.swift:64:28:64:28 | tainted | url.swift:64:16:64:35 | call to init(string:) |
diff --git a/swift/ql/test/library-tests/dataflow/taint/Taint.expected b/swift/ql/test/library-tests/dataflow/taint/Taint.expected
index e8532ed6d9d..cfeb88cff08 100644
--- a/swift/ql/test/library-tests/dataflow/taint/Taint.expected
+++ b/swift/ql/test/library-tests/dataflow/taint/Taint.expected
@@ -1,4 +1,5 @@
edges
+| file://:0:0:0:0 | [summary] to write: argument 1.parameter 0 in dataTask(with:completionHandler:) : | url.swift:67:61:67:61 | data : |
| string.swift:5:11:5:18 | call to source() : | string.swift:7:13:7:13 | "..." |
| string.swift:5:11:5:18 | call to source() : | string.swift:9:13:9:13 | "..." |
| string.swift:5:11:5:18 | call to source() : | string.swift:11:13:11:13 | "..." |
@@ -12,11 +13,34 @@ edges
| try.swift:9:17:9:24 | call to source() : | try.swift:9:13:9:24 | try ... |
| try.swift:15:17:15:24 | call to source() : | try.swift:15:12:15:24 | try! ... |
| try.swift:18:18:18:25 | call to source() : | try.swift:18:12:18:27 | ...! |
-| url.swift:13:16:13:23 | call to source() : | url.swift:18:12:18:12 | urlTainted |
-| url.swift:13:16:13:23 | call to source() : | url.swift:21:12:21:49 | ...! |
-| url.swift:13:16:13:23 | call to source() : | url.swift:23:12:23:54 | ...! |
-| url.swift:13:16:13:23 | call to source() : | url.swift:39:12:39:12 | ...! |
+| url.swift:8:2:8:25 | [summary param] 0 in init(string:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : |
+| url.swift:8:8:8:16 | string : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : |
+| url.swift:26:2:29:55 | [summary param] 0 in dataTask(with:completionHandler:) : | file://:0:0:0:0 | [summary] to write: argument 1.parameter 0 in dataTask(with:completionHandler:) : |
+| url.swift:27:5:27:15 | url : | file://:0:0:0:0 | [summary] to write: argument 1.parameter 0 in dataTask(with:completionHandler:) : |
+| url.swift:39:16:39:23 | call to source() : | url.swift:41:31:41:31 | tainted : |
+| url.swift:39:16:39:23 | call to source() : | url.swift:44:12:44:12 | urlTainted |
+| url.swift:39:16:39:23 | call to source() : | url.swift:47:12:47:49 | ...! |
+| url.swift:39:16:39:23 | call to source() : | url.swift:49:12:49:54 | ...! |
+| url.swift:39:16:39:23 | call to source() : | url.swift:64:28:64:28 | tainted : |
+| url.swift:39:16:39:23 | call to source() : | url.swift:65:12:65:12 | ...! |
+| url.swift:39:16:39:23 | call to source() : | url.swift:67:46:67:46 | urlTainted : |
+| url.swift:41:19:41:38 | call to init(string:) : | url.swift:44:12:44:12 | urlTainted |
+| url.swift:41:19:41:38 | call to init(string:) : | url.swift:49:12:49:54 | ...! |
+| url.swift:41:19:41:38 | call to init(string:) : | url.swift:67:46:67:46 | urlTainted : |
+| url.swift:41:31:41:31 | tainted : | url.swift:8:2:8:25 | [summary param] 0 in init(string:) : |
+| url.swift:41:31:41:31 | tainted : | url.swift:8:8:8:16 | string : |
+| url.swift:41:31:41:31 | tainted : | url.swift:41:19:41:38 | call to init(string:) : |
+| url.swift:64:16:64:35 | call to init(string:) : | url.swift:65:12:65:12 | ...! |
+| url.swift:64:28:64:28 | tainted : | url.swift:8:2:8:25 | [summary param] 0 in init(string:) : |
+| url.swift:64:28:64:28 | tainted : | url.swift:8:8:8:16 | string : |
+| url.swift:64:28:64:28 | tainted : | url.swift:64:16:64:35 | call to init(string:) : |
+| url.swift:67:46:67:46 | urlTainted : | url.swift:26:2:29:55 | [summary param] 0 in dataTask(with:completionHandler:) : |
+| url.swift:67:46:67:46 | urlTainted : | url.swift:27:5:27:15 | url : |
+| url.swift:67:61:67:61 | data : | url.swift:68:15:68:19 | ...! |
nodes
+| file://:0:0:0:0 | [summary] to write: argument 1.parameter 0 in dataTask(with:completionHandler:) : | semmle.label | [summary] to write: argument 1.parameter 0 in dataTask(with:completionHandler:) : |
+| file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : | semmle.label | [summary] to write: return (return) in init(string:) : |
+| file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : | semmle.label | [summary] to write: return (return) in init(string:) : |
| string.swift:5:11:5:18 | call to source() : | semmle.label | call to source() : |
| string.swift:7:13:7:13 | "..." | semmle.label | "..." |
| string.swift:9:13:9:13 | "..." | semmle.label | "..." |
@@ -35,12 +59,27 @@ nodes
| try.swift:15:17:15:24 | call to source() : | semmle.label | call to source() : |
| try.swift:18:12:18:27 | ...! | semmle.label | ...! |
| try.swift:18:18:18:25 | call to source() : | semmle.label | call to source() : |
-| url.swift:13:16:13:23 | call to source() : | semmle.label | call to source() : |
-| url.swift:18:12:18:12 | urlTainted | semmle.label | urlTainted |
-| url.swift:21:12:21:49 | ...! | semmle.label | ...! |
-| url.swift:23:12:23:54 | ...! | semmle.label | ...! |
-| url.swift:39:12:39:12 | ...! | semmle.label | ...! |
+| url.swift:8:2:8:25 | [summary param] 0 in init(string:) : | semmle.label | [summary param] 0 in init(string:) : |
+| url.swift:8:8:8:16 | string : | semmle.label | string : |
+| url.swift:26:2:29:55 | [summary param] 0 in dataTask(with:completionHandler:) : | semmle.label | [summary param] 0 in dataTask(with:completionHandler:) : |
+| url.swift:27:5:27:15 | url : | semmle.label | url : |
+| url.swift:39:16:39:23 | call to source() : | semmle.label | call to source() : |
+| url.swift:41:19:41:38 | call to init(string:) : | semmle.label | call to init(string:) : |
+| url.swift:41:31:41:31 | tainted : | semmle.label | tainted : |
+| url.swift:44:12:44:12 | urlTainted | semmle.label | urlTainted |
+| url.swift:47:12:47:49 | ...! | semmle.label | ...! |
+| url.swift:49:12:49:54 | ...! | semmle.label | ...! |
+| url.swift:64:16:64:35 | call to init(string:) : | semmle.label | call to init(string:) : |
+| url.swift:64:28:64:28 | tainted : | semmle.label | tainted : |
+| url.swift:65:12:65:12 | ...! | semmle.label | ...! |
+| url.swift:67:46:67:46 | urlTainted : | semmle.label | urlTainted : |
+| url.swift:67:61:67:61 | data : | semmle.label | data : |
+| url.swift:68:15:68:19 | ...! | semmle.label | ...! |
subpaths
+| url.swift:41:31:41:31 | tainted : | url.swift:8:2:8:25 | [summary param] 0 in init(string:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : | url.swift:41:19:41:38 | call to init(string:) : |
+| url.swift:41:31:41:31 | tainted : | url.swift:8:8:8:16 | string : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : | url.swift:41:19:41:38 | call to init(string:) : |
+| url.swift:64:28:64:28 | tainted : | url.swift:8:2:8:25 | [summary param] 0 in init(string:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : | url.swift:64:16:64:35 | call to init(string:) : |
+| url.swift:64:28:64:28 | tainted : | url.swift:8:8:8:16 | string : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : | url.swift:64:16:64:35 | call to init(string:) : |
#select
| string.swift:7:13:7:13 | "..." | string.swift:5:11:5:18 | call to source() : | string.swift:7:13:7:13 | "..." | result |
| string.swift:9:13:9:13 | "..." | string.swift:5:11:5:18 | call to source() : | string.swift:9:13:9:13 | "..." | result |
@@ -55,7 +94,8 @@ subpaths
| try.swift:9:13:9:24 | try ... | try.swift:9:17:9:24 | call to source() : | try.swift:9:13:9:24 | try ... | result |
| try.swift:15:12:15:24 | try! ... | try.swift:15:17:15:24 | call to source() : | try.swift:15:12:15:24 | try! ... | result |
| try.swift:18:12:18:27 | ...! | try.swift:18:18:18:25 | call to source() : | try.swift:18:12:18:27 | ...! | result |
-| url.swift:18:12:18:12 | urlTainted | url.swift:13:16:13:23 | call to source() : | url.swift:18:12:18:12 | urlTainted | result |
-| url.swift:21:12:21:49 | ...! | url.swift:13:16:13:23 | call to source() : | url.swift:21:12:21:49 | ...! | result |
-| url.swift:23:12:23:54 | ...! | url.swift:13:16:13:23 | call to source() : | url.swift:23:12:23:54 | ...! | result |
-| url.swift:39:12:39:12 | ...! | url.swift:13:16:13:23 | call to source() : | url.swift:39:12:39:12 | ...! | result |
+| url.swift:44:12:44:12 | urlTainted | url.swift:39:16:39:23 | call to source() : | url.swift:44:12:44:12 | urlTainted | result |
+| url.swift:47:12:47:49 | ...! | url.swift:39:16:39:23 | call to source() : | url.swift:47:12:47:49 | ...! | result |
+| url.swift:49:12:49:54 | ...! | url.swift:39:16:39:23 | call to source() : | url.swift:49:12:49:54 | ...! | result |
+| url.swift:65:12:65:12 | ...! | url.swift:39:16:39:23 | call to source() : | url.swift:65:12:65:12 | ...! | result |
+| url.swift:68:15:68:19 | ...! | url.swift:39:16:39:23 | call to source() : | url.swift:68:15:68:19 | ...! | result |
diff --git a/swift/ql/test/library-tests/dataflow/taint/url.swift b/swift/ql/test/library-tests/dataflow/taint/url.swift
index 9498d73cb5a..c6839bd79ca 100644
--- a/swift/ql/test/library-tests/dataflow/taint/url.swift
+++ b/swift/ql/test/library-tests/dataflow/taint/url.swift
@@ -1,12 +1,38 @@
+class NSObject
+{
+}
+
struct URL
{
init?(string: String) {}
init?(string: String, relativeTo: URL?) {}
}
+class Data
+{
+ init(_ elements: S) {}
+}
+
+class URLResponse : NSObject {}
+
+class URLSessionTask : NSObject { }
+
+class URLSessionDataTask : URLSessionTask { }
+
+class URLSession {
+ class var shared: URLSession { get { return URLSession() } }
+
+ func dataTask(
+ with url: URL,
+ completionHandler: (Data?, URLResponse?, Error?) -> Void
+) -> URLSessionDataTask { return URLSessionDataTask() }
+}
+
func source() -> String { return "" }
func sink(arg: URL) {}
+func sink(data: Data) {}
+func sink(string: String) {}
func taintThroughURL() {
let clean = "http://example.com/"
@@ -15,19 +41,19 @@ func taintThroughURL() {
let urlTainted = URL(string: tainted)!
sink(arg: urlClean)
- sink(arg: urlTainted) // $ tainted=13
+ sink(arg: urlTainted) // $ tainted=39
sink(arg: URL(string: clean, relativeTo: nil)!)
- sink(arg: URL(string: tainted, relativeTo: nil)!) // $ tainted=13
+ sink(arg: URL(string: tainted, relativeTo: nil)!) // $ tainted=39
sink(arg: URL(string: clean, relativeTo: urlClean)!)
- sink(arg: URL(string: clean, relativeTo: urlTainted)!) // $ tainted=13
+ sink(arg: URL(string: clean, relativeTo: urlTainted)!) // $ tainted=39
if let x = URL(string: clean) {
sink(arg: x)
}
if let y = URL(string: tainted) {
- sink(arg: y) // $ MISSING: tainted=13
+ sink(arg: y) // $ MISSING: tainted=39
}
var urlClean2 : URL!
@@ -36,5 +62,9 @@ func taintThroughURL() {
var urlTainted2 : URL!
urlTainted2 = URL(string: tainted)
- sink(arg: urlTainted2) // $ tainted=13
+ sink(arg: urlTainted2) // $ tainted=39
+
+ let task = URLSession.shared.dataTask(with: urlTainted) { (data, response, error) in
+ sink(data: data!) // $ tainted=39
+ }
}
From 0065a5af96eedbae38e6461a5159f3fd1dc37d2e Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Thu, 6 Oct 2022 01:04:25 +0100
Subject: [PATCH 404/991] Swift: Accept path-explanation test changes.
---
.../internal/TaintTrackingPrivate.qll | 10 ---
.../dataflow/taint/LocalTaint.expected | 14 ----
.../dataflow/taint/Taint.expected | 36 ++++++++--
.../CWE-079/UnsafeWebViewFetch.expected | 68 +++++++++++++++----
4 files changed, 86 insertions(+), 42 deletions(-)
diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll
index 6c8642ccdd9..bb637a1e275 100644
--- a/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll
+++ b/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll
@@ -49,16 +49,6 @@ private module Cached {
ae.getType().getName() = "String"
)
or
- // allow flow through `URL.init`.
- exists(CallExpr call, StructDecl c, AbstractFunctionDecl f |
- c.getName() = "URL" and
- c.getAMember() = f and
- f.getName() = ["init(string:)", "init(string:relativeTo:)"] and
- call.getStaticTarget() = f and
- nodeFrom.asExpr() = call.getAnArgument().getExpr() and
- nodeTo.asExpr() = call
- )
- or
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, false)
}
diff --git a/swift/ql/test/library-tests/dataflow/taint/LocalTaint.expected b/swift/ql/test/library-tests/dataflow/taint/LocalTaint.expected
index 6f7098f10a0..504e4ca636b 100644
--- a/swift/ql/test/library-tests/dataflow/taint/LocalTaint.expected
+++ b/swift/ql/test/library-tests/dataflow/taint/LocalTaint.expected
@@ -123,17 +123,3 @@
| string.swift:39:13:39:19 | ... .+(_:_:) ... | string.swift:39:13:39:29 | ... .+(_:_:) ... |
| string.swift:39:19:39:19 | tainted | string.swift:39:13:39:19 | ... .+(_:_:) ... |
| string.swift:39:29:39:29 | < | string.swift:39:13:39:29 | ... .+(_:_:) ... |
-| url.swift:40:29:40:29 | clean | url.swift:40:17:40:34 | call to init(string:) |
-| url.swift:41:31:41:31 | tainted | url.swift:41:19:41:38 | call to init(string:) |
-| url.swift:46:24:46:24 | clean | url.swift:46:12:46:46 | call to init(string:relativeTo:) |
-| url.swift:46:43:46:43 | nil | url.swift:46:12:46:46 | call to init(string:relativeTo:) |
-| url.swift:47:24:47:24 | tainted | url.swift:47:12:47:48 | call to init(string:relativeTo:) |
-| url.swift:47:45:47:45 | nil | url.swift:47:12:47:48 | call to init(string:relativeTo:) |
-| url.swift:48:24:48:24 | clean | url.swift:48:12:48:51 | call to init(string:relativeTo:) |
-| url.swift:48:43:48:43 | urlClean | url.swift:48:12:48:51 | call to init(string:relativeTo:) |
-| url.swift:49:24:49:24 | clean | url.swift:49:12:49:53 | call to init(string:relativeTo:) |
-| url.swift:49:43:49:43 | urlTainted | url.swift:49:12:49:53 | call to init(string:relativeTo:) |
-| url.swift:51:25:51:25 | clean | url.swift:51:13:51:30 | call to init(string:) |
-| url.swift:55:25:55:25 | tainted | url.swift:55:13:55:32 | call to init(string:) |
-| url.swift:60:26:60:26 | clean | url.swift:60:14:60:31 | call to init(string:) |
-| url.swift:64:28:64:28 | tainted | url.swift:64:16:64:35 | call to init(string:) |
diff --git a/swift/ql/test/library-tests/dataflow/taint/Taint.expected b/swift/ql/test/library-tests/dataflow/taint/Taint.expected
index cfeb88cff08..5670d7ddd70 100644
--- a/swift/ql/test/library-tests/dataflow/taint/Taint.expected
+++ b/swift/ql/test/library-tests/dataflow/taint/Taint.expected
@@ -15,21 +15,29 @@ edges
| try.swift:18:18:18:25 | call to source() : | try.swift:18:12:18:27 | ...! |
| url.swift:8:2:8:25 | [summary param] 0 in init(string:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : |
| url.swift:8:8:8:16 | string : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : |
+| url.swift:9:2:9:43 | [summary param] 0 in init(string:relativeTo:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:relativeTo:) : |
+| url.swift:9:2:9:43 | [summary param] 1 in init(string:relativeTo:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:relativeTo:) : |
+| url.swift:9:8:9:16 | string : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:relativeTo:) : |
+| url.swift:9:24:9:39 | relativeTo : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:relativeTo:) : |
| url.swift:26:2:29:55 | [summary param] 0 in dataTask(with:completionHandler:) : | file://:0:0:0:0 | [summary] to write: argument 1.parameter 0 in dataTask(with:completionHandler:) : |
| url.swift:27:5:27:15 | url : | file://:0:0:0:0 | [summary] to write: argument 1.parameter 0 in dataTask(with:completionHandler:) : |
| url.swift:39:16:39:23 | call to source() : | url.swift:41:31:41:31 | tainted : |
-| url.swift:39:16:39:23 | call to source() : | url.swift:44:12:44:12 | urlTainted |
-| url.swift:39:16:39:23 | call to source() : | url.swift:47:12:47:49 | ...! |
-| url.swift:39:16:39:23 | call to source() : | url.swift:49:12:49:54 | ...! |
+| url.swift:39:16:39:23 | call to source() : | url.swift:47:24:47:24 | tainted : |
| url.swift:39:16:39:23 | call to source() : | url.swift:64:28:64:28 | tainted : |
-| url.swift:39:16:39:23 | call to source() : | url.swift:65:12:65:12 | ...! |
-| url.swift:39:16:39:23 | call to source() : | url.swift:67:46:67:46 | urlTainted : |
| url.swift:41:19:41:38 | call to init(string:) : | url.swift:44:12:44:12 | urlTainted |
-| url.swift:41:19:41:38 | call to init(string:) : | url.swift:49:12:49:54 | ...! |
+| url.swift:41:19:41:38 | call to init(string:) : | url.swift:49:43:49:43 | urlTainted : |
| url.swift:41:19:41:38 | call to init(string:) : | url.swift:67:46:67:46 | urlTainted : |
| url.swift:41:31:41:31 | tainted : | url.swift:8:2:8:25 | [summary param] 0 in init(string:) : |
| url.swift:41:31:41:31 | tainted : | url.swift:8:8:8:16 | string : |
| url.swift:41:31:41:31 | tainted : | url.swift:41:19:41:38 | call to init(string:) : |
+| url.swift:47:12:47:48 | call to init(string:relativeTo:) : | url.swift:47:12:47:49 | ...! |
+| url.swift:47:24:47:24 | tainted : | url.swift:9:2:9:43 | [summary param] 0 in init(string:relativeTo:) : |
+| url.swift:47:24:47:24 | tainted : | url.swift:9:8:9:16 | string : |
+| url.swift:47:24:47:24 | tainted : | url.swift:47:12:47:48 | call to init(string:relativeTo:) : |
+| url.swift:49:12:49:53 | call to init(string:relativeTo:) : | url.swift:49:12:49:54 | ...! |
+| url.swift:49:43:49:43 | urlTainted : | url.swift:9:2:9:43 | [summary param] 1 in init(string:relativeTo:) : |
+| url.swift:49:43:49:43 | urlTainted : | url.swift:9:24:9:39 | relativeTo : |
+| url.swift:49:43:49:43 | urlTainted : | url.swift:49:12:49:53 | call to init(string:relativeTo:) : |
| url.swift:64:16:64:35 | call to init(string:) : | url.swift:65:12:65:12 | ...! |
| url.swift:64:28:64:28 | tainted : | url.swift:8:2:8:25 | [summary param] 0 in init(string:) : |
| url.swift:64:28:64:28 | tainted : | url.swift:8:8:8:16 | string : |
@@ -41,6 +49,10 @@ nodes
| file://:0:0:0:0 | [summary] to write: argument 1.parameter 0 in dataTask(with:completionHandler:) : | semmle.label | [summary] to write: argument 1.parameter 0 in dataTask(with:completionHandler:) : |
| file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : | semmle.label | [summary] to write: return (return) in init(string:) : |
| file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : | semmle.label | [summary] to write: return (return) in init(string:) : |
+| file://:0:0:0:0 | [summary] to write: return (return) in init(string:relativeTo:) : | semmle.label | [summary] to write: return (return) in init(string:relativeTo:) : |
+| file://:0:0:0:0 | [summary] to write: return (return) in init(string:relativeTo:) : | semmle.label | [summary] to write: return (return) in init(string:relativeTo:) : |
+| file://:0:0:0:0 | [summary] to write: return (return) in init(string:relativeTo:) : | semmle.label | [summary] to write: return (return) in init(string:relativeTo:) : |
+| file://:0:0:0:0 | [summary] to write: return (return) in init(string:relativeTo:) : | semmle.label | [summary] to write: return (return) in init(string:relativeTo:) : |
| string.swift:5:11:5:18 | call to source() : | semmle.label | call to source() : |
| string.swift:7:13:7:13 | "..." | semmle.label | "..." |
| string.swift:9:13:9:13 | "..." | semmle.label | "..." |
@@ -61,14 +73,22 @@ nodes
| try.swift:18:18:18:25 | call to source() : | semmle.label | call to source() : |
| url.swift:8:2:8:25 | [summary param] 0 in init(string:) : | semmle.label | [summary param] 0 in init(string:) : |
| url.swift:8:8:8:16 | string : | semmle.label | string : |
+| url.swift:9:2:9:43 | [summary param] 0 in init(string:relativeTo:) : | semmle.label | [summary param] 0 in init(string:relativeTo:) : |
+| url.swift:9:2:9:43 | [summary param] 1 in init(string:relativeTo:) : | semmle.label | [summary param] 1 in init(string:relativeTo:) : |
+| url.swift:9:8:9:16 | string : | semmle.label | string : |
+| url.swift:9:24:9:39 | relativeTo : | semmle.label | relativeTo : |
| url.swift:26:2:29:55 | [summary param] 0 in dataTask(with:completionHandler:) : | semmle.label | [summary param] 0 in dataTask(with:completionHandler:) : |
| url.swift:27:5:27:15 | url : | semmle.label | url : |
| url.swift:39:16:39:23 | call to source() : | semmle.label | call to source() : |
| url.swift:41:19:41:38 | call to init(string:) : | semmle.label | call to init(string:) : |
| url.swift:41:31:41:31 | tainted : | semmle.label | tainted : |
| url.swift:44:12:44:12 | urlTainted | semmle.label | urlTainted |
+| url.swift:47:12:47:48 | call to init(string:relativeTo:) : | semmle.label | call to init(string:relativeTo:) : |
| url.swift:47:12:47:49 | ...! | semmle.label | ...! |
+| url.swift:47:24:47:24 | tainted : | semmle.label | tainted : |
+| url.swift:49:12:49:53 | call to init(string:relativeTo:) : | semmle.label | call to init(string:relativeTo:) : |
| url.swift:49:12:49:54 | ...! | semmle.label | ...! |
+| url.swift:49:43:49:43 | urlTainted : | semmle.label | urlTainted : |
| url.swift:64:16:64:35 | call to init(string:) : | semmle.label | call to init(string:) : |
| url.swift:64:28:64:28 | tainted : | semmle.label | tainted : |
| url.swift:65:12:65:12 | ...! | semmle.label | ...! |
@@ -78,6 +98,10 @@ nodes
subpaths
| url.swift:41:31:41:31 | tainted : | url.swift:8:2:8:25 | [summary param] 0 in init(string:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : | url.swift:41:19:41:38 | call to init(string:) : |
| url.swift:41:31:41:31 | tainted : | url.swift:8:8:8:16 | string : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : | url.swift:41:19:41:38 | call to init(string:) : |
+| url.swift:47:24:47:24 | tainted : | url.swift:9:2:9:43 | [summary param] 0 in init(string:relativeTo:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:relativeTo:) : | url.swift:47:12:47:48 | call to init(string:relativeTo:) : |
+| url.swift:47:24:47:24 | tainted : | url.swift:9:8:9:16 | string : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:relativeTo:) : | url.swift:47:12:47:48 | call to init(string:relativeTo:) : |
+| url.swift:49:43:49:43 | urlTainted : | url.swift:9:2:9:43 | [summary param] 1 in init(string:relativeTo:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:relativeTo:) : | url.swift:49:12:49:53 | call to init(string:relativeTo:) : |
+| url.swift:49:43:49:43 | urlTainted : | url.swift:9:24:9:39 | relativeTo : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:relativeTo:) : | url.swift:49:12:49:53 | call to init(string:relativeTo:) : |
| url.swift:64:28:64:28 | tainted : | url.swift:8:2:8:25 | [summary param] 0 in init(string:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : | url.swift:64:16:64:35 | call to init(string:) : |
| url.swift:64:28:64:28 | tainted : | url.swift:8:8:8:16 | string : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : | url.swift:64:16:64:35 | call to init(string:) : |
#select
diff --git a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected
index 0fc232db90b..05c93fd2dad 100644
--- a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected
+++ b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected
@@ -1,4 +1,8 @@
edges
+| UnsafeWebViewFetch.swift:10:2:10:25 | [summary param] 0 in init(string:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : |
+| UnsafeWebViewFetch.swift:10:8:10:16 | string : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : |
+| UnsafeWebViewFetch.swift:11:2:11:43 | [summary param] 1 in init(string:relativeTo:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:relativeTo:) : |
+| UnsafeWebViewFetch.swift:11:24:11:39 | relativeTo : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:relativeTo:) : |
| UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : |
| UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | UnsafeWebViewFetch.swift:120:25:120:39 | call to getRemoteData() |
| UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : |
@@ -11,32 +15,52 @@ edges
| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:121:25:121:25 | remoteString |
| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:124:25:124:51 | ... .+(_:_:) ... |
| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:127:25:127:25 | "..." |
+| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:131:30:131:30 | remoteString : |
| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:135:25:135:25 | remoteString |
| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:137:25:137:25 | remoteString |
-| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:138:47:138:56 | ...! |
| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:139:25:139:25 | remoteString |
-| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:139:48:139:57 | ...! |
-| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:140:47:140:57 | ...! |
| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:141:25:141:25 | remoteString |
-| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:141:48:141:58 | ...! |
-| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:153:85:153:94 | ...! |
-| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:154:86:154:95 | ...! |
+| UnsafeWebViewFetch.swift:131:18:131:42 | call to init(string:) : | UnsafeWebViewFetch.swift:132:52:132:52 | remoteURL : |
+| UnsafeWebViewFetch.swift:131:18:131:42 | call to init(string:) : | UnsafeWebViewFetch.swift:138:47:138:56 | ...! |
+| UnsafeWebViewFetch.swift:131:18:131:42 | call to init(string:) : | UnsafeWebViewFetch.swift:139:48:139:57 | ...! |
+| UnsafeWebViewFetch.swift:131:18:131:42 | call to init(string:) : | UnsafeWebViewFetch.swift:153:85:153:94 | ...! |
+| UnsafeWebViewFetch.swift:131:18:131:42 | call to init(string:) : | UnsafeWebViewFetch.swift:154:86:154:95 | ...! |
+| UnsafeWebViewFetch.swift:131:30:131:30 | remoteString : | UnsafeWebViewFetch.swift:10:2:10:25 | [summary param] 0 in init(string:) : |
+| UnsafeWebViewFetch.swift:131:30:131:30 | remoteString : | UnsafeWebViewFetch.swift:10:8:10:16 | string : |
+| UnsafeWebViewFetch.swift:131:30:131:30 | remoteString : | UnsafeWebViewFetch.swift:131:18:131:42 | call to init(string:) : |
+| UnsafeWebViewFetch.swift:132:19:132:61 | call to init(string:relativeTo:) : | UnsafeWebViewFetch.swift:140:47:140:57 | ...! |
+| UnsafeWebViewFetch.swift:132:19:132:61 | call to init(string:relativeTo:) : | UnsafeWebViewFetch.swift:141:48:141:58 | ...! |
+| UnsafeWebViewFetch.swift:132:52:132:52 | remoteURL : | UnsafeWebViewFetch.swift:11:2:11:43 | [summary param] 1 in init(string:relativeTo:) : |
+| UnsafeWebViewFetch.swift:132:52:132:52 | remoteURL : | UnsafeWebViewFetch.swift:11:24:11:39 | relativeTo : |
+| UnsafeWebViewFetch.swift:132:52:132:52 | remoteURL : | UnsafeWebViewFetch.swift:132:19:132:61 | call to init(string:relativeTo:) : |
| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:168:25:168:25 | remoteString |
| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:171:25:171:51 | ... .+(_:_:) ... |
| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:174:25:174:25 | "..." |
+| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:178:30:178:30 | remoteString : |
| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:182:25:182:25 | remoteString |
| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:184:25:184:25 | remoteString |
-| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:185:47:185:56 | ...! |
| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:186:25:186:25 | remoteString |
-| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:186:48:186:57 | ...! |
-| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:187:47:187:57 | ...! |
| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:188:25:188:25 | remoteString |
-| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:188:48:188:58 | ...! |
-| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:200:90:200:99 | ...! |
-| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:201:91:201:100 | ...! |
+| UnsafeWebViewFetch.swift:178:18:178:42 | call to init(string:) : | UnsafeWebViewFetch.swift:179:52:179:52 | remoteURL : |
+| UnsafeWebViewFetch.swift:178:18:178:42 | call to init(string:) : | UnsafeWebViewFetch.swift:185:47:185:56 | ...! |
+| UnsafeWebViewFetch.swift:178:18:178:42 | call to init(string:) : | UnsafeWebViewFetch.swift:186:48:186:57 | ...! |
+| UnsafeWebViewFetch.swift:178:18:178:42 | call to init(string:) : | UnsafeWebViewFetch.swift:200:90:200:99 | ...! |
+| UnsafeWebViewFetch.swift:178:18:178:42 | call to init(string:) : | UnsafeWebViewFetch.swift:201:91:201:100 | ...! |
+| UnsafeWebViewFetch.swift:178:30:178:30 | remoteString : | UnsafeWebViewFetch.swift:10:2:10:25 | [summary param] 0 in init(string:) : |
+| UnsafeWebViewFetch.swift:178:30:178:30 | remoteString : | UnsafeWebViewFetch.swift:10:8:10:16 | string : |
+| UnsafeWebViewFetch.swift:178:30:178:30 | remoteString : | UnsafeWebViewFetch.swift:178:18:178:42 | call to init(string:) : |
+| UnsafeWebViewFetch.swift:179:19:179:61 | call to init(string:relativeTo:) : | UnsafeWebViewFetch.swift:187:47:187:57 | ...! |
+| UnsafeWebViewFetch.swift:179:19:179:61 | call to init(string:relativeTo:) : | UnsafeWebViewFetch.swift:188:48:188:58 | ...! |
+| UnsafeWebViewFetch.swift:179:52:179:52 | remoteURL : | UnsafeWebViewFetch.swift:11:2:11:43 | [summary param] 1 in init(string:relativeTo:) : |
+| UnsafeWebViewFetch.swift:179:52:179:52 | remoteURL : | UnsafeWebViewFetch.swift:11:24:11:39 | relativeTo : |
+| UnsafeWebViewFetch.swift:179:52:179:52 | remoteURL : | UnsafeWebViewFetch.swift:179:19:179:61 | call to init(string:relativeTo:) : |
| UnsafeWebViewFetch.swift:206:17:206:31 | call to getRemoteData() : | UnsafeWebViewFetch.swift:210:25:210:25 | htmlData |
| UnsafeWebViewFetch.swift:206:17:206:31 | call to getRemoteData() : | UnsafeWebViewFetch.swift:211:25:211:25 | htmlData |
nodes
+| UnsafeWebViewFetch.swift:10:2:10:25 | [summary param] 0 in init(string:) : | semmle.label | [summary param] 0 in init(string:) : |
+| UnsafeWebViewFetch.swift:10:8:10:16 | string : | semmle.label | string : |
+| UnsafeWebViewFetch.swift:11:2:11:43 | [summary param] 1 in init(string:relativeTo:) : | semmle.label | [summary param] 1 in init(string:relativeTo:) : |
+| UnsafeWebViewFetch.swift:11:24:11:39 | relativeTo : | semmle.label | relativeTo : |
| UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | semmle.label | try ... : |
| UnsafeWebViewFetch.swift:94:14:94:37 | call to init(contentsOf:) : | semmle.label | call to init(contentsOf:) : |
| UnsafeWebViewFetch.swift:103:25:103:84 | try! ... | semmle.label | try! ... |
@@ -50,6 +74,10 @@ nodes
| UnsafeWebViewFetch.swift:121:25:121:25 | remoteString | semmle.label | remoteString |
| UnsafeWebViewFetch.swift:124:25:124:51 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... |
| UnsafeWebViewFetch.swift:127:25:127:25 | "..." | semmle.label | "..." |
+| UnsafeWebViewFetch.swift:131:18:131:42 | call to init(string:) : | semmle.label | call to init(string:) : |
+| UnsafeWebViewFetch.swift:131:30:131:30 | remoteString : | semmle.label | remoteString : |
+| UnsafeWebViewFetch.swift:132:19:132:61 | call to init(string:relativeTo:) : | semmle.label | call to init(string:relativeTo:) : |
+| UnsafeWebViewFetch.swift:132:52:132:52 | remoteURL : | semmle.label | remoteURL : |
| UnsafeWebViewFetch.swift:135:25:135:25 | remoteString | semmle.label | remoteString |
| UnsafeWebViewFetch.swift:137:25:137:25 | remoteString | semmle.label | remoteString |
| UnsafeWebViewFetch.swift:138:47:138:56 | ...! | semmle.label | ...! |
@@ -65,6 +93,10 @@ nodes
| UnsafeWebViewFetch.swift:168:25:168:25 | remoteString | semmle.label | remoteString |
| UnsafeWebViewFetch.swift:171:25:171:51 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... |
| UnsafeWebViewFetch.swift:174:25:174:25 | "..." | semmle.label | "..." |
+| UnsafeWebViewFetch.swift:178:18:178:42 | call to init(string:) : | semmle.label | call to init(string:) : |
+| UnsafeWebViewFetch.swift:178:30:178:30 | remoteString : | semmle.label | remoteString : |
+| UnsafeWebViewFetch.swift:179:19:179:61 | call to init(string:relativeTo:) : | semmle.label | call to init(string:relativeTo:) : |
+| UnsafeWebViewFetch.swift:179:52:179:52 | remoteURL : | semmle.label | remoteURL : |
| UnsafeWebViewFetch.swift:182:25:182:25 | remoteString | semmle.label | remoteString |
| UnsafeWebViewFetch.swift:184:25:184:25 | remoteString | semmle.label | remoteString |
| UnsafeWebViewFetch.swift:185:47:185:56 | ...! | semmle.label | ...! |
@@ -78,7 +110,19 @@ nodes
| UnsafeWebViewFetch.swift:206:17:206:31 | call to getRemoteData() : | semmle.label | call to getRemoteData() : |
| UnsafeWebViewFetch.swift:210:25:210:25 | htmlData | semmle.label | htmlData |
| UnsafeWebViewFetch.swift:211:25:211:25 | htmlData | semmle.label | htmlData |
+| file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : | semmle.label | [summary] to write: return (return) in init(string:) : |
+| file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : | semmle.label | [summary] to write: return (return) in init(string:) : |
+| file://:0:0:0:0 | [summary] to write: return (return) in init(string:relativeTo:) : | semmle.label | [summary] to write: return (return) in init(string:relativeTo:) : |
+| file://:0:0:0:0 | [summary] to write: return (return) in init(string:relativeTo:) : | semmle.label | [summary] to write: return (return) in init(string:relativeTo:) : |
subpaths
+| UnsafeWebViewFetch.swift:131:30:131:30 | remoteString : | UnsafeWebViewFetch.swift:10:2:10:25 | [summary param] 0 in init(string:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : | UnsafeWebViewFetch.swift:131:18:131:42 | call to init(string:) : |
+| UnsafeWebViewFetch.swift:131:30:131:30 | remoteString : | UnsafeWebViewFetch.swift:10:8:10:16 | string : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : | UnsafeWebViewFetch.swift:131:18:131:42 | call to init(string:) : |
+| UnsafeWebViewFetch.swift:132:52:132:52 | remoteURL : | UnsafeWebViewFetch.swift:11:2:11:43 | [summary param] 1 in init(string:relativeTo:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:relativeTo:) : | UnsafeWebViewFetch.swift:132:19:132:61 | call to init(string:relativeTo:) : |
+| UnsafeWebViewFetch.swift:132:52:132:52 | remoteURL : | UnsafeWebViewFetch.swift:11:24:11:39 | relativeTo : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:relativeTo:) : | UnsafeWebViewFetch.swift:132:19:132:61 | call to init(string:relativeTo:) : |
+| UnsafeWebViewFetch.swift:178:30:178:30 | remoteString : | UnsafeWebViewFetch.swift:10:2:10:25 | [summary param] 0 in init(string:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : | UnsafeWebViewFetch.swift:178:18:178:42 | call to init(string:) : |
+| UnsafeWebViewFetch.swift:178:30:178:30 | remoteString : | UnsafeWebViewFetch.swift:10:8:10:16 | string : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : | UnsafeWebViewFetch.swift:178:18:178:42 | call to init(string:) : |
+| UnsafeWebViewFetch.swift:179:52:179:52 | remoteURL : | UnsafeWebViewFetch.swift:11:2:11:43 | [summary param] 1 in init(string:relativeTo:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:relativeTo:) : | UnsafeWebViewFetch.swift:179:19:179:61 | call to init(string:relativeTo:) : |
+| UnsafeWebViewFetch.swift:179:52:179:52 | remoteURL : | UnsafeWebViewFetch.swift:11:24:11:39 | relativeTo : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:relativeTo:) : | UnsafeWebViewFetch.swift:179:19:179:61 | call to init(string:relativeTo:) : |
#select
| UnsafeWebViewFetch.swift:103:25:103:84 | try! ... | UnsafeWebViewFetch.swift:103:30:103:84 | call to init(contentsOf:) : | UnsafeWebViewFetch.swift:103:25:103:84 | try! ... | Tainted data is used in a WebView fetch without restricting the base URL. |
| UnsafeWebViewFetch.swift:106:25:106:25 | data | UnsafeWebViewFetch.swift:105:18:105:72 | call to init(contentsOf:) : | UnsafeWebViewFetch.swift:106:25:106:25 | data | Tainted data is used in a WebView fetch without restricting the base URL. |
From 48bdf13c8979cf5688e8c7c884f3ee03e8c60975 Mon Sep 17 00:00:00 2001
From: Tom Hvitved
Date: Thu, 6 Oct 2022 10:11:23 +0200
Subject: [PATCH 405/991] Ruby: Take overrides into account for singleton
methods defined on modules
---
.../dataflow/internal/DataFlowDispatch.qll | 23 +++++++++++++++++--
.../library-tests/modules/callgraph.expected | 4 ++--
2 files changed, 23 insertions(+), 4 deletions(-)
diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll
index b7e9b14a910..355b235e926 100644
--- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll
+++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll
@@ -389,7 +389,7 @@ private module Cached {
// ```
exists(DataFlow::Node sourceNode, Module m |
flowsToMethodCall(call, sourceNode, method) and
- singletonMethodOnModule(result, method, m)
+ result = lookupSingletonMethod(m, method)
|
// ```rb
// def C.singleton; end # <- result
@@ -725,7 +725,10 @@ private predicate singletonMethodOnModule(MethodBase method, string name, Module
selfInModule(object.(SelfVariableReadAccess).getVariable(), m)
)
or
- flowsToSingletonMethodObject(trackModuleAccess(m), method, name)
+ exists(DataFlow::LocalSourceNode sourceNode |
+ m = resolveConstantReadAccess(sourceNode.asExpr().getExpr()) and
+ flowsToSingletonMethodObject(sourceNode, method, name)
+ )
or
exists(Module other |
extendCallModule(m, other) and
@@ -733,6 +736,22 @@ private predicate singletonMethodOnModule(MethodBase method, string name, Module
)
}
+pragma[nomagic]
+private MethodBase lookupSingletonMethod(Module m, string name) {
+ singletonMethodOnModule(result, name, m)
+ or
+ // cannot be part of `singletonMethodOnModule` because it would introduce
+ // negative recursion below
+ exists(DataFlow::LocalSourceNode sourceNode |
+ sourceNode = trackModuleAccess(m) and
+ not m = resolveConstantReadAccess(sourceNode.asExpr().getExpr()) and
+ flowsToSingletonMethodObject(sourceNode, result, name)
+ )
+ or
+ not singletonMethodOnModule(_, name, m) and
+ result = lookupSingletonMethod(m.getSuperClass(), name)
+}
+
/**
* Holds if `method` is a singleton method named `name`, defined on expression
* `object`, where `object` is not likely to resolve to a module:
diff --git a/ruby/ql/test/library-tests/modules/callgraph.expected b/ruby/ql/test/library-tests/modules/callgraph.expected
index 4af83afbe26..df2bffe2933 100644
--- a/ruby/ql/test/library-tests/modules/callgraph.expected
+++ b/ruby/ql/test/library-tests/modules/callgraph.expected
@@ -161,6 +161,8 @@ getTarget
| calls.rb:412:9:412:44 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:416:1:416:29 | call to singleton1 | calls.rb:406:9:408:11 | singleton1 |
| calls.rb:417:1:417:29 | call to singleton2 | calls.rb:411:5:413:7 | singleton2 |
+| calls.rb:418:1:418:34 | call to call_singleton1 | calls.rb:383:9:385:11 | call_singleton1 |
+| calls.rb:419:1:419:34 | call to call_singleton2 | calls.rb:392:5:394:7 | call_singleton2 |
| calls.rb:424:13:424:48 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:429:9:429:44 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:432:13:432:48 | call to puts | calls.rb:102:5:102:30 | puts |
@@ -290,8 +292,6 @@ unresolvedCall
| calls.rb:274:1:274:14 | call to singleton_g |
| calls.rb:276:1:276:14 | call to singleton_g |
| calls.rb:313:9:313:20 | call to instance |
-| calls.rb:418:1:418:34 | call to call_singleton1 |
-| calls.rb:419:1:419:34 | call to call_singleton2 |
| calls.rb:422:8:422:13 | call to rand |
| calls.rb:422:8:422:17 | ... > ... |
| calls.rb:439:9:439:10 | call to m3 |
From 6f3c9e4403a09d5876d28724bef113a27966a1b4 Mon Sep 17 00:00:00 2001
From: Chris Smowton
Date: Tue, 27 Sep 2022 12:55:54 +0100
Subject: [PATCH 406/991] Split up extractRawMethodAccess
---
.../src/main/kotlin/KotlinFileExtractor.kt | 150 +++++++++---------
1 file changed, 76 insertions(+), 74 deletions(-)
diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt
index b7aff59b599..81acc665be3 100644
--- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt
+++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt
@@ -1557,6 +1557,64 @@ open class KotlinFileExtractor(
}
+ private fun getFunctionInvokeMethod(typeArgs: List): IrFunction? {
+ // For `kotlin.FunctionX` and `kotlin.reflect.KFunctionX` interfaces, we're making sure that we
+ // extract the call to the `invoke` method that does exist, `kotlin.jvm.functions.FunctionX::invoke`.
+ val functionalInterface = getFunctionalInterfaceTypeWithTypeArgs(typeArgs)
+ if (functionalInterface == null) {
+ logger.warn("Cannot find functional interface type for raw method access")
+ return null
+ }
+ val functionalInterfaceClass = functionalInterface.classOrNull
+ if (functionalInterfaceClass == null) {
+ logger.warn("Cannot find functional interface class for raw method access")
+ return null
+ }
+ val interfaceType = functionalInterfaceClass.owner
+ val substituted = getJavaEquivalentClass(interfaceType) ?: interfaceType
+ val function = findFunction(substituted, OperatorNameConventions.INVOKE.asString())
+ if (function == null) {
+ logger.warn("Cannot find invoke function for raw method access")
+ return null
+ }
+ return function
+ }
+
+ private fun isFunctionInvoke(callTarget: IrFunction, drType: IrSimpleType) =
+ (drType.isFunctionOrKFunction() || drType.isSuspendFunctionOrKFunction()) &&
+ callTarget.name.asString() == OperatorNameConventions.INVOKE.asString()
+
+ private fun getCalleeMethodId(callTarget: IrFunction, drType: IrType?, allowInstantiatedGenericMethod: Boolean): Label? {
+ if (callTarget.isLocalFunction())
+ return getLocallyVisibleFunctionLabels(callTarget).function
+
+ if (allowInstantiatedGenericMethod && drType is IrSimpleType && !isUnspecialised(drType, logger)) {
+ val calleeIsInvoke = isFunctionInvoke(callTarget, drType)
+
+ val extractionMethod =
+ if (calleeIsInvoke)
+ getFunctionInvokeMethod(drType.arguments)
+ else
+ callTarget
+
+ return extractionMethod?.let {
+ val typeArgs =
+ if (calleeIsInvoke && drType.arguments.size > BuiltInFunctionArity.BIG_ARITY) {
+ // Big arity `invoke` methods have a special implementation on JVM, they are transformed to a call to
+ // `kotlin.jvm.functions.FunctionN::invoke(vararg args: Any?)`, so we only need to pass the type
+ // argument for the return type. Additionally, the arguments are extracted inside an array literal below.
+ listOf(drType.arguments.last())
+ } else {
+ getDeclaringTypeArguments(callTarget, drType)
+ }
+ useFunction(extractionMethod, typeArgs)
+ }
+ }
+ else {
+ return useFunction(callTarget)
+ }
+ }
+
fun extractRawMethodAccess(
syntacticCallTarget: IrFunction,
@@ -1588,86 +1646,30 @@ open class KotlinFileExtractor(
// type arguments at index -2, -3, ...
extractTypeArguments(typeArguments, locId, id, enclosingCallable, enclosingStmt, -2, true)
- val (isFunctionInvoke, isBigArityFunctionInvoke) =
- if (drType is IrSimpleType &&
- (drType.isFunctionOrKFunction() || drType.isSuspendFunctionOrKFunction()) &&
- callTarget.name.asString() == OperatorNameConventions.INVOKE.asString()) {
- Pair(true, drType.arguments.size > BuiltInFunctionArity.BIG_ARITY)
- } else {
- Pair(false, false)
- }
+ val methodId = getCalleeMethodId(callTarget, drType, extractClassTypeArguments)
+
+ if (methodId == null) {
+ logger.warn("No method to bind call to for raw method access")
+ } else {
+ tw.writeCallableBinding(id, methodId)
+ }
if (callTarget.isLocalFunction()) {
- val ids = getLocallyVisibleFunctionLabels(callTarget)
-
- val methodId = ids.function
- tw.writeCallableBinding(id, methodId)
-
- extractNewExprForLocalFunction(ids, id, locId, enclosingCallable, enclosingStmt)
- } else {
- val methodId =
- if (extractClassTypeArguments && drType is IrSimpleType && !isUnspecialised(drType, logger)) {
-
- val extractionMethod = if (isFunctionInvoke) {
- // For `kotlin.FunctionX` and `kotlin.reflect.KFunctionX` interfaces, we're making sure that we
- // extract the call to the `invoke` method that does exist, `kotlin.jvm.functions.FunctionX::invoke`.
- val functionalInterface = getFunctionalInterfaceTypeWithTypeArgs(drType.arguments)
- if (functionalInterface == null) {
- logger.warn("Cannot find functional interface type for raw method access")
- null
- } else {
- val functionalInterfaceClass = functionalInterface.classOrNull
- if (functionalInterfaceClass == null) {
- logger.warn("Cannot find functional interface class for raw method access")
- null
- } else {
- val interfaceType = functionalInterfaceClass.owner
- val substituted = getJavaEquivalentClass(interfaceType) ?: interfaceType
- val function = findFunction(substituted, OperatorNameConventions.INVOKE.asString())
- if (function == null) {
- logger.warn("Cannot find invoke function for raw method access")
- null
- } else {
- function
- }
- }
- }
- } else {
- callTarget
- }
-
- if (extractionMethod == null) {
- null
- } else if (isBigArityFunctionInvoke) {
- // Big arity `invoke` methods have a special implementation on JVM, they are transformed to a call to
- // `kotlin.jvm.functions.FunctionN::invoke(vararg args: Any?)`, so we only need to pass the type
- // argument for the return type. Additionally, the arguments are extracted inside an array literal below.
- useFunction(extractionMethod, listOf(drType.arguments.last()))
- } else {
- useFunction(extractionMethod, getDeclaringTypeArguments(callTarget, drType))
- }
- }
- else {
- useFunction(callTarget)
- }
-
- if (methodId == null) {
- logger.warn("No method to bind call to for raw method access")
- } else {
- tw.writeCallableBinding(id, methodId)
- }
-
- if (callTarget.shouldExtractAsStatic) {
- extractStaticTypeAccessQualifier(callTarget, id, locId, enclosingCallable, enclosingStmt)
- } else if (superQualifierSymbol != null) {
- extractSuperAccess(superQualifierSymbol.typeWith(), enclosingCallable, id, -1, enclosingStmt, locId)
- } else if (extractDispatchReceiver != null) {
- extractDispatchReceiver(id)
- }
+ extractNewExprForLocalFunction(getLocallyVisibleFunctionLabels(callTarget), id, locId, enclosingCallable, enclosingStmt)
+ } else if (callTarget.shouldExtractAsStatic) {
+ extractStaticTypeAccessQualifier(callTarget, id, locId, enclosingCallable, enclosingStmt)
+ } else if (superQualifierSymbol != null) {
+ extractSuperAccess(superQualifierSymbol.typeWith(), enclosingCallable, id, -1, enclosingStmt, locId)
+ } else if (extractDispatchReceiver != null) {
+ extractDispatchReceiver(id)
}
val idxOffset = if (extractExtensionReceiver != null) 1 else 0
+ val isBigArityFunctionInvoke = drType is IrSimpleType &&
+ isFunctionInvoke(callTarget, drType) &&
+ drType.arguments.size > BuiltInFunctionArity.BIG_ARITY
+
val argParent = if (isBigArityFunctionInvoke) {
extractArrayCreationWithInitializer(id, nValueArguments + idxOffset, locId, enclosingCallable, enclosingStmt)
} else {
From c6b7bb436d6400f11c5fab93a821ec0f8c060f26 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Thu, 6 Oct 2022 11:25:22 +0100
Subject: [PATCH 407/991] C++: Make the ql-for-ql checks happy.
---
cpp/ql/src/Security/CWE/CWE-121/UnterminatedVarargsCall.ql | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cpp/ql/src/Security/CWE/CWE-121/UnterminatedVarargsCall.ql b/cpp/ql/src/Security/CWE/CWE-121/UnterminatedVarargsCall.ql
index f2ae677813a..a378d50b0df 100644
--- a/cpp/ql/src/Security/CWE/CWE-121/UnterminatedVarargsCall.ql
+++ b/cpp/ql/src/Security/CWE/CWE-121/UnterminatedVarargsCall.ql
@@ -60,11 +60,11 @@ class VarargsFunction extends Function {
// the terminator is 0 or -1
result = ["0", "-1"] and
// at least 80% of calls have the terminator
- cnt = trailingArgValueCount(result) and
- totalCount = totalCount() and
+ cnt = this.trailingArgValueCount(result) and
+ totalCount = this.totalCount() and
100 * cnt / totalCount >= 80 and
// terminator value is not used in a non-terminating position
- not exists(FunctionCall fc, int index | nonTrailingVarArgValue(fc, index) = result)
+ not exists(FunctionCall fc, int index | this.nonTrailingVarArgValue(fc, index) = result)
}
predicate isWhitelisted() {
From 9960d1104238234b12c856e29725fdb819fba8ef Mon Sep 17 00:00:00 2001
From: gregxsunday
Date: Thu, 6 Oct 2022 13:23:56 +0200
Subject: [PATCH 408/991] added RequestBody source to Beego framework
---
go/ql/lib/semmle/go/frameworks/Beego.qll | 11 +
.../Beego/CleartextLogging.expected | 132 ++--
.../go/frameworks/Beego/OpenRedirect.expected | 16 +-
.../go/frameworks/Beego/ReflectedXss.expected | 590 +++++++++---------
.../go/frameworks/Beego/TaintedPath.expected | 24 +-
.../semmle/go/frameworks/Beego/test.go | 9 +
6 files changed, 403 insertions(+), 379 deletions(-)
diff --git a/go/ql/lib/semmle/go/frameworks/Beego.qll b/go/ql/lib/semmle/go/frameworks/Beego.qll
index 6d927112584..85334e83ab8 100644
--- a/go/ql/lib/semmle/go/frameworks/Beego.qll
+++ b/go/ql/lib/semmle/go/frameworks/Beego.qll
@@ -103,6 +103,17 @@ module Beego {
}
}
+ /**
+ * `BeegoInputRequestBody` sources of untrusted data.
+ */
+ private class BeegoInputRequestBodySource extends UntrustedFlowSource::Range {
+ BeegoInputRequestBodySource() {
+ exists(DataFlow::FieldReadNode frn | this = frn |
+ frn.getField().hasQualifiedName(contextPackagePath(), "BeegoInput", "RequestBody")
+ )
+ }
+ }
+
/**
* `beego/context.Context` sources of untrusted data.
*/
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/Beego/CleartextLogging.expected b/go/ql/test/library-tests/semmle/go/frameworks/Beego/CleartextLogging.expected
index 6056f572d7c..11e51609b05 100644
--- a/go/ql/test/library-tests/semmle/go/frameworks/Beego/CleartextLogging.expected
+++ b/go/ql/test/library-tests/semmle/go/frameworks/Beego/CleartextLogging.expected
@@ -1,72 +1,72 @@
edges
nodes
-| test.go:147:14:147:21 | password | semmle.label | password |
-| test.go:148:17:148:24 | password | semmle.label | password |
-| test.go:149:14:149:21 | password | semmle.label | password |
-| test.go:150:18:150:25 | password | semmle.label | password |
-| test.go:151:14:151:21 | password | semmle.label | password |
-| test.go:152:13:152:20 | password | semmle.label | password |
-| test.go:153:22:153:29 | password | semmle.label | password |
-| test.go:154:15:154:22 | password | semmle.label | password |
-| test.go:155:14:155:21 | password | semmle.label | password |
-| test.go:156:13:156:20 | password | semmle.label | password |
-| test.go:157:16:157:23 | password | semmle.label | password |
-| test.go:158:13:158:20 | password | semmle.label | password |
-| test.go:159:16:159:23 | password | semmle.label | password |
-| test.go:160:13:160:20 | password | semmle.label | password |
-| test.go:161:17:161:24 | password | semmle.label | password |
-| test.go:162:13:162:20 | password | semmle.label | password |
-| test.go:163:12:163:19 | password | semmle.label | password |
-| test.go:164:21:164:28 | password | semmle.label | password |
-| test.go:165:14:165:21 | password | semmle.label | password |
-| test.go:166:13:166:20 | password | semmle.label | password |
-| test.go:167:12:167:19 | password | semmle.label | password |
-| test.go:168:15:168:22 | password | semmle.label | password |
+| test.go:148:14:148:21 | password | semmle.label | password |
+| test.go:149:17:149:24 | password | semmle.label | password |
+| test.go:150:14:150:21 | password | semmle.label | password |
+| test.go:151:18:151:25 | password | semmle.label | password |
+| test.go:152:14:152:21 | password | semmle.label | password |
+| test.go:153:13:153:20 | password | semmle.label | password |
+| test.go:154:22:154:29 | password | semmle.label | password |
+| test.go:155:15:155:22 | password | semmle.label | password |
+| test.go:156:14:156:21 | password | semmle.label | password |
+| test.go:157:13:157:20 | password | semmle.label | password |
+| test.go:158:16:158:23 | password | semmle.label | password |
+| test.go:159:13:159:20 | password | semmle.label | password |
+| test.go:160:16:160:23 | password | semmle.label | password |
+| test.go:161:13:161:20 | password | semmle.label | password |
+| test.go:162:17:162:24 | password | semmle.label | password |
+| test.go:163:13:163:20 | password | semmle.label | password |
+| test.go:164:12:164:19 | password | semmle.label | password |
+| test.go:165:21:165:28 | password | semmle.label | password |
+| test.go:166:14:166:21 | password | semmle.label | password |
+| test.go:167:13:167:20 | password | semmle.label | password |
+| test.go:168:12:168:19 | password | semmle.label | password |
| test.go:169:15:169:22 | password | semmle.label | password |
-| test.go:170:18:170:25 | password | semmle.label | password |
-| test.go:171:15:171:22 | password | semmle.label | password |
-| test.go:172:19:172:26 | password | semmle.label | password |
-| test.go:173:15:173:22 | password | semmle.label | password |
-| test.go:174:14:174:21 | password | semmle.label | password |
-| test.go:175:23:175:30 | password | semmle.label | password |
-| test.go:176:16:176:23 | password | semmle.label | password |
-| test.go:177:15:177:22 | password | semmle.label | password |
-| test.go:178:14:178:21 | password | semmle.label | password |
-| test.go:179:17:179:24 | password | semmle.label | password |
-| test.go:180:16:180:23 | password | semmle.label | password |
+| test.go:170:15:170:22 | password | semmle.label | password |
+| test.go:171:18:171:25 | password | semmle.label | password |
+| test.go:172:15:172:22 | password | semmle.label | password |
+| test.go:173:19:173:26 | password | semmle.label | password |
+| test.go:174:15:174:22 | password | semmle.label | password |
+| test.go:175:14:175:21 | password | semmle.label | password |
+| test.go:176:23:176:30 | password | semmle.label | password |
+| test.go:177:16:177:23 | password | semmle.label | password |
+| test.go:178:15:178:22 | password | semmle.label | password |
+| test.go:179:14:179:21 | password | semmle.label | password |
+| test.go:180:17:180:24 | password | semmle.label | password |
+| test.go:181:16:181:23 | password | semmle.label | password |
subpaths
#select
-| test.go:147:14:147:21 | password | test.go:147:14:147:21 | password | test.go:147:14:147:21 | password | $@ flows to a logging call. | test.go:147:14:147:21 | password | Sensitive data returned by an access to password |
-| test.go:148:17:148:24 | password | test.go:148:17:148:24 | password | test.go:148:17:148:24 | password | $@ flows to a logging call. | test.go:148:17:148:24 | password | Sensitive data returned by an access to password |
-| test.go:149:14:149:21 | password | test.go:149:14:149:21 | password | test.go:149:14:149:21 | password | $@ flows to a logging call. | test.go:149:14:149:21 | password | Sensitive data returned by an access to password |
-| test.go:150:18:150:25 | password | test.go:150:18:150:25 | password | test.go:150:18:150:25 | password | $@ flows to a logging call. | test.go:150:18:150:25 | password | Sensitive data returned by an access to password |
-| test.go:151:14:151:21 | password | test.go:151:14:151:21 | password | test.go:151:14:151:21 | password | $@ flows to a logging call. | test.go:151:14:151:21 | password | Sensitive data returned by an access to password |
-| test.go:152:13:152:20 | password | test.go:152:13:152:20 | password | test.go:152:13:152:20 | password | $@ flows to a logging call. | test.go:152:13:152:20 | password | Sensitive data returned by an access to password |
-| test.go:153:22:153:29 | password | test.go:153:22:153:29 | password | test.go:153:22:153:29 | password | $@ flows to a logging call. | test.go:153:22:153:29 | password | Sensitive data returned by an access to password |
-| test.go:154:15:154:22 | password | test.go:154:15:154:22 | password | test.go:154:15:154:22 | password | $@ flows to a logging call. | test.go:154:15:154:22 | password | Sensitive data returned by an access to password |
-| test.go:155:14:155:21 | password | test.go:155:14:155:21 | password | test.go:155:14:155:21 | password | $@ flows to a logging call. | test.go:155:14:155:21 | password | Sensitive data returned by an access to password |
-| test.go:156:13:156:20 | password | test.go:156:13:156:20 | password | test.go:156:13:156:20 | password | $@ flows to a logging call. | test.go:156:13:156:20 | password | Sensitive data returned by an access to password |
-| test.go:157:16:157:23 | password | test.go:157:16:157:23 | password | test.go:157:16:157:23 | password | $@ flows to a logging call. | test.go:157:16:157:23 | password | Sensitive data returned by an access to password |
-| test.go:158:13:158:20 | password | test.go:158:13:158:20 | password | test.go:158:13:158:20 | password | $@ flows to a logging call. | test.go:158:13:158:20 | password | Sensitive data returned by an access to password |
-| test.go:159:16:159:23 | password | test.go:159:16:159:23 | password | test.go:159:16:159:23 | password | $@ flows to a logging call. | test.go:159:16:159:23 | password | Sensitive data returned by an access to password |
-| test.go:160:13:160:20 | password | test.go:160:13:160:20 | password | test.go:160:13:160:20 | password | $@ flows to a logging call. | test.go:160:13:160:20 | password | Sensitive data returned by an access to password |
-| test.go:161:17:161:24 | password | test.go:161:17:161:24 | password | test.go:161:17:161:24 | password | $@ flows to a logging call. | test.go:161:17:161:24 | password | Sensitive data returned by an access to password |
-| test.go:162:13:162:20 | password | test.go:162:13:162:20 | password | test.go:162:13:162:20 | password | $@ flows to a logging call. | test.go:162:13:162:20 | password | Sensitive data returned by an access to password |
-| test.go:163:12:163:19 | password | test.go:163:12:163:19 | password | test.go:163:12:163:19 | password | $@ flows to a logging call. | test.go:163:12:163:19 | password | Sensitive data returned by an access to password |
-| test.go:164:21:164:28 | password | test.go:164:21:164:28 | password | test.go:164:21:164:28 | password | $@ flows to a logging call. | test.go:164:21:164:28 | password | Sensitive data returned by an access to password |
-| test.go:165:14:165:21 | password | test.go:165:14:165:21 | password | test.go:165:14:165:21 | password | $@ flows to a logging call. | test.go:165:14:165:21 | password | Sensitive data returned by an access to password |
-| test.go:166:13:166:20 | password | test.go:166:13:166:20 | password | test.go:166:13:166:20 | password | $@ flows to a logging call. | test.go:166:13:166:20 | password | Sensitive data returned by an access to password |
-| test.go:167:12:167:19 | password | test.go:167:12:167:19 | password | test.go:167:12:167:19 | password | $@ flows to a logging call. | test.go:167:12:167:19 | password | Sensitive data returned by an access to password |
-| test.go:168:15:168:22 | password | test.go:168:15:168:22 | password | test.go:168:15:168:22 | password | $@ flows to a logging call. | test.go:168:15:168:22 | password | Sensitive data returned by an access to password |
+| test.go:148:14:148:21 | password | test.go:148:14:148:21 | password | test.go:148:14:148:21 | password | $@ flows to a logging call. | test.go:148:14:148:21 | password | Sensitive data returned by an access to password |
+| test.go:149:17:149:24 | password | test.go:149:17:149:24 | password | test.go:149:17:149:24 | password | $@ flows to a logging call. | test.go:149:17:149:24 | password | Sensitive data returned by an access to password |
+| test.go:150:14:150:21 | password | test.go:150:14:150:21 | password | test.go:150:14:150:21 | password | $@ flows to a logging call. | test.go:150:14:150:21 | password | Sensitive data returned by an access to password |
+| test.go:151:18:151:25 | password | test.go:151:18:151:25 | password | test.go:151:18:151:25 | password | $@ flows to a logging call. | test.go:151:18:151:25 | password | Sensitive data returned by an access to password |
+| test.go:152:14:152:21 | password | test.go:152:14:152:21 | password | test.go:152:14:152:21 | password | $@ flows to a logging call. | test.go:152:14:152:21 | password | Sensitive data returned by an access to password |
+| test.go:153:13:153:20 | password | test.go:153:13:153:20 | password | test.go:153:13:153:20 | password | $@ flows to a logging call. | test.go:153:13:153:20 | password | Sensitive data returned by an access to password |
+| test.go:154:22:154:29 | password | test.go:154:22:154:29 | password | test.go:154:22:154:29 | password | $@ flows to a logging call. | test.go:154:22:154:29 | password | Sensitive data returned by an access to password |
+| test.go:155:15:155:22 | password | test.go:155:15:155:22 | password | test.go:155:15:155:22 | password | $@ flows to a logging call. | test.go:155:15:155:22 | password | Sensitive data returned by an access to password |
+| test.go:156:14:156:21 | password | test.go:156:14:156:21 | password | test.go:156:14:156:21 | password | $@ flows to a logging call. | test.go:156:14:156:21 | password | Sensitive data returned by an access to password |
+| test.go:157:13:157:20 | password | test.go:157:13:157:20 | password | test.go:157:13:157:20 | password | $@ flows to a logging call. | test.go:157:13:157:20 | password | Sensitive data returned by an access to password |
+| test.go:158:16:158:23 | password | test.go:158:16:158:23 | password | test.go:158:16:158:23 | password | $@ flows to a logging call. | test.go:158:16:158:23 | password | Sensitive data returned by an access to password |
+| test.go:159:13:159:20 | password | test.go:159:13:159:20 | password | test.go:159:13:159:20 | password | $@ flows to a logging call. | test.go:159:13:159:20 | password | Sensitive data returned by an access to password |
+| test.go:160:16:160:23 | password | test.go:160:16:160:23 | password | test.go:160:16:160:23 | password | $@ flows to a logging call. | test.go:160:16:160:23 | password | Sensitive data returned by an access to password |
+| test.go:161:13:161:20 | password | test.go:161:13:161:20 | password | test.go:161:13:161:20 | password | $@ flows to a logging call. | test.go:161:13:161:20 | password | Sensitive data returned by an access to password |
+| test.go:162:17:162:24 | password | test.go:162:17:162:24 | password | test.go:162:17:162:24 | password | $@ flows to a logging call. | test.go:162:17:162:24 | password | Sensitive data returned by an access to password |
+| test.go:163:13:163:20 | password | test.go:163:13:163:20 | password | test.go:163:13:163:20 | password | $@ flows to a logging call. | test.go:163:13:163:20 | password | Sensitive data returned by an access to password |
+| test.go:164:12:164:19 | password | test.go:164:12:164:19 | password | test.go:164:12:164:19 | password | $@ flows to a logging call. | test.go:164:12:164:19 | password | Sensitive data returned by an access to password |
+| test.go:165:21:165:28 | password | test.go:165:21:165:28 | password | test.go:165:21:165:28 | password | $@ flows to a logging call. | test.go:165:21:165:28 | password | Sensitive data returned by an access to password |
+| test.go:166:14:166:21 | password | test.go:166:14:166:21 | password | test.go:166:14:166:21 | password | $@ flows to a logging call. | test.go:166:14:166:21 | password | Sensitive data returned by an access to password |
+| test.go:167:13:167:20 | password | test.go:167:13:167:20 | password | test.go:167:13:167:20 | password | $@ flows to a logging call. | test.go:167:13:167:20 | password | Sensitive data returned by an access to password |
+| test.go:168:12:168:19 | password | test.go:168:12:168:19 | password | test.go:168:12:168:19 | password | $@ flows to a logging call. | test.go:168:12:168:19 | password | Sensitive data returned by an access to password |
| test.go:169:15:169:22 | password | test.go:169:15:169:22 | password | test.go:169:15:169:22 | password | $@ flows to a logging call. | test.go:169:15:169:22 | password | Sensitive data returned by an access to password |
-| test.go:170:18:170:25 | password | test.go:170:18:170:25 | password | test.go:170:18:170:25 | password | $@ flows to a logging call. | test.go:170:18:170:25 | password | Sensitive data returned by an access to password |
-| test.go:171:15:171:22 | password | test.go:171:15:171:22 | password | test.go:171:15:171:22 | password | $@ flows to a logging call. | test.go:171:15:171:22 | password | Sensitive data returned by an access to password |
-| test.go:172:19:172:26 | password | test.go:172:19:172:26 | password | test.go:172:19:172:26 | password | $@ flows to a logging call. | test.go:172:19:172:26 | password | Sensitive data returned by an access to password |
-| test.go:173:15:173:22 | password | test.go:173:15:173:22 | password | test.go:173:15:173:22 | password | $@ flows to a logging call. | test.go:173:15:173:22 | password | Sensitive data returned by an access to password |
-| test.go:174:14:174:21 | password | test.go:174:14:174:21 | password | test.go:174:14:174:21 | password | $@ flows to a logging call. | test.go:174:14:174:21 | password | Sensitive data returned by an access to password |
-| test.go:175:23:175:30 | password | test.go:175:23:175:30 | password | test.go:175:23:175:30 | password | $@ flows to a logging call. | test.go:175:23:175:30 | password | Sensitive data returned by an access to password |
-| test.go:176:16:176:23 | password | test.go:176:16:176:23 | password | test.go:176:16:176:23 | password | $@ flows to a logging call. | test.go:176:16:176:23 | password | Sensitive data returned by an access to password |
-| test.go:177:15:177:22 | password | test.go:177:15:177:22 | password | test.go:177:15:177:22 | password | $@ flows to a logging call. | test.go:177:15:177:22 | password | Sensitive data returned by an access to password |
-| test.go:178:14:178:21 | password | test.go:178:14:178:21 | password | test.go:178:14:178:21 | password | $@ flows to a logging call. | test.go:178:14:178:21 | password | Sensitive data returned by an access to password |
-| test.go:179:17:179:24 | password | test.go:179:17:179:24 | password | test.go:179:17:179:24 | password | $@ flows to a logging call. | test.go:179:17:179:24 | password | Sensitive data returned by an access to password |
-| test.go:180:16:180:23 | password | test.go:180:16:180:23 | password | test.go:180:16:180:23 | password | $@ flows to a logging call. | test.go:180:16:180:23 | password | Sensitive data returned by an access to password |
+| test.go:170:15:170:22 | password | test.go:170:15:170:22 | password | test.go:170:15:170:22 | password | $@ flows to a logging call. | test.go:170:15:170:22 | password | Sensitive data returned by an access to password |
+| test.go:171:18:171:25 | password | test.go:171:18:171:25 | password | test.go:171:18:171:25 | password | $@ flows to a logging call. | test.go:171:18:171:25 | password | Sensitive data returned by an access to password |
+| test.go:172:15:172:22 | password | test.go:172:15:172:22 | password | test.go:172:15:172:22 | password | $@ flows to a logging call. | test.go:172:15:172:22 | password | Sensitive data returned by an access to password |
+| test.go:173:19:173:26 | password | test.go:173:19:173:26 | password | test.go:173:19:173:26 | password | $@ flows to a logging call. | test.go:173:19:173:26 | password | Sensitive data returned by an access to password |
+| test.go:174:15:174:22 | password | test.go:174:15:174:22 | password | test.go:174:15:174:22 | password | $@ flows to a logging call. | test.go:174:15:174:22 | password | Sensitive data returned by an access to password |
+| test.go:175:14:175:21 | password | test.go:175:14:175:21 | password | test.go:175:14:175:21 | password | $@ flows to a logging call. | test.go:175:14:175:21 | password | Sensitive data returned by an access to password |
+| test.go:176:23:176:30 | password | test.go:176:23:176:30 | password | test.go:176:23:176:30 | password | $@ flows to a logging call. | test.go:176:23:176:30 | password | Sensitive data returned by an access to password |
+| test.go:177:16:177:23 | password | test.go:177:16:177:23 | password | test.go:177:16:177:23 | password | $@ flows to a logging call. | test.go:177:16:177:23 | password | Sensitive data returned by an access to password |
+| test.go:178:15:178:22 | password | test.go:178:15:178:22 | password | test.go:178:15:178:22 | password | $@ flows to a logging call. | test.go:178:15:178:22 | password | Sensitive data returned by an access to password |
+| test.go:179:14:179:21 | password | test.go:179:14:179:21 | password | test.go:179:14:179:21 | password | $@ flows to a logging call. | test.go:179:14:179:21 | password | Sensitive data returned by an access to password |
+| test.go:180:17:180:24 | password | test.go:180:17:180:24 | password | test.go:180:17:180:24 | password | $@ flows to a logging call. | test.go:180:17:180:24 | password | Sensitive data returned by an access to password |
+| test.go:181:16:181:23 | password | test.go:181:16:181:23 | password | test.go:181:16:181:23 | password | $@ flows to a logging call. | test.go:181:16:181:23 | password | Sensitive data returned by an access to password |
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/Beego/OpenRedirect.expected b/go/ql/test/library-tests/semmle/go/frameworks/Beego/OpenRedirect.expected
index ff1902aa72a..9a80d4698cc 100644
--- a/go/ql/test/library-tests/semmle/go/frameworks/Beego/OpenRedirect.expected
+++ b/go/ql/test/library-tests/semmle/go/frameworks/Beego/OpenRedirect.expected
@@ -1,12 +1,12 @@
edges
nodes
-| test.go:246:13:246:34 | call to GetString | semmle.label | call to GetString |
-| test.go:247:20:247:41 | call to GetString | semmle.label | call to GetString |
-| test.go:310:13:310:27 | call to URI | semmle.label | call to URI |
-| test.go:310:13:310:27 | call to URI | semmle.label | call to URI |
-| test.go:311:20:311:34 | call to URL | semmle.label | call to URL |
-| test.go:311:20:311:34 | call to URL | semmle.label | call to URL |
+| test.go:247:13:247:34 | call to GetString | semmle.label | call to GetString |
+| test.go:248:20:248:41 | call to GetString | semmle.label | call to GetString |
+| test.go:311:13:311:27 | call to URI | semmle.label | call to URI |
+| test.go:311:13:311:27 | call to URI | semmle.label | call to URI |
+| test.go:312:20:312:34 | call to URL | semmle.label | call to URL |
+| test.go:312:20:312:34 | call to URL | semmle.label | call to URL |
subpaths
#select
-| test.go:246:13:246:34 | call to GetString | test.go:246:13:246:34 | call to GetString | test.go:246:13:246:34 | call to GetString | Untrusted URL redirection depends on a $@. | test.go:246:13:246:34 | call to GetString | user-provided value |
-| test.go:247:20:247:41 | call to GetString | test.go:247:20:247:41 | call to GetString | test.go:247:20:247:41 | call to GetString | Untrusted URL redirection depends on a $@. | test.go:247:20:247:41 | call to GetString | user-provided value |
+| test.go:247:13:247:34 | call to GetString | test.go:247:13:247:34 | call to GetString | test.go:247:13:247:34 | call to GetString | Untrusted URL redirection depends on a $@. | test.go:247:13:247:34 | call to GetString | user-provided value |
+| test.go:248:20:248:41 | call to GetString | test.go:248:20:248:41 | call to GetString | test.go:248:20:248:41 | call to GetString | Untrusted URL redirection depends on a $@. | test.go:248:20:248:41 | call to GetString | user-provided value |
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/Beego/ReflectedXss.expected b/go/ql/test/library-tests/semmle/go/frameworks/Beego/ReflectedXss.expected
index 2de1ce81fff..cb39cfdd247 100644
--- a/go/ql/test/library-tests/semmle/go/frameworks/Beego/ReflectedXss.expected
+++ b/go/ql/test/library-tests/semmle/go/frameworks/Beego/ReflectedXss.expected
@@ -1,317 +1,317 @@
edges
-| test.go:26:6:26:10 | definition of bound : bindMe | test.go:28:13:28:30 | type conversion |
-| test.go:26:6:26:10 | definition of bound : bindMe | test.go:28:20:28:26 | selection of a : slice type |
-| test.go:26:6:26:10 | definition of bound : bindMe | test.go:29:13:29:27 | type conversion |
-| test.go:26:6:26:10 | definition of bound : bindMe | test.go:30:13:30:29 | type conversion |
-| test.go:26:6:26:10 | definition of bound : bindMe | test.go:30:20:30:26 | selection of c : subBindMe |
-| test.go:28:20:28:26 | selection of a : slice type | test.go:28:13:28:30 | type conversion |
-| test.go:30:20:30:26 | selection of c : subBindMe | test.go:30:13:30:29 | type conversion |
-| test.go:35:20:35:42 | call to Cookie : string | test.go:35:13:35:43 | type conversion |
-| test.go:40:20:40:31 | call to Data : map type | test.go:40:13:40:52 | type conversion |
-| test.go:45:20:45:43 | call to GetData : basic interface type | test.go:45:13:45:53 | type conversion |
-| test.go:50:20:50:42 | call to Header : string | test.go:50:13:50:43 | type conversion |
-| test.go:55:20:55:41 | call to Param : string | test.go:55:13:55:42 | type conversion |
-| test.go:60:20:60:33 | call to Params : map type | test.go:60:13:60:45 | type conversion |
-| test.go:65:20:65:41 | call to Query : string | test.go:65:13:65:42 | type conversion |
-| test.go:70:20:70:32 | call to Refer : string | test.go:70:13:70:33 | type conversion |
-| test.go:75:20:75:34 | call to Referer : string | test.go:75:13:75:35 | type conversion |
-| test.go:80:20:80:30 | call to URI : string | test.go:80:13:80:31 | type conversion |
-| test.go:85:20:85:30 | call to URL : string | test.go:85:13:85:31 | type conversion |
-| test.go:90:20:90:36 | call to UserAgent : string | test.go:90:13:90:37 | type conversion |
-| test.go:95:14:95:25 | call to Data : map type | test.go:95:14:95:45 | type assertion |
-| test.go:107:14:107:25 | call to Data : map type | test.go:107:14:107:45 | type assertion |
-| test.go:119:14:119:25 | call to Data : map type | test.go:119:14:119:45 | type assertion |
-| test.go:136:23:136:42 | call to Data : map type | test.go:136:23:136:62 | type assertion |
-| test.go:192:15:192:26 | call to Data : map type | test.go:193:14:193:55 | type conversion |
-| test.go:192:15:192:26 | call to Data : map type | test.go:194:14:194:58 | type conversion |
-| test.go:192:15:192:26 | call to Data : map type | test.go:196:14:196:28 | type assertion |
-| test.go:192:15:192:26 | call to Data : map type | test.go:197:14:197:55 | type conversion |
-| test.go:192:15:192:26 | call to Data : map type | test.go:198:14:198:59 | type conversion |
-| test.go:201:18:201:33 | selection of Form : Values | test.go:202:14:202:28 | type conversion |
-| test.go:216:2:216:34 | ... := ...[0] : File | test.go:219:14:219:20 | content |
-| test.go:216:2:216:34 | ... := ...[1] : pointer type | test.go:217:14:217:32 | type conversion |
-| test.go:216:2:216:34 | ... := ...[1] : pointer type | test.go:217:21:217:22 | implicit dereference : FileHeader |
-| test.go:217:21:217:22 | implicit dereference : FileHeader | test.go:217:14:217:32 | type conversion |
-| test.go:217:21:217:22 | implicit dereference : FileHeader | test.go:217:21:217:22 | implicit dereference : FileHeader |
-| test.go:221:2:221:40 | ... := ...[0] : slice type | test.go:222:14:222:38 | type conversion |
-| test.go:221:2:221:40 | ... := ...[0] : slice type | test.go:222:21:222:28 | implicit dereference : FileHeader |
-| test.go:221:2:221:40 | ... := ...[0] : slice type | test.go:222:21:222:28 | index expression : pointer type |
-| test.go:222:21:222:28 | implicit dereference : FileHeader | test.go:222:14:222:38 | type conversion |
-| test.go:222:21:222:28 | implicit dereference : FileHeader | test.go:222:21:222:28 | implicit dereference : FileHeader |
-| test.go:222:21:222:28 | implicit dereference : FileHeader | test.go:222:21:222:28 | index expression : pointer type |
-| test.go:222:21:222:28 | index expression : pointer type | test.go:222:14:222:38 | type conversion |
-| test.go:222:21:222:28 | index expression : pointer type | test.go:222:21:222:28 | implicit dereference : FileHeader |
-| test.go:222:21:222:28 | index expression : pointer type | test.go:222:21:222:28 | index expression : pointer type |
-| test.go:224:7:224:28 | call to GetString : string | test.go:225:14:225:22 | type conversion |
-| test.go:227:8:227:35 | call to GetStrings : slice type | test.go:228:14:228:26 | type conversion |
-| test.go:230:9:230:17 | call to Input : Values | test.go:231:14:231:27 | type conversion |
-| test.go:233:6:233:8 | definition of str : myStruct | test.go:235:14:235:30 | type conversion |
-| test.go:239:15:239:36 | call to GetString : string | test.go:242:21:242:29 | untrusted |
-| test.go:252:23:252:44 | call to GetCookie : string | test.go:252:16:252:45 | type conversion |
-| test.go:263:62:263:83 | call to GetCookie : string | test.go:263:55:263:84 | type conversion |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:276:21:276:61 | call to GetDisplayString |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:276:44:276:51 | implicit dereference : FileHeader |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:276:44:276:51 | index expression : pointer type |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:277:21:277:53 | call to SliceChunk : slice type |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:277:21:277:56 | index expression : slice type |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:277:21:277:83 | implicit dereference : FileHeader |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:277:21:277:92 | selection of Filename |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:278:21:278:60 | call to SliceDiff : slice type |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:278:21:278:87 | implicit dereference : FileHeader |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:278:21:278:96 | selection of Filename |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:283:3:285:44 | call to SliceFilter : slice type |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:283:3:285:71 | implicit dereference : FileHeader |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:283:3:285:80 | selection of Filename |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:286:21:286:65 | call to SliceIntersect : slice type |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:286:21:286:92 | implicit dereference : FileHeader |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:286:21:286:101 | selection of Filename |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:287:21:287:65 | call to SliceIntersect : slice type |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:287:21:287:92 | implicit dereference : FileHeader |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:287:21:287:101 | selection of Filename |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:288:21:288:61 | call to SliceMerge : slice type |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:288:21:288:88 | implicit dereference : FileHeader |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:288:21:288:97 | selection of Filename |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:289:21:289:61 | call to SliceMerge : slice type |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:289:21:289:88 | implicit dereference : FileHeader |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:289:21:289:97 | selection of Filename |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:290:21:290:66 | call to SlicePad : slice type |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:290:21:290:93 | implicit dereference : FileHeader |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:290:21:290:102 | selection of Filename |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:291:21:291:66 | call to SlicePad : slice type |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:291:21:291:93 | implicit dereference : FileHeader |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:291:21:291:102 | selection of Filename |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:292:21:292:73 | implicit dereference : FileHeader |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:292:21:292:82 | selection of Filename |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:294:21:294:97 | call to SliceReduce : slice type |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:294:21:294:124 | implicit dereference : FileHeader |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:294:21:294:133 | selection of Filename |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:295:21:295:52 | call to SliceShuffle : slice type |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:295:21:295:79 | implicit dereference : FileHeader |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:295:21:295:88 | selection of Filename |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:296:21:296:51 | call to SliceUnique : slice type |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:296:21:296:78 | implicit dereference : FileHeader |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:296:21:296:87 | selection of Filename |
-| test.go:276:44:276:51 | implicit dereference : FileHeader | test.go:276:21:276:61 | call to GetDisplayString |
-| test.go:276:44:276:51 | implicit dereference : FileHeader | test.go:276:44:276:51 | implicit dereference : FileHeader |
-| test.go:276:44:276:51 | implicit dereference : FileHeader | test.go:276:44:276:51 | index expression : pointer type |
-| test.go:276:44:276:51 | index expression : pointer type | test.go:276:21:276:61 | call to GetDisplayString |
-| test.go:276:44:276:51 | index expression : pointer type | test.go:276:44:276:51 | implicit dereference : FileHeader |
-| test.go:276:44:276:51 | index expression : pointer type | test.go:276:44:276:51 | index expression : pointer type |
-| test.go:277:21:277:53 | call to SliceChunk : slice type | test.go:277:21:277:56 | index expression : slice type |
-| test.go:277:21:277:53 | call to SliceChunk : slice type | test.go:277:21:277:83 | implicit dereference : FileHeader |
-| test.go:277:21:277:53 | call to SliceChunk : slice type | test.go:277:21:277:92 | selection of Filename |
-| test.go:277:21:277:56 | index expression : slice type | test.go:277:21:277:83 | implicit dereference : FileHeader |
-| test.go:277:21:277:56 | index expression : slice type | test.go:277:21:277:92 | selection of Filename |
-| test.go:277:21:277:83 | implicit dereference : FileHeader | test.go:277:21:277:92 | selection of Filename |
-| test.go:278:21:278:60 | call to SliceDiff : slice type | test.go:278:21:278:87 | implicit dereference : FileHeader |
-| test.go:278:21:278:60 | call to SliceDiff : slice type | test.go:278:21:278:96 | selection of Filename |
-| test.go:278:21:278:87 | implicit dereference : FileHeader | test.go:278:21:278:96 | selection of Filename |
-| test.go:283:3:285:44 | call to SliceFilter : slice type | test.go:283:3:285:71 | implicit dereference : FileHeader |
-| test.go:283:3:285:44 | call to SliceFilter : slice type | test.go:283:3:285:80 | selection of Filename |
-| test.go:283:3:285:71 | implicit dereference : FileHeader | test.go:283:3:285:80 | selection of Filename |
-| test.go:286:21:286:65 | call to SliceIntersect : slice type | test.go:286:21:286:92 | implicit dereference : FileHeader |
-| test.go:286:21:286:65 | call to SliceIntersect : slice type | test.go:286:21:286:101 | selection of Filename |
-| test.go:286:21:286:92 | implicit dereference : FileHeader | test.go:286:21:286:101 | selection of Filename |
+| test.go:27:6:27:10 | definition of bound : bindMe | test.go:29:13:29:30 | type conversion |
+| test.go:27:6:27:10 | definition of bound : bindMe | test.go:29:20:29:26 | selection of a : slice type |
+| test.go:27:6:27:10 | definition of bound : bindMe | test.go:30:13:30:27 | type conversion |
+| test.go:27:6:27:10 | definition of bound : bindMe | test.go:31:13:31:29 | type conversion |
+| test.go:27:6:27:10 | definition of bound : bindMe | test.go:31:20:31:26 | selection of c : subBindMe |
+| test.go:29:20:29:26 | selection of a : slice type | test.go:29:13:29:30 | type conversion |
+| test.go:31:20:31:26 | selection of c : subBindMe | test.go:31:13:31:29 | type conversion |
+| test.go:36:20:36:42 | call to Cookie : string | test.go:36:13:36:43 | type conversion |
+| test.go:41:20:41:31 | call to Data : map type | test.go:41:13:41:52 | type conversion |
+| test.go:46:20:46:43 | call to GetData : basic interface type | test.go:46:13:46:53 | type conversion |
+| test.go:51:20:51:42 | call to Header : string | test.go:51:13:51:43 | type conversion |
+| test.go:56:20:56:41 | call to Param : string | test.go:56:13:56:42 | type conversion |
+| test.go:61:20:61:33 | call to Params : map type | test.go:61:13:61:45 | type conversion |
+| test.go:66:20:66:41 | call to Query : string | test.go:66:13:66:42 | type conversion |
+| test.go:71:20:71:32 | call to Refer : string | test.go:71:13:71:33 | type conversion |
+| test.go:76:20:76:34 | call to Referer : string | test.go:76:13:76:35 | type conversion |
+| test.go:81:20:81:30 | call to URI : string | test.go:81:13:81:31 | type conversion |
+| test.go:86:20:86:30 | call to URL : string | test.go:86:13:86:31 | type conversion |
+| test.go:91:20:91:36 | call to UserAgent : string | test.go:91:13:91:37 | type conversion |
+| test.go:96:14:96:25 | call to Data : map type | test.go:96:14:96:45 | type assertion |
+| test.go:108:14:108:25 | call to Data : map type | test.go:108:14:108:45 | type assertion |
+| test.go:120:14:120:25 | call to Data : map type | test.go:120:14:120:45 | type assertion |
+| test.go:137:23:137:42 | call to Data : map type | test.go:137:23:137:62 | type assertion |
+| test.go:193:15:193:26 | call to Data : map type | test.go:194:14:194:55 | type conversion |
+| test.go:193:15:193:26 | call to Data : map type | test.go:195:14:195:58 | type conversion |
+| test.go:193:15:193:26 | call to Data : map type | test.go:197:14:197:28 | type assertion |
+| test.go:193:15:193:26 | call to Data : map type | test.go:198:14:198:55 | type conversion |
+| test.go:193:15:193:26 | call to Data : map type | test.go:199:14:199:59 | type conversion |
+| test.go:202:18:202:33 | selection of Form : Values | test.go:203:14:203:28 | type conversion |
+| test.go:217:2:217:34 | ... := ...[0] : File | test.go:220:14:220:20 | content |
+| test.go:217:2:217:34 | ... := ...[1] : pointer type | test.go:218:14:218:32 | type conversion |
+| test.go:217:2:217:34 | ... := ...[1] : pointer type | test.go:218:21:218:22 | implicit dereference : FileHeader |
+| test.go:218:21:218:22 | implicit dereference : FileHeader | test.go:218:14:218:32 | type conversion |
+| test.go:218:21:218:22 | implicit dereference : FileHeader | test.go:218:21:218:22 | implicit dereference : FileHeader |
+| test.go:222:2:222:40 | ... := ...[0] : slice type | test.go:223:14:223:38 | type conversion |
+| test.go:222:2:222:40 | ... := ...[0] : slice type | test.go:223:21:223:28 | implicit dereference : FileHeader |
+| test.go:222:2:222:40 | ... := ...[0] : slice type | test.go:223:21:223:28 | index expression : pointer type |
+| test.go:223:21:223:28 | implicit dereference : FileHeader | test.go:223:14:223:38 | type conversion |
+| test.go:223:21:223:28 | implicit dereference : FileHeader | test.go:223:21:223:28 | implicit dereference : FileHeader |
+| test.go:223:21:223:28 | implicit dereference : FileHeader | test.go:223:21:223:28 | index expression : pointer type |
+| test.go:223:21:223:28 | index expression : pointer type | test.go:223:14:223:38 | type conversion |
+| test.go:223:21:223:28 | index expression : pointer type | test.go:223:21:223:28 | implicit dereference : FileHeader |
+| test.go:223:21:223:28 | index expression : pointer type | test.go:223:21:223:28 | index expression : pointer type |
+| test.go:225:7:225:28 | call to GetString : string | test.go:226:14:226:22 | type conversion |
+| test.go:228:8:228:35 | call to GetStrings : slice type | test.go:229:14:229:26 | type conversion |
+| test.go:231:9:231:17 | call to Input : Values | test.go:232:14:232:27 | type conversion |
+| test.go:234:6:234:8 | definition of str : myStruct | test.go:236:14:236:30 | type conversion |
+| test.go:240:15:240:36 | call to GetString : string | test.go:243:21:243:29 | untrusted |
+| test.go:253:23:253:44 | call to GetCookie : string | test.go:253:16:253:45 | type conversion |
+| test.go:264:62:264:83 | call to GetCookie : string | test.go:264:55:264:84 | type conversion |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:277:21:277:61 | call to GetDisplayString |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:277:44:277:51 | implicit dereference : FileHeader |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:277:44:277:51 | index expression : pointer type |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:278:21:278:53 | call to SliceChunk : slice type |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:278:21:278:56 | index expression : slice type |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:278:21:278:83 | implicit dereference : FileHeader |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:278:21:278:92 | selection of Filename |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:279:21:279:60 | call to SliceDiff : slice type |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:279:21:279:87 | implicit dereference : FileHeader |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:279:21:279:96 | selection of Filename |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:284:3:286:44 | call to SliceFilter : slice type |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:284:3:286:71 | implicit dereference : FileHeader |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:284:3:286:80 | selection of Filename |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:287:21:287:65 | call to SliceIntersect : slice type |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:287:21:287:92 | implicit dereference : FileHeader |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:287:21:287:101 | selection of Filename |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:288:21:288:65 | call to SliceIntersect : slice type |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:288:21:288:92 | implicit dereference : FileHeader |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:288:21:288:101 | selection of Filename |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:289:21:289:61 | call to SliceMerge : slice type |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:289:21:289:88 | implicit dereference : FileHeader |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:289:21:289:97 | selection of Filename |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:290:21:290:61 | call to SliceMerge : slice type |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:290:21:290:88 | implicit dereference : FileHeader |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:290:21:290:97 | selection of Filename |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:291:21:291:66 | call to SlicePad : slice type |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:291:21:291:93 | implicit dereference : FileHeader |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:291:21:291:102 | selection of Filename |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:292:21:292:66 | call to SlicePad : slice type |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:292:21:292:93 | implicit dereference : FileHeader |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:292:21:292:102 | selection of Filename |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:293:21:293:73 | implicit dereference : FileHeader |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:293:21:293:82 | selection of Filename |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:295:21:295:97 | call to SliceReduce : slice type |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:295:21:295:124 | implicit dereference : FileHeader |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:295:21:295:133 | selection of Filename |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:296:21:296:52 | call to SliceShuffle : slice type |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:296:21:296:79 | implicit dereference : FileHeader |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:296:21:296:88 | selection of Filename |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:297:21:297:51 | call to SliceUnique : slice type |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:297:21:297:78 | implicit dereference : FileHeader |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:297:21:297:87 | selection of Filename |
+| test.go:277:44:277:51 | implicit dereference : FileHeader | test.go:277:21:277:61 | call to GetDisplayString |
+| test.go:277:44:277:51 | implicit dereference : FileHeader | test.go:277:44:277:51 | implicit dereference : FileHeader |
+| test.go:277:44:277:51 | implicit dereference : FileHeader | test.go:277:44:277:51 | index expression : pointer type |
+| test.go:277:44:277:51 | index expression : pointer type | test.go:277:21:277:61 | call to GetDisplayString |
+| test.go:277:44:277:51 | index expression : pointer type | test.go:277:44:277:51 | implicit dereference : FileHeader |
+| test.go:277:44:277:51 | index expression : pointer type | test.go:277:44:277:51 | index expression : pointer type |
+| test.go:278:21:278:53 | call to SliceChunk : slice type | test.go:278:21:278:56 | index expression : slice type |
+| test.go:278:21:278:53 | call to SliceChunk : slice type | test.go:278:21:278:83 | implicit dereference : FileHeader |
+| test.go:278:21:278:53 | call to SliceChunk : slice type | test.go:278:21:278:92 | selection of Filename |
+| test.go:278:21:278:56 | index expression : slice type | test.go:278:21:278:83 | implicit dereference : FileHeader |
+| test.go:278:21:278:56 | index expression : slice type | test.go:278:21:278:92 | selection of Filename |
+| test.go:278:21:278:83 | implicit dereference : FileHeader | test.go:278:21:278:92 | selection of Filename |
+| test.go:279:21:279:60 | call to SliceDiff : slice type | test.go:279:21:279:87 | implicit dereference : FileHeader |
+| test.go:279:21:279:60 | call to SliceDiff : slice type | test.go:279:21:279:96 | selection of Filename |
+| test.go:279:21:279:87 | implicit dereference : FileHeader | test.go:279:21:279:96 | selection of Filename |
+| test.go:284:3:286:44 | call to SliceFilter : slice type | test.go:284:3:286:71 | implicit dereference : FileHeader |
+| test.go:284:3:286:44 | call to SliceFilter : slice type | test.go:284:3:286:80 | selection of Filename |
+| test.go:284:3:286:71 | implicit dereference : FileHeader | test.go:284:3:286:80 | selection of Filename |
| test.go:287:21:287:65 | call to SliceIntersect : slice type | test.go:287:21:287:92 | implicit dereference : FileHeader |
| test.go:287:21:287:65 | call to SliceIntersect : slice type | test.go:287:21:287:101 | selection of Filename |
| test.go:287:21:287:92 | implicit dereference : FileHeader | test.go:287:21:287:101 | selection of Filename |
-| test.go:288:21:288:61 | call to SliceMerge : slice type | test.go:288:21:288:88 | implicit dereference : FileHeader |
-| test.go:288:21:288:61 | call to SliceMerge : slice type | test.go:288:21:288:97 | selection of Filename |
-| test.go:288:21:288:88 | implicit dereference : FileHeader | test.go:288:21:288:97 | selection of Filename |
+| test.go:288:21:288:65 | call to SliceIntersect : slice type | test.go:288:21:288:92 | implicit dereference : FileHeader |
+| test.go:288:21:288:65 | call to SliceIntersect : slice type | test.go:288:21:288:101 | selection of Filename |
+| test.go:288:21:288:92 | implicit dereference : FileHeader | test.go:288:21:288:101 | selection of Filename |
| test.go:289:21:289:61 | call to SliceMerge : slice type | test.go:289:21:289:88 | implicit dereference : FileHeader |
| test.go:289:21:289:61 | call to SliceMerge : slice type | test.go:289:21:289:97 | selection of Filename |
| test.go:289:21:289:88 | implicit dereference : FileHeader | test.go:289:21:289:97 | selection of Filename |
-| test.go:290:21:290:66 | call to SlicePad : slice type | test.go:290:21:290:93 | implicit dereference : FileHeader |
-| test.go:290:21:290:66 | call to SlicePad : slice type | test.go:290:21:290:102 | selection of Filename |
-| test.go:290:21:290:93 | implicit dereference : FileHeader | test.go:290:21:290:102 | selection of Filename |
+| test.go:290:21:290:61 | call to SliceMerge : slice type | test.go:290:21:290:88 | implicit dereference : FileHeader |
+| test.go:290:21:290:61 | call to SliceMerge : slice type | test.go:290:21:290:97 | selection of Filename |
+| test.go:290:21:290:88 | implicit dereference : FileHeader | test.go:290:21:290:97 | selection of Filename |
| test.go:291:21:291:66 | call to SlicePad : slice type | test.go:291:21:291:93 | implicit dereference : FileHeader |
| test.go:291:21:291:66 | call to SlicePad : slice type | test.go:291:21:291:102 | selection of Filename |
| test.go:291:21:291:93 | implicit dereference : FileHeader | test.go:291:21:291:102 | selection of Filename |
-| test.go:292:21:292:73 | implicit dereference : FileHeader | test.go:292:21:292:82 | selection of Filename |
-| test.go:294:21:294:97 | call to SliceReduce : slice type | test.go:294:21:294:124 | implicit dereference : FileHeader |
-| test.go:294:21:294:97 | call to SliceReduce : slice type | test.go:294:21:294:133 | selection of Filename |
-| test.go:294:21:294:124 | implicit dereference : FileHeader | test.go:294:21:294:133 | selection of Filename |
-| test.go:295:21:295:52 | call to SliceShuffle : slice type | test.go:295:21:295:79 | implicit dereference : FileHeader |
-| test.go:295:21:295:52 | call to SliceShuffle : slice type | test.go:295:21:295:88 | selection of Filename |
-| test.go:295:21:295:79 | implicit dereference : FileHeader | test.go:295:21:295:88 | selection of Filename |
-| test.go:296:21:296:51 | call to SliceUnique : slice type | test.go:296:21:296:78 | implicit dereference : FileHeader |
-| test.go:296:21:296:51 | call to SliceUnique : slice type | test.go:296:21:296:87 | selection of Filename |
-| test.go:296:21:296:78 | implicit dereference : FileHeader | test.go:296:21:296:87 | selection of Filename |
-| test.go:302:15:302:36 | call to GetString : string | test.go:304:21:304:48 | type assertion |
-| test.go:302:15:302:36 | call to GetString : string | test.go:305:21:305:32 | call to Items : map type |
-| test.go:302:15:302:36 | call to GetString : string | test.go:305:21:305:52 | type assertion |
-| test.go:305:21:305:32 | call to Items : map type | test.go:305:21:305:52 | type assertion |
+| test.go:292:21:292:66 | call to SlicePad : slice type | test.go:292:21:292:93 | implicit dereference : FileHeader |
+| test.go:292:21:292:66 | call to SlicePad : slice type | test.go:292:21:292:102 | selection of Filename |
+| test.go:292:21:292:93 | implicit dereference : FileHeader | test.go:292:21:292:102 | selection of Filename |
+| test.go:293:21:293:73 | implicit dereference : FileHeader | test.go:293:21:293:82 | selection of Filename |
+| test.go:295:21:295:97 | call to SliceReduce : slice type | test.go:295:21:295:124 | implicit dereference : FileHeader |
+| test.go:295:21:295:97 | call to SliceReduce : slice type | test.go:295:21:295:133 | selection of Filename |
+| test.go:295:21:295:124 | implicit dereference : FileHeader | test.go:295:21:295:133 | selection of Filename |
+| test.go:296:21:296:52 | call to SliceShuffle : slice type | test.go:296:21:296:79 | implicit dereference : FileHeader |
+| test.go:296:21:296:52 | call to SliceShuffle : slice type | test.go:296:21:296:88 | selection of Filename |
+| test.go:296:21:296:79 | implicit dereference : FileHeader | test.go:296:21:296:88 | selection of Filename |
+| test.go:297:21:297:51 | call to SliceUnique : slice type | test.go:297:21:297:78 | implicit dereference : FileHeader |
+| test.go:297:21:297:51 | call to SliceUnique : slice type | test.go:297:21:297:87 | selection of Filename |
+| test.go:297:21:297:78 | implicit dereference : FileHeader | test.go:297:21:297:87 | selection of Filename |
+| test.go:303:15:303:36 | call to GetString : string | test.go:305:21:305:48 | type assertion |
+| test.go:303:15:303:36 | call to GetString : string | test.go:306:21:306:32 | call to Items : map type |
+| test.go:303:15:303:36 | call to GetString : string | test.go:306:21:306:52 | type assertion |
+| test.go:306:21:306:32 | call to Items : map type | test.go:306:21:306:52 | type assertion |
nodes
-| test.go:26:6:26:10 | definition of bound : bindMe | semmle.label | definition of bound : bindMe |
-| test.go:28:13:28:30 | type conversion | semmle.label | type conversion |
-| test.go:28:20:28:26 | selection of a : slice type | semmle.label | selection of a : slice type |
-| test.go:29:13:29:27 | type conversion | semmle.label | type conversion |
-| test.go:30:13:30:29 | type conversion | semmle.label | type conversion |
-| test.go:30:20:30:26 | selection of c : subBindMe | semmle.label | selection of c : subBindMe |
-| test.go:35:13:35:43 | type conversion | semmle.label | type conversion |
-| test.go:35:20:35:42 | call to Cookie : string | semmle.label | call to Cookie : string |
-| test.go:40:13:40:52 | type conversion | semmle.label | type conversion |
-| test.go:40:20:40:31 | call to Data : map type | semmle.label | call to Data : map type |
-| test.go:45:13:45:53 | type conversion | semmle.label | type conversion |
-| test.go:45:20:45:43 | call to GetData : basic interface type | semmle.label | call to GetData : basic interface type |
-| test.go:50:13:50:43 | type conversion | semmle.label | type conversion |
-| test.go:50:20:50:42 | call to Header : string | semmle.label | call to Header : string |
-| test.go:55:13:55:42 | type conversion | semmle.label | type conversion |
-| test.go:55:20:55:41 | call to Param : string | semmle.label | call to Param : string |
-| test.go:60:13:60:45 | type conversion | semmle.label | type conversion |
-| test.go:60:20:60:33 | call to Params : map type | semmle.label | call to Params : map type |
-| test.go:65:13:65:42 | type conversion | semmle.label | type conversion |
-| test.go:65:20:65:41 | call to Query : string | semmle.label | call to Query : string |
-| test.go:70:13:70:33 | type conversion | semmle.label | type conversion |
-| test.go:70:20:70:32 | call to Refer : string | semmle.label | call to Refer : string |
-| test.go:75:13:75:35 | type conversion | semmle.label | type conversion |
-| test.go:75:20:75:34 | call to Referer : string | semmle.label | call to Referer : string |
-| test.go:80:13:80:31 | type conversion | semmle.label | type conversion |
-| test.go:80:20:80:30 | call to URI : string | semmle.label | call to URI : string |
-| test.go:85:13:85:31 | type conversion | semmle.label | type conversion |
-| test.go:85:20:85:30 | call to URL : string | semmle.label | call to URL : string |
-| test.go:90:13:90:37 | type conversion | semmle.label | type conversion |
-| test.go:90:20:90:36 | call to UserAgent : string | semmle.label | call to UserAgent : string |
-| test.go:95:14:95:25 | call to Data : map type | semmle.label | call to Data : map type |
-| test.go:95:14:95:45 | type assertion | semmle.label | type assertion |
-| test.go:107:14:107:25 | call to Data : map type | semmle.label | call to Data : map type |
-| test.go:107:14:107:45 | type assertion | semmle.label | type assertion |
-| test.go:119:14:119:25 | call to Data : map type | semmle.label | call to Data : map type |
-| test.go:119:14:119:45 | type assertion | semmle.label | type assertion |
-| test.go:136:23:136:42 | call to Data : map type | semmle.label | call to Data : map type |
-| test.go:136:23:136:62 | type assertion | semmle.label | type assertion |
-| test.go:192:15:192:26 | call to Data : map type | semmle.label | call to Data : map type |
-| test.go:193:14:193:55 | type conversion | semmle.label | type conversion |
-| test.go:194:14:194:58 | type conversion | semmle.label | type conversion |
-| test.go:196:14:196:28 | type assertion | semmle.label | type assertion |
-| test.go:197:14:197:55 | type conversion | semmle.label | type conversion |
-| test.go:198:14:198:59 | type conversion | semmle.label | type conversion |
-| test.go:201:18:201:33 | selection of Form : Values | semmle.label | selection of Form : Values |
-| test.go:202:14:202:28 | type conversion | semmle.label | type conversion |
-| test.go:216:2:216:34 | ... := ...[0] : File | semmle.label | ... := ...[0] : File |
-| test.go:216:2:216:34 | ... := ...[1] : pointer type | semmle.label | ... := ...[1] : pointer type |
-| test.go:217:14:217:32 | type conversion | semmle.label | type conversion |
-| test.go:217:21:217:22 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
-| test.go:219:14:219:20 | content | semmle.label | content |
-| test.go:221:2:221:40 | ... := ...[0] : slice type | semmle.label | ... := ...[0] : slice type |
-| test.go:222:14:222:38 | type conversion | semmle.label | type conversion |
-| test.go:222:21:222:28 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
-| test.go:222:21:222:28 | index expression : pointer type | semmle.label | index expression : pointer type |
-| test.go:224:7:224:28 | call to GetString : string | semmle.label | call to GetString : string |
-| test.go:225:14:225:22 | type conversion | semmle.label | type conversion |
-| test.go:227:8:227:35 | call to GetStrings : slice type | semmle.label | call to GetStrings : slice type |
-| test.go:228:14:228:26 | type conversion | semmle.label | type conversion |
-| test.go:230:9:230:17 | call to Input : Values | semmle.label | call to Input : Values |
-| test.go:231:14:231:27 | type conversion | semmle.label | type conversion |
-| test.go:233:6:233:8 | definition of str : myStruct | semmle.label | definition of str : myStruct |
-| test.go:235:14:235:30 | type conversion | semmle.label | type conversion |
-| test.go:239:15:239:36 | call to GetString : string | semmle.label | call to GetString : string |
-| test.go:242:21:242:29 | untrusted | semmle.label | untrusted |
-| test.go:252:16:252:45 | type conversion | semmle.label | type conversion |
-| test.go:252:23:252:44 | call to GetCookie : string | semmle.label | call to GetCookie : string |
-| test.go:257:16:257:37 | call to GetCookie | semmle.label | call to GetCookie |
-| test.go:258:15:258:41 | call to GetCookie | semmle.label | call to GetCookie |
-| test.go:263:55:263:84 | type conversion | semmle.label | type conversion |
-| test.go:263:62:263:83 | call to GetCookie : string | semmle.label | call to GetCookie : string |
-| test.go:268:2:268:40 | ... := ...[0] : slice type | semmle.label | ... := ...[0] : slice type |
-| test.go:276:21:276:61 | call to GetDisplayString | semmle.label | call to GetDisplayString |
-| test.go:276:44:276:51 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
-| test.go:276:44:276:51 | index expression : pointer type | semmle.label | index expression : pointer type |
-| test.go:277:21:277:53 | call to SliceChunk : slice type | semmle.label | call to SliceChunk : slice type |
-| test.go:277:21:277:56 | index expression : slice type | semmle.label | index expression : slice type |
-| test.go:277:21:277:83 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
-| test.go:277:21:277:92 | selection of Filename | semmle.label | selection of Filename |
-| test.go:278:21:278:60 | call to SliceDiff : slice type | semmle.label | call to SliceDiff : slice type |
-| test.go:278:21:278:87 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
-| test.go:278:21:278:96 | selection of Filename | semmle.label | selection of Filename |
-| test.go:283:3:285:44 | call to SliceFilter : slice type | semmle.label | call to SliceFilter : slice type |
-| test.go:283:3:285:71 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
-| test.go:283:3:285:80 | selection of Filename | semmle.label | selection of Filename |
-| test.go:286:21:286:65 | call to SliceIntersect : slice type | semmle.label | call to SliceIntersect : slice type |
-| test.go:286:21:286:92 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
-| test.go:286:21:286:101 | selection of Filename | semmle.label | selection of Filename |
+| test.go:27:6:27:10 | definition of bound : bindMe | semmle.label | definition of bound : bindMe |
+| test.go:29:13:29:30 | type conversion | semmle.label | type conversion |
+| test.go:29:20:29:26 | selection of a : slice type | semmle.label | selection of a : slice type |
+| test.go:30:13:30:27 | type conversion | semmle.label | type conversion |
+| test.go:31:13:31:29 | type conversion | semmle.label | type conversion |
+| test.go:31:20:31:26 | selection of c : subBindMe | semmle.label | selection of c : subBindMe |
+| test.go:36:13:36:43 | type conversion | semmle.label | type conversion |
+| test.go:36:20:36:42 | call to Cookie : string | semmle.label | call to Cookie : string |
+| test.go:41:13:41:52 | type conversion | semmle.label | type conversion |
+| test.go:41:20:41:31 | call to Data : map type | semmle.label | call to Data : map type |
+| test.go:46:13:46:53 | type conversion | semmle.label | type conversion |
+| test.go:46:20:46:43 | call to GetData : basic interface type | semmle.label | call to GetData : basic interface type |
+| test.go:51:13:51:43 | type conversion | semmle.label | type conversion |
+| test.go:51:20:51:42 | call to Header : string | semmle.label | call to Header : string |
+| test.go:56:13:56:42 | type conversion | semmle.label | type conversion |
+| test.go:56:20:56:41 | call to Param : string | semmle.label | call to Param : string |
+| test.go:61:13:61:45 | type conversion | semmle.label | type conversion |
+| test.go:61:20:61:33 | call to Params : map type | semmle.label | call to Params : map type |
+| test.go:66:13:66:42 | type conversion | semmle.label | type conversion |
+| test.go:66:20:66:41 | call to Query : string | semmle.label | call to Query : string |
+| test.go:71:13:71:33 | type conversion | semmle.label | type conversion |
+| test.go:71:20:71:32 | call to Refer : string | semmle.label | call to Refer : string |
+| test.go:76:13:76:35 | type conversion | semmle.label | type conversion |
+| test.go:76:20:76:34 | call to Referer : string | semmle.label | call to Referer : string |
+| test.go:81:13:81:31 | type conversion | semmle.label | type conversion |
+| test.go:81:20:81:30 | call to URI : string | semmle.label | call to URI : string |
+| test.go:86:13:86:31 | type conversion | semmle.label | type conversion |
+| test.go:86:20:86:30 | call to URL : string | semmle.label | call to URL : string |
+| test.go:91:13:91:37 | type conversion | semmle.label | type conversion |
+| test.go:91:20:91:36 | call to UserAgent : string | semmle.label | call to UserAgent : string |
+| test.go:96:14:96:25 | call to Data : map type | semmle.label | call to Data : map type |
+| test.go:96:14:96:45 | type assertion | semmle.label | type assertion |
+| test.go:108:14:108:25 | call to Data : map type | semmle.label | call to Data : map type |
+| test.go:108:14:108:45 | type assertion | semmle.label | type assertion |
+| test.go:120:14:120:25 | call to Data : map type | semmle.label | call to Data : map type |
+| test.go:120:14:120:45 | type assertion | semmle.label | type assertion |
+| test.go:137:23:137:42 | call to Data : map type | semmle.label | call to Data : map type |
+| test.go:137:23:137:62 | type assertion | semmle.label | type assertion |
+| test.go:193:15:193:26 | call to Data : map type | semmle.label | call to Data : map type |
+| test.go:194:14:194:55 | type conversion | semmle.label | type conversion |
+| test.go:195:14:195:58 | type conversion | semmle.label | type conversion |
+| test.go:197:14:197:28 | type assertion | semmle.label | type assertion |
+| test.go:198:14:198:55 | type conversion | semmle.label | type conversion |
+| test.go:199:14:199:59 | type conversion | semmle.label | type conversion |
+| test.go:202:18:202:33 | selection of Form : Values | semmle.label | selection of Form : Values |
+| test.go:203:14:203:28 | type conversion | semmle.label | type conversion |
+| test.go:217:2:217:34 | ... := ...[0] : File | semmle.label | ... := ...[0] : File |
+| test.go:217:2:217:34 | ... := ...[1] : pointer type | semmle.label | ... := ...[1] : pointer type |
+| test.go:218:14:218:32 | type conversion | semmle.label | type conversion |
+| test.go:218:21:218:22 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
+| test.go:220:14:220:20 | content | semmle.label | content |
+| test.go:222:2:222:40 | ... := ...[0] : slice type | semmle.label | ... := ...[0] : slice type |
+| test.go:223:14:223:38 | type conversion | semmle.label | type conversion |
+| test.go:223:21:223:28 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
+| test.go:223:21:223:28 | index expression : pointer type | semmle.label | index expression : pointer type |
+| test.go:225:7:225:28 | call to GetString : string | semmle.label | call to GetString : string |
+| test.go:226:14:226:22 | type conversion | semmle.label | type conversion |
+| test.go:228:8:228:35 | call to GetStrings : slice type | semmle.label | call to GetStrings : slice type |
+| test.go:229:14:229:26 | type conversion | semmle.label | type conversion |
+| test.go:231:9:231:17 | call to Input : Values | semmle.label | call to Input : Values |
+| test.go:232:14:232:27 | type conversion | semmle.label | type conversion |
+| test.go:234:6:234:8 | definition of str : myStruct | semmle.label | definition of str : myStruct |
+| test.go:236:14:236:30 | type conversion | semmle.label | type conversion |
+| test.go:240:15:240:36 | call to GetString : string | semmle.label | call to GetString : string |
+| test.go:243:21:243:29 | untrusted | semmle.label | untrusted |
+| test.go:253:16:253:45 | type conversion | semmle.label | type conversion |
+| test.go:253:23:253:44 | call to GetCookie : string | semmle.label | call to GetCookie : string |
+| test.go:258:16:258:37 | call to GetCookie | semmle.label | call to GetCookie |
+| test.go:259:15:259:41 | call to GetCookie | semmle.label | call to GetCookie |
+| test.go:264:55:264:84 | type conversion | semmle.label | type conversion |
+| test.go:264:62:264:83 | call to GetCookie : string | semmle.label | call to GetCookie : string |
+| test.go:269:2:269:40 | ... := ...[0] : slice type | semmle.label | ... := ...[0] : slice type |
+| test.go:277:21:277:61 | call to GetDisplayString | semmle.label | call to GetDisplayString |
+| test.go:277:44:277:51 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
+| test.go:277:44:277:51 | index expression : pointer type | semmle.label | index expression : pointer type |
+| test.go:278:21:278:53 | call to SliceChunk : slice type | semmle.label | call to SliceChunk : slice type |
+| test.go:278:21:278:56 | index expression : slice type | semmle.label | index expression : slice type |
+| test.go:278:21:278:83 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
+| test.go:278:21:278:92 | selection of Filename | semmle.label | selection of Filename |
+| test.go:279:21:279:60 | call to SliceDiff : slice type | semmle.label | call to SliceDiff : slice type |
+| test.go:279:21:279:87 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
+| test.go:279:21:279:96 | selection of Filename | semmle.label | selection of Filename |
+| test.go:284:3:286:44 | call to SliceFilter : slice type | semmle.label | call to SliceFilter : slice type |
+| test.go:284:3:286:71 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
+| test.go:284:3:286:80 | selection of Filename | semmle.label | selection of Filename |
| test.go:287:21:287:65 | call to SliceIntersect : slice type | semmle.label | call to SliceIntersect : slice type |
| test.go:287:21:287:92 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
| test.go:287:21:287:101 | selection of Filename | semmle.label | selection of Filename |
-| test.go:288:21:288:61 | call to SliceMerge : slice type | semmle.label | call to SliceMerge : slice type |
-| test.go:288:21:288:88 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
-| test.go:288:21:288:97 | selection of Filename | semmle.label | selection of Filename |
+| test.go:288:21:288:65 | call to SliceIntersect : slice type | semmle.label | call to SliceIntersect : slice type |
+| test.go:288:21:288:92 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
+| test.go:288:21:288:101 | selection of Filename | semmle.label | selection of Filename |
| test.go:289:21:289:61 | call to SliceMerge : slice type | semmle.label | call to SliceMerge : slice type |
| test.go:289:21:289:88 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
| test.go:289:21:289:97 | selection of Filename | semmle.label | selection of Filename |
-| test.go:290:21:290:66 | call to SlicePad : slice type | semmle.label | call to SlicePad : slice type |
-| test.go:290:21:290:93 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
-| test.go:290:21:290:102 | selection of Filename | semmle.label | selection of Filename |
+| test.go:290:21:290:61 | call to SliceMerge : slice type | semmle.label | call to SliceMerge : slice type |
+| test.go:290:21:290:88 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
+| test.go:290:21:290:97 | selection of Filename | semmle.label | selection of Filename |
| test.go:291:21:291:66 | call to SlicePad : slice type | semmle.label | call to SlicePad : slice type |
| test.go:291:21:291:93 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
| test.go:291:21:291:102 | selection of Filename | semmle.label | selection of Filename |
-| test.go:292:21:292:73 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
-| test.go:292:21:292:82 | selection of Filename | semmle.label | selection of Filename |
-| test.go:294:21:294:97 | call to SliceReduce : slice type | semmle.label | call to SliceReduce : slice type |
-| test.go:294:21:294:124 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
-| test.go:294:21:294:133 | selection of Filename | semmle.label | selection of Filename |
-| test.go:295:21:295:52 | call to SliceShuffle : slice type | semmle.label | call to SliceShuffle : slice type |
-| test.go:295:21:295:79 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
-| test.go:295:21:295:88 | selection of Filename | semmle.label | selection of Filename |
-| test.go:296:21:296:51 | call to SliceUnique : slice type | semmle.label | call to SliceUnique : slice type |
-| test.go:296:21:296:78 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
-| test.go:296:21:296:87 | selection of Filename | semmle.label | selection of Filename |
-| test.go:302:15:302:36 | call to GetString : string | semmle.label | call to GetString : string |
-| test.go:304:21:304:48 | type assertion | semmle.label | type assertion |
-| test.go:305:21:305:32 | call to Items : map type | semmle.label | call to Items : map type |
-| test.go:305:21:305:52 | type assertion | semmle.label | type assertion |
+| test.go:292:21:292:66 | call to SlicePad : slice type | semmle.label | call to SlicePad : slice type |
+| test.go:292:21:292:93 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
+| test.go:292:21:292:102 | selection of Filename | semmle.label | selection of Filename |
+| test.go:293:21:293:73 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
+| test.go:293:21:293:82 | selection of Filename | semmle.label | selection of Filename |
+| test.go:295:21:295:97 | call to SliceReduce : slice type | semmle.label | call to SliceReduce : slice type |
+| test.go:295:21:295:124 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
+| test.go:295:21:295:133 | selection of Filename | semmle.label | selection of Filename |
+| test.go:296:21:296:52 | call to SliceShuffle : slice type | semmle.label | call to SliceShuffle : slice type |
+| test.go:296:21:296:79 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
+| test.go:296:21:296:88 | selection of Filename | semmle.label | selection of Filename |
+| test.go:297:21:297:51 | call to SliceUnique : slice type | semmle.label | call to SliceUnique : slice type |
+| test.go:297:21:297:78 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
+| test.go:297:21:297:87 | selection of Filename | semmle.label | selection of Filename |
+| test.go:303:15:303:36 | call to GetString : string | semmle.label | call to GetString : string |
+| test.go:305:21:305:48 | type assertion | semmle.label | type assertion |
+| test.go:306:21:306:32 | call to Items : map type | semmle.label | call to Items : map type |
+| test.go:306:21:306:52 | type assertion | semmle.label | type assertion |
subpaths
#select
-| test.go:28:13:28:30 | type conversion | test.go:26:6:26:10 | definition of bound : bindMe | test.go:28:13:28:30 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:26:6:26:10 | definition of bound | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:29:13:29:27 | type conversion | test.go:26:6:26:10 | definition of bound : bindMe | test.go:29:13:29:27 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:26:6:26:10 | definition of bound | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:30:13:30:29 | type conversion | test.go:26:6:26:10 | definition of bound : bindMe | test.go:30:13:30:29 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:26:6:26:10 | definition of bound | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:35:13:35:43 | type conversion | test.go:35:20:35:42 | call to Cookie : string | test.go:35:13:35:43 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:35:20:35:42 | call to Cookie | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:40:13:40:52 | type conversion | test.go:40:20:40:31 | call to Data : map type | test.go:40:13:40:52 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:40:20:40:31 | call to Data | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:45:13:45:53 | type conversion | test.go:45:20:45:43 | call to GetData : basic interface type | test.go:45:13:45:53 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:45:20:45:43 | call to GetData | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:50:13:50:43 | type conversion | test.go:50:20:50:42 | call to Header : string | test.go:50:13:50:43 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:50:20:50:42 | call to Header | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:55:13:55:42 | type conversion | test.go:55:20:55:41 | call to Param : string | test.go:55:13:55:42 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:55:20:55:41 | call to Param | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:60:13:60:45 | type conversion | test.go:60:20:60:33 | call to Params : map type | test.go:60:13:60:45 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:60:20:60:33 | call to Params | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:65:13:65:42 | type conversion | test.go:65:20:65:41 | call to Query : string | test.go:65:13:65:42 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:65:20:65:41 | call to Query | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:70:13:70:33 | type conversion | test.go:70:20:70:32 | call to Refer : string | test.go:70:13:70:33 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:70:20:70:32 | call to Refer | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:75:13:75:35 | type conversion | test.go:75:20:75:34 | call to Referer : string | test.go:75:13:75:35 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:75:20:75:34 | call to Referer | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:80:13:80:31 | type conversion | test.go:80:20:80:30 | call to URI : string | test.go:80:13:80:31 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:80:20:80:30 | call to URI | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:85:13:85:31 | type conversion | test.go:85:20:85:30 | call to URL : string | test.go:85:13:85:31 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:85:20:85:30 | call to URL | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:90:13:90:37 | type conversion | test.go:90:20:90:36 | call to UserAgent : string | test.go:90:13:90:37 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:90:20:90:36 | call to UserAgent | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:95:14:95:45 | type assertion | test.go:95:14:95:25 | call to Data : map type | test.go:95:14:95:45 | type assertion | Cross-site scripting vulnerability due to $@. | test.go:95:14:95:25 | call to Data | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:107:14:107:45 | type assertion | test.go:107:14:107:25 | call to Data : map type | test.go:107:14:107:45 | type assertion | Cross-site scripting vulnerability due to $@. | test.go:107:14:107:25 | call to Data | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:119:14:119:45 | type assertion | test.go:119:14:119:25 | call to Data : map type | test.go:119:14:119:45 | type assertion | Cross-site scripting vulnerability due to $@. | test.go:119:14:119:25 | call to Data | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:136:23:136:62 | type assertion | test.go:136:23:136:42 | call to Data : map type | test.go:136:23:136:62 | type assertion | Cross-site scripting vulnerability due to $@. | test.go:136:23:136:42 | call to Data | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:193:14:193:55 | type conversion | test.go:192:15:192:26 | call to Data : map type | test.go:193:14:193:55 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:192:15:192:26 | call to Data | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:194:14:194:58 | type conversion | test.go:192:15:192:26 | call to Data : map type | test.go:194:14:194:58 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:192:15:192:26 | call to Data | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:196:14:196:28 | type assertion | test.go:192:15:192:26 | call to Data : map type | test.go:196:14:196:28 | type assertion | Cross-site scripting vulnerability due to $@. | test.go:192:15:192:26 | call to Data | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:197:14:197:55 | type conversion | test.go:192:15:192:26 | call to Data : map type | test.go:197:14:197:55 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:192:15:192:26 | call to Data | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:198:14:198:59 | type conversion | test.go:192:15:192:26 | call to Data : map type | test.go:198:14:198:59 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:192:15:192:26 | call to Data | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:202:14:202:28 | type conversion | test.go:201:18:201:33 | selection of Form : Values | test.go:202:14:202:28 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:201:18:201:33 | selection of Form | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:217:14:217:32 | type conversion | test.go:216:2:216:34 | ... := ...[1] : pointer type | test.go:217:14:217:32 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:216:2:216:34 | ... := ...[1] | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:219:14:219:20 | content | test.go:216:2:216:34 | ... := ...[0] : File | test.go:219:14:219:20 | content | Cross-site scripting vulnerability due to $@. | test.go:216:2:216:34 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:222:14:222:38 | type conversion | test.go:221:2:221:40 | ... := ...[0] : slice type | test.go:222:14:222:38 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:221:2:221:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:225:14:225:22 | type conversion | test.go:224:7:224:28 | call to GetString : string | test.go:225:14:225:22 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:224:7:224:28 | call to GetString | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:228:14:228:26 | type conversion | test.go:227:8:227:35 | call to GetStrings : slice type | test.go:228:14:228:26 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:227:8:227:35 | call to GetStrings | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:231:14:231:27 | type conversion | test.go:230:9:230:17 | call to Input : Values | test.go:231:14:231:27 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:230:9:230:17 | call to Input | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:235:14:235:30 | type conversion | test.go:233:6:233:8 | definition of str : myStruct | test.go:235:14:235:30 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:233:6:233:8 | definition of str | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:242:21:242:29 | untrusted | test.go:239:15:239:36 | call to GetString : string | test.go:242:21:242:29 | untrusted | Cross-site scripting vulnerability due to $@. | test.go:239:15:239:36 | call to GetString | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:252:16:252:45 | type conversion | test.go:252:23:252:44 | call to GetCookie : string | test.go:252:16:252:45 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:252:23:252:44 | call to GetCookie | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:257:16:257:37 | call to GetCookie | test.go:257:16:257:37 | call to GetCookie | test.go:257:16:257:37 | call to GetCookie | Cross-site scripting vulnerability due to $@. | test.go:257:16:257:37 | call to GetCookie | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:258:15:258:41 | call to GetCookie | test.go:258:15:258:41 | call to GetCookie | test.go:258:15:258:41 | call to GetCookie | Cross-site scripting vulnerability due to $@. | test.go:258:15:258:41 | call to GetCookie | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:263:55:263:84 | type conversion | test.go:263:62:263:83 | call to GetCookie : string | test.go:263:55:263:84 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:263:62:263:83 | call to GetCookie | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:276:21:276:61 | call to GetDisplayString | test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:276:21:276:61 | call to GetDisplayString | Cross-site scripting vulnerability due to $@. | test.go:268:2:268:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:277:21:277:92 | selection of Filename | test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:277:21:277:92 | selection of Filename | Cross-site scripting vulnerability due to $@. | test.go:268:2:268:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:278:21:278:96 | selection of Filename | test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:278:21:278:96 | selection of Filename | Cross-site scripting vulnerability due to $@. | test.go:268:2:268:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:283:3:285:80 | selection of Filename | test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:283:3:285:80 | selection of Filename | Cross-site scripting vulnerability due to $@. | test.go:268:2:268:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:286:21:286:101 | selection of Filename | test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:286:21:286:101 | selection of Filename | Cross-site scripting vulnerability due to $@. | test.go:268:2:268:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:287:21:287:101 | selection of Filename | test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:287:21:287:101 | selection of Filename | Cross-site scripting vulnerability due to $@. | test.go:268:2:268:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:288:21:288:97 | selection of Filename | test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:288:21:288:97 | selection of Filename | Cross-site scripting vulnerability due to $@. | test.go:268:2:268:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:289:21:289:97 | selection of Filename | test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:289:21:289:97 | selection of Filename | Cross-site scripting vulnerability due to $@. | test.go:268:2:268:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:290:21:290:102 | selection of Filename | test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:290:21:290:102 | selection of Filename | Cross-site scripting vulnerability due to $@. | test.go:268:2:268:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:291:21:291:102 | selection of Filename | test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:291:21:291:102 | selection of Filename | Cross-site scripting vulnerability due to $@. | test.go:268:2:268:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:292:21:292:82 | selection of Filename | test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:292:21:292:82 | selection of Filename | Cross-site scripting vulnerability due to $@. | test.go:268:2:268:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:294:21:294:133 | selection of Filename | test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:294:21:294:133 | selection of Filename | Cross-site scripting vulnerability due to $@. | test.go:268:2:268:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:295:21:295:88 | selection of Filename | test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:295:21:295:88 | selection of Filename | Cross-site scripting vulnerability due to $@. | test.go:268:2:268:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:296:21:296:87 | selection of Filename | test.go:268:2:268:40 | ... := ...[0] : slice type | test.go:296:21:296:87 | selection of Filename | Cross-site scripting vulnerability due to $@. | test.go:268:2:268:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:304:21:304:48 | type assertion | test.go:302:15:302:36 | call to GetString : string | test.go:304:21:304:48 | type assertion | Cross-site scripting vulnerability due to $@. | test.go:302:15:302:36 | call to GetString | user-provided value | test.go:0:0:0:0 | test.go | |
-| test.go:305:21:305:52 | type assertion | test.go:302:15:302:36 | call to GetString : string | test.go:305:21:305:52 | type assertion | Cross-site scripting vulnerability due to $@. | test.go:302:15:302:36 | call to GetString | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:29:13:29:30 | type conversion | test.go:27:6:27:10 | definition of bound : bindMe | test.go:29:13:29:30 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:27:6:27:10 | definition of bound | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:30:13:30:27 | type conversion | test.go:27:6:27:10 | definition of bound : bindMe | test.go:30:13:30:27 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:27:6:27:10 | definition of bound | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:31:13:31:29 | type conversion | test.go:27:6:27:10 | definition of bound : bindMe | test.go:31:13:31:29 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:27:6:27:10 | definition of bound | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:36:13:36:43 | type conversion | test.go:36:20:36:42 | call to Cookie : string | test.go:36:13:36:43 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:36:20:36:42 | call to Cookie | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:41:13:41:52 | type conversion | test.go:41:20:41:31 | call to Data : map type | test.go:41:13:41:52 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:41:20:41:31 | call to Data | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:46:13:46:53 | type conversion | test.go:46:20:46:43 | call to GetData : basic interface type | test.go:46:13:46:53 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:46:20:46:43 | call to GetData | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:51:13:51:43 | type conversion | test.go:51:20:51:42 | call to Header : string | test.go:51:13:51:43 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:51:20:51:42 | call to Header | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:56:13:56:42 | type conversion | test.go:56:20:56:41 | call to Param : string | test.go:56:13:56:42 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:56:20:56:41 | call to Param | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:61:13:61:45 | type conversion | test.go:61:20:61:33 | call to Params : map type | test.go:61:13:61:45 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:61:20:61:33 | call to Params | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:66:13:66:42 | type conversion | test.go:66:20:66:41 | call to Query : string | test.go:66:13:66:42 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:66:20:66:41 | call to Query | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:71:13:71:33 | type conversion | test.go:71:20:71:32 | call to Refer : string | test.go:71:13:71:33 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:71:20:71:32 | call to Refer | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:76:13:76:35 | type conversion | test.go:76:20:76:34 | call to Referer : string | test.go:76:13:76:35 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:76:20:76:34 | call to Referer | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:81:13:81:31 | type conversion | test.go:81:20:81:30 | call to URI : string | test.go:81:13:81:31 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:81:20:81:30 | call to URI | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:86:13:86:31 | type conversion | test.go:86:20:86:30 | call to URL : string | test.go:86:13:86:31 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:86:20:86:30 | call to URL | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:91:13:91:37 | type conversion | test.go:91:20:91:36 | call to UserAgent : string | test.go:91:13:91:37 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:91:20:91:36 | call to UserAgent | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:96:14:96:45 | type assertion | test.go:96:14:96:25 | call to Data : map type | test.go:96:14:96:45 | type assertion | Cross-site scripting vulnerability due to $@. | test.go:96:14:96:25 | call to Data | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:108:14:108:45 | type assertion | test.go:108:14:108:25 | call to Data : map type | test.go:108:14:108:45 | type assertion | Cross-site scripting vulnerability due to $@. | test.go:108:14:108:25 | call to Data | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:120:14:120:45 | type assertion | test.go:120:14:120:25 | call to Data : map type | test.go:120:14:120:45 | type assertion | Cross-site scripting vulnerability due to $@. | test.go:120:14:120:25 | call to Data | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:137:23:137:62 | type assertion | test.go:137:23:137:42 | call to Data : map type | test.go:137:23:137:62 | type assertion | Cross-site scripting vulnerability due to $@. | test.go:137:23:137:42 | call to Data | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:194:14:194:55 | type conversion | test.go:193:15:193:26 | call to Data : map type | test.go:194:14:194:55 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:193:15:193:26 | call to Data | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:195:14:195:58 | type conversion | test.go:193:15:193:26 | call to Data : map type | test.go:195:14:195:58 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:193:15:193:26 | call to Data | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:197:14:197:28 | type assertion | test.go:193:15:193:26 | call to Data : map type | test.go:197:14:197:28 | type assertion | Cross-site scripting vulnerability due to $@. | test.go:193:15:193:26 | call to Data | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:198:14:198:55 | type conversion | test.go:193:15:193:26 | call to Data : map type | test.go:198:14:198:55 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:193:15:193:26 | call to Data | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:199:14:199:59 | type conversion | test.go:193:15:193:26 | call to Data : map type | test.go:199:14:199:59 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:193:15:193:26 | call to Data | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:203:14:203:28 | type conversion | test.go:202:18:202:33 | selection of Form : Values | test.go:203:14:203:28 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:202:18:202:33 | selection of Form | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:218:14:218:32 | type conversion | test.go:217:2:217:34 | ... := ...[1] : pointer type | test.go:218:14:218:32 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:217:2:217:34 | ... := ...[1] | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:220:14:220:20 | content | test.go:217:2:217:34 | ... := ...[0] : File | test.go:220:14:220:20 | content | Cross-site scripting vulnerability due to $@. | test.go:217:2:217:34 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:223:14:223:38 | type conversion | test.go:222:2:222:40 | ... := ...[0] : slice type | test.go:223:14:223:38 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:222:2:222:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:226:14:226:22 | type conversion | test.go:225:7:225:28 | call to GetString : string | test.go:226:14:226:22 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:225:7:225:28 | call to GetString | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:229:14:229:26 | type conversion | test.go:228:8:228:35 | call to GetStrings : slice type | test.go:229:14:229:26 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:228:8:228:35 | call to GetStrings | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:232:14:232:27 | type conversion | test.go:231:9:231:17 | call to Input : Values | test.go:232:14:232:27 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:231:9:231:17 | call to Input | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:236:14:236:30 | type conversion | test.go:234:6:234:8 | definition of str : myStruct | test.go:236:14:236:30 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:234:6:234:8 | definition of str | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:243:21:243:29 | untrusted | test.go:240:15:240:36 | call to GetString : string | test.go:243:21:243:29 | untrusted | Cross-site scripting vulnerability due to $@. | test.go:240:15:240:36 | call to GetString | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:253:16:253:45 | type conversion | test.go:253:23:253:44 | call to GetCookie : string | test.go:253:16:253:45 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:253:23:253:44 | call to GetCookie | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:258:16:258:37 | call to GetCookie | test.go:258:16:258:37 | call to GetCookie | test.go:258:16:258:37 | call to GetCookie | Cross-site scripting vulnerability due to $@. | test.go:258:16:258:37 | call to GetCookie | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:259:15:259:41 | call to GetCookie | test.go:259:15:259:41 | call to GetCookie | test.go:259:15:259:41 | call to GetCookie | Cross-site scripting vulnerability due to $@. | test.go:259:15:259:41 | call to GetCookie | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:264:55:264:84 | type conversion | test.go:264:62:264:83 | call to GetCookie : string | test.go:264:55:264:84 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:264:62:264:83 | call to GetCookie | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:277:21:277:61 | call to GetDisplayString | test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:277:21:277:61 | call to GetDisplayString | Cross-site scripting vulnerability due to $@. | test.go:269:2:269:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:278:21:278:92 | selection of Filename | test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:278:21:278:92 | selection of Filename | Cross-site scripting vulnerability due to $@. | test.go:269:2:269:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:279:21:279:96 | selection of Filename | test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:279:21:279:96 | selection of Filename | Cross-site scripting vulnerability due to $@. | test.go:269:2:269:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:284:3:286:80 | selection of Filename | test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:284:3:286:80 | selection of Filename | Cross-site scripting vulnerability due to $@. | test.go:269:2:269:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:287:21:287:101 | selection of Filename | test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:287:21:287:101 | selection of Filename | Cross-site scripting vulnerability due to $@. | test.go:269:2:269:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:288:21:288:101 | selection of Filename | test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:288:21:288:101 | selection of Filename | Cross-site scripting vulnerability due to $@. | test.go:269:2:269:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:289:21:289:97 | selection of Filename | test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:289:21:289:97 | selection of Filename | Cross-site scripting vulnerability due to $@. | test.go:269:2:269:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:290:21:290:97 | selection of Filename | test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:290:21:290:97 | selection of Filename | Cross-site scripting vulnerability due to $@. | test.go:269:2:269:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:291:21:291:102 | selection of Filename | test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:291:21:291:102 | selection of Filename | Cross-site scripting vulnerability due to $@. | test.go:269:2:269:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:292:21:292:102 | selection of Filename | test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:292:21:292:102 | selection of Filename | Cross-site scripting vulnerability due to $@. | test.go:269:2:269:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:293:21:293:82 | selection of Filename | test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:293:21:293:82 | selection of Filename | Cross-site scripting vulnerability due to $@. | test.go:269:2:269:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:295:21:295:133 | selection of Filename | test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:295:21:295:133 | selection of Filename | Cross-site scripting vulnerability due to $@. | test.go:269:2:269:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:296:21:296:88 | selection of Filename | test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:296:21:296:88 | selection of Filename | Cross-site scripting vulnerability due to $@. | test.go:269:2:269:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:297:21:297:87 | selection of Filename | test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:297:21:297:87 | selection of Filename | Cross-site scripting vulnerability due to $@. | test.go:269:2:269:40 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:305:21:305:48 | type assertion | test.go:303:15:303:36 | call to GetString : string | test.go:305:21:305:48 | type assertion | Cross-site scripting vulnerability due to $@. | test.go:303:15:303:36 | call to GetString | user-provided value | test.go:0:0:0:0 | test.go | |
+| test.go:306:21:306:52 | type assertion | test.go:303:15:303:36 | call to GetString : string | test.go:306:21:306:52 | type assertion | Cross-site scripting vulnerability due to $@. | test.go:303:15:303:36 | call to GetString | user-provided value | test.go:0:0:0:0 | test.go | |
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/Beego/TaintedPath.expected b/go/ql/test/library-tests/semmle/go/frameworks/Beego/TaintedPath.expected
index 76fec9f68d4..116d5d44a6d 100644
--- a/go/ql/test/library-tests/semmle/go/frameworks/Beego/TaintedPath.expected
+++ b/go/ql/test/library-tests/semmle/go/frameworks/Beego/TaintedPath.expected
@@ -1,14 +1,18 @@
edges
-| test.go:208:15:208:26 | call to Data : map type | test.go:209:18:209:26 | untrusted |
-| test.go:208:15:208:26 | call to Data : map type | test.go:210:10:210:18 | untrusted |
-| test.go:208:15:208:26 | call to Data : map type | test.go:211:35:211:43 | untrusted |
+| test.go:209:15:209:26 | call to Data : map type | test.go:210:18:210:26 | untrusted |
+| test.go:209:15:209:26 | call to Data : map type | test.go:211:10:211:18 | untrusted |
+| test.go:209:15:209:26 | call to Data : map type | test.go:212:35:212:43 | untrusted |
+| test.go:318:17:318:37 | selection of RequestBody : slice type | test.go:320:35:320:43 | untrusted |
nodes
-| test.go:208:15:208:26 | call to Data : map type | semmle.label | call to Data : map type |
-| test.go:209:18:209:26 | untrusted | semmle.label | untrusted |
-| test.go:210:10:210:18 | untrusted | semmle.label | untrusted |
-| test.go:211:35:211:43 | untrusted | semmle.label | untrusted |
+| test.go:209:15:209:26 | call to Data : map type | semmle.label | call to Data : map type |
+| test.go:210:18:210:26 | untrusted | semmle.label | untrusted |
+| test.go:211:10:211:18 | untrusted | semmle.label | untrusted |
+| test.go:212:35:212:43 | untrusted | semmle.label | untrusted |
+| test.go:318:17:318:37 | selection of RequestBody : slice type | semmle.label | selection of RequestBody : slice type |
+| test.go:320:35:320:43 | untrusted | semmle.label | untrusted |
subpaths
#select
-| test.go:209:18:209:26 | untrusted | test.go:208:15:208:26 | call to Data : map type | test.go:209:18:209:26 | untrusted | This path depends on a $@. | test.go:208:15:208:26 | call to Data | user-provided value |
-| test.go:210:10:210:18 | untrusted | test.go:208:15:208:26 | call to Data : map type | test.go:210:10:210:18 | untrusted | This path depends on a $@. | test.go:208:15:208:26 | call to Data | user-provided value |
-| test.go:211:35:211:43 | untrusted | test.go:208:15:208:26 | call to Data : map type | test.go:211:35:211:43 | untrusted | This path depends on a $@. | test.go:208:15:208:26 | call to Data | user-provided value |
+| test.go:210:18:210:26 | untrusted | test.go:209:15:209:26 | call to Data : map type | test.go:210:18:210:26 | untrusted | This path depends on a $@. | test.go:209:15:209:26 | call to Data | user-provided value |
+| test.go:211:10:211:18 | untrusted | test.go:209:15:209:26 | call to Data : map type | test.go:211:10:211:18 | untrusted | This path depends on a $@. | test.go:209:15:209:26 | call to Data | user-provided value |
+| test.go:212:35:212:43 | untrusted | test.go:209:15:209:26 | call to Data : map type | test.go:212:35:212:43 | untrusted | This path depends on a $@. | test.go:209:15:209:26 | call to Data | user-provided value |
+| test.go:320:35:320:43 | untrusted | test.go:318:17:318:37 | selection of RequestBody : slice type | test.go:320:35:320:43 | untrusted | This path depends on a $@. | test.go:318:17:318:37 | selection of RequestBody | user-provided value |
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/Beego/test.go b/go/ql/test/library-tests/semmle/go/frameworks/Beego/test.go
index b5953d8e768..4a2f8101286 100644
--- a/go/ql/test/library-tests/semmle/go/frameworks/Beego/test.go
+++ b/go/ql/test/library-tests/semmle/go/frameworks/Beego/test.go
@@ -9,6 +9,7 @@ import (
"mime/multipart"
"net/http"
"os"
+ "encoding/json"
)
type subBindMe struct {
@@ -310,3 +311,11 @@ func testSafeRedirects(c *beego.Controller, ctx *context.Context) {
c.Redirect(ctx.Input.URI(), 304)
ctx.Redirect(304, ctx.Input.URL())
}
+
+// BAD: using RequestBody data as path in a file-system operation
+func requestBodySourceTest(ctx *context.Context, c *beego.Controller) {
+ var dat map[string]interface{}
+ json.Unmarshal(ctx.Input.RequestBody, &dat)
+ untrusted := dat["filepath"].(string)
+ c.SaveToFile("someReceviedFile", untrusted)
+}
\ No newline at end of file
From 169965cfb9318dde36cc6ead8c637882a09eb948 Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Thu, 6 Oct 2022 13:28:10 +0200
Subject: [PATCH 409/991] make rb/meta/taint-steps into a @kind problem query
---
ruby/ql/src/queries/meta/TaintSteps.ql | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/ruby/ql/src/queries/meta/TaintSteps.ql b/ruby/ql/src/queries/meta/TaintSteps.ql
index c93322500c3..dfcf2dc9e77 100644
--- a/ruby/ql/src/queries/meta/TaintSteps.ql
+++ b/ruby/ql/src/queries/meta/TaintSteps.ql
@@ -1,11 +1,11 @@
/**
* @name Taint steps
- * @description The number of default taint steps.
- * @kind metric
- * @metricType project
- * @metricAggregate sum
- * @tags meta
+ * @description All taint steps.
+ * @kind problem
+ * @problem.severity recommendation
* @id rb/meta/taint-steps
+ * @tags meta
+ * @precision very-low
*/
import ruby
@@ -14,4 +14,8 @@ import codeql.ruby.dataflow.internal.TaintTrackingPublic
predicate relevantStep(DataFlow::Node pred, DataFlow::Node succ) { localTaintStep(pred, succ) }
-select projectRoot(), count(DataFlow::Node pred, DataFlow::Node succ | relevantStep(pred, succ))
+from DataFlow::Node pred, int numOfSuccessors
+where
+ relevantStep(pred, _) and
+ numOfSuccessors = count(DataFlow::Node succ | relevantStep(pred, succ))
+select pred, "Step to " + numOfSuccessors + " other nodes."
From 34a0a0d080baac79099aba8862ba0fe23bc377bc Mon Sep 17 00:00:00 2001
From: Chris Smowton
Date: Mon, 3 Oct 2022 14:47:07 +0100
Subject: [PATCH 410/991] Implement $default method synthesis
This adds methods that fill in default parameters whenever a constructor or method uses default parameter values. I use as similar an approach to the real Kotlin compiler as possible both because this produces the desirable dataflow, and because it should merge cleanly with the same class file seen by the Java extractor, which will see and
extract the signatures of the default methods.
---
.../src/main/kotlin/KotlinFileExtractor.kt | 494 ++++++++--
.../src/main/kotlin/KotlinUsesExtractor.kt | 32 +-
java/ql/lib/semmle/code/java/Element.qll | 2 +
.../parameter-defaults/PrintAst.expected | 849 ++++++++++++++++++
.../parameter-defaults/PrintAst.qlref | 1 +
.../parameter-defaults/flowTest.expected | 2 +
.../parameter-defaults/flowTest.ql | 34 +
.../library-tests/parameter-defaults/test.kt | 141 +++
8 files changed, 1460 insertions(+), 95 deletions(-)
create mode 100644 java/ql/test/kotlin/library-tests/parameter-defaults/PrintAst.expected
create mode 100644 java/ql/test/kotlin/library-tests/parameter-defaults/PrintAst.qlref
create mode 100644 java/ql/test/kotlin/library-tests/parameter-defaults/flowTest.expected
create mode 100644 java/ql/test/kotlin/library-tests/parameter-defaults/flowTest.ql
create mode 100644 java/ql/test/kotlin/library-tests/parameter-defaults/test.kt
diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt
index 81acc665be3..d5615a1cfa9 100644
--- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt
+++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
+import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
@@ -688,7 +689,7 @@ open class KotlinFileExtractor(
"",
listOf(),
pluginContext.irBuiltIns.unitType,
- extensionReceiverParameter = null,
+ extensionParamType = null,
functionTypeParameters = listOf(),
classTypeArgsIncludingOuterClasses = listOf(),
overridesCollectionsMethod = false,
@@ -832,10 +833,117 @@ open class KotlinFileExtractor(
null
else {
forceExtractFunction(f, parentId, extractBody, extractMethodAndParameterTypeAccesses, typeSubstitution, classTypeArgsIncludingOuterClasses).also {
+ // The defaults-forwarder function is a static utility, not a member, so we only need to extract this for the unspecialised instance of this class.
+ if (classTypeArgsIncludingOuterClasses.isNullOrEmpty())
+ extractDefaultsFunction(f, parentId, extractBody)
extractGeneratedOverloads(f, parentId, null, extractBody, extractMethodAndParameterTypeAccesses, typeSubstitution, classTypeArgsIncludingOuterClasses)
}
}
+ private fun extractDefaultsFunction(f: IrFunction, parentId: Label, extractBody: Boolean) {
+ if (f.valueParameters.none { it.defaultValue != null })
+ return
+
+ val id = getDefaultsMethodLabel(f)
+ val locId = getLocation(f, null)
+ val extReceiver = f.extensionReceiverParameter
+ val dispatchReceiver = if (f.shouldExtractAsStatic) null else f.dispatchReceiverParameter
+ val parameterTypes = listOfNotNull(extReceiver?.let { erase(it.type) }) + getDefaultsMethodArgTypes(f)
+ val allParamTypeResults = parameterTypes.mapIndexed { i, paramType ->
+ val paramId = tw.getLabelFor(getValueParameterLabel(id, i))
+ extractValueParameter(paramId, paramType, "p$i", locId, id, i, paramId, isVararg = false, syntheticParameterNames = true, isCrossinline = false, isNoinline = false).also {
+ extractTypeAccess(useType(paramType), locId, paramId, -1)
+ }
+ }
+ val paramsSignature = allParamTypeResults.joinToString(separator = ",", prefix = "(", postfix = ")") { it.javaResult.signature }
+ val shortName = getDefaultsMethodName(f)
+
+ if (f.symbol is IrConstructorSymbol) {
+ val constrId = id.cast()
+ extractConstructor(constrId, shortName, paramsSignature, parentId, constrId)
+ } else {
+ val methodId = id.cast()
+ extractMethod(methodId, locId, shortName, erase(f.returnType), paramsSignature, parentId, methodId, origin = null, extractTypeAccess = true)
+ addModifiers(id, "static")
+ }
+ tw.writeHasLocation(id, locId)
+ addModifiers(id, "public")
+ tw.writeCompiler_generated(id, CompilerGeneratedKinds.DEFAULT_ARGUMENTS_METHOD.kind)
+
+ if (extractBody) {
+ val nonSyntheticParams = listOfNotNull(dispatchReceiver) + f.valueParameters
+ // This stack entry represents as if we're extracting the 'real' function `f`, giving the indices of its non-synthetic parameters
+ // such that when we extract the default expressions below, any reference to f's nth parameter will resolve to f$default's
+ // n + o'th parameter, where `o` is the parameter offset caused by adding any dispatch receiver to the parameter list.
+ // Note we don't need to add the extension receiver here because `useValueParameter` always assumes an extension receiver
+ // will be prepended if one exists.
+ DeclarationStackAdjuster(f, OverriddenFunctionAttributes(id, id, locId, nonSyntheticParams)).use {
+ val realParamsVarId = getValueParameterLabel(id, parameterTypes.size - 2)
+ val intType = pluginContext.irBuiltIns.intType
+ val paramIdxOffset = listOf(dispatchReceiver, f.extensionReceiverParameter).count { it != null }
+ extractBlockBody(id, locId).also { blockId ->
+ var nextStmt = 0
+ // For each parameter with a default, sub in the default value if the caller hasn't supplied a value:
+ f.valueParameters.forEachIndexed { paramIdx, param ->
+ val defaultVal = param.defaultValue
+ if (defaultVal != null) {
+ extractIfStmt(locId, blockId, nextStmt++, id).also { ifId ->
+ // if (realParams & thisParamBit == 0) ...
+ extractEqualsExpression(locId, ifId, 0, id, ifId).also { eqId ->
+ extractAndbitExpression(intType, locId, eqId, 0, id, ifId).also { opId ->
+ extractConstantInteger(1 shl paramIdx, locId, opId, 0, id, ifId)
+ extractVariableAccess(tw.getLabelFor(realParamsVarId), intType, locId, opId, 1, id, ifId)
+ }
+ extractConstantInteger(0, locId, eqId, 1, id, ifId)
+ }
+ // thisParamVar = defaultExpr...
+ extractExpressionStmt(locId, ifId, 1, id).also { exprStmtId ->
+ extractAssignExpr(param.type, locId, exprStmtId, 0, id, exprStmtId).also { assignId ->
+ extractVariableAccess(tw.getLabelFor(getValueParameterLabel(id, paramIdx + paramIdxOffset)), param.type, locId, assignId, 0, id, exprStmtId)
+ extractExpressionExpr(defaultVal.expression, id, assignId, 1, exprStmtId)
+ }
+ }
+ }
+ }
+ }
+ // Now call the real function:
+ val realFunctionId = useFunction(f)
+ if (f is IrConstructor) {
+ tw.getFreshIdLabel().also { thisCallId ->
+ tw.writeStmts_constructorinvocationstmt(thisCallId, blockId, nextStmt++, id)
+ tw.writeHasLocation(thisCallId, locId)
+ f.valueParameters.forEachIndexed { idx, param ->
+ extractVariableAccess(tw.getLabelFor(getValueParameterLabel(id, idx)), param.type, locId, thisCallId, idx, id, thisCallId)
+ }
+ tw.writeCallableBinding(thisCallId, realFunctionId)
+ }
+ } else {
+ tw.getFreshIdLabel().also { returnId ->
+ tw.writeStmts_returnstmt(returnId, blockId, nextStmt++, id)
+ tw.writeHasLocation(returnId, locId)
+ extractMethodAccessWithoutArgs(f.returnType, locId, id, returnId, 0, returnId, realFunctionId).also { thisCallId ->
+ val realFnIdxOffset = if (f.extensionReceiverParameter != null) 1 else 0
+ val paramMappings = f.valueParameters.mapIndexed { idx, param -> Triple(param.type, idx + paramIdxOffset, idx + realFnIdxOffset) } +
+ listOfNotNull(
+ dispatchReceiver?.let { Triple(it.type, realFnIdxOffset, -1) },
+ extReceiver?.let { Triple(it.type, 0, 0) }
+ )
+ paramMappings.forEach { (type, fromIdx, toIdx) ->
+ extractVariableAccess(tw.getLabelFor(getValueParameterLabel(id, fromIdx)), type, locId, thisCallId, toIdx, id, returnId)
+ }
+ if (f.shouldExtractAsStatic)
+ extractStaticTypeAccessQualifier(f, thisCallId, locId, id, returnId)
+ else if (f.isLocalFunction()) {
+ extractNewExprForLocalFunction(getLocallyVisibleFunctionLabels(f), thisCallId, locId, id, returnId)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
private val jvmOverloadsFqName = FqName("kotlin.jvm.JvmOverloads")
private fun extractGeneratedOverloads(f: IrFunction, parentId: Label, maybeSourceParentId: Label?, extractBody: Boolean, extractMethodAndParameterTypeAccesses: Boolean, typeSubstitution: TypeSubstitution?, classTypeArgsIncludingOuterClasses: List?) {
@@ -932,6 +1040,29 @@ open class KotlinFileExtractor(
}
}
+ private fun extractConstructor(id: Label, shortName: String, paramsSignature: String, parentId: Label, sourceDeclaration: Label) {
+ val unitType = useType(pluginContext.irBuiltIns.unitType, TypeContext.RETURN)
+ tw.writeConstrs(id, shortName, "$shortName$paramsSignature", unitType.javaResult.id, parentId, sourceDeclaration)
+ tw.writeConstrsKotlinType(id, unitType.kotlinResult.id)
+ }
+
+ private fun extractMethod(id: Label, locId: Label, shortName: String, returnType: IrType, paramsSignature: String, parentId: Label, sourceDeclaration: Label, origin: IrDeclarationOrigin?, extractTypeAccess: Boolean) {
+ val returnTypeResults = useType(returnType, TypeContext.RETURN)
+ tw.writeMethods(id, shortName, "$shortName$paramsSignature", returnTypeResults.javaResult.id, parentId, sourceDeclaration)
+ tw.writeMethodsKotlinType(id, returnTypeResults.kotlinResult.id)
+ when (origin) {
+ IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER ->
+ tw.writeCompiler_generated(id, CompilerGeneratedKinds.GENERATED_DATA_CLASS_MEMBER.kind)
+ IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR ->
+ tw.writeCompiler_generated(id, CompilerGeneratedKinds.DEFAULT_PROPERTY_ACCESSOR.kind)
+ IrDeclarationOrigin.ENUM_CLASS_SPECIAL_MEMBER ->
+ tw.writeCompiler_generated(id, CompilerGeneratedKinds.ENUM_CLASS_SPECIAL_MEMBER.kind)
+ }
+ if (extractTypeAccess) {
+ extractTypeAccessRecursive(returnType, locId, id, -1)
+ }
+ }
+
private fun forceExtractFunction(f: IrFunction, parentId: Label, extractBody: Boolean, extractMethodAndParameterTypeAccesses: Boolean, typeSubstitution: TypeSubstitution?, classTypeArgsIncludingOuterClasses: List?, extractOrigin: Boolean = true, overriddenAttributes: OverriddenFunctionAttributes? = null): Label {
with("function", f) {
DeclarationStackAdjuster(f, overriddenAttributes).use {
@@ -976,42 +1107,23 @@ open class KotlinFileExtractor(
val locId = overriddenAttributes?.sourceLoc ?: getLocation(f, classTypeArgsIncludingOuterClasses)
if (f.symbol is IrConstructorSymbol) {
- val unitType = useType(pluginContext.irBuiltIns.unitType, TypeContext.RETURN)
val shortName = when {
adjustedReturnType.isAnonymous -> ""
typeSubstitution != null -> useType(substReturnType).javaResult.shortName
else -> adjustedReturnType.classFqName?.shortName()?.asString() ?: f.name.asString()
}
- val constrId = id.cast()
- tw.writeConstrs(constrId, shortName, "$shortName$paramsSignature", unitType.javaResult.id, parentId, sourceDeclaration.cast())
- tw.writeConstrsKotlinType(constrId, unitType.kotlinResult.id)
+ extractConstructor(id.cast(), shortName, paramsSignature, parentId, sourceDeclaration.cast())
} else {
- val returnType = useType(substReturnType, TypeContext.RETURN)
- val shortName = getFunctionShortName(f)
+ val shortNames = getFunctionShortName(f)
val methodId = id.cast()
- tw.writeMethods(methodId, shortName.nameInDB, "${shortName.nameInDB}$paramsSignature", returnType.javaResult.id, parentId, sourceDeclaration.cast())
- tw.writeMethodsKotlinType(methodId, returnType.kotlinResult.id)
- if (extractOrigin) {
- when (f.origin) {
- IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER ->
- tw.writeCompiler_generated(methodId, CompilerGeneratedKinds.GENERATED_DATA_CLASS_MEMBER.kind)
- IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR ->
- tw.writeCompiler_generated(methodId, CompilerGeneratedKinds.DEFAULT_PROPERTY_ACCESSOR.kind)
- IrDeclarationOrigin.ENUM_CLASS_SPECIAL_MEMBER ->
- tw.writeCompiler_generated(methodId, CompilerGeneratedKinds.ENUM_CLASS_SPECIAL_MEMBER.kind)
- }
- }
+ extractMethod(methodId, locId, shortNames.nameInDB, substReturnType, paramsSignature, parentId, sourceDeclaration.cast(), if (extractOrigin) f.origin else null, extractMethodAndParameterTypeAccesses)
- if (extractMethodAndParameterTypeAccesses) {
- extractTypeAccessRecursive(substReturnType, locId, id, -1)
- }
-
- if (shortName.nameInDB != shortName.kotlinName) {
- tw.writeKtFunctionOriginalNames(methodId, shortName.kotlinName)
+ if (shortNames.nameInDB != shortNames.kotlinName) {
+ tw.writeKtFunctionOriginalNames(methodId, shortNames.kotlinName)
}
if (f.hasInterfaceParent() && f.body != null) {
- addModifiers(id, "default") // The actual output class file may or may not have this modifier, depending on the -Xjvm-default setting.
+ addModifiers(methodId, "default") // The actual output class file may or may not have this modifier, depending on the -Xjvm-default setting.
}
}
@@ -1209,14 +1321,18 @@ open class KotlinFileExtractor(
}
}
+ private fun extractBlockBody(callable: Label, locId: Label) =
+ tw.getFreshIdLabel().also {
+ tw.writeStmts_block(it, callable, 0, callable)
+ tw.writeHasLocation(it, locId)
+ }
+
private fun extractBlockBody(b: IrBlockBody, callable: Label) {
with("block body", b) {
- val id = tw.getFreshIdLabel()
- val locId = tw.getLocation(b)
- tw.writeStmts_block(id, callable, 0, callable)
- tw.writeHasLocation(id, locId)
- for ((sIdx, stmt) in b.statements.withIndex()) {
- extractStatement(stmt, callable, id, sIdx)
+ extractBlockBody(callable, tw.getLocation(b)).also {
+ for ((sIdx, stmt) in b.statements.withIndex()) {
+ extractStatement(stmt, callable, it, sIdx)
+ }
}
}
}
@@ -1294,6 +1410,12 @@ open class KotlinFileExtractor(
}
}
+ private fun extractIfStmt(locId: Label, parent: Label, idx: Int, callable: Label) =
+ tw.getFreshIdLabel().also {
+ tw.writeStmts_ifstmt(it, parent, idx, callable)
+ tw.writeHasLocation(it, locId)
+ }
+
private fun extractStatement(s: IrStatement, callable: Label, parent: Label, idx: Int) {
with("statement", s) {
when(s) {
@@ -1521,6 +1643,147 @@ open class KotlinFileExtractor(
extractTypeAccessRecursive(pluginContext.irBuiltIns.anyType, locId, idNewexpr, -3, enclosingCallable, enclosingStmt)
}
+ fun extractMethodAccessWithoutArgs(
+ returnType: IrType,
+ locId: Label,
+ enclosingCallable: Label,
+ callsiteParent: Label,
+ childIdx: Int,
+ enclosingStmt: Label,
+ methodLabel: Label?
+ ) = tw.getFreshIdLabel().also { id ->
+ val type = useType(returnType)
+
+ tw.writeExprs_methodaccess(id, type.javaResult.id, callsiteParent, childIdx)
+ tw.writeExprsKotlinType(id, type.kotlinResult.id)
+ tw.writeHasLocation(id, locId)
+ tw.writeCallableEnclosingExpr(id, enclosingCallable)
+ tw.writeStatementEnclosingExpr(id, enclosingStmt)
+
+ // The caller should have warned about this before, so we don't repeat the warning here.
+ if (methodLabel != null)
+ tw.writeCallableBinding(id, methodLabel)
+ }
+
+ private val defaultConstructorMarkerClass by lazy {
+ val result = pluginContext.referenceClass(FqName("kotlin.jvm.internal.DefaultConstructorMarker"))?.owner
+ result?.let { extractExternalClassLater(it) }
+ result
+ }
+
+ private val defaultConstructorMarkerType by lazy {
+ defaultConstructorMarkerClass?.typeWith()
+ }
+
+ private fun getDefaultsMethodLastArgType(f: IrFunction) =
+ (
+ if (f is IrConstructor)
+ defaultConstructorMarkerType
+ else
+ null
+ ) ?: pluginContext.irBuiltIns.anyType
+
+ private fun getDefaultsMethodArgTypes(f: IrFunction) =
+ // The $default method has type ([extensionReceiver], [dispatchReciever], paramTypes..., int, Object)
+ // All parameter types are erased. The trailing int is a mask indicating which parameter values are real
+ // and which should be replaced by defaults. The final Object parameter is apparently always null.
+ (
+ listOfNotNull(if (f.shouldExtractAsStatic) null else f.dispatchReceiverParameter?.type) +
+ f.valueParameters.map { it.type } +
+ listOf(pluginContext.irBuiltIns.intType, getDefaultsMethodLastArgType(f))
+ ).map { erase(it) }
+
+ private fun getDefaultsMethodName(f: IrFunction) =
+ if (f is IrConstructor) {
+ f.returnType.let {
+ when {
+ it.isAnonymous -> ""
+ else -> it.classFqName?.shortName()?.asString() ?: f.name.asString()
+ }
+ }
+ } else {
+ f.name.asString() + "\$default"
+ }
+
+ private fun getDefaultsMethodLabel(f: IrFunction): Label {
+ val defaultsMethodName = getDefaultsMethodName(f)
+ val normalArgTypes = getDefaultsMethodArgTypes(f)
+ val extensionParamType = f.extensionReceiverParameter?.let { erase(it.type) }
+
+ val defaultMethodLabelStr = getFunctionLabel(
+ f.parent,
+ maybeParentId = null,
+ defaultsMethodName,
+ normalArgTypes,
+ erase(f.returnType),
+ extensionParamType,
+ listOf(),
+ classTypeArgsIncludingOuterClasses = null,
+ overridesCollectionsMethod = false,
+ javaSignature = null,
+ addParameterWildcardsByDefault = false
+ )
+
+ return tw.getLabelFor(defaultMethodLabelStr)
+ }
+
+ private fun extractsDefaultsCall(
+ syntacticCallTarget: IrFunction,
+ locId: Label,
+ callsite: IrCall,
+ enclosingCallable: Label,
+ callsiteParent: Label,
+ childIdx: Int,
+ enclosingStmt: Label,
+ valueArguments: List,
+ dispatchReceiver: IrExpression?,
+ extensionReceiver: IrExpression?
+ ) {
+ val callTarget = syntacticCallTarget.target.realOverrideTarget
+ val defaultMethodLabel = getDefaultsMethodLabel(callTarget)
+ val id = extractMethodAccessWithoutArgs(callsite.type, locId, enclosingCallable, callsiteParent, childIdx, enclosingStmt, defaultMethodLabel)
+
+ if (callTarget.isLocalFunction()) {
+ extractNewExprForLocalFunction(getLocallyVisibleFunctionLabels(callTarget), id, locId, enclosingCallable, enclosingStmt)
+ } else {
+ extractStaticTypeAccessQualifierUnchecked(callTarget.parent, id, locId, enclosingCallable, enclosingStmt)
+ }
+
+ extractDefaultsCallArguments(id, callTarget, enclosingCallable, enclosingStmt, valueArguments, dispatchReceiver, extensionReceiver)
+ }
+
+ private fun extractDefaultsCallArguments(
+ id: Label,
+ callTarget: IrFunction,
+ enclosingCallable: Label,
+ enclosingStmt: Label,
+ valueArguments: List,
+ dispatchReceiver: IrExpression?,
+ extensionReceiver: IrExpression?
+ ) {
+ var nextIdx = 0
+ if (extensionReceiver != null) {
+ extractExpressionExpr(extensionReceiver, enclosingCallable, id, nextIdx++, enclosingStmt)
+ }
+ if (dispatchReceiver != null && !callTarget.shouldExtractAsStatic) {
+ extractExpressionExpr(dispatchReceiver, enclosingCallable, id, nextIdx++, enclosingStmt)
+ }
+
+ val valueArgsWithDummies = valueArguments.zip(callTarget.valueParameters).map {
+ (expr, param) -> expr ?: IrConstImpl.defaultValueForType(0, 0, param.type)
+ }
+
+ var realParamsMask = 0
+ valueArguments.forEachIndexed { index, arg -> if (arg != null) realParamsMask = realParamsMask or (1 shl index) }
+
+ val extraArgs = listOf(
+ IrConstImpl.int(0, 0, pluginContext.irBuiltIns.intType, realParamsMask),
+ IrConstImpl.defaultValueForType(0, 0, getDefaultsMethodLastArgType(callTarget))
+ )
+
+ extractCallValueArguments(id, valueArgsWithDummies + extraArgs, enclosingStmt, enclosingCallable, nextIdx)
+ }
+
fun extractRawMethodAccess(
syntacticCallTarget: IrFunction,
callsite: IrCall,
@@ -1537,24 +1800,38 @@ open class KotlinFileExtractor(
val locId = tw.getLocation(callsite)
- extractRawMethodAccess(
- syntacticCallTarget,
- locId,
- callsite.type,
- enclosingCallable,
- callsiteParent,
- childIdx,
- enclosingStmt,
- valueArguments.size,
- { argParent, idxOffset -> extractCallValueArguments(argParent, valueArguments, enclosingStmt, enclosingCallable, idxOffset) },
- dispatchReceiver?.type,
- dispatchReceiver?.let { { callId -> extractExpressionExpr(dispatchReceiver, enclosingCallable, callId, -1, enclosingStmt) } },
- extensionReceiver?.let { { argParent -> extractExpressionExpr(extensionReceiver, enclosingCallable, argParent, 0, enclosingStmt) } },
- typeArguments,
- extractClassTypeArguments,
- superQualifierSymbol
- )
-
+ if (valueArguments.any { it == null }) {
+ extractsDefaultsCall(
+ syntacticCallTarget,
+ locId,
+ callsite,
+ enclosingCallable,
+ callsiteParent,
+ childIdx,
+ enclosingStmt,
+ valueArguments,
+ dispatchReceiver,
+ extensionReceiver
+ )
+ } else {
+ extractRawMethodAccess(
+ syntacticCallTarget,
+ locId,
+ callsite.type,
+ enclosingCallable,
+ callsiteParent,
+ childIdx,
+ enclosingStmt,
+ valueArguments.size,
+ { argParent, idxOffset -> extractCallValueArguments(argParent, valueArguments, enclosingStmt, enclosingCallable, idxOffset) },
+ dispatchReceiver?.type,
+ dispatchReceiver?.let { { callId -> extractExpressionExpr(dispatchReceiver, enclosingCallable, callId, -1, enclosingStmt) } },
+ extensionReceiver?.let { { argParent -> extractExpressionExpr(extensionReceiver, enclosingCallable, argParent, 0, enclosingStmt) } },
+ typeArguments,
+ extractClassTypeArguments,
+ superQualifierSymbol
+ )
+ }
}
private fun getFunctionInvokeMethod(typeArgs: List): IrFunction? {
@@ -1634,26 +1911,16 @@ open class KotlinFileExtractor(
superQualifierSymbol: IrClassSymbol? = null) {
val callTarget = syntacticCallTarget.target.realOverrideTarget
- val id = tw.getFreshIdLabel()
- val type = useType(returnType)
+ val methodId = getCalleeMethodId(callTarget, drType, extractClassTypeArguments)
+ if (methodId == null) {
+ logger.warn("No method to bind call to for raw method access")
+ }
- tw.writeExprs_methodaccess(id, type.javaResult.id, callsiteParent, childIdx)
- tw.writeExprsKotlinType(id, type.kotlinResult.id)
- tw.writeHasLocation(id, locId)
- tw.writeCallableEnclosingExpr(id, enclosingCallable)
- tw.writeStatementEnclosingExpr(id, enclosingStmt)
+ val id = extractMethodAccessWithoutArgs(returnType, locId, enclosingCallable, callsiteParent, childIdx, enclosingStmt, methodId)
// type arguments at index -2, -3, ...
extractTypeArguments(typeArguments, locId, id, enclosingCallable, enclosingStmt, -2, true)
- val methodId = getCalleeMethodId(callTarget, drType, extractClassTypeArguments)
-
- if (methodId == null) {
- logger.warn("No method to bind call to for raw method access")
- } else {
- tw.writeCallableBinding(id, methodId)
- }
-
if (callTarget.isLocalFunction()) {
extractNewExprForLocalFunction(getLocallyVisibleFunctionLabels(callTarget), id, locId, enclosingCallable, enclosingStmt)
} else if (callTarget.shouldExtractAsStatic) {
@@ -1683,16 +1950,19 @@ open class KotlinFileExtractor(
extractValueArguments(argParent, idxOffset)
}
+ private fun extractStaticTypeAccessQualifierUnchecked(parent: IrDeclarationParent, parentExpr: Label, locId: Label, enclosingCallable: Label, enclosingStmt: Label) {
+ if (parent is IrClass) {
+ extractTypeAccessRecursive(parent.toRawType(), locId, parentExpr, -1, enclosingCallable, enclosingStmt)
+ } else if (parent is IrFile) {
+ extractTypeAccess(useFileClassType(parent), locId, parentExpr, -1, enclosingCallable, enclosingStmt)
+ } else {
+ logger.warnElement("Unexpected static type access qualifier ${parent.javaClass}", parent)
+ }
+ }
+
private fun extractStaticTypeAccessQualifier(target: IrDeclaration, parentExpr: Label, locId: Label, enclosingCallable: Label, enclosingStmt: Label) {
if (target.shouldExtractAsStatic) {
- val parent = target.parent
- if (parent is IrClass) {
- extractTypeAccessRecursive(parent.toRawType(), locId, parentExpr, -1, enclosingCallable, enclosingStmt)
- } else if (parent is IrFile) {
- extractTypeAccess(useFileClassType(parent), locId, parentExpr, -1, enclosingCallable, enclosingStmt)
- } else {
- logger.warnElement("Unexpected static type access qualifer ${parent.javaClass}", target)
- }
+ extractStaticTypeAccessQualifierUnchecked(target.parent, parentExpr, locId, enclosingCallable, enclosingStmt)
}
}
@@ -2666,14 +2936,22 @@ open class KotlinFileExtractor(
useType(eType)
}
val locId = tw.getLocation(e)
- val id = extractNewExpr(e.symbol.owner, eType.arguments, type, locId, parent, idx, callable, enclosingStmt)
+ val valueArgs = (0 until e.valueArgumentsCount).map { e.getValueArgument(it) }
+ val anyDefaultArgs = valueArgs.any { it == null }
+ val id = if (anyDefaultArgs) {
+ extractNewExpr(getDefaultsMethodLabel(e.symbol.owner).cast(), type, locId, parent, idx, callable, enclosingStmt).also {
+ extractDefaultsCallArguments(it, e.symbol.owner, callable, enclosingStmt, valueArgs, null, null)
+ }
+ } else {
+ extractNewExpr(e.symbol.owner, eType.arguments, type, locId, parent, idx, callable, enclosingStmt).also {
+ extractCallValueArguments(it, e, enclosingStmt, callable, 0)
+ }
+ }
if (isAnonymous) {
tw.writeIsAnonymClass(type.javaResult.id.cast(), id)
}
- extractCallValueArguments(id, e, enclosingStmt, callable, 0)
-
val dr = e.dispatchReceiver
if (dr != null) {
extractExpressionExpr(dr, callable, id, -2, enclosingStmt)
@@ -2872,6 +3150,12 @@ open class KotlinFileExtractor(
return false
}
+ private fun extractExpressionStmt(locId: Label, parent: Label, idx: Int, callable: Label) =
+ tw.getFreshIdLabel().also {
+ tw.writeStmts_exprstmt(it, parent, idx, callable)
+ tw.writeHasLocation(it, locId)
+ }
+
private fun extractExpressionStmt(e: IrExpression, callable: Label, parent: Label, idx: Int) {
extractExpression(e, callable, StmtParent(parent, idx))
}
@@ -2880,6 +3164,45 @@ open class KotlinFileExtractor(
extractExpression(e, callable, ExprParent(parent, idx, enclosingStmt))
}
+ private fun extractExprContext(id: Label, locId: Label, callable: Label, enclosingStmt: Label) {
+ tw.writeHasLocation(id, locId)
+ tw.writeCallableEnclosingExpr(id, callable)
+ tw.writeStatementEnclosingExpr(id, enclosingStmt)
+ }
+
+ private fun extractEqualsExpression(locId: Label, parent: Label, idx: Int, callable: Label, enclosingStmt: Label) =
+ tw.getFreshIdLabel().also {
+ val type = useType(pluginContext.irBuiltIns.booleanType)
+ tw.writeExprs_eqexpr(it, type.javaResult.id, parent, idx)
+ tw.writeExprsKotlinType(it, type.kotlinResult.id)
+ extractExprContext(it, locId, callable, enclosingStmt)
+ }
+
+ private fun extractAndbitExpression(type: IrType, locId: Label, parent: Label, idx: Int, callable: Label, enclosingStmt: Label) =
+ tw.getFreshIdLabel().also {
+ val typeResults = useType(type)
+ tw.writeExprs_andbitexpr(it, typeResults.javaResult.id, parent, idx)
+ tw.writeExprsKotlinType(it, typeResults.kotlinResult.id)
+ extractExprContext(it, locId, callable, enclosingStmt)
+ }
+
+ private fun extractConstantInteger(v: Int, locId: Label, parent: Label, idx: Int, callable: Label, enclosingStmt: Label) =
+ tw.getFreshIdLabel().also {
+ val type = useType(pluginContext.irBuiltIns.intType)
+ tw.writeExprs_integerliteral(it, type.javaResult.id, parent, idx)
+ tw.writeExprsKotlinType(it, type.kotlinResult.id)
+ tw.writeNamestrings(v.toString(), v.toString(), it)
+ extractExprContext(it, locId, callable, enclosingStmt)
+ }
+
+ private fun extractAssignExpr(type: IrType, locId: Label, parent: Label, idx: Int, callable: Label, enclosingStmt: Label) =
+ tw.getFreshIdLabel().also {
+ val typeResults = useType(type)
+ tw.writeExprs_assignexpr(it, typeResults.javaResult.id, parent, idx)
+ tw.writeExprsKotlinType(it, typeResults.kotlinResult.id)
+ extractExprContext(it, locId, callable, enclosingStmt)
+ }
+
private fun extractExpression(e: IrExpression, callable: Label, parent: StmtExprParent) {
with("expression", e) {
when(e) {
@@ -3130,7 +3453,7 @@ open class KotlinFileExtractor(
val exprParent = parent.expr(e, callable)
val owner = e.symbol.owner
if (owner is IrValueParameter && owner.index == -1 && !owner.isExtensionReceiver()) {
- extractThisAccess(e, exprParent, callable)
+ extractThisAccess(e, owner.parent, exprParent, callable)
} else {
val isAnnotationClassParameter = ((owner as? IrValueParameter)?.parent as? IrConstructor)?.parentClassOrNull?.kind == ClassKind.ANNOTATION_CLASS
val extractType = if (isAnnotationClassParameter) kClassToJavaClass(e.type) else e.type
@@ -3482,7 +3805,7 @@ open class KotlinFileExtractor(
tw.writeStatementEnclosingExpr(it, enclosingStmt)
}
- private fun extractThisAccess(e: IrGetValue, exprParent: ExprParent, callable: Label) {
+ private fun extractThisAccess(e: IrGetValue, thisParamParent: IrDeclarationParent, exprParent: ExprParent, callable: Label) {
val containingDeclaration = declarationStack.peek().first
val locId = tw.getLocation(e)
@@ -3494,6 +3817,20 @@ open class KotlinFileExtractor(
extractStaticTypeAccessQualifier(containingDeclaration, varAccessId, locId, callable, exprParent.enclosingStmt)
}
} else {
+ if (thisParamParent is IrFunction) {
+ val overriddenAttributes = declarationStack.findOverriddenAttributes(thisParamParent)
+ val replaceWithParamIdx = overriddenAttributes?.valueParameters?.indexOf(e.symbol.owner)
+ if (replaceWithParamIdx != null && replaceWithParamIdx != -1) {
+ // Use of 'this' in a function where the dispatch receiver is passed like an ordinary parameter,
+ // such as a `$default` static function that substitutes in default arguments as needed.
+ val paramDeclarerId = overriddenAttributes.id ?: useDeclarationParent(thisParamParent, false)
+ val extensionParamOffset = if (thisParamParent.extensionReceiverParameter != null) 1 else 0
+ val replacementParamId = tw.getLabelFor(getValueParameterLabel(paramDeclarerId, replaceWithParamIdx + extensionParamOffset))
+ extractVariableAccess(replacementParamId, e.type, locId, exprParent.parent, exprParent.idx, callable, exprParent.enclosingStmt)
+ return
+ }
+ }
+
val id = extractThisAccess(e.type, callable, exprParent.parent, exprParent.idx, exprParent.enclosingStmt, locId)
fun extractTypeAccess(parent: IrClass) {
@@ -5048,5 +5385,6 @@ open class KotlinFileExtractor(
DELEGATED_PROPERTY_SETTER(7),
JVMSTATIC_PROXY_METHOD(8),
JVMOVERLOADS_METHOD(9),
+ DEFAULT_ARGUMENTS_METHOD(10)
}
}
diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt
index 957450474c5..5873b06c280 100644
--- a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt
+++ b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt
@@ -1043,9 +1043,9 @@ open class KotlinUsesExtractor(
f.parent,
maybeParentId,
getFunctionShortName(f).nameInDB,
- maybeParameterList ?: f.valueParameters,
+ (maybeParameterList ?: f.valueParameters).map { it.type },
getAdjustedReturnType(f),
- f.extensionReceiverParameter,
+ f.extensionReceiverParameter?.type,
getFunctionTypeParameters(f),
classTypeArgsIncludingOuterClasses,
overridesCollectionsMethodWithAlteredParameterTypes(f),
@@ -1067,12 +1067,12 @@ open class KotlinUsesExtractor(
maybeParentId: Label?,
// The name of the function; normally f.name.asString().
name: String,
- // The value parameters that the functions takes; normally f.valueParameters.
- parameters: List,
+ // The types of the value parameters that the functions takes; normally f.valueParameters.map { it.type }.
+ parameterTypes: List,
// The return type of the function; normally f.returnType.
returnType: IrType,
- // The extension receiver of the function, if any; normally f.extensionReceiverParameter.
- extensionReceiverParameter: IrValueParameter?,
+ // The extension receiver of the function, if any; normally f.extensionReceiverParameter?.type.
+ extensionParamType: IrType?,
// The type parameters of the function. This does not include type parameters of enclosing classes.
functionTypeParameters: List,
// The type arguments of enclosing classes of the function.
@@ -1089,11 +1089,7 @@ open class KotlinUsesExtractor(
prefix: String = "callable"
): String {
val parentId = maybeParentId ?: useDeclarationParent(parent, false, classTypeArgsIncludingOuterClasses, true)
- val allParams = if (extensionReceiverParameter == null) {
- parameters
- } else {
- listOf(extensionReceiverParameter) + parameters
- }
+ val allParamTypes = if (extensionParamType == null) parameterTypes else listOf(extensionParamType) + parameterTypes
val substitutionMap = classTypeArgsIncludingOuterClasses?.let { notNullArgs ->
if (notNullArgs.isEmpty()) {
@@ -1103,11 +1099,11 @@ open class KotlinUsesExtractor(
enclosingClass?.let { notNullClass -> makeTypeGenericSubstitutionMap(notNullClass, notNullArgs) }
}
}
- val getIdForFunctionLabel = { it: IndexedValue ->
+ val getIdForFunctionLabel = { it: IndexedValue ->
// Kotlin rewrites certain Java collections types adding additional generic constraints-- for example,
// Collection.remove(Object) because Collection.remove(Collection::E) in the Kotlin universe.
// If this has happened, erase the type again to get the correct Java signature.
- val maybeAmendedForCollections = if (overridesCollectionsMethod) eraseCollectionsMethodParameterType(it.value.type, name, it.index) else it.value.type
+ val maybeAmendedForCollections = if (overridesCollectionsMethod) eraseCollectionsMethodParameterType(it.value, name, it.index) else it.value
// Add any wildcard types that the Kotlin compiler would add in the Java lowering of this function:
val withAddedWildcards = addJavaLoweringWildcards(maybeAmendedForCollections, addParameterWildcardsByDefault, javaSignature?.let { sig -> getJavaValueParameterType(sig, it.index) })
// Now substitute any class type parameters in:
@@ -1117,7 +1113,7 @@ open class KotlinUsesExtractor(
val maybeErased = if (functionTypeParameters.isEmpty()) maybeSubbed else erase(maybeSubbed)
"{${useType(maybeErased).javaResult.id}}"
}
- val paramTypeIds = allParams.withIndex().joinToString(separator = ",", transform = getIdForFunctionLabel)
+ val paramTypeIds = allParamTypes.withIndex().joinToString(separator = ",", transform = getIdForFunctionLabel)
val labelReturnType =
if (name == "")
pluginContext.irBuiltIns.unitType
@@ -1551,7 +1547,7 @@ open class KotlinUsesExtractor(
* Note that `Array` is retained (with `T` itself erased) because these are expected to be lowered to Java
* arrays, which are not generic.
*/
- private fun erase (t: IrType): IrType {
+ fun erase (t: IrType): IrType {
if (t is IrSimpleType) {
val classifier = t.classifier
val owner = classifier.owner
@@ -1578,6 +1574,8 @@ open class KotlinUsesExtractor(
private fun eraseTypeParameter(t: IrTypeParameter) =
erase(t.superTypes[0])
+ fun getValueParameterLabel(parentId: Label?, idx: Int) = "@\"params;{$parentId};$idx\""
+
/**
* Gets the label for `vp` in the context of function instance `parent`, or in that of its declaring function if
* `parent` is null.
@@ -1607,7 +1605,7 @@ open class KotlinUsesExtractor(
logger.error("Unexpected negative index for parameter")
}
- return "@\"params;{$parentId};$idx\""
+ return getValueParameterLabel(parentId, idx)
}
@@ -1669,7 +1667,7 @@ open class KotlinUsesExtractor(
val returnType = getter?.returnType ?: setter?.valueParameters?.singleOrNull()?.type ?: pluginContext.irBuiltIns.unitType
val typeParams = getFunctionTypeParameters(func)
- getFunctionLabel(p.parent, parentId, p.name.asString(), listOf(), returnType, ext, typeParams, classTypeArgsIncludingOuterClasses, overridesCollectionsMethod = false, javaSignature = null, addParameterWildcardsByDefault = false, prefix = "property")
+ getFunctionLabel(p.parent, parentId, p.name.asString(), listOf(), returnType, ext.type, typeParams, classTypeArgsIncludingOuterClasses, overridesCollectionsMethod = false, javaSignature = null, addParameterWildcardsByDefault = false, prefix = "property")
}
}
diff --git a/java/ql/lib/semmle/code/java/Element.qll b/java/ql/lib/semmle/code/java/Element.qll
index d2cbfbc120e..0120cdc158f 100644
--- a/java/ql/lib/semmle/code/java/Element.qll
+++ b/java/ql/lib/semmle/code/java/Element.qll
@@ -65,6 +65,8 @@ class Element extends @element, Top {
i = 8 and result = "Proxy static method for a @JvmStatic-annotated function or property"
or
i = 9 and result = "Forwarder for a @JvmOverloads-annotated function"
+ or
+ i = 10 and result = "Forwarder for Kotlin calls that need default arguments filling in"
)
}
}
diff --git a/java/ql/test/kotlin/library-tests/parameter-defaults/PrintAst.expected b/java/ql/test/kotlin/library-tests/parameter-defaults/PrintAst.expected
new file mode 100644
index 00000000000..068b34f79b4
--- /dev/null
+++ b/java/ql/test/kotlin/library-tests/parameter-defaults/PrintAst.expected
@@ -0,0 +1,849 @@
+test.kt:
+# 0| [CompilationUnit] test
+# 0| 1: [Class] TestKt
+# 1| 1: [Method] sink
+# 1| 3: [TypeAccess] Unit
+#-----| 4: (Parameters)
+# 1| 0: [Parameter] a
+# 1| 0: [TypeAccess] Object
+# 1| 5: [BlockStmt] { ... }
+# 3| 2: [Class] TestMember
+# 3| 1: [Constructor] TestMember
+# 3| 5: [BlockStmt] { ... }
+# 3| 0: [SuperConstructorInvocationStmt] super(...)
+# 3| 1: [BlockStmt] { ... }
+# 5| 2: [Method] f
+# 5| 3: [TypeAccess] Unit
+#-----| 4: (Parameters)
+# 5| 0: [Parameter] x
+# 5| 0: [TypeAccess] String
+# 5| 1: [Parameter] y
+# 5| 0: [TypeAccess] String
+# 5| 2: [Parameter] z
+# 5| 0: [TypeAccess] String
+# 5| 5: [BlockStmt] { ... }
+# 6| 0: [ExprStmt] ;
+# 6| 0: [MethodAccess] sink(...)
+# 6| -1: [TypeAccess] TestKt
+# 6| 0: [VarAccess] y
+# 5| 3: [Method] f$default
+# 5| 3: [TypeAccess] Unit
+#-----| 4: (Parameters)
+# 5| 0: [Parameter] p0
+# 5| 0: [TypeAccess] TestMember
+# 5| 1: [Parameter] p1
+# 5| 0: [TypeAccess] String
+# 5| 2: [Parameter] p2
+# 5| 0: [TypeAccess] String
+# 5| 3: [Parameter] p3
+# 5| 0: [TypeAccess] String
+# 5| 4: [Parameter] p4
+# 5| 0: [TypeAccess] int
+# 5| 5: [Parameter] p5
+# 5| 0: [TypeAccess] Object
+# 5| 5: [BlockStmt] { ... }
+# 5| 0: [IfStmt] if (...)
+# 5| 0: [EQExpr] ... == ...
+# 5| 0: [AndBitwiseExpr] ... & ...
+# 5| 0: [IntegerLiteral] 2
+# 5| 1: [VarAccess] p4
+# 5| 1: [IntegerLiteral] 0
+# 5| 1: [ExprStmt] ;
+# 5| 0: [AssignExpr] ...=...
+# 5| 0: [VarAccess] p2
+# 5| 1: [VarAccess] p1
+# 5| 1: [IfStmt] if (...)
+# 5| 0: [EQExpr] ... == ...
+# 5| 0: [AndBitwiseExpr] ... & ...
+# 5| 0: [IntegerLiteral] 4
+# 5| 1: [VarAccess] p4
+# 5| 1: [IntegerLiteral] 0
+# 5| 1: [ExprStmt] ;
+# 5| 0: [AssignExpr] ...=...
+# 5| 0: [VarAccess] p3
+# 5| 1: [StringLiteral] hello world
+# 5| 2: [ReturnStmt] return ...
+# 5| 0: [MethodAccess] f(...)
+# 5| -1: [VarAccess] p0
+# 5| 0: [VarAccess] p1
+# 5| 1: [VarAccess] p2
+# 5| 2: [VarAccess] p3
+# 9| 4: [Method] user
+# 9| 3: [TypeAccess] Unit
+# 9| 5: [BlockStmt] { ... }
+# 10| 0: [ExprStmt] ;
+# 10| 0: [MethodAccess] f$default(...)
+# 10| -1: [TypeAccess] TestMember
+# 10| 0: [ThisAccess] this
+# 10| 1: [StringLiteral] member sunk
+# 1| 2: [NullLiteral] null
+# 1| 3: [NullLiteral] null
+# 1| 4: [IntegerLiteral] 1
+# 1| 5: [NullLiteral] null
+# 11| 1: [ExprStmt] ;
+# 11| 0: [MethodAccess] f$default(...)
+# 11| -1: [TypeAccess] TestMember
+# 11| 0: [ThisAccess] this
+# 11| 1: [StringLiteral] member sunk fp
+# 11| 2: [StringLiteral] member sunk 2
+# 1| 3: [NullLiteral] null
+# 1| 4: [IntegerLiteral] 3
+# 1| 5: [NullLiteral] null
+# 12| 2: [ExprStmt] ;
+# 12| 0: [MethodAccess] f(...)
+# 12| -1: [ThisAccess] this
+# 12| 0: [StringLiteral] not sunk
+# 12| 1: [StringLiteral] member sunk 3
+# 12| 2: [StringLiteral] not sunk
+# 17| 3: [Class] TestExtensionMember
+# 17| 1: [Constructor] TestExtensionMember
+# 17| 5: [BlockStmt] { ... }
+# 17| 0: [SuperConstructorInvocationStmt] super(...)
+# 17| 1: [BlockStmt] { ... }
+# 19| 2: [ExtensionMethod] f
+# 19| 3: [TypeAccess] Unit
+#-----| 4: (Parameters)
+# 19| 0: [Parameter]
+# 19| 0: [TypeAccess] String
+# 19| 1: [Parameter] x
+# 19| 0: [TypeAccess] String
+# 19| 2: [Parameter] y
+# 19| 0: [TypeAccess] String
+# 19| 3: [Parameter] z
+# 19| 0: [TypeAccess] String
+# 19| 5: [BlockStmt] { ... }
+# 20| 0: [ExprStmt] ;
+# 20| 0: [MethodAccess] sink(...)
+# 20| -1: [TypeAccess] TestKt
+# 20| 0: [ExtensionReceiverAccess] this
+# 21| 1: [ExprStmt] ;
+# 21| 0: [MethodAccess] sink(...)
+# 21| -1: [TypeAccess] TestKt
+# 21| 0: [VarAccess] y
+# 19| 3: [Method] f$default
+# 19| 3: [TypeAccess] Unit
+#-----| 4: (Parameters)
+# 19| 0: [Parameter] p0
+# 19| 0: [TypeAccess] String
+# 19| 1: [Parameter] p1
+# 19| 0: [TypeAccess] TestExtensionMember
+# 19| 2: [Parameter] p2
+# 19| 0: [TypeAccess] String
+# 19| 3: [Parameter] p3
+# 19| 0: [TypeAccess] String
+# 19| 4: [Parameter] p4
+# 19| 0: [TypeAccess] String
+# 19| 5: [Parameter] p5
+# 19| 0: [TypeAccess] int
+# 19| 6: [Parameter] p6
+# 19| 0: [TypeAccess] Object
+# 19| 5: [BlockStmt] { ... }
+# 19| 0: [IfStmt] if (...)
+# 19| 0: [EQExpr] ... == ...
+# 19| 0: [AndBitwiseExpr] ... & ...
+# 19| 0: [IntegerLiteral] 2
+# 19| 1: [VarAccess] p5
+# 19| 1: [IntegerLiteral] 0
+# 19| 1: [ExprStmt] ;
+# 19| 0: [AssignExpr] ...=...
+# 19| 0: [VarAccess] p3
+# 19| 1: [VarAccess] p2
+# 19| 1: [IfStmt] if (...)
+# 19| 0: [EQExpr] ... == ...
+# 19| 0: [AndBitwiseExpr] ... & ...
+# 19| 0: [IntegerLiteral] 4
+# 19| 1: [VarAccess] p5
+# 19| 1: [IntegerLiteral] 0
+# 19| 1: [ExprStmt] ;
+# 19| 0: [AssignExpr] ...=...
+# 19| 0: [VarAccess] p4
+# 19| 1: [StringLiteral] hello world
+# 19| 2: [ReturnStmt] return ...
+# 19| 0: [MethodAccess] f(...)
+# 19| -1: [VarAccess] p1
+# 19| 0: [VarAccess] p0
+# 19| 1: [VarAccess] p2
+# 19| 2: [VarAccess] p3
+# 19| 3: [VarAccess] p4
+# 24| 4: [Method] user
+# 24| 3: [TypeAccess] Unit
+#-----| 4: (Parameters)
+# 24| 0: [Parameter] sunk
+# 24| 0: [TypeAccess] String
+# 24| 5: [BlockStmt] { ... }
+# 25| 0: [ExprStmt] ;
+# 25| 0: [MethodAccess] f$default(...)
+# 25| -1: [TypeAccess] TestExtensionMember
+# 25| 0: [VarAccess] sunk
+# 25| 1: [ThisAccess] this
+# 25| 2: [StringLiteral] extension sunk
+# 1| 3: [NullLiteral] null
+# 1| 4: [NullLiteral] null
+# 1| 5: [IntegerLiteral] 1
+# 1| 6: [NullLiteral] null
+# 26| 1: [ExprStmt] ;
+# 26| 0: [MethodAccess] f$default(...)
+# 26| -1: [TypeAccess] TestExtensionMember
+# 26| 0: [VarAccess] sunk
+# 26| 1: [ThisAccess] this
+# 26| 2: [StringLiteral] extension sunk fp
+# 26| 3: [StringLiteral] extension sunk 2
+# 1| 4: [NullLiteral] null
+# 1| 5: [IntegerLiteral] 3
+# 1| 6: [NullLiteral] null
+# 27| 2: [ExprStmt] ;
+# 27| 0: [MethodAccess] f(...)
+# 27| -1: [ThisAccess] this
+# 27| 0: [VarAccess] sunk
+# 27| 1: [StringLiteral] not sunk
+# 27| 2: [StringLiteral] extension sunk 3
+# 27| 3: [StringLiteral] not sunk
+# 32| 4: [Class] TestStaticMember
+# 32| 1: [Constructor] TestStaticMember
+# 32| 5: [BlockStmt] { ... }
+# 32| 0: [SuperConstructorInvocationStmt] super(...)
+# 32| 1: [BlockStmt] { ... }
+# 34| 2: [Method] f
+# 34| 3: [TypeAccess] Unit
+#-----| 4: (Parameters)
+# 34| 0: [Parameter] x
+# 34| 0: [TypeAccess] String
+# 34| 1: [Parameter] y
+# 34| 0: [TypeAccess] String
+# 34| 2: [Parameter] z
+# 34| 0: [TypeAccess] String
+# 34| 5: [BlockStmt] { ... }
+# 35| 0: [ExprStmt] ;
+# 35| 0: [MethodAccess] sink(...)
+# 35| -1: [TypeAccess] TestKt
+# 35| 0: [VarAccess] y
+# 34| 3: [Method] f$default
+# 34| 3: [TypeAccess] Unit
+#-----| 4: (Parameters)
+# 34| 0: [Parameter] p0
+# 34| 0: [TypeAccess] String
+# 34| 1: [Parameter] p1
+# 34| 0: [TypeAccess] String
+# 34| 2: [Parameter] p2
+# 34| 0: [TypeAccess] String
+# 34| 3: [Parameter] p3
+# 34| 0: [TypeAccess] int
+# 34| 4: [Parameter] p4
+# 34| 0: [TypeAccess] Object
+# 34| 5: [BlockStmt] { ... }
+# 34| 0: [IfStmt] if (...)
+# 34| 0: [EQExpr] ... == ...
+# 34| 0: [AndBitwiseExpr] ... & ...
+# 34| 0: [IntegerLiteral] 2
+# 34| 1: [VarAccess] p3
+# 34| 1: [IntegerLiteral] 0
+# 34| 1: [ExprStmt] ;
+# 34| 0: [AssignExpr] ...=...
+# 34| 0: [VarAccess] p1
+# 34| 1: [VarAccess] p0
+# 34| 1: [IfStmt] if (...)
+# 34| 0: [EQExpr] ... == ...
+# 34| 0: [AndBitwiseExpr] ... & ...
+# 34| 0: [IntegerLiteral] 4
+# 34| 1: [VarAccess] p3
+# 34| 1: [IntegerLiteral] 0
+# 34| 1: [ExprStmt] ;
+# 34| 0: [AssignExpr] ...=...
+# 34| 0: [VarAccess] p2
+# 34| 1: [StringLiteral] hello world
+# 34| 2: [ReturnStmt] return ...
+# 34| 0: [MethodAccess] f(...)
+# 34| -1: [TypeAccess] TestStaticMember
+# 34| 0: [VarAccess] p0
+# 34| 1: [VarAccess] p1
+# 34| 2: [VarAccess] p2
+# 38| 4: [Method] user
+# 38| 3: [TypeAccess] Unit
+# 38| 5: [BlockStmt] { ... }
+# 39| 0: [ExprStmt] ;
+# 39| 0: [MethodAccess] f$default(...)
+# 39| -1: [TypeAccess] TestStaticMember
+# 39| 0: [StringLiteral] static sunk
+# 1| 1: [NullLiteral] null
+# 1| 2: [NullLiteral] null
+# 1| 3: [IntegerLiteral] 1
+# 1| 4: [NullLiteral] null
+# 40| 1: [ExprStmt] ;
+# 40| 0: [MethodAccess] f$default(...)
+# 40| -1: [TypeAccess] TestStaticMember
+# 40| 0: [StringLiteral] static sunk fp
+# 40| 1: [StringLiteral] static sunk 2
+# 1| 2: [NullLiteral] null
+# 1| 3: [IntegerLiteral] 3
+# 1| 4: [NullLiteral] null
+# 41| 2: [ExprStmt] ;
+# 41| 0: [MethodAccess] f(...)
+# 41| -1: [TypeAccess] TestStaticMember
+# 41| 0: [StringLiteral] not sunk
+# 41| 1: [StringLiteral] static sunk 3
+# 41| 2: [StringLiteral] not sunk
+# 46| 5: [Class] ExtendMe
+# 46| 1: [Constructor] ExtendMe
+# 46| 5: [BlockStmt] { ... }
+# 46| 0: [SuperConstructorInvocationStmt] super(...)
+# 46| 1: [BlockStmt] { ... }
+# 48| 2: [Method] f
+# 48| 3: [TypeAccess] String
+#-----| 4: (Parameters)
+# 48| 0: [Parameter] x
+# 48| 0: [TypeAccess] String
+# 48| 5: [BlockStmt] { ... }
+# 48| 0: [ReturnStmt] return ...
+# 48| 0: [VarAccess] x
+# 52| 6: [Class] TestReceiverReferences
+# 52| 1: [Constructor] TestReceiverReferences
+# 52| 5: [BlockStmt] { ... }
+# 52| 0: [SuperConstructorInvocationStmt] super(...)
+# 52| 1: [BlockStmt] { ... }
+# 54| 2: [Method] g
+# 54| 3: [TypeAccess] String
+#-----| 4: (Parameters)
+# 54| 0: [Parameter] x
+# 54| 0: [TypeAccess] String
+# 54| 5: [BlockStmt] { ... }
+# 54| 0: [ReturnStmt] return ...
+# 54| 0: [VarAccess] x
+# 56| 3: [ExtensionMethod] test
+# 56| 3: [TypeAccess] Unit
+#-----| 4: (Parameters)
+# 56| 0: [Parameter]
+# 56| 0: [TypeAccess] ExtendMe
+# 56| 1: [Parameter] x
+# 56| 0: [TypeAccess] String
+# 56| 2: [Parameter] y
+# 56| 0: [TypeAccess] String
+# 56| 3: [Parameter] z
+# 56| 0: [TypeAccess] String
+# 56| 5: [BlockStmt] { ... }
+# 57| 0: [ExprStmt] ;
+# 57| 0: [MethodAccess] sink(...)
+# 57| -1: [TypeAccess] TestKt
+# 57| 0: [VarAccess] y
+# 56| 4: [Method] test$default
+# 56| 3: [TypeAccess] Unit
+#-----| 4: (Parameters)
+# 56| 0: [Parameter] p0
+# 56| 0: [TypeAccess] ExtendMe
+# 56| 1: [Parameter] p1
+# 56| 0: [TypeAccess] TestReceiverReferences
+# 56| 2: [Parameter] p2
+# 56| 0: [TypeAccess] String
+# 56| 3: [Parameter] p3
+# 56| 0: [TypeAccess] String
+# 56| 4: [Parameter] p4
+# 56| 0: [TypeAccess] String
+# 56| 5: [Parameter] p5
+# 56| 0: [TypeAccess] int
+# 56| 6: [Parameter] p6
+# 56| 0: [TypeAccess] Object
+# 56| 5: [BlockStmt] { ... }
+# 56| 0: [IfStmt] if (...)
+# 56| 0: [EQExpr] ... == ...
+# 56| 0: [AndBitwiseExpr] ... & ...
+# 56| 0: [IntegerLiteral] 2
+# 56| 1: [VarAccess] p5
+# 56| 1: [IntegerLiteral] 0
+# 56| 1: [ExprStmt] ;
+# 56| 0: [AssignExpr] ...=...
+# 56| 0: [VarAccess] p3
+# 56| 1: [MethodAccess] f(...)
+# 56| -1: [VarAccess] p0
+# 56| 0: [MethodAccess] g(...)
+# 56| -1: [VarAccess] p1
+# 56| 0: [VarAccess] p2
+# 56| 1: [IfStmt] if (...)
+# 56| 0: [EQExpr] ... == ...
+# 56| 0: [AndBitwiseExpr] ... & ...
+# 56| 0: [IntegerLiteral] 4
+# 56| 1: [VarAccess] p5
+# 56| 1: [IntegerLiteral] 0
+# 56| 1: [ExprStmt] ;
+# 56| 0: [AssignExpr] ...=...
+# 56| 0: [VarAccess] p4
+# 56| 1: [StringLiteral] hello world
+# 56| 2: [ReturnStmt] return ...
+# 56| 0: [MethodAccess] test(...)
+# 56| -1: [VarAccess] p1
+# 56| 0: [VarAccess] p0
+# 56| 1: [VarAccess] p2
+# 56| 2: [VarAccess] p3
+# 56| 3: [VarAccess] p4
+# 60| 5: [Method] user
+# 60| 3: [TypeAccess] Unit
+#-----| 4: (Parameters)
+# 60| 0: [Parameter] t
+# 60| 0: [TypeAccess] ExtendMe
+# 60| 5: [BlockStmt] { ... }
+# 61| 0: [ExprStmt] ;
+# 61| 0: [MethodAccess] test$default(...)
+# 61| -1: [TypeAccess] TestReceiverReferences
+# 61| 0: [VarAccess] t
+# 61| 1: [ThisAccess] this
+# 61| 2: [StringLiteral] receiver refs sunk
+# 1| 3: [NullLiteral] null
+# 1| 4: [NullLiteral] null
+# 1| 5: [IntegerLiteral] 1
+# 1| 6: [NullLiteral] null
+# 62| 1: [ExprStmt] ;
+# 62| 0: [MethodAccess] test$default(...)
+# 62| -1: [TypeAccess] TestReceiverReferences
+# 62| 0: [VarAccess] t
+# 62| 1: [ThisAccess] this
+# 62| 2: [StringLiteral] receiver refs sunk fp
+# 62| 3: [StringLiteral] receiver refs sunk 2
+# 1| 4: [NullLiteral] null
+# 1| 5: [IntegerLiteral] 3
+# 1| 6: [NullLiteral] null
+# 63| 2: [ExprStmt] ;
+# 63| 0: [MethodAccess] test(...)
+# 63| -1: [ThisAccess] this
+# 63| 0: [VarAccess] t
+# 63| 1: [StringLiteral] not sunk
+# 63| 2: [StringLiteral] receiver refs sunk 3
+# 63| 3: [StringLiteral] not sunk
+# 68| 7: [Class] TestConstructor
+# 68| 1: [Constructor] TestConstructor
+#-----| 4: (Parameters)
+# 68| 0: [Parameter] x
+# 68| 0: [TypeAccess] String
+# 68| 1: [Parameter] y
+# 68| 0: [TypeAccess] String
+# 68| 2: [Parameter] z
+# 68| 0: [TypeAccess] String
+# 68| 5: [BlockStmt] { ... }
+# 68| 0: [SuperConstructorInvocationStmt] super(...)
+# 68| 1: [BlockStmt] { ... }
+# 71| 0: [ExprStmt] ;
+# 71| 0: [MethodAccess] sink(...)
+# 71| -1: [TypeAccess] TestKt
+# 71| 0: [VarAccess] y
+# 68| 2: [Constructor] TestConstructor
+#-----| 4: (Parameters)
+# 68| 0: [Parameter] p0
+# 68| 0: [TypeAccess] String
+# 68| 1: [Parameter] p1
+# 68| 0: [TypeAccess] String
+# 68| 2: [Parameter] p2
+# 68| 0: [TypeAccess] String
+# 68| 3: [Parameter] p3
+# 68| 0: [TypeAccess] int
+# 68| 4: [Parameter] p4
+# 68| 0: [TypeAccess] DefaultConstructorMarker
+# 68| 5: [BlockStmt] { ... }
+# 68| 0: [IfStmt] if (...)
+# 68| 0: [EQExpr] ... == ...
+# 68| 0: [AndBitwiseExpr] ... & ...
+# 68| 0: [IntegerLiteral] 2
+# 68| 1: [VarAccess] p3
+# 68| 1: [IntegerLiteral] 0
+# 68| 1: [ExprStmt] ;
+# 68| 0: [AssignExpr] ...=...
+# 68| 0: [VarAccess] p1
+# 68| 1: [VarAccess] p0
+# 68| 1: [IfStmt] if (...)
+# 68| 0: [EQExpr] ... == ...
+# 68| 0: [AndBitwiseExpr] ... & ...
+# 68| 0: [IntegerLiteral] 4
+# 68| 1: [VarAccess] p3
+# 68| 1: [IntegerLiteral] 0
+# 68| 1: [ExprStmt] ;
+# 68| 0: [AssignExpr] ...=...
+# 68| 0: [VarAccess] p2
+# 68| 1: [StringLiteral] hello world
+# 68| 2: [ThisConstructorInvocationStmt] this(...)
+# 68| 0: [VarAccess] p0
+# 68| 1: [VarAccess] p1
+# 68| 2: [VarAccess] p2
+# 74| 3: [Method] user
+# 74| 3: [TypeAccess] Unit
+# 74| 5: [BlockStmt] { ... }
+# 75| 0: [ExprStmt] ;
+# 75| 0: [ImplicitCoercionToUnitExpr]
+# 75| 0: [TypeAccess] Unit
+# 75| 1: [ClassInstanceExpr] new TestConstructor(...)
+# 75| -3: [TypeAccess] TestConstructor
+# 75| 0: [StringLiteral] constructor sunk
+# 1| 1: [NullLiteral] null
+# 1| 2: [NullLiteral] null
+# 1| 3: [IntegerLiteral] 1
+# 1| 4: [NullLiteral] null
+# 76| 1: [ExprStmt] ;
+# 76| 0: [ImplicitCoercionToUnitExpr]
+# 76| 0: [TypeAccess] Unit
+# 76| 1: [ClassInstanceExpr] new TestConstructor(...)
+# 76| -3: [TypeAccess] TestConstructor
+# 76| 0: [StringLiteral] constructor sunk fp
+# 76| 1: [StringLiteral] constructor sunk 2
+# 1| 2: [NullLiteral] null
+# 1| 3: [IntegerLiteral] 3
+# 1| 4: [NullLiteral] null
+# 77| 2: [ExprStmt] ;
+# 77| 0: [ImplicitCoercionToUnitExpr]
+# 77| 0: [TypeAccess] Unit
+# 77| 1: [ClassInstanceExpr] new TestConstructor(...)
+# 77| -3: [TypeAccess] TestConstructor
+# 77| 0: [StringLiteral] not sunk
+# 77| 1: [StringLiteral] constructor sunk 3
+# 77| 2: [StringLiteral] not sunk
+# 82| 8: [Class] TestLocal
+# 82| 1: [Constructor] TestLocal
+# 82| 5: [BlockStmt] { ... }
+# 82| 0: [SuperConstructorInvocationStmt] super(...)
+# 82| 1: [BlockStmt] { ... }
+# 84| 2: [Method] enclosing
+# 84| 3: [TypeAccess] Unit
+# 84| 5: [BlockStmt] { ... }
+# 86| 0: [LocalTypeDeclStmt] class ...
+# 86| 0: [LocalClass]
+# 86| 1: [Constructor]
+# 86| 5: [BlockStmt] { ... }
+# 86| 0: [SuperConstructorInvocationStmt] super(...)
+# 86| 2: [Method] f
+# 86| 3: [TypeAccess] Unit
+#-----| 4: (Parameters)
+# 86| 0: [Parameter] x
+# 86| 0: [TypeAccess] String
+# 86| 1: [Parameter] y
+# 86| 0: [TypeAccess] String
+# 86| 2: [Parameter] z
+# 86| 0: [TypeAccess] String
+# 86| 5: [BlockStmt] { ... }
+# 87| 0: [ExprStmt] ;
+# 87| 0: [MethodAccess] sink(...)
+# 87| -1: [TypeAccess] TestKt
+# 87| 0: [VarAccess] y
+# 86| 3: [Method] f$default
+# 86| 3: [TypeAccess] Unit
+#-----| 4: (Parameters)
+# 86| 0: [Parameter] p0
+# 86| 0: [TypeAccess] String
+# 86| 1: [Parameter] p1
+# 86| 0: [TypeAccess] String
+# 86| 2: [Parameter] p2
+# 86| 0: [TypeAccess] String
+# 86| 3: [Parameter] p3
+# 86| 0: [TypeAccess] int
+# 86| 4: [Parameter] p4
+# 86| 0: [TypeAccess] Object
+# 86| 5: [BlockStmt] { ... }
+# 86| 0: [IfStmt] if (...)
+# 86| 0: [EQExpr] ... == ...
+# 86| 0: [AndBitwiseExpr] ... & ...
+# 86| 0: [IntegerLiteral] 2
+# 86| 1: [VarAccess] p3
+# 86| 1: [IntegerLiteral] 0
+# 86| 1: [ExprStmt] ;
+# 86| 0: [AssignExpr] ...=...
+# 86| 0: [VarAccess] p1
+# 86| 1: [VarAccess] p0
+# 86| 1: [IfStmt] if (...)
+# 86| 0: [EQExpr] ... == ...
+# 86| 0: [AndBitwiseExpr] ... & ...
+# 86| 0: [IntegerLiteral] 4
+# 86| 1: [VarAccess] p3
+# 86| 1: [IntegerLiteral] 0
+# 86| 1: [ExprStmt] ;
+# 86| 0: [AssignExpr] ...=...
+# 86| 0: [VarAccess] p2
+# 86| 1: [StringLiteral] hello world
+# 86| 2: [ReturnStmt] return ...
+# 86| 0: [MethodAccess] f(...)
+# 86| -1: [ClassInstanceExpr] new (...)
+# 86| -3: [TypeAccess] Object
+# 86| 0: [VarAccess] p0
+# 86| 1: [VarAccess] p1
+# 86| 2: [VarAccess] p2
+# 90| 1: [LocalTypeDeclStmt] class ...
+# 90| 0: [LocalClass]
+# 90| 1: [Constructor]
+# 90| 5: [BlockStmt] { ... }
+# 90| 0: [SuperConstructorInvocationStmt] super(...)
+# 90| 2: [Method] user
+# 90| 3: [TypeAccess] Unit
+# 90| 5: [BlockStmt] { ... }
+# 91| 0: [ExprStmt] ;
+# 91| 0: [MethodAccess] f$default(...)
+# 91| -1: [ClassInstanceExpr] new (...)
+# 91| -3: [TypeAccess] Object
+# 91| 0: [StringLiteral] local sunk
+# 1| 1: [NullLiteral] null
+# 1| 2: [NullLiteral] null
+# 1| 3: [IntegerLiteral] 1
+# 1| 4: [NullLiteral] null
+# 92| 1: [ExprStmt] ;
+# 92| 0: [MethodAccess] f$default(...)
+# 92| -1: [ClassInstanceExpr] new (...)
+# 92| -3: [TypeAccess] Object
+# 92| 0: [StringLiteral] local sunk fp
+# 92| 1: [StringLiteral] local sunk 2
+# 1| 2: [NullLiteral] null
+# 1| 3: [IntegerLiteral] 3
+# 1| 4: [NullLiteral] null
+# 93| 2: [ExprStmt] ;
+# 93| 0: [MethodAccess] f(...)
+# 93| -1: [ClassInstanceExpr] new (...)
+# 93| -3: [TypeAccess] Object
+# 93| 0: [StringLiteral] not sunk
+# 93| 1: [StringLiteral] local sunk 3
+# 93| 2: [StringLiteral] not sunk
+# 100| 9: [Class] TestLocalClass
+# 100| 1: [Constructor] TestLocalClass
+# 100| 5: [BlockStmt] { ... }
+# 100| 0: [SuperConstructorInvocationStmt] super(...)
+# 100| 1: [BlockStmt] { ... }
+# 102| 2: [Method] enclosingFunction
+# 102| 3: [TypeAccess] Unit
+# 102| 5: [BlockStmt] { ... }
+# 104| 0: [LocalTypeDeclStmt] class ...
+# 104| 0: [LocalClass] EnclosingLocalClass
+# 104| 1: [Constructor] EnclosingLocalClass
+# 104| 5: [BlockStmt] { ... }
+# 104| 0: [SuperConstructorInvocationStmt] super(...)
+# 104| 1: [BlockStmt] { ... }
+# 106| 2: [Method] f
+# 106| 3: [TypeAccess] Unit
+#-----| 4: (Parameters)
+# 106| 0: [Parameter] x
+# 106| 0: [TypeAccess] String
+# 106| 1: [Parameter] y
+# 106| 0: [TypeAccess] String
+# 106| 2: [Parameter] z
+# 106| 0: [TypeAccess] String
+# 106| 5: [BlockStmt] { ... }
+# 107| 0: [ExprStmt] ;
+# 107| 0: [MethodAccess] sink(...)
+# 107| -1: [TypeAccess] TestKt
+# 107| 0: [VarAccess] y
+# 106| 3: [Method] f$default
+# 106| 3: [TypeAccess] Unit
+#-----| 4: (Parameters)
+# 106| 0: [Parameter] p0
+# 106| 0: [TypeAccess] EnclosingLocalClass
+# 106| 1: [Parameter] p1
+# 106| 0: [TypeAccess] String
+# 106| 2: [Parameter] p2
+# 106| 0: [TypeAccess] String
+# 106| 3: [Parameter] p3
+# 106| 0: [TypeAccess] String
+# 106| 4: [Parameter] p4
+# 106| 0: [TypeAccess] int
+# 106| 5: [Parameter] p5
+# 106| 0: [TypeAccess] Object
+# 106| 5: [BlockStmt] { ... }
+# 106| 0: [IfStmt] if (...)
+# 106| 0: [EQExpr] ... == ...
+# 106| 0: [AndBitwiseExpr] ... & ...
+# 106| 0: [IntegerLiteral] 2
+# 106| 1: [VarAccess] p4
+# 106| 1: [IntegerLiteral] 0
+# 106| 1: [ExprStmt] ;
+# 106| 0: [AssignExpr] ...=...
+# 106| 0: [VarAccess] p2
+# 106| 1: [VarAccess] p1
+# 106| 1: [IfStmt] if (...)
+# 106| 0: [EQExpr] ... == ...
+# 106| 0: [AndBitwiseExpr] ... & ...
+# 106| 0: [IntegerLiteral] 4
+# 106| 1: [VarAccess] p4
+# 106| 1: [IntegerLiteral] 0
+# 106| 1: [ExprStmt] ;
+# 106| 0: [AssignExpr] ...=...
+# 106| 0: [VarAccess] p3
+# 106| 1: [StringLiteral] hello world
+# 106| 2: [ReturnStmt] return ...
+# 106| 0: [MethodAccess] f(...)
+# 106| -1: [VarAccess] p0
+# 106| 0: [VarAccess] p1
+# 106| 1: [VarAccess] p2
+# 106| 2: [VarAccess] p3
+# 110| 4: [Method] user
+# 110| 3: [TypeAccess] Unit
+# 110| 5: [BlockStmt] { ... }
+# 111| 0: [ExprStmt] ;
+# 111| 0: [MethodAccess] f$default(...)
+# 111| -1: [TypeAccess] EnclosingLocalClass
+# 111| 0: [ThisAccess] this
+# 111| 1: [StringLiteral] local sunk
+# 1| 2: [NullLiteral] null
+# 1| 3: [NullLiteral] null
+# 1| 4: [IntegerLiteral] 1
+# 1| 5: [NullLiteral] null
+# 112| 1: [ExprStmt] ;
+# 112| 0: [MethodAccess] f$default(...)
+# 112| -1: [TypeAccess] EnclosingLocalClass
+# 112| 0: [ThisAccess] this
+# 112| 1: [StringLiteral] local sunk fp
+# 112| 2: [StringLiteral] local sunk 2
+# 1| 3: [NullLiteral] null
+# 1| 4: [IntegerLiteral] 3
+# 1| 5: [NullLiteral] null
+# 113| 2: [ExprStmt] ;
+# 113| 0: [MethodAccess] f(...)
+# 113| -1: [ThisAccess] this
+# 113| 0: [StringLiteral] not sunk
+# 113| 1: [StringLiteral] local sunk 3
+# 113| 2: [StringLiteral] not sunk
+# 122| 10: [Class,GenericType,ParameterizedType] TestGeneric
+#-----| -2: (Generic Parameters)
+# 122| 0: [TypeVariable] T
+# 122| 1: [Constructor] TestGeneric
+# 122| 5: [BlockStmt] { ... }
+# 122| 0: [SuperConstructorInvocationStmt] super(...)
+# 122| 1: [BlockStmt] { ... }
+# 124| 2: [Method] f
+# 124| 3: [TypeAccess] Unit
+#-----| 4: (Parameters)
+# 124| 0: [Parameter] x
+# 124| 0: [TypeAccess] T
+# 124| 1: [Parameter] y
+# 124| 0: [TypeAccess] T
+# 124| 2: [Parameter] z
+# 124| 0: [TypeAccess] T
+# 124| 5: [BlockStmt] { ... }
+# 125| 0: [ExprStmt] ;
+# 125| 0: [MethodAccess] sink(...)
+# 125| -1: [TypeAccess] TestKt
+# 125| 0: [VarAccess] y
+# 124| 3: [Method] f$default
+# 124| 3: [TypeAccess] Unit
+#-----| 4: (Parameters)
+# 124| 0: [Parameter] p0
+# 124| 0: [TypeAccess] TestGeneric<>
+# 124| 1: [Parameter] p1
+# 124| 0: [TypeAccess] Object
+# 124| 2: [Parameter] p2
+# 124| 0: [TypeAccess] Object
+# 124| 3: [Parameter] p3
+# 124| 0: [TypeAccess] Object
+# 124| 4: [Parameter] p4
+# 124| 0: [TypeAccess] int
+# 124| 5: [Parameter] p5
+# 124| 0: [TypeAccess] Object
+# 124| 5: [BlockStmt] { ... }
+# 124| 0: [IfStmt] if (...)
+# 124| 0: [EQExpr] ... == ...
+# 124| 0: [AndBitwiseExpr] ... & ...
+# 124| 0: [IntegerLiteral] 2
+# 124| 1: [VarAccess] p4
+# 124| 1: [IntegerLiteral] 0
+# 124| 1: [ExprStmt] ;
+# 124| 0: [AssignExpr] ...=...
+# 124| 0: [VarAccess] p2
+# 124| 1: [VarAccess] p1
+# 124| 1: [IfStmt] if (...)
+# 124| 0: [EQExpr] ... == ...
+# 124| 0: [AndBitwiseExpr] ... & ...
+# 124| 0: [IntegerLiteral] 4
+# 124| 1: [VarAccess] p4
+# 124| 1: [IntegerLiteral] 0
+# 124| 1: [ExprStmt] ;
+# 124| 0: [AssignExpr] ...=...
+# 124| 0: [VarAccess] p3
+# 124| 1: [NullLiteral] null
+# 124| 2: [ReturnStmt] return ...
+# 124| 0: [MethodAccess] f(...)
+# 124| -1: [VarAccess] p0
+# 124| 0: [VarAccess] p1
+# 124| 1: [VarAccess] p2
+# 124| 2: [VarAccess] p3
+# 128| 4: [Method] user
+# 128| 3: [TypeAccess] Unit
+#-----| 4: (Parameters)
+# 128| 0: [Parameter] tgs
+# 128| 0: [TypeAccess] TestGeneric
+# 128| 0: [TypeAccess] String
+# 128| 1: [Parameter] tcs
+# 128| 0: [TypeAccess] TestGeneric
+# 128| 0: [TypeAccess] CharSequence
+# 128| 5: [BlockStmt] { ... }
+# 129| 0: [ExprStmt] ;
+# 129| 0: [MethodAccess] f$default(...)
+# 129| -1: [TypeAccess] TestGeneric<>
+# 129| 0: [VarAccess] tgs
+# 129| 1: [StringLiteral] generic sunk
+# 1| 2: [NullLiteral] null
+# 1| 3: [NullLiteral] null
+# 1| 4: [IntegerLiteral] 1
+# 1| 5: [NullLiteral] null
+# 130| 1: [ExprStmt] ;
+# 130| 0: [MethodAccess] f$default(...)
+# 130| -1: [TypeAccess] TestGeneric<>
+# 130| 0: [VarAccess] tcs
+# 130| 1: [StringLiteral] generic sunk fp
+# 130| 2: [StringLiteral] generic sunk 2
+# 1| 3: [NullLiteral] null
+# 1| 4: [IntegerLiteral] 3
+# 1| 5: [NullLiteral] null
+# 131| 2: [ExprStmt] ;
+# 131| 0: [MethodAccess] f(...)
+# 131| -1: [VarAccess] tgs
+# 131| 0: [StringLiteral] not sunk
+# 131| 1: [StringLiteral] generic sunk 3
+# 131| 2: [StringLiteral] not sunk
+# 132| 3: [ExprStmt] ;
+# 132| 0: [MethodAccess] f(...)
+# 132| -1: [VarAccess] tcs
+# 132| 0: [StringLiteral] not sunk
+# 132| 1: [StringLiteral] generic sunk 3
+# 132| 2: [StringLiteral] not sunk
+# 135| 5: [Method] testReturn
+# 135| 3: [TypeAccess] T
+#-----| 4: (Parameters)
+# 135| 0: [Parameter] t1
+# 135| 0: [TypeAccess] T
+# 135| 1: [Parameter] t2
+# 135| 0: [TypeAccess] T
+# 135| 5: [BlockStmt] { ... }
+# 135| 0: [ReturnStmt] return ...
+# 135| 0: [VarAccess] t1
+# 135| 6: [Method] testReturn$default
+# 135| 3: [TypeAccess] Object
+#-----| 4: (Parameters)
+# 135| 0: [Parameter] p0
+# 135| 0: [TypeAccess] TestGeneric<>
+# 135| 1: [Parameter] p1
+# 135| 0: [TypeAccess] Object
+# 135| 2: [Parameter] p2
+# 135| 0: [TypeAccess] Object
+# 135| 3: [Parameter] p3
+# 135| 0: [TypeAccess] int
+# 135| 4: [Parameter] p4
+# 135| 0: [TypeAccess] Object
+# 135| 5: [BlockStmt] { ... }
+# 135| 0: [IfStmt] if (...)
+# 135| 0: [EQExpr] ... == ...
+# 135| 0: [AndBitwiseExpr] ... & ...
+# 135| 0: [IntegerLiteral] 2
+# 135| 1: [VarAccess] p3
+# 135| 1: [IntegerLiteral] 0
+# 135| 1: [ExprStmt] ;
+# 135| 0: [AssignExpr] ...=...
+# 135| 0: [VarAccess] p2
+# 135| 1: [NullLiteral] null
+# 135| 1: [ReturnStmt] return ...
+# 135| 0: [MethodAccess] testReturn(...)
+# 135| -1: [VarAccess] p0
+# 135| 0: [VarAccess] p1
+# 135| 1: [VarAccess] p2
+# 137| 7: [Method] testReturnUser
+# 137| 3: [TypeAccess] Unit
+#-----| 4: (Parameters)
+# 137| 0: [Parameter] tgs
+# 137| 0: [TypeAccess] TestGeneric
+# 137| 0: [TypeAccess] String
+# 137| 5: [BlockStmt] { ... }
+# 138| 0: [ExprStmt] ;
+# 138| 0: [MethodAccess] sink(...)
+# 138| -1: [TypeAccess] TestKt
+# 138| 0: [MethodAccess] testReturn$default(...)
+# 138| -1: [TypeAccess] TestGeneric<>
+# 138| 0: [VarAccess] tgs
+# 138| 1: [StringLiteral] sunk return value
+# 1| 2: [NullLiteral] null
+# 1| 3: [IntegerLiteral] 1
+# 1| 4: [NullLiteral] null
diff --git a/java/ql/test/kotlin/library-tests/parameter-defaults/PrintAst.qlref b/java/ql/test/kotlin/library-tests/parameter-defaults/PrintAst.qlref
new file mode 100644
index 00000000000..c7fd5faf239
--- /dev/null
+++ b/java/ql/test/kotlin/library-tests/parameter-defaults/PrintAst.qlref
@@ -0,0 +1 @@
+semmle/code/java/PrintAst.ql
\ No newline at end of file
diff --git a/java/ql/test/kotlin/library-tests/parameter-defaults/flowTest.expected b/java/ql/test/kotlin/library-tests/parameter-defaults/flowTest.expected
new file mode 100644
index 00000000000..37b80612273
--- /dev/null
+++ b/java/ql/test/kotlin/library-tests/parameter-defaults/flowTest.expected
@@ -0,0 +1,2 @@
+shouldBeSunkButIsnt
+shouldntBeSunkButIs
diff --git a/java/ql/test/kotlin/library-tests/parameter-defaults/flowTest.ql b/java/ql/test/kotlin/library-tests/parameter-defaults/flowTest.ql
new file mode 100644
index 00000000000..28151ecdc85
--- /dev/null
+++ b/java/ql/test/kotlin/library-tests/parameter-defaults/flowTest.ql
@@ -0,0 +1,34 @@
+import java
+import semmle.code.java.dataflow.DataFlow
+
+class ShouldNotBeSunk extends StringLiteral {
+ ShouldNotBeSunk() { this.getValue().matches("%not sunk%") }
+}
+
+class ShouldBeSunk extends StringLiteral {
+ ShouldBeSunk() {
+ this.getValue().matches("%sunk%") and
+ not this instanceof ShouldNotBeSunk
+ }
+}
+
+class Config extends DataFlow::Configuration {
+ Config() { this = "Config" }
+
+ override predicate isSource(DataFlow::Node n) {
+ n.asExpr() instanceof ShouldBeSunk or
+ n.asExpr() instanceof ShouldNotBeSunk
+ }
+
+ override predicate isSink(DataFlow::Node n) {
+ n.asExpr().(Argument).getCall().getCallee().getName() = "sink"
+ }
+}
+
+predicate isSunk(StringLiteral sl) {
+ exists(Config c, DataFlow::Node source | c.hasFlow(source, _) and sl = source.asExpr())
+}
+
+query predicate shouldBeSunkButIsnt(ShouldBeSunk src) { not isSunk(src) }
+
+query predicate shouldntBeSunkButIs(ShouldNotBeSunk src) { isSunk(src) }
diff --git a/java/ql/test/kotlin/library-tests/parameter-defaults/test.kt b/java/ql/test/kotlin/library-tests/parameter-defaults/test.kt
new file mode 100644
index 00000000000..144664f0a07
--- /dev/null
+++ b/java/ql/test/kotlin/library-tests/parameter-defaults/test.kt
@@ -0,0 +1,141 @@
+fun sink(a: Any?) { }
+
+class TestMember {
+
+ fun f(x: String, y: String = x, z: String = "hello world") {
+ sink(y)
+ }
+
+ fun user() {
+ f("member sunk")
+ f("member sunk fp", "member sunk 2")
+ f("not sunk", "member sunk 3", "not sunk")
+ }
+
+}
+
+class TestExtensionMember {
+
+ fun String.f(x: String, y: String = x, z: String = "hello world") {
+ sink(this)
+ sink(y)
+ }
+
+ fun user(sunk: String) {
+ sunk.f("extension sunk")
+ sunk.f("extension sunk fp", "extension sunk 2")
+ sunk.f("not sunk", "extension sunk 3", "not sunk")
+ }
+
+}
+
+object TestStaticMember {
+
+ @JvmStatic fun f(x: String, y: String = x, z: String = "hello world") {
+ sink(y)
+ }
+
+ fun user() {
+ f("static sunk")
+ f("static sunk fp", "static sunk 2")
+ f("not sunk", "static sunk 3", "not sunk")
+ }
+
+}
+
+class ExtendMe {
+
+ fun f(x: String) = x
+
+}
+
+class TestReceiverReferences {
+
+ fun g(x: String) = x
+
+ fun ExtendMe.test(x: String, y: String = this.f(this@TestReceiverReferences.g(x)), z: String = "hello world") {
+ sink(y)
+ }
+
+ fun user(t: ExtendMe) {
+ t.test("receiver refs sunk")
+ t.test("receiver refs sunk fp", "receiver refs sunk 2")
+ t.test("not sunk", "receiver refs sunk 3", "not sunk")
+ }
+
+}
+
+class TestConstructor(x: String, y: String = x, z: String = "hello world") {
+
+ init {
+ sink(y)
+ }
+
+ fun user() {
+ TestConstructor("constructor sunk")
+ TestConstructor("constructor sunk fp", "constructor sunk 2")
+ TestConstructor("not sunk", "constructor sunk 3", "not sunk")
+ }
+
+}
+
+class TestLocal {
+
+ fun enclosing() {
+
+ fun f(x: String, y: String = x, z: String = "hello world") {
+ sink(y)
+ }
+
+ fun user() {
+ f("local sunk")
+ f("local sunk fp", "local sunk 2")
+ f("not sunk", "local sunk 3", "not sunk")
+ }
+
+ }
+
+}
+
+class TestLocalClass {
+
+ fun enclosingFunction() {
+
+ class EnclosingLocalClass {
+
+ fun f(x: String, y: String = x, z: String = "hello world") {
+ sink(y)
+ }
+
+ fun user() {
+ f("local sunk")
+ f("local sunk fp", "local sunk 2")
+ f("not sunk", "local sunk 3", "not sunk")
+ }
+
+ }
+
+ }
+
+}
+
+class TestGeneric {
+
+ fun f(x: T, y: T = x, z: T? = null) {
+ sink(y)
+ }
+
+ fun user(tgs: TestGeneric, tcs: TestGeneric) {
+ tgs.f("generic sunk")
+ tcs.f("generic sunk fp", "generic sunk 2")
+ tgs.f("not sunk", "generic sunk 3", "not sunk")
+ tcs.f("not sunk", "generic sunk 3", "not sunk")
+ }
+
+ fun testReturn(t1: T, t2: T? = null) = t1
+
+ fun testReturnUser(tgs: TestGeneric) {
+ sink(tgs.testReturn("sunk return value"))
+ }
+
+}
From 6cc74da00431acc5ead87b8eba60f4c457a3e124 Mon Sep 17 00:00:00 2001
From: Chris Smowton
Date: Tue, 4 Oct 2022 17:21:21 +0100
Subject: [PATCH 411/991] Defaults function extraction: respect the
extract-type-accesses flag
---
.../src/main/kotlin/KotlinFileExtractor.kt | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt
index d5615a1cfa9..8aec0a0acd7 100644
--- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt
+++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt
@@ -835,12 +835,12 @@ open class KotlinFileExtractor(
forceExtractFunction(f, parentId, extractBody, extractMethodAndParameterTypeAccesses, typeSubstitution, classTypeArgsIncludingOuterClasses).also {
// The defaults-forwarder function is a static utility, not a member, so we only need to extract this for the unspecialised instance of this class.
if (classTypeArgsIncludingOuterClasses.isNullOrEmpty())
- extractDefaultsFunction(f, parentId, extractBody)
+ extractDefaultsFunction(f, parentId, extractBody, extractMethodAndParameterTypeAccesses)
extractGeneratedOverloads(f, parentId, null, extractBody, extractMethodAndParameterTypeAccesses, typeSubstitution, classTypeArgsIncludingOuterClasses)
}
}
- private fun extractDefaultsFunction(f: IrFunction, parentId: Label, extractBody: Boolean) {
+ private fun extractDefaultsFunction(f: IrFunction, parentId: Label, extractBody: Boolean, extractMethodAndParameterTypeAccesses: Boolean) {
if (f.valueParameters.none { it.defaultValue != null })
return
@@ -852,7 +852,8 @@ open class KotlinFileExtractor(
val allParamTypeResults = parameterTypes.mapIndexed { i, paramType ->
val paramId = tw.getLabelFor(getValueParameterLabel(id, i))
extractValueParameter(paramId, paramType, "p$i", locId, id, i, paramId, isVararg = false, syntheticParameterNames = true, isCrossinline = false, isNoinline = false).also {
- extractTypeAccess(useType(paramType), locId, paramId, -1)
+ if (extractMethodAndParameterTypeAccesses)
+ extractTypeAccess(useType(paramType), locId, paramId, -1)
}
}
val paramsSignature = allParamTypeResults.joinToString(separator = ",", prefix = "(", postfix = ")") { it.javaResult.signature }
@@ -863,7 +864,7 @@ open class KotlinFileExtractor(
extractConstructor(constrId, shortName, paramsSignature, parentId, constrId)
} else {
val methodId = id.cast()
- extractMethod(methodId, locId, shortName, erase(f.returnType), paramsSignature, parentId, methodId, origin = null, extractTypeAccess = true)
+ extractMethod(methodId, locId, shortName, erase(f.returnType), paramsSignature, parentId, methodId, origin = null, extractTypeAccess = extractMethodAndParameterTypeAccesses)
addModifiers(id, "static")
}
tw.writeHasLocation(id, locId)
From 720cf5682b4fb83d80b16b7d2bcb1534a32bd401 Mon Sep 17 00:00:00 2001
From: Chris Smowton
Date: Tue, 4 Oct 2022 17:21:53 +0100
Subject: [PATCH 412/991] Exclude enum constructor invocations from defaults
handling
These seem to provide null arguments even though the constructor doesn't provide defaults, presumably for completion by a later compiler phase.
---
java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt
index 8aec0a0acd7..23b7af20659 100644
--- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt
+++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt
@@ -2938,7 +2938,9 @@ open class KotlinFileExtractor(
}
val locId = tw.getLocation(e)
val valueArgs = (0 until e.valueArgumentsCount).map { e.getValueArgument(it) }
- val anyDefaultArgs = valueArgs.any { it == null }
+ // For now, don't try to use default methods for enum constructor calls,
+ // which have null arguments even though the parameters don't give default values.
+ val anyDefaultArgs = e !is IrEnumConstructorCall && valueArgs.any { it == null }
val id = if (anyDefaultArgs) {
extractNewExpr(getDefaultsMethodLabel(e.symbol.owner).cast(), type, locId, parent, idx, callable, enclosingStmt).also {
extractDefaultsCallArguments(it, e.symbol.owner, callable, enclosingStmt, valueArgs, null, null)
From 6119670be88cbf7a55ef85d1c60be136a2d5030f Mon Sep 17 00:00:00 2001
From: Chris Smowton
Date: Tue, 4 Oct 2022 17:22:57 +0100
Subject: [PATCH 413/991] Suppress use of function type parameters in the
context of building a $defaults method
These methods have erased signatures and no type parameters, so anything that refers to one must itself be erased. For signatures this would be easy, but for potentially deep default expressions these types can occur in various places and need erasing at each occurence.
---
.../src/main/kotlin/KotlinFileExtractor.kt | 4 ++--
.../src/main/kotlin/KotlinUsesExtractor.kt | 17 ++++++++++++++++-
2 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt
index 23b7af20659..b03c84af571 100644
--- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt
+++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt
@@ -878,7 +878,7 @@ open class KotlinFileExtractor(
// n + o'th parameter, where `o` is the parameter offset caused by adding any dispatch receiver to the parameter list.
// Note we don't need to add the extension receiver here because `useValueParameter` always assumes an extension receiver
// will be prepended if one exists.
- DeclarationStackAdjuster(f, OverriddenFunctionAttributes(id, id, locId, nonSyntheticParams)).use {
+ DeclarationStackAdjuster(f, OverriddenFunctionAttributes(id, id, locId, nonSyntheticParams, typeParameters = listOf())).use {
val realParamsVarId = getValueParameterLabel(id, parameterTypes.size - 2)
val intType = pluginContext.irBuiltIns.intType
val paramIdxOffset = listOf(dispatchReceiver, f.extensionReceiverParameter).count { it != null }
@@ -5363,7 +5363,7 @@ open class KotlinFileExtractor(
stack.firstOrNull { it.first == f } ?.second
}
- data class OverriddenFunctionAttributes(val id: Label? = null, val sourceDeclarationId: Label? = null, val sourceLoc: Label? = null, val valueParameters: List? = null)
+ data class OverriddenFunctionAttributes(val id: Label? = null, val sourceDeclarationId: Label? = null, val sourceLoc: Label? = null, val valueParameters: List? = null, val typeParameters: List? = null)
private fun peekDeclStackAsDeclarationParent(elementToReportOn: IrElement): IrDeclarationParent? {
val trapWriter = tw
diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt
index 5873b06c280..3d3bc361ae1 100644
--- a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt
+++ b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt
@@ -658,6 +658,19 @@ open class KotlinUsesExtractor(
RETURN, GENERIC_ARGUMENT, OTHER
}
+ private fun isOnDeclarationStackWithoutTypeParameters(f: IrFunction) =
+ this is KotlinFileExtractor && this.declarationStack.findOverriddenAttributes(f)?.typeParameters?.isEmpty() == true
+
+ private fun isUnavailableTypeParameter(t: IrType) =
+ t is IrSimpleType && t.classifier.owner.let { owner ->
+ owner is IrTypeParameter && owner.parent.let { parent ->
+ parent is IrFunction && isOnDeclarationStackWithoutTypeParameters(parent)
+ }
+ }
+
+ private fun argIsUnavailableTypeParameter(t: IrTypeArgument) =
+ t is IrTypeProjection && isUnavailableTypeParameter(t.type)
+
private fun useSimpleType(s: IrSimpleType, context: TypeContext): TypeResults {
if (s.abbreviation != null) {
// TODO: Extract this information
@@ -729,11 +742,13 @@ open class KotlinUsesExtractor(
}
owner is IrClass -> {
- val args = if (s.isRawType()) null else s.arguments
+ val args = if (s.isRawType() || s.arguments.any { argIsUnavailableTypeParameter(it) }) null else s.arguments
return useSimpleTypeClass(owner, args, s.isNullable())
}
owner is IrTypeParameter -> {
+ if (isUnavailableTypeParameter(s))
+ return useType(erase(s), context)
val javaResult = useTypeParameter(owner)
val aClassId = makeClass("kotlin", "TypeParam") // TODO: Wrong
val kotlinResult = if (true) TypeResult(fakeKotlinType(), "TODO", "TODO") else
From 03c895853bdcbbd24403610a5841b6263bd6fcd5 Mon Sep 17 00:00:00 2001
From: Chris Smowton
Date: Tue, 4 Oct 2022 17:24:52 +0100
Subject: [PATCH 414/991] Clarify test and accept test changes
---
.../jvmoverloads-annotation/PrintAst.expected | 459 +++++++++++++++++-
.../jvmoverloads-annotation/test.expected | 9 +
.../jvmoverloads_flow/test.expected | 48 +-
.../library-tests/jvmoverloads_flow/test.ql | 2 +-
4 files changed, 479 insertions(+), 39 deletions(-)
diff --git a/java/ql/test/kotlin/library-tests/jvmoverloads-annotation/PrintAst.expected b/java/ql/test/kotlin/library-tests/jvmoverloads-annotation/PrintAst.expected
index 44e1e5f5950..4c7b75edc3a 100644
--- a/java/ql/test/kotlin/library-tests/jvmoverloads-annotation/PrintAst.expected
+++ b/java/ql/test/kotlin/library-tests/jvmoverloads-annotation/PrintAst.expected
@@ -69,6 +69,56 @@ test.kt:
# 45| 5: [BlockStmt] { ... }
# 45| 0: [ReturnStmt] return ...
# 45| 0: [VarAccess] a
+# 45| 5: [Method] testExtensionFunction$default
+# 45| 3: [TypeAccess] int
+#-----| 4: (Parameters)
+# 45| 0: [Parameter] p0
+# 45| 0: [TypeAccess] Test
+# 45| 1: [Parameter] p1
+# 45| 0: [TypeAccess] int
+# 45| 2: [Parameter] p2
+# 45| 0: [TypeAccess] String
+# 45| 3: [Parameter] p3
+# 45| 0: [TypeAccess] double
+# 45| 4: [Parameter] p4
+# 45| 0: [TypeAccess] float
+# 45| 5: [Parameter] p5
+# 45| 0: [TypeAccess] boolean
+# 45| 6: [Parameter] p6
+# 45| 0: [TypeAccess] int
+# 45| 7: [Parameter] p7
+# 45| 0: [TypeAccess] Object
+# 45| 5: [BlockStmt] { ... }
+# 45| 0: [IfStmt] if (...)
+# 45| 0: [EQExpr] ... == ...
+# 45| 0: [AndBitwiseExpr] ... & ...
+# 45| 0: [IntegerLiteral] 2
+# 45| 1: [VarAccess] p6
+# 45| 1: [IntegerLiteral] 0
+# 45| 1: [ExprStmt] ;
+# 45| 0: [AssignExpr] ...=...
+# 45| 0: [VarAccess] p2
+# 45| 1: [MethodAccess] getString(...)
+# 45| -1: [TypeAccess] TestKt
+# 45| 1: [IfStmt] if (...)
+# 45| 0: [EQExpr] ... == ...
+# 45| 0: [AndBitwiseExpr] ... & ...
+# 45| 0: [IntegerLiteral] 8
+# 45| 1: [VarAccess] p6
+# 45| 1: [IntegerLiteral] 0
+# 45| 1: [ExprStmt] ;
+# 45| 0: [AssignExpr] ...=...
+# 45| 0: [VarAccess] p4
+# 45| 1: [FloatLiteral] 1.0
+# 45| 2: [ReturnStmt] return ...
+# 45| 0: [MethodAccess] testExtensionFunction(...)
+# 45| -1: [TypeAccess] TestKt
+# 45| 0: [VarAccess] p0
+# 45| 1: [VarAccess] p1
+# 45| 2: [VarAccess] p2
+# 45| 3: [VarAccess] p3
+# 45| 4: [VarAccess] p4
+# 45| 5: [VarAccess] p5
# 3| 2: [Class] Test
# 3| 1: [Constructor] Test
# 3| 5: [BlockStmt] { ... }
@@ -129,7 +179,54 @@ test.kt:
# 6| 5: [BlockStmt] { ... }
# 6| 0: [ReturnStmt] return ...
# 6| 0: [VarAccess] a
-# 9| 5: [Method] testMemberFunction
+# 6| 5: [Method] testStaticFunction$default
+# 6| 3: [TypeAccess] int
+#-----| 4: (Parameters)
+# 6| 0: [Parameter] p0
+# 6| 0: [TypeAccess] int
+# 6| 1: [Parameter] p1
+# 6| 0: [TypeAccess] String
+# 6| 2: [Parameter] p2
+# 6| 0: [TypeAccess] double
+# 6| 3: [Parameter] p3
+# 6| 0: [TypeAccess] float
+# 6| 4: [Parameter] p4
+# 6| 0: [TypeAccess] boolean
+# 6| 5: [Parameter] p5
+# 6| 0: [TypeAccess] int
+# 6| 6: [Parameter] p6
+# 6| 0: [TypeAccess] Object
+# 6| 5: [BlockStmt] { ... }
+# 6| 0: [IfStmt] if (...)
+# 6| 0: [EQExpr] ... == ...
+# 6| 0: [AndBitwiseExpr] ... & ...
+# 6| 0: [IntegerLiteral] 2
+# 6| 1: [VarAccess] p5
+# 6| 1: [IntegerLiteral] 0
+# 6| 1: [ExprStmt] ;
+# 6| 0: [AssignExpr] ...=...
+# 6| 0: [VarAccess] p1
+# 6| 1: [MethodAccess] getString(...)
+# 6| -1: [TypeAccess] TestKt
+# 6| 1: [IfStmt] if (...)
+# 6| 0: [EQExpr] ... == ...
+# 6| 0: [AndBitwiseExpr] ... & ...
+# 6| 0: [IntegerLiteral] 8
+# 6| 1: [VarAccess] p5
+# 6| 1: [IntegerLiteral] 0
+# 6| 1: [ExprStmt] ;
+# 6| 0: [AssignExpr] ...=...
+# 6| 0: [VarAccess] p3
+# 6| 1: [FloatLiteral] 1.0
+# 6| 2: [ReturnStmt] return ...
+# 6| 0: [MethodAccess] testStaticFunction(...)
+# 6| -1: [TypeAccess] Test
+# 6| 0: [VarAccess] p0
+# 6| 1: [VarAccess] p1
+# 6| 2: [VarAccess] p2
+# 6| 3: [VarAccess] p3
+# 6| 4: [VarAccess] p4
+# 9| 6: [Method] testMemberFunction
# 9| 3: [TypeAccess] int
#-----| 4: (Parameters)
# 9| 0: [Parameter] a
@@ -148,7 +245,7 @@ test.kt:
# 9| 2: [VarAccess] c
# 9| 3: [FloatLiteral] 1.0
# 9| 4: [VarAccess] e
-# 9| 6: [Method] testMemberFunction
+# 9| 7: [Method] testMemberFunction
# 9| 3: [TypeAccess] int
#-----| 4: (Parameters)
# 9| 0: [Parameter] a
@@ -168,7 +265,7 @@ test.kt:
# 9| 2: [VarAccess] c
# 9| 3: [FloatLiteral] 1.0
# 9| 4: [VarAccess] e
-# 9| 7: [Method] testMemberFunction
+# 9| 8: [Method] testMemberFunction
# 9| 3: [TypeAccess] int
#-----| 4: (Parameters)
# 9| 0: [Parameter] a
@@ -184,7 +281,56 @@ test.kt:
# 9| 5: [BlockStmt] { ... }
# 9| 0: [ReturnStmt] return ...
# 9| 0: [VarAccess] a
-# 12| 8: [ExtensionMethod] testMemberExtensionFunction
+# 9| 9: [Method] testMemberFunction$default
+# 9| 3: [TypeAccess] int
+#-----| 4: (Parameters)
+# 9| 0: [Parameter] p0
+# 9| 0: [TypeAccess] Test
+# 9| 1: [Parameter] p1
+# 9| 0: [TypeAccess] int
+# 9| 2: [Parameter] p2
+# 9| 0: [TypeAccess] String
+# 9| 3: [Parameter] p3
+# 9| 0: [TypeAccess] double
+# 9| 4: [Parameter] p4
+# 9| 0: [TypeAccess] float
+# 9| 5: [Parameter] p5
+# 9| 0: [TypeAccess] boolean
+# 9| 6: [Parameter] p6
+# 9| 0: [TypeAccess] int
+# 9| 7: [Parameter] p7
+# 9| 0: [TypeAccess] Object
+# 9| 5: [BlockStmt] { ... }
+# 9| 0: [IfStmt] if (...)
+# 9| 0: [EQExpr] ... == ...
+# 9| 0: [AndBitwiseExpr] ... & ...
+# 9| 0: [IntegerLiteral] 2
+# 9| 1: [VarAccess] p6
+# 9| 1: [IntegerLiteral] 0
+# 9| 1: [ExprStmt] ;
+# 9| 0: [AssignExpr] ...=...
+# 9| 0: [VarAccess] p2
+# 9| 1: [MethodAccess] getString(...)
+# 9| -1: [TypeAccess] TestKt
+# 9| 1: [IfStmt] if (...)
+# 9| 0: [EQExpr] ... == ...
+# 9| 0: [AndBitwiseExpr] ... & ...
+# 9| 0: [IntegerLiteral] 8
+# 9| 1: [VarAccess] p6
+# 9| 1: [IntegerLiteral] 0
+# 9| 1: [ExprStmt] ;
+# 9| 0: [AssignExpr] ...=...
+# 9| 0: [VarAccess] p4
+# 9| 1: [FloatLiteral] 1.0
+# 9| 2: [ReturnStmt] return ...
+# 9| 0: [MethodAccess] testMemberFunction(...)
+# 9| -1: [VarAccess] p0
+# 9| 0: [VarAccess] p1
+# 9| 1: [VarAccess] p2
+# 9| 2: [VarAccess] p3
+# 9| 3: [VarAccess] p4
+# 9| 4: [VarAccess] p5
+# 12| 10: [ExtensionMethod] testMemberExtensionFunction
# 12| 3: [TypeAccess] int
#-----| 4: (Parameters)
# 12| 0: [Parameter]
@@ -206,7 +352,7 @@ test.kt:
# 12| 3: [VarAccess] c
# 12| 4: [FloatLiteral] 1.0
# 12| 5: [VarAccess] e
-# 12| 9: [ExtensionMethod] testMemberExtensionFunction
+# 12| 11: [ExtensionMethod] testMemberExtensionFunction
# 12| 3: [TypeAccess] int
#-----| 4: (Parameters)
# 12| 0: [Parameter]
@@ -229,7 +375,7 @@ test.kt:
# 12| 3: [VarAccess] c
# 12| 4: [FloatLiteral] 1.0
# 12| 5: [VarAccess] e
-# 12| 10: [ExtensionMethod] testMemberExtensionFunction
+# 12| 12: [ExtensionMethod] testMemberExtensionFunction
# 12| 3: [TypeAccess] int
#-----| 4: (Parameters)
# 12| 0: [Parameter]
@@ -247,6 +393,58 @@ test.kt:
# 12| 5: [BlockStmt] { ... }
# 12| 0: [ReturnStmt] return ...
# 12| 0: [VarAccess] a
+# 12| 13: [Method] testMemberExtensionFunction$default
+# 12| 3: [TypeAccess] int
+#-----| 4: (Parameters)
+# 12| 0: [Parameter] p0
+# 12| 0: [TypeAccess] Test2
+# 12| 1: [Parameter] p1
+# 12| 0: [TypeAccess] Test
+# 12| 2: [Parameter] p2
+# 12| 0: [TypeAccess] int
+# 12| 3: [Parameter] p3
+# 12| 0: [TypeAccess] String
+# 12| 4: [Parameter] p4
+# 12| 0: [TypeAccess] double
+# 12| 5: [Parameter] p5
+# 12| 0: [TypeAccess] float
+# 12| 6: [Parameter] p6
+# 12| 0: [TypeAccess] boolean
+# 12| 7: [Parameter] p7
+# 12| 0: [TypeAccess] int
+# 12| 8: [Parameter] p8
+# 12| 0: [TypeAccess] Object
+# 12| 5: [BlockStmt] { ... }
+# 12| 0: [IfStmt] if (...)
+# 12| 0: [EQExpr] ... == ...
+# 12| 0: [AndBitwiseExpr] ... & ...
+# 12| 0: [IntegerLiteral] 2
+# 12| 1: [VarAccess] p7
+# 12| 1: [IntegerLiteral] 0
+# 12| 1: [ExprStmt] ;
+# 12| 0: [AssignExpr] ...=...
+# 12| 0: [VarAccess] p3
+# 12| 1: [MethodAccess] getString(...)
+# 12| -1: [TypeAccess] TestKt
+# 12| 1: [IfStmt] if (...)
+# 12| 0: [EQExpr] ... == ...
+# 12| 0: [AndBitwiseExpr] ... & ...
+# 12| 0: [IntegerLiteral] 8
+# 12| 1: [VarAccess] p7
+# 12| 1: [IntegerLiteral] 0
+# 12| 1: [ExprStmt] ;
+# 12| 0: [AssignExpr] ...=...
+# 12| 0: [VarAccess] p5
+# 12| 1: [FloatLiteral] 1.0
+# 12| 2: [ReturnStmt] return ...
+# 12| 0: [MethodAccess] testMemberExtensionFunction(...)
+# 12| -1: [VarAccess] p1
+# 12| 0: [VarAccess] p0
+# 12| 1: [VarAccess] p2
+# 12| 2: [VarAccess] p3
+# 12| 3: [VarAccess] p4
+# 12| 4: [VarAccess] p5
+# 12| 5: [VarAccess] p6
# 16| 3: [Class] Test2
# 16| 1: [Constructor] Test2
#-----| 4: (Parameters)
@@ -296,7 +494,51 @@ test.kt:
# 16| 5: [BlockStmt] { ... }
# 16| 0: [SuperConstructorInvocationStmt] super(...)
# 16| 1: [BlockStmt] { ... }
-# 18| 4: [Class] Companion
+# 16| 4: [Constructor] Test2
+#-----| 4: (Parameters)
+# 16| 0: [Parameter] p0
+# 16| 0: [TypeAccess] int
+# 16| 1: [Parameter] p1
+# 16| 0: [TypeAccess] String
+# 16| 2: [Parameter] p2
+# 16| 0: [TypeAccess] double
+# 16| 3: [Parameter] p3
+# 16| 0: [TypeAccess] float
+# 16| 4: [Parameter] p4
+# 16| 0: [TypeAccess] boolean
+# 16| 5: [Parameter] p5
+# 16| 0: [TypeAccess] int
+# 16| 6: [Parameter] p6
+# 16| 0: [TypeAccess] DefaultConstructorMarker
+# 16| 5: [BlockStmt] { ... }
+# 16| 0: [IfStmt] if (...)
+# 16| 0: [EQExpr] ... == ...
+# 16| 0: [AndBitwiseExpr] ... & ...
+# 16| 0: [IntegerLiteral] 2
+# 16| 1: [VarAccess] p5
+# 16| 1: [IntegerLiteral] 0
+# 16| 1: [ExprStmt] ;
+# 16| 0: [AssignExpr] ...=...
+# 16| 0: [VarAccess] p1
+# 16| 1: [MethodAccess] getString(...)
+# 16| -1: [TypeAccess] TestKt
+# 16| 1: [IfStmt] if (...)
+# 16| 0: [EQExpr] ... == ...
+# 16| 0: [AndBitwiseExpr] ... & ...
+# 16| 0: [IntegerLiteral] 8
+# 16| 1: [VarAccess] p5
+# 16| 1: [IntegerLiteral] 0
+# 16| 1: [ExprStmt] ;
+# 16| 0: [AssignExpr] ...=...
+# 16| 0: [VarAccess] p3
+# 16| 1: [FloatLiteral] 1.0
+# 16| 2: [ThisConstructorInvocationStmt] this(...)
+# 16| 0: [VarAccess] p0
+# 16| 1: [VarAccess] p1
+# 16| 2: [VarAccess] p2
+# 16| 3: [VarAccess] p3
+# 16| 4: [VarAccess] p4
+# 18| 5: [Class] Companion
# 18| 1: [Constructor] Companion
# 18| 5: [BlockStmt] { ... }
# 18| 0: [SuperConstructorInvocationStmt] super(...)
@@ -356,7 +598,56 @@ test.kt:
# 21| 5: [BlockStmt] { ... }
# 21| 0: [ReturnStmt] return ...
# 21| 0: [VarAccess] a
-# 24| 5: [Method] testStaticCompanionFunction
+# 21| 5: [Method] testCompanionFunction$default
+# 21| 3: [TypeAccess] int
+#-----| 4: (Parameters)
+# 21| 0: [Parameter] p0
+# 21| 0: [TypeAccess] Companion
+# 21| 1: [Parameter] p1
+# 21| 0: [TypeAccess] int
+# 21| 2: [Parameter] p2
+# 21| 0: [TypeAccess] String
+# 21| 3: [Parameter] p3
+# 21| 0: [TypeAccess] double
+# 21| 4: [Parameter] p4
+# 21| 0: [TypeAccess] float
+# 21| 5: [Parameter] p5
+# 21| 0: [TypeAccess] boolean
+# 21| 6: [Parameter] p6
+# 21| 0: [TypeAccess] int
+# 21| 7: [Parameter] p7
+# 21| 0: [TypeAccess] Object
+# 21| 5: [BlockStmt] { ... }
+# 21| 0: [IfStmt] if (...)
+# 21| 0: [EQExpr] ... == ...
+# 21| 0: [AndBitwiseExpr] ... & ...
+# 21| 0: [IntegerLiteral] 2
+# 21| 1: [VarAccess] p6
+# 21| 1: [IntegerLiteral] 0
+# 21| 1: [ExprStmt] ;
+# 21| 0: [AssignExpr] ...=...
+# 21| 0: [VarAccess] p2
+# 21| 1: [MethodAccess] getString(...)
+# 21| -1: [TypeAccess] TestKt
+# 21| 1: [IfStmt] if (...)
+# 21| 0: [EQExpr] ... == ...
+# 21| 0: [AndBitwiseExpr] ... & ...
+# 21| 0: [IntegerLiteral] 8
+# 21| 1: [VarAccess] p6
+# 21| 1: [IntegerLiteral] 0
+# 21| 1: [ExprStmt] ;
+# 21| 0: [AssignExpr] ...=...
+# 21| 0: [VarAccess] p4
+# 21| 1: [FloatLiteral] 1.0
+# 21| 2: [ReturnStmt] return ...
+# 21| 0: [MethodAccess] testCompanionFunction(...)
+# 21| -1: [VarAccess] p0
+# 21| 0: [VarAccess] p1
+# 21| 1: [VarAccess] p2
+# 21| 2: [VarAccess] p3
+# 21| 3: [VarAccess] p4
+# 21| 4: [VarAccess] p5
+# 24| 6: [Method] testStaticCompanionFunction
# 24| 3: [TypeAccess] int
#-----| 4: (Parameters)
# 24| 0: [Parameter] a
@@ -375,7 +666,7 @@ test.kt:
# 24| 2: [VarAccess] c
# 24| 3: [FloatLiteral] 1.0
# 24| 4: [VarAccess] e
-# 24| 6: [Method] testStaticCompanionFunction
+# 24| 7: [Method] testStaticCompanionFunction
# 24| 3: [TypeAccess] int
#-----| 4: (Parameters)
# 24| 0: [Parameter] a
@@ -395,7 +686,7 @@ test.kt:
# 24| 2: [VarAccess] c
# 24| 3: [FloatLiteral] 1.0
# 24| 4: [VarAccess] e
-# 24| 7: [Method] testStaticCompanionFunction
+# 24| 8: [Method] testStaticCompanionFunction
# 24| 3: [TypeAccess] int
#-----| 4: (Parameters)
# 24| 0: [Parameter] a
@@ -411,7 +702,56 @@ test.kt:
# 24| 5: [BlockStmt] { ... }
# 24| 0: [ReturnStmt] return ...
# 24| 0: [VarAccess] a
-# 24| 5: [Method] testStaticCompanionFunction
+# 24| 9: [Method] testStaticCompanionFunction$default
+# 24| 3: [TypeAccess] int
+#-----| 4: (Parameters)
+# 24| 0: [Parameter] p0
+# 24| 0: [TypeAccess] Companion
+# 24| 1: [Parameter] p1
+# 24| 0: [TypeAccess] int
+# 24| 2: [Parameter] p2
+# 24| 0: [TypeAccess] String
+# 24| 3: [Parameter] p3
+# 24| 0: [TypeAccess] double
+# 24| 4: [Parameter] p4
+# 24| 0: [TypeAccess] float
+# 24| 5: [Parameter] p5
+# 24| 0: [TypeAccess] boolean
+# 24| 6: [Parameter] p6
+# 24| 0: [TypeAccess] int
+# 24| 7: [Parameter] p7
+# 24| 0: [TypeAccess] Object
+# 24| 5: [BlockStmt] { ... }
+# 24| 0: [IfStmt] if (...)
+# 24| 0: [EQExpr] ... == ...
+# 24| 0: [AndBitwiseExpr] ... & ...
+# 24| 0: [IntegerLiteral] 2
+# 24| 1: [VarAccess] p6
+# 24| 1: [IntegerLiteral] 0
+# 24| 1: [ExprStmt] ;
+# 24| 0: [AssignExpr] ...=...
+# 24| 0: [VarAccess] p2
+# 24| 1: [MethodAccess] getString(...)
+# 24| -1: [TypeAccess] TestKt
+# 24| 1: [IfStmt] if (...)
+# 24| 0: [EQExpr] ... == ...
+# 24| 0: [AndBitwiseExpr] ... & ...
+# 24| 0: [IntegerLiteral] 8
+# 24| 1: [VarAccess] p6
+# 24| 1: [IntegerLiteral] 0
+# 24| 1: [ExprStmt] ;
+# 24| 0: [AssignExpr] ...=...
+# 24| 0: [VarAccess] p4
+# 24| 1: [FloatLiteral] 1.0
+# 24| 2: [ReturnStmt] return ...
+# 24| 0: [MethodAccess] testStaticCompanionFunction(...)
+# 24| -1: [VarAccess] p0
+# 24| 0: [VarAccess] p1
+# 24| 1: [VarAccess] p2
+# 24| 2: [VarAccess] p3
+# 24| 3: [VarAccess] p4
+# 24| 4: [VarAccess] p5
+# 24| 6: [Method] testStaticCompanionFunction
# 24| 3: [TypeAccess] int
#-----| 4: (Parameters)
# 24| 0: [Parameter] a
@@ -430,7 +770,7 @@ test.kt:
# 24| 2: [VarAccess] c
# 24| 3: [FloatLiteral] 1.0
# 24| 4: [VarAccess] e
-# 24| 6: [Method] testStaticCompanionFunction
+# 24| 7: [Method] testStaticCompanionFunction
# 24| 3: [TypeAccess] int
#-----| 4: (Parameters)
# 24| 0: [Parameter] a
@@ -450,7 +790,7 @@ test.kt:
# 24| 2: [VarAccess] c
# 24| 3: [FloatLiteral] 1.0
# 24| 4: [VarAccess] e
-# 24| 7: [Method] testStaticCompanionFunction
+# 24| 8: [Method] testStaticCompanionFunction
# 24| 3: [TypeAccess] int
#-----| 4: (Parameters)
# 24| 0: [Parameter] a
@@ -515,7 +855,47 @@ test.kt:
# 30| 5: [BlockStmt] { ... }
# 30| 0: [SuperConstructorInvocationStmt] super(...)
# 30| 1: [BlockStmt] { ... }
-# 33| 4: [Method] testMemberFunction
+# 30| 4: [Constructor] GenericTest
+#-----| 4: (Parameters)
+# 30| 0: [Parameter] p0
+# 30| 0: [TypeAccess] int
+# 30| 1: [Parameter] p1
+# 30| 0: [TypeAccess] Object
+# 30| 2: [Parameter] p2
+# 30| 0: [TypeAccess] String
+# 30| 3: [Parameter] p3
+# 30| 0: [TypeAccess] Object
+# 30| 4: [Parameter] p4
+# 30| 0: [TypeAccess] int
+# 30| 5: [Parameter] p5
+# 30| 0: [TypeAccess] DefaultConstructorMarker
+# 30| 5: [BlockStmt] { ... }
+# 30| 0: [IfStmt] if (...)
+# 30| 0: [EQExpr] ... == ...
+# 30| 0: [AndBitwiseExpr] ... & ...
+# 30| 0: [IntegerLiteral] 1
+# 30| 1: [VarAccess] p4
+# 30| 1: [IntegerLiteral] 0
+# 30| 1: [ExprStmt] ;
+# 30| 0: [AssignExpr] ...=...
+# 30| 0: [VarAccess] p0
+# 30| 1: [IntegerLiteral] 1
+# 30| 1: [IfStmt] if (...)
+# 30| 0: [EQExpr] ... == ...
+# 30| 0: [AndBitwiseExpr] ... & ...
+# 30| 0: [IntegerLiteral] 4
+# 30| 1: [VarAccess] p4
+# 30| 1: [IntegerLiteral] 0
+# 30| 1: [ExprStmt]