From 8f7ff1a537f6d7fe8f2139ad9dade07fb82ec691 Mon Sep 17 00:00:00 2001
From: Philippe Antoine
Date: Mon, 6 Jul 2020 21:45:54 +0200
Subject: [PATCH 001/146] Adds another redundant null check rule
---
.../Likely Bugs/RedundantNullCheckParam.cpp | 11 ++++
.../Likely Bugs/RedundantNullCheckParam.qhelp | 29 +++++++++
.../Likely Bugs/RedundantNullCheckParam.ql | 59 +++++++++++++++++++
3 files changed, 99 insertions(+)
create mode 100644 cpp/ql/src/Likely Bugs/RedundantNullCheckParam.cpp
create mode 100644 cpp/ql/src/Likely Bugs/RedundantNullCheckParam.qhelp
create mode 100644 cpp/ql/src/Likely Bugs/RedundantNullCheckParam.ql
diff --git a/cpp/ql/src/Likely Bugs/RedundantNullCheckParam.cpp b/cpp/ql/src/Likely Bugs/RedundantNullCheckParam.cpp
new file mode 100644
index 00000000000..5e9c102703d
--- /dev/null
+++ b/cpp/ql/src/Likely Bugs/RedundantNullCheckParam.cpp
@@ -0,0 +1,11 @@
+void test(char *arg1, int *arg2) {
+ if (arg1[0] == 'A') {
+ if (arg2 != NULL) {
+ *arg2 = 42;
+ }
+ }
+ if (arg1[1] == 'B')
+ {
+ *arg2 = 54;
+ }
+}
diff --git a/cpp/ql/src/Likely Bugs/RedundantNullCheckParam.qhelp b/cpp/ql/src/Likely Bugs/RedundantNullCheckParam.qhelp
new file mode 100644
index 00000000000..25cbeff355e
--- /dev/null
+++ b/cpp/ql/src/Likely Bugs/RedundantNullCheckParam.qhelp
@@ -0,0 +1,29 @@
+
+
+
+
+This rule finds comparisons of a function parameter to null that occur when in another path the parameter is dereferenced without a guard check. It's
+likely either the check is not required and can be removed, or it should be added before the dereference
+so that a null pointer dereference does not occur.
+
+
+
+A check should be added to before the dereference, in a way that prevents a null pointer value from
+being dereferenced. If it's clear that the pointer cannot be null, consider removing the check instead.
+
+
+
+
+
+
+
+
+
+ Null Dereference
+
+
+
+
+
diff --git a/cpp/ql/src/Likely Bugs/RedundantNullCheckParam.ql b/cpp/ql/src/Likely Bugs/RedundantNullCheckParam.ql
new file mode 100644
index 00000000000..c0788dfbcfa
--- /dev/null
+++ b/cpp/ql/src/Likely Bugs/RedundantNullCheckParam.ql
@@ -0,0 +1,59 @@
+/**
+ * @name Redundant null check or missing null check
+ * @description Checking a parameter for nullness in one path,
+ * and not in another is likely to be a sign that either
+ * the check can be removed, or added in the other case.
+ * @kind problem
+ * @id cpp/redundant-null-check-param
+ * @problem.severity recommendation
+ * @tags reliability
+ * security
+ * external/cwe/cwe-476
+ */
+
+import cpp
+
+predicate blockDominates(Block check, Block access) {
+ check.getLocation().getStartLine() <= access.getLocation().getStartLine() and
+ check.getLocation().getEndLine() >= access.getLocation().getEndLine()
+}
+
+predicate isCheckedInstruction(VariableAccess unchecked, VariableAccess checked) {
+ checked =
+ any(VariableAccess va |
+ va.getTarget() = unchecked.getTarget()
+ ) and
+//Simple test if the first access in this code path is dereferenced
+ not dereferenced(checked) and
+ blockDominates(checked.getEnclosingBlock(), unchecked.getEnclosingBlock())
+}
+
+pragma[noinline]
+predicate candidateResultUnchecked(VariableAccess unchecked) {
+ not isCheckedInstruction(unchecked, _)
+}
+
+pragma[noinline]
+predicate candidateResultChecked(VariableAccess check, EqualityOperation eqop, Parameter param) {
+//not dereferenced to check against pointer, not its pointed value
+ not dereferenced(check) and
+//assert macros are not taken into account
+ not check.isInMacroExpansion() and
+// is part of a comarison against some constant NULL
+ eqop.getAnOperand() = check and eqop.getAnOperand() instanceof NullValue and
+// this function parameter is not overwritten
+ count(param.getAnAssignment()) = 0
+}
+
+from VariableAccess unchecked, VariableAccess check, EqualityOperation eqop, Parameter param
+where
+// a dereference
+ dereferenced(unchecked) and
+// for a function parameter
+ unchecked.getTarget() = param and
+ check.getTarget() = param and
+// which is once checked
+ candidateResultChecked(check, eqop, param) and
+// and which has not been checked before in this code path
+ candidateResultUnchecked(unchecked)
+select check, "This null check is redundant because the value is $@ ", unchecked, "dereferenced here"
From 5eff8d3165651cbe63b1bff5ea5ec665cc8cb32f Mon Sep 17 00:00:00 2001
From: Philippe Antoine
Date: Thu, 9 Jul 2020 11:31:47 +0200
Subject: [PATCH 002/146] Performance improvements suggested
---
cpp/ql/src/Likely Bugs/RedundantNullCheckParam.ql | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/cpp/ql/src/Likely Bugs/RedundantNullCheckParam.ql b/cpp/ql/src/Likely Bugs/RedundantNullCheckParam.ql
index c0788dfbcfa..aae04b19f07 100644
--- a/cpp/ql/src/Likely Bugs/RedundantNullCheckParam.ql
+++ b/cpp/ql/src/Likely Bugs/RedundantNullCheckParam.ql
@@ -28,21 +28,17 @@ predicate isCheckedInstruction(VariableAccess unchecked, VariableAccess checked)
blockDominates(checked.getEnclosingBlock(), unchecked.getEnclosingBlock())
}
-pragma[noinline]
predicate candidateResultUnchecked(VariableAccess unchecked) {
not isCheckedInstruction(unchecked, _)
}
-pragma[noinline]
-predicate candidateResultChecked(VariableAccess check, EqualityOperation eqop, Parameter param) {
+predicate candidateResultChecked(VariableAccess check, EqualityOperation eqop) {
//not dereferenced to check against pointer, not its pointed value
not dereferenced(check) and
//assert macros are not taken into account
not check.isInMacroExpansion() and
// is part of a comarison against some constant NULL
- eqop.getAnOperand() = check and eqop.getAnOperand() instanceof NullValue and
-// this function parameter is not overwritten
- count(param.getAnAssignment()) = 0
+ eqop.getAnOperand() = check and eqop.getAnOperand() instanceof NullValue
}
from VariableAccess unchecked, VariableAccess check, EqualityOperation eqop, Parameter param
@@ -51,9 +47,10 @@ where
dereferenced(unchecked) and
// for a function parameter
unchecked.getTarget() = param and
- check.getTarget() = param and
+// this function parameter is not overwritten
+ count(param.getAnAssignment()) = 0 and
// which is once checked
- candidateResultChecked(check, eqop, param) and
+ candidateResultChecked(check, eqop) and
// and which has not been checked before in this code path
candidateResultUnchecked(unchecked)
select check, "This null check is redundant because the value is $@ ", unchecked, "dereferenced here"
From d2763e8149e11cf11eb0d37a0597243995244fbb Mon Sep 17 00:00:00 2001
From: Philippe Antoine
Date: Thu, 9 Jul 2020 16:05:24 +0200
Subject: [PATCH 003/146] Comments taken into account
---
cpp/ql/src/Likely Bugs/RedundantNullCheckParam.cpp | 4 ++--
cpp/ql/src/Likely Bugs/RedundantNullCheckParam.ql | 7 +------
2 files changed, 3 insertions(+), 8 deletions(-)
diff --git a/cpp/ql/src/Likely Bugs/RedundantNullCheckParam.cpp b/cpp/ql/src/Likely Bugs/RedundantNullCheckParam.cpp
index 5e9c102703d..3765d0b14d4 100644
--- a/cpp/ql/src/Likely Bugs/RedundantNullCheckParam.cpp
+++ b/cpp/ql/src/Likely Bugs/RedundantNullCheckParam.cpp
@@ -1,11 +1,11 @@
void test(char *arg1, int *arg2) {
if (arg1[0] == 'A') {
- if (arg2 != NULL) {
+ if (arg2 != NULL) { //maybe redundant
*arg2 = 42;
}
}
if (arg1[1] == 'B')
{
- *arg2 = 54;
+ *arg2 = 54; //dereferenced without checking first
}
}
diff --git a/cpp/ql/src/Likely Bugs/RedundantNullCheckParam.ql b/cpp/ql/src/Likely Bugs/RedundantNullCheckParam.ql
index aae04b19f07..84d08da6cf0 100644
--- a/cpp/ql/src/Likely Bugs/RedundantNullCheckParam.ql
+++ b/cpp/ql/src/Likely Bugs/RedundantNullCheckParam.ql
@@ -13,11 +13,6 @@
import cpp
-predicate blockDominates(Block check, Block access) {
- check.getLocation().getStartLine() <= access.getLocation().getStartLine() and
- check.getLocation().getEndLine() >= access.getLocation().getEndLine()
-}
-
predicate isCheckedInstruction(VariableAccess unchecked, VariableAccess checked) {
checked =
any(VariableAccess va |
@@ -25,7 +20,7 @@ predicate isCheckedInstruction(VariableAccess unchecked, VariableAccess checked)
) and
//Simple test if the first access in this code path is dereferenced
not dereferenced(checked) and
- blockDominates(checked.getEnclosingBlock(), unchecked.getEnclosingBlock())
+ bbDominates(checked.getBasicBlock(), unchecked.getBasicBlock())
}
predicate candidateResultUnchecked(VariableAccess unchecked) {
From 06c8a0bf2067cd37778de09beb9de470a2802b21 Mon Sep 17 00:00:00 2001
From: Philippe Antoine
Date: Thu, 9 Jul 2020 16:09:57 +0200
Subject: [PATCH 004/146] move to experimental
---
.../{ => experimental}/Likely Bugs/RedundantNullCheckParam.cpp | 0
.../{ => experimental}/Likely Bugs/RedundantNullCheckParam.qhelp | 0
.../src/{ => experimental}/Likely Bugs/RedundantNullCheckParam.ql | 0
3 files changed, 0 insertions(+), 0 deletions(-)
rename cpp/ql/src/{ => experimental}/Likely Bugs/RedundantNullCheckParam.cpp (100%)
rename cpp/ql/src/{ => experimental}/Likely Bugs/RedundantNullCheckParam.qhelp (100%)
rename cpp/ql/src/{ => experimental}/Likely Bugs/RedundantNullCheckParam.ql (100%)
diff --git a/cpp/ql/src/Likely Bugs/RedundantNullCheckParam.cpp b/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.cpp
similarity index 100%
rename from cpp/ql/src/Likely Bugs/RedundantNullCheckParam.cpp
rename to cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.cpp
diff --git a/cpp/ql/src/Likely Bugs/RedundantNullCheckParam.qhelp b/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.qhelp
similarity index 100%
rename from cpp/ql/src/Likely Bugs/RedundantNullCheckParam.qhelp
rename to cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.qhelp
diff --git a/cpp/ql/src/Likely Bugs/RedundantNullCheckParam.ql b/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.ql
similarity index 100%
rename from cpp/ql/src/Likely Bugs/RedundantNullCheckParam.ql
rename to cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.ql
From 50f2f69f5f182f799991beff54654adc89c03a86 Mon Sep 17 00:00:00 2001
From: Philippe Antoine
Date: Thu, 9 Jul 2020 16:14:26 +0200
Subject: [PATCH 005/146] indent comments
---
.../Likely Bugs/RedundantNullCheckParam.ql | 20 +++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.ql b/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.ql
index 84d08da6cf0..71e0b950d6c 100644
--- a/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.ql
+++ b/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.ql
@@ -1,5 +1,5 @@
/**
- * @name Redundant null check or missing null check
+ * @name Redundant null check or missing null check of parameter
* @description Checking a parameter for nullness in one path,
* and not in another is likely to be a sign that either
* the check can be removed, or added in the other case.
@@ -18,7 +18,7 @@ predicate isCheckedInstruction(VariableAccess unchecked, VariableAccess checked)
any(VariableAccess va |
va.getTarget() = unchecked.getTarget()
) and
-//Simple test if the first access in this code path is dereferenced
+ //Simple test if the first access in this code path is dereferenced
not dereferenced(checked) and
bbDominates(checked.getBasicBlock(), unchecked.getBasicBlock())
}
@@ -28,24 +28,24 @@ predicate candidateResultUnchecked(VariableAccess unchecked) {
}
predicate candidateResultChecked(VariableAccess check, EqualityOperation eqop) {
-//not dereferenced to check against pointer, not its pointed value
+ //not dereferenced to check against pointer, not its pointed value
not dereferenced(check) and
-//assert macros are not taken into account
+ //assert macros are not taken into account
not check.isInMacroExpansion() and
-// is part of a comarison against some constant NULL
+ // is part of a comparison against some constant NULL
eqop.getAnOperand() = check and eqop.getAnOperand() instanceof NullValue
}
from VariableAccess unchecked, VariableAccess check, EqualityOperation eqop, Parameter param
where
-// a dereference
+ // a dereference
dereferenced(unchecked) and
-// for a function parameter
+ // for a function parameter
unchecked.getTarget() = param and
-// this function parameter is not overwritten
+ // this function parameter is not overwritten
count(param.getAnAssignment()) = 0 and
-// which is once checked
+ // which is once checked
candidateResultChecked(check, eqop) and
-// and which has not been checked before in this code path
+ // and which has not been checked before in this code path
candidateResultUnchecked(unchecked)
select check, "This null check is redundant because the value is $@ ", unchecked, "dereferenced here"
From 3117c67a66e5a535cc6781948b7d1e9b9307bc9a Mon Sep 17 00:00:00 2001
From: Philippe Antoine
Date: Fri, 10 Jul 2020 14:24:24 +0200
Subject: [PATCH 006/146] Updates result message to be more precise
---
cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.ql | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.ql b/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.ql
index 71e0b950d6c..59420957ad5 100644
--- a/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.ql
+++ b/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.ql
@@ -48,4 +48,4 @@ where
candidateResultChecked(check, eqop) and
// and which has not been checked before in this code path
candidateResultUnchecked(unchecked)
-select check, "This null check is redundant because the value is $@ ", unchecked, "dereferenced here"
+select check, "This null check is redundant or there is a missing null check before $@ ", unchecked, "where dereferencing happens"
From 50b2b12ce288289369b511a6ec8aa01c0fee25bb Mon Sep 17 00:00:00 2001
From: Philippe Antoine
Date: Fri, 10 Jul 2020 14:40:58 +0200
Subject: [PATCH 007/146] put back missing condition
---
cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.ql | 1 +
1 file changed, 1 insertion(+)
diff --git a/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.ql b/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.ql
index 59420957ad5..400e8387761 100644
--- a/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.ql
+++ b/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.ql
@@ -44,6 +44,7 @@ where
unchecked.getTarget() = param and
// this function parameter is not overwritten
count(param.getAnAssignment()) = 0 and
+ check.getTarget() = param and
// which is once checked
candidateResultChecked(check, eqop) and
// and which has not been checked before in this code path
From bf7e3a004e4bef63604fd8b23ec276f3309a78f5 Mon Sep 17 00:00:00 2001
From: Philippe Antoine
Date: Fri, 10 Jul 2020 14:58:00 +0200
Subject: [PATCH 008/146] Reverting to enclosing block logic
---
.../experimental/Likely Bugs/RedundantNullCheckParam.ql | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.ql b/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.ql
index 400e8387761..d09191a3891 100644
--- a/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.ql
+++ b/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.ql
@@ -13,6 +13,11 @@
import cpp
+predicate blockDominates(Block check, Block access) {
+ check.getLocation().getStartLine() <= access.getLocation().getStartLine() and
+ check.getLocation().getEndLine() >= access.getLocation().getEndLine()
+}
+
predicate isCheckedInstruction(VariableAccess unchecked, VariableAccess checked) {
checked =
any(VariableAccess va |
@@ -20,7 +25,7 @@ predicate isCheckedInstruction(VariableAccess unchecked, VariableAccess checked)
) and
//Simple test if the first access in this code path is dereferenced
not dereferenced(checked) and
- bbDominates(checked.getBasicBlock(), unchecked.getBasicBlock())
+ blockDominates(checked.getEnclosingBlock(), unchecked.getEnclosingBlock())
}
predicate candidateResultUnchecked(VariableAccess unchecked) {
From c469f71957051e768b8e481867a7194524a6674e Mon Sep 17 00:00:00 2001
From: ubuntu <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 26 Jul 2020 22:56:36 +0200
Subject: [PATCH 009/146] Add Codeql query to detect if cookies are sent
without the flag being set
---
.../Security/CWE-614/InsecureCookie.qhelp | 84 +++++++++
.../Security/CWE-614/InsecureCookie.ql | 19 +++
.../Security/CWE-614/InsecureCookie.qll | 161 ++++++++++++++++++
.../CWE-614/examples/cookie-session_bad.js | 17 ++
.../CWE-614/examples/cookie-session_good.js | 17 ++
.../examples/express-session_bad1_false.js | 7 +
.../examples/express-session_bad2_notSet.js | 7 +
.../examples/express-session_bad3_setEmpty.js | 7 +
.../CWE-614/examples/express-session_bad4.js | 9 +
.../CWE-614/examples/express-session_good.js | 9 +
.../examples/express_response-cookie_bad1.js | 12 ++
.../examples/express_response-cookie_bad2.js | 12 ++
.../examples/express_response-cookie_good1.js | 12 ++
.../CWE-614/examples/httpserver_bad.js | 8 +
.../CWE-614/examples/httpserver_good.js | 8 +
.../Security/CWE-614/examples/jsCookie_bad.js | 2 +
.../CWE-614/examples/jsCookie_good.js | 2 +
17 files changed, 393 insertions(+)
create mode 100644 javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qhelp
create mode 100644 javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.ql
create mode 100644 javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
create mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/cookie-session_bad.js
create mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/cookie-session_good.js
create mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad1_false.js
create mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad2_notSet.js
create mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad3_setEmpty.js
create mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad4.js
create mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/express-session_good.js
create mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/express_response-cookie_bad1.js
create mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/express_response-cookie_bad2.js
create mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/express_response-cookie_good1.js
create mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/httpserver_bad.js
create mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/httpserver_good.js
create mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/jsCookie_bad.js
create mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/jsCookie_good.js
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qhelp b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qhelp
new file mode 100644
index 00000000000..cff9e878e59
--- /dev/null
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qhelp
@@ -0,0 +1,84 @@
+
+
+
+Failing to set the 'secure' flag on a cookie can cause it to be sent in cleartext.
+This makes it easier for an attacker to intercept.
+
+
+
+Always set the secure flag to `true` on a cookie before adding it
+to an HTTP response (if the default value is `false`).
+
+
+
+
+In the first example the `secure` flag is set to `false` using the express middleware `cookie-session`.
+In the second example the `secure` flag is set to `true` (it is set `false` by default for HTTP, `true` by default for HTTPS).
+
+
+
+
+
+
+
+
+The first four examples show four ways of adding a cookie using the express middleware `express-session`.
+Since the default value for the flag `secure` is false, each example shows a possible scenario where a cookie is set with
+the `secure` to `false`.
+In the last example the `secure` flag is set to `true`.
+
+
+
+
+
+
+
+
+
+
+
+The first two examples show two ways of adding a cookie using the method `response.cookie`.
+In both cases the `secure` flag is to `false`.
+In the last example the `secure` flag is set to `true`.
+
+
+
+
+
+
+
+
+
+
+The first example shows when the `secure` flag is set using the method `Set-Cookie` header of an `HTTP` response.
+In this case the `secure` flag is not set.
+In the last example the `secure` flag is set.
+
+
+
+
+
+
+
+
+In the first example the `secure` flag is set to `false` using the `js-cookie` library.
+In the second example the `secure` flag is set to `true`.
+
+
+
+
+
+
+
+
+Production Best Practices: Security:Use cookies securely.
+NodeJS security cheat sheet:Set cookie flags appropriately.
+express-session:cookie.secure.
+cookie-session:Cookie Options.
+express response.cookie.
+Set-Cookie.
+js-cookie.
+
+
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.ql b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.ql
new file mode 100644
index 00000000000..228a38d25fa
--- /dev/null
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.ql
@@ -0,0 +1,19 @@
+/**
+ * @name Failure to set secure cookies
+ * @description Insecure cookies may be sent in cleartext, which makes them vulnerable to
+ * interception.
+ * @kind problem
+ * @problem.severity error
+ * @precision high
+ * @id js/insecure-cookie
+ * @tags security
+ * external/cwe/cwe-614
+ */
+
+import javascript
+import InsecureCookie::InsecureCookie
+
+from InsecureCookies insecureCookies
+where insecureCookies.isInsecure()
+select "Cookie is added to response without the 'secure' flag being set to true (using " +
+ insecureCookies.getKind() + ").", insecureCookies
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
new file mode 100644
index 00000000000..82deffa60ca
--- /dev/null
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
@@ -0,0 +1,161 @@
+/**
+ * Provides classes for reasoning about cookies added to response without the 'secure' flag being set.
+ * A cookie without the 'secure' flag being set can be intercepted and read by a malicious user.
+ */
+
+import javascript
+
+module InsecureCookie {
+ /**
+ * `secure` property of the cookie options.
+ */
+ string flag() { result = "secure" }
+
+ /**
+ * Abstract class to represent different cases of insecure cookie settings.
+ */
+ abstract class InsecureCookies extends DataFlow::Node {
+ /**
+ * The name of the middleware/library used to set the cookie.
+ */
+ abstract string getKind();
+
+ /**
+ * The `cookie` options.
+ */
+ abstract DataFlow::Node getCookieOptionsArgument();
+
+ /**
+ * Predicate that determines if a cookie is insecure.
+ */
+ abstract predicate isInsecure();
+ }
+
+ /**
+ * A cookie set using the `express` module `cookie-session` (https://github.com/expressjs/cookie-session).
+ * The flag `secure` is set to `false` by default for HTTP, `true` by default for HTTPS (https://github.com/expressjs/cookie-session#cookie-options).
+ */
+ class InsecureCookieSession extends ExpressLibraries::CookieSession::MiddlewareInstance,
+ InsecureCookies {
+ InsecureCookieSession() { this instanceof ExpressLibraries::CookieSession::MiddlewareInstance }
+
+ override string getKind() { result = "cookie-session" }
+
+ override DataFlow::SourceNode getCookieOptionsArgument() {
+ result = this.getOption("cookie").(DataFlow::SourceNode)
+ }
+
+ DataFlow::Node getCookieFlagValue(string flag) {
+ result = this.getCookieOptionsArgument().getAPropertyWrite(flag).getRhs()
+ }
+
+ // A cookie is insecure if the `secure` flag is explicitly set to `false`.
+ override predicate isInsecure() { getCookieFlagValue(flag()).mayHaveBooleanValue(false) }
+ }
+
+ /**
+ * A cookie set using the `express` module `express-session` (https://github.com/expressjs/session).
+ * The flag `secure` is not set by default (https://github.com/expressjs/session#cookiesecure).
+ * The default value for cookie options is { path: '/', httpOnly: true, secure: false, maxAge: null }.
+ */
+ class InsecureExpressSessionCookie extends ExpressLibraries::ExpressSession::MiddlewareInstance,
+ InsecureCookies {
+ override string getKind() { result = "express-session" }
+
+ override DataFlow::SourceNode getCookieOptionsArgument() {
+ result = this.getOption("cookie").(DataFlow::SourceNode)
+ }
+
+ DataFlow::Node getCookieFlagValue(string flag) {
+ result = this.getCookieOptionsArgument().getAPropertyWrite(flag).getRhs()
+ }
+
+ // A cookie is insecure if there are not cookie options with the `secure` flag set to `true`.
+ override predicate isInsecure() {
+ not exists(DataFlow::SourceNode cookieOptions |
+ cookieOptions = this.getCookieOptionsArgument() and
+ getCookieFlagValue(flag()).mayHaveBooleanValue(true)
+ ) and
+ not getCookieFlagValue(flag()).mayHaveStringValue("auto")
+ }
+ }
+
+ /**
+ * A cookie set using `response.cookie` from `express` module (https://expressjs.com/en/api.html#res.cookie).
+ */
+ class InsecureExpressCookieResponse extends InsecureCookies {
+ InsecureExpressCookieResponse() {
+ this = any(Express::ResponseExpr response).flow().getALocalSource().getAMemberCall("cookie")
+ }
+
+ override string getKind() { result = "response.cookie" }
+
+ override DataFlow::SourceNode getCookieOptionsArgument() {
+ result = this.(DataFlow::InvokeNode).getLastArgument().getALocalSource()
+ }
+
+ DataFlow::Node getCookieFlagValue(string flag) {
+ result = this.getCookieOptionsArgument().getAPropertyWrite(flag).getRhs()
+ }
+
+ // A cookie is insecure if there are not cookie options with the `secure` flag set to `true`.
+ override predicate isInsecure() {
+ not exists(DataFlow::SourceNode cookieOptions |
+ cookieOptions = this.getCookieOptionsArgument() and
+ getCookieFlagValue(flag()).mayHaveBooleanValue(true)
+ )
+ }
+ }
+
+ /**
+ * A cookie set using `Set-Cookie` header of an `HTTP` response (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie).
+ */
+ class InsecureSetCookieHeader extends InsecureCookies {
+ InsecureSetCookieHeader() {
+ this.asExpr() = any(HTTP::SetCookieHeader setCookie).getHeaderArgument()
+ }
+
+ override string getKind() { result = "set-cookie header" }
+
+ override DataFlow::Node getCookieOptionsArgument() {
+ result.asExpr() = this.asExpr().(ArrayExpr).getAnElement()
+ }
+
+ // A cookie is insecure if the 'secure' flag is not specified in the cookie definition.
+ override predicate isInsecure() {
+ not exists(string s |
+ getCookieOptionsArgument().mayHaveStringValue(s) and
+ s.matches("%; secure%")
+ )
+ }
+ }
+
+ /**
+ * A cookie set using `js-cookie` library (https://github.com/js-cookie/js-cookie).
+ */
+ class InsecureJsCookie extends InsecureCookies {
+ InsecureJsCookie() {
+ this = DataFlow::globalVarRef("Cookie").getAMemberCall("set") or
+ this = DataFlow::globalVarRef("Cookie").getAMemberCall("noConflict").getAMemberCall("set") or
+ this = DataFlow::moduleMember("js-cookie", "set").getACall()
+ }
+
+ override string getKind() { result = "js-cookie" }
+
+ override DataFlow::SourceNode getCookieOptionsArgument() {
+ result = this.(DataFlow::CallNode).getArgument(2).getALocalSource()
+ }
+
+ DataFlow::Node getCookieFlagValue(string flag) {
+ result = this.getCookieOptionsArgument().getAPropertyWrite(flag).getRhs()
+ }
+
+ // A cookie is insecure if there are not cookie options with the `secure` flag set to `true`.
+ override predicate isInsecure() {
+ not exists(DataFlow::SourceNode cookieOptions |
+ cookieOptions = this.getCookieOptionsArgument() and
+ getCookieFlagValue(flag()).mayHaveBooleanValue(true)
+ )
+ }
+ }
+}
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/cookie-session_bad.js b/javascript/ql/src/experimental/Security/CWE-614/examples/cookie-session_bad.js
new file mode 100644
index 00000000000..009ef60fde3
--- /dev/null
+++ b/javascript/ql/src/experimental/Security/CWE-614/examples/cookie-session_bad.js
@@ -0,0 +1,17 @@
+const session = require('cookie-session')
+const express = require('express')
+const app = express()
+
+const expiryDate = new Date(Date.now() + 60 * 60 * 1000)
+
+app.use(session({
+ name: 'session',
+ keys: ['key1', 'key2'],
+ cookie: {
+ secure: false, // BAD
+ httpOnly: true,
+ domain: 'example.com',
+ path: 'foo/bar',
+ expires: expiryDate
+ }
+}))
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/cookie-session_good.js b/javascript/ql/src/experimental/Security/CWE-614/examples/cookie-session_good.js
new file mode 100644
index 00000000000..b16458d2edf
--- /dev/null
+++ b/javascript/ql/src/experimental/Security/CWE-614/examples/cookie-session_good.js
@@ -0,0 +1,17 @@
+const session = require('cookie-session')
+const express = require('express')
+const app = express()
+
+const expiryDate = new Date(Date.now() + 60 * 60 * 1000)
+
+app.use(session({
+ name: 'session',
+ keys: ['key1', 'key2'],
+ cookie: {
+ secure: true, // GOOD: false by default for HTTP, true by default for HTTPS
+ httpOnly: true,
+ domain: 'example.com',
+ path: 'foo/bar',
+ expires: expiryDate
+ }
+}))
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad1_false.js b/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad1_false.js
new file mode 100644
index 00000000000..02868dee748
--- /dev/null
+++ b/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad1_false.js
@@ -0,0 +1,7 @@
+const app = express()
+const session = require('express-session')
+
+app.use(session({
+ secret: 'secret',
+ cookie: { secure: false } // BAD
+}))
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad2_notSet.js b/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad2_notSet.js
new file mode 100644
index 00000000000..d7b1841b542
--- /dev/null
+++ b/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad2_notSet.js
@@ -0,0 +1,7 @@
+const app = express()
+const session = require('express-session')
+
+app.use(session({
+ secret: 'secret'
+ // BAD: in this case the default value of `secure` flag is `false`
+}))
\ No newline at end of file
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad3_setEmpty.js b/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad3_setEmpty.js
new file mode 100644
index 00000000000..0ec001ec4fb
--- /dev/null
+++ b/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad3_setEmpty.js
@@ -0,0 +1,7 @@
+const app = express()
+const session = require('express-session')
+
+app.use(session({
+ secret: 'secret',
+ cookie: {} // BAD: in this case the default value of `secure` flag is `false`
+}))
\ No newline at end of file
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad4.js b/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad4.js
new file mode 100644
index 00000000000..0b0f5cd328d
--- /dev/null
+++ b/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad4.js
@@ -0,0 +1,9 @@
+const app = express()
+const session = require('express-session')
+
+const sess = {
+ secret: 'secret',
+ cookie: { secure: false } // BAD
+}
+
+app.use(session(sess))
\ No newline at end of file
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_good.js b/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_good.js
new file mode 100644
index 00000000000..ae2a7f41f55
--- /dev/null
+++ b/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_good.js
@@ -0,0 +1,9 @@
+const app = express()
+const session = require('express-session')
+
+app.set('trust proxy', 1)
+
+app.use(session({
+ secret: 'secret',
+ cookie: { secure: true } // GOOD
+}))
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/express_response-cookie_bad1.js b/javascript/ql/src/experimental/Security/CWE-614/examples/express_response-cookie_bad1.js
new file mode 100644
index 00000000000..1a762c27a2c
--- /dev/null
+++ b/javascript/ql/src/experimental/Security/CWE-614/examples/express_response-cookie_bad1.js
@@ -0,0 +1,12 @@
+const express = require('express')
+const app = express()
+
+app.get('/', function (req, res, next) {
+ res.cookie('name', 'value',
+ {
+ maxAge: 9000000000,
+ httpOnly: true,
+ secure: false // BAD
+ });
+ res.end('ok')
+})
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/express_response-cookie_bad2.js b/javascript/ql/src/experimental/Security/CWE-614/examples/express_response-cookie_bad2.js
new file mode 100644
index 00000000000..ba67049f167
--- /dev/null
+++ b/javascript/ql/src/experimental/Security/CWE-614/examples/express_response-cookie_bad2.js
@@ -0,0 +1,12 @@
+const express = require('express')
+const app = express()
+
+app.get('/', function (req, res, next) {
+ let options = {
+ maxAge: 9000000000,
+ httpOnly: true,
+ secure: false // BAD
+ }
+ res.cookie('name', 'value', options);
+ res.end('ok')
+})
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/express_response-cookie_good1.js b/javascript/ql/src/experimental/Security/CWE-614/examples/express_response-cookie_good1.js
new file mode 100644
index 00000000000..ee5fe440b93
--- /dev/null
+++ b/javascript/ql/src/experimental/Security/CWE-614/examples/express_response-cookie_good1.js
@@ -0,0 +1,12 @@
+const express = require('express')
+const app = express()
+
+app.get('/', function (req, res, next) {
+ res.cookie('name', 'value',
+ {
+ maxAge: 9000000000,
+ httpOnly: true,
+ secure: true // GOOD
+ });
+ res.end('ok')
+})
\ No newline at end of file
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/httpserver_bad.js b/javascript/ql/src/experimental/Security/CWE-614/examples/httpserver_bad.js
new file mode 100644
index 00000000000..dfd2c4bb409
--- /dev/null
+++ b/javascript/ql/src/experimental/Security/CWE-614/examples/httpserver_bad.js
@@ -0,0 +1,8 @@
+const http = require('http');
+const server = http.createServer((req, res) => {
+ res.setHeader('Content-Type', 'text/html');
+ // BAD
+ res.setHeader("Set-Cookie", ["type=ninja", "language=javascript"]);
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end('ok');
+});
\ No newline at end of file
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/httpserver_good.js b/javascript/ql/src/experimental/Security/CWE-614/examples/httpserver_good.js
new file mode 100644
index 00000000000..8f607aaf972
--- /dev/null
+++ b/javascript/ql/src/experimental/Security/CWE-614/examples/httpserver_good.js
@@ -0,0 +1,8 @@
+const http = require('http');
+const server = http.createServer((req, res) => {
+ res.setHeader('Content-Type', 'text/html');
+ // GOOD
+ res.setHeader("Set-Cookie", ["type=ninja; Secure", "language=javascript; secure"]);
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end('ok');
+});
\ No newline at end of file
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/jsCookie_bad.js b/javascript/ql/src/experimental/Security/CWE-614/examples/jsCookie_bad.js
new file mode 100644
index 00000000000..6ab10c64e26
--- /dev/null
+++ b/javascript/ql/src/experimental/Security/CWE-614/examples/jsCookie_bad.js
@@ -0,0 +1,2 @@
+const js_cookie = require('js-cookie')
+js_cookie.set('key', 'value', { secure: false }); // BAD
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/jsCookie_good.js b/javascript/ql/src/experimental/Security/CWE-614/examples/jsCookie_good.js
new file mode 100644
index 00000000000..b18c476b2f5
--- /dev/null
+++ b/javascript/ql/src/experimental/Security/CWE-614/examples/jsCookie_good.js
@@ -0,0 +1,2 @@
+const js_cookie = require('js-cookie')
+js_cookie.set('key', 'value', { secure: true });
From 2cec8f7e9d5a4914f0e83a289fbee963ad89c4c2 Mon Sep 17 00:00:00 2001
From: ubuntu <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 26 Jul 2020 23:23:56 +0200
Subject: [PATCH 010/146] Update .qhelp
---
.../ql/src/experimental/Security/CWE-614/InsecureCookie.qhelp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qhelp b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qhelp
index cff9e878e59..d967a59ce97 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qhelp
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qhelp
@@ -8,7 +8,7 @@ This makes it easier for an attacker to intercept.
-Always set the secure flag to `true` on a cookie before adding it
+
Always set the `secure` flag to `true` on a cookie before adding it
to an HTTP response (if the default value is `false`).
From ac7c511d86f5da9c4ab97a9b7a8094c23c140be1 Mon Sep 17 00:00:00 2001
From: ubuntu <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 26 Jul 2020 23:47:53 +0200
Subject: [PATCH 011/146] Update .qhelp
---
.../ql/src/experimental/Security/CWE-614/InsecureCookie.qhelp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qhelp b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qhelp
index d967a59ce97..c8875967298 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qhelp
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qhelp
@@ -8,7 +8,7 @@ This makes it easier for an attacker to intercept.
-Always set the `secure` flag to `true` on a cookie before adding it
+
Always set the secure flag to `true` on a cookie before adding it
to an HTTP response (if the default value is `false`).
From 8dee3da4fee2d45d22d7f9e570c09132bc059bc9 Mon Sep 17 00:00:00 2001
From: ubuntu <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 26 Jul 2020 23:50:22 +0200
Subject: [PATCH 012/146] Update .qhelp
---
.../ql/src/experimental/Security/CWE-614/InsecureCookie.qhelp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qhelp b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qhelp
index c8875967298..cff9e878e59 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qhelp
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qhelp
@@ -8,7 +8,7 @@ This makes it easier for an attacker to intercept.
-Always set the secure flag to `true` on a cookie before adding it
+Always set the secure flag to `true` on a cookie before adding it
to an HTTP response (if the default value is `false`).
From 99c95246396071b898f9fd746c45643255d657d8 Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Tue, 11 Aug 2020 13:09:27 +0200
Subject: [PATCH 013/146] Java: Make XssSink extensible.
---
java/ql/src/Security/CWE/CWE-079/XSS.ql | 7 +++---
java/ql/src/Security/CWE/CWE-079/XSSLocal.ql | 7 +++---
.../CWE/CWE-209/StackTraceExposure.ql | 6 ++---
java/ql/src/semmle/code/java/security/XSS.qll | 23 +++++++++++--------
4 files changed, 22 insertions(+), 21 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-079/XSS.ql b/java/ql/src/Security/CWE/CWE-079/XSS.ql
index 009c8fa6935..1a1996c5c16 100644
--- a/java/ql/src/Security/CWE/CWE-079/XSS.ql
+++ b/java/ql/src/Security/CWE/CWE-079/XSS.ql
@@ -12,11 +12,10 @@
import java
import semmle.code.java.dataflow.FlowSources
-import semmle.code.java.dataflow.TaintTracking2
import semmle.code.java.security.XSS
-import DataFlow2::PathGraph
+import DataFlow::PathGraph
-class XSSConfig extends TaintTracking2::Configuration {
+class XSSConfig extends TaintTracking::Configuration {
XSSConfig() { this = "XSSConfig" }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
@@ -28,7 +27,7 @@ class XSSConfig extends TaintTracking2::Configuration {
}
}
-from DataFlow2::PathNode source, DataFlow2::PathNode sink, XSSConfig conf
+from DataFlow::PathNode source, DataFlow::PathNode sink, XSSConfig conf
where conf.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "Cross-site scripting vulnerability due to $@.",
source.getNode(), "user-provided value"
diff --git a/java/ql/src/Security/CWE/CWE-079/XSSLocal.ql b/java/ql/src/Security/CWE/CWE-079/XSSLocal.ql
index 3c6691986e1..a11a3ade0fd 100644
--- a/java/ql/src/Security/CWE/CWE-079/XSSLocal.ql
+++ b/java/ql/src/Security/CWE/CWE-079/XSSLocal.ql
@@ -12,11 +12,10 @@
import java
import semmle.code.java.dataflow.FlowSources
-import semmle.code.java.dataflow.TaintTracking2
import semmle.code.java.security.XSS
-import DataFlow2::PathGraph
+import DataFlow::PathGraph
-class XSSLocalConfig extends TaintTracking2::Configuration {
+class XSSLocalConfig extends TaintTracking::Configuration {
XSSLocalConfig() { this = "XSSLocalConfig" }
override predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
@@ -24,7 +23,7 @@ class XSSLocalConfig extends TaintTracking2::Configuration {
override predicate isSink(DataFlow::Node sink) { sink instanceof XssSink }
}
-from DataFlow2::PathNode source, DataFlow2::PathNode sink, XSSLocalConfig conf
+from DataFlow::PathNode source, DataFlow::PathNode sink, XSSLocalConfig conf
where conf.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "Cross-site scripting vulnerability due to $@.",
source.getNode(), "user-provided value"
diff --git a/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql b/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql
index bfb0ae0ad59..c9c1e2917c0 100644
--- a/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql
+++ b/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql
@@ -14,7 +14,7 @@
import java
import semmle.code.java.dataflow.DataFlow
-import semmle.code.java.dataflow.TaintTracking2
+import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.security.XSS
/**
@@ -84,7 +84,7 @@ predicate stackTraceExpr(Expr exception, MethodAccess stackTraceString) {
)
}
-class StackTraceStringToXssSinkFlowConfig extends TaintTracking2::Configuration {
+class StackTraceStringToXssSinkFlowConfig extends TaintTracking::Configuration {
StackTraceStringToXssSinkFlowConfig() {
this = "StackTraceExposure::StackTraceStringToXssSinkFlowConfig"
}
@@ -124,7 +124,7 @@ class GetMessageFlowSource extends MethodAccess {
}
}
-class GetMessageFlowSourceToXssSinkFlowConfig extends TaintTracking2::Configuration {
+class GetMessageFlowSourceToXssSinkFlowConfig extends TaintTracking::Configuration {
GetMessageFlowSourceToXssSinkFlowConfig() {
this = "StackTraceExposure::GetMessageFlowSourceToXssSinkFlowConfig"
}
diff --git a/java/ql/src/semmle/code/java/security/XSS.qll b/java/ql/src/semmle/code/java/security/XSS.qll
index 9f5ed3fe9d6..84f06f7b41d 100644
--- a/java/ql/src/semmle/code/java/security/XSS.qll
+++ b/java/ql/src/semmle/code/java/security/XSS.qll
@@ -3,33 +3,36 @@ import semmle.code.java.frameworks.Servlets
import semmle.code.java.frameworks.android.WebView
import semmle.code.java.frameworks.spring.SpringController
import semmle.code.java.frameworks.spring.SpringHttp
-import semmle.code.java.dataflow.TaintTracking
+import semmle.code.java.dataflow.DataFlow
+import semmle.code.java.dataflow.TaintTracking2
/*
* Definitions for XSS sinks
*/
-class XssSink extends DataFlow::ExprNode {
- XssSink() {
+abstract class XssSink extends DataFlow::Node { }
+
+private class DefaultXssSink extends XssSink {
+ DefaultXssSink() {
exists(HttpServletResponseSendErrorMethod m, MethodAccess ma |
ma.getMethod() = m and
- this.getExpr() = ma.getArgument(1)
+ this.asExpr() = ma.getArgument(1)
)
or
exists(ServletWriterSourceToWritingMethodFlowConfig writer, MethodAccess ma |
ma.getMethod() instanceof WritingMethod and
writer.hasFlowToExpr(ma.getQualifier()) and
- this.getExpr() = ma.getArgument(_)
+ this.asExpr() = ma.getArgument(_)
)
or
exists(Method m |
m.getDeclaringType() instanceof TypeWebView and
(
- m.getAReference().getArgument(0) = this.getExpr() and m.getName() = "loadData"
+ m.getAReference().getArgument(0) = this.asExpr() and m.getName() = "loadData"
or
- m.getAReference().getArgument(0) = this.getExpr() and m.getName() = "loadUrl"
+ m.getAReference().getArgument(0) = this.asExpr() and m.getName() = "loadUrl"
or
- m.getAReference().getArgument(1) = this.getExpr() and m.getName() = "loadDataWithBaseURL"
+ m.getAReference().getArgument(1) = this.asExpr() and m.getName() = "loadDataWithBaseURL"
)
)
or
@@ -77,7 +80,7 @@ class XssSink extends DataFlow::ExprNode {
}
}
-class ServletWriterSourceToWritingMethodFlowConfig extends TaintTracking::Configuration {
+private class ServletWriterSourceToWritingMethodFlowConfig extends TaintTracking2::Configuration {
ServletWriterSourceToWritingMethodFlowConfig() {
this = "XSS::ServletWriterSourceToWritingMethodFlowConfig"
}
@@ -91,7 +94,7 @@ class ServletWriterSourceToWritingMethodFlowConfig extends TaintTracking::Config
}
}
-class WritingMethod extends Method {
+private class WritingMethod extends Method {
WritingMethod() {
getDeclaringType().getASupertype*().hasQualifiedName("java.io", _) and
(
From ba2d19c70f6bb60b0a0e446460149c8667800c98 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Tue, 11 Aug 2020 11:46:21 +0200
Subject: [PATCH 014/146] upgrade ts version in package.json
---
javascript/extractor/lib/typescript/package.json | 2 +-
javascript/extractor/lib/typescript/yarn.lock | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/javascript/extractor/lib/typescript/package.json b/javascript/extractor/lib/typescript/package.json
index c8bee62fba8..e66383a5ef3 100644
--- a/javascript/extractor/lib/typescript/package.json
+++ b/javascript/extractor/lib/typescript/package.json
@@ -2,7 +2,7 @@
"name": "typescript-parser-wrapper",
"private": true,
"dependencies": {
- "typescript": "3.9.2"
+ "typescript": "4.0.1-rc"
},
"scripts": {
"build": "tsc --project tsconfig.json",
diff --git a/javascript/extractor/lib/typescript/yarn.lock b/javascript/extractor/lib/typescript/yarn.lock
index 19d96a56650..0fcb30d02f2 100644
--- a/javascript/extractor/lib/typescript/yarn.lock
+++ b/javascript/extractor/lib/typescript/yarn.lock
@@ -225,9 +225,9 @@ tsutils@^2.12.1:
dependencies:
tslib "^1.8.1"
-typescript@3.9.2:
- version "3.9.2"
- resolved typescript-3.9.2.tgz#64e9c8e9be6ea583c54607677dd4680a1cf35db9
+typescript@4.0.1-rc:
+ version "4.0.1-rc"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.1-rc.tgz#8adc78223eae56fe71d906a5fa90c3543b07a677"
wrappy@1:
version "1.0.2"
From 08c017eb0909b5fd612a3ae5e9b370c16f9ca2e0 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Tue, 11 Aug 2020 10:17:28 +0200
Subject: [PATCH 015/146] change where tuples elements are loaded from to match
TS 4.0
---
.../src/com/semmle/js/parser/TypeScriptASTConverter.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java b/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java
index 4a57871c871..8331d91326e 100644
--- a/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java
+++ b/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java
@@ -2177,7 +2177,7 @@ public class TypeScriptASTConverter {
}
private Node convertTupleType(JsonObject node, SourceLocation loc) throws ParseError {
- return new TupleTypeExpr(loc, convertChildrenAsTypes(node, "elementTypes"));
+ return new TupleTypeExpr(loc, convertChildrenAsTypes(node, "elements"));
}
private Node convertTypeAliasDeclaration(JsonObject node, SourceLocation loc) throws ParseError {
From b602a36a24198473c1e8222389f48721f7f598a8 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Tue, 11 Aug 2020 10:18:06 +0200
Subject: [PATCH 016/146] add test for generic spreads in a tuple
---
.../TypeScript/TypeAnnotations/tests.expected | 4 ++++
.../test/library-tests/TypeScript/TypeAnnotations/tst.ts | 8 ++++++++
2 files changed, 12 insertions(+)
diff --git a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected
index 5db52c2afcd..0e0d34153e7 100644
--- a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected
+++ b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected
@@ -108,6 +108,7 @@ test_VariableTypes
| tst.ts:142:17:142:25 | condition | condition | tst.ts:142:28:142:30 | any |
| tst.ts:142:33:142:35 | msg | msg | tst.ts:142:39:142:44 | string |
| tst.ts:148:25:148:27 | val | val | tst.ts:148:30:148:32 | any |
+| tst.ts:157:32:157:34 | arr | arr | tst.ts:157:37:157:56 | readonly [any, ...T] |
test_QualifiedTypeAccess
| tst.ts:63:19:63:21 | N.I | tst.ts:63:19:63:19 | N | tst.ts:63:21:63:21 | I |
| tst.ts:64:20:64:24 | N.M.I | tst.ts:64:20:64:22 | N.M | tst.ts:64:24:64:24 | I |
@@ -198,6 +199,8 @@ test_TupleTypeExpr
| tst.ts:136:39:136:68 | [number ... mber[]] | 0 | 3 | tst.ts:136:40:136:45 | number |
| tst.ts:136:39:136:68 | [number ... mber[]] | 1 | 3 | tst.ts:136:48:136:54 | string? |
| tst.ts:136:39:136:68 | [number ... mber[]] | 2 | 3 | tst.ts:136:57:136:67 | ...number[] |
+| tst.ts:157:46:157:56 | [any, ...T] | 0 | 2 | tst.ts:157:47:157:49 | any |
+| tst.ts:157:46:157:56 | [any, ...T] | 1 | 2 | tst.ts:157:52:157:55 | ...T |
test_FieldTypes
| tst.ts:15:3:15:22 | numberField: number; | tst.ts:15:16:15:21 | number |
| tst.ts:16:3:16:22 | stringField: string; | tst.ts:16:16:16:21 | string |
@@ -219,6 +222,7 @@ test_ArrayTypeExpr
| tst.ts:81:27:81:34 | string[] | tst.ts:81:27:81:32 | string |
| tst.ts:135:39:135:46 | string[] | tst.ts:135:39:135:44 | string |
| tst.ts:136:60:136:67 | number[] | tst.ts:136:60:136:65 | number |
+| tst.ts:157:25:157:29 | any[] | tst.ts:157:25:157:27 | any |
test_TypeAccessHelpers
| tst.ts:65:18:65:24 | Generic | 1 | 0 | tst.ts:65:26:65:31 | number |
| tst.ts:66:18:66:32 | N.InnerGeneric1 | 1 | 0 | tst.ts:66:34:66:39 | number |
diff --git a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tst.ts b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tst.ts
index faeb9e9c833..975432f4576 100644
--- a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tst.ts
+++ b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tst.ts
@@ -150,3 +150,11 @@ function assertIsString(val: any): asserts val is string {
throw new AssertionError("Not a string!");
}
}
+
+// TypeScript 4.0
+
+// spreads in tuple type syntax can now be generic
+function tail(arr: readonly [any, ...T]) {
+ const [_ignored, ...rest] = arr;
+ return rest;
+}
\ No newline at end of file
From 2612e0c5dd8acec40f65b4297a0efd8932a9cba5 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Tue, 11 Aug 2020 10:19:27 +0200
Subject: [PATCH 017/146] add test for spread in tuple in non-last position
---
.../library-tests/TypeScript/TypeAnnotations/tests.expected | 6 ++++++
.../ql/test/library-tests/TypeScript/TypeAnnotations/tst.ts | 6 ++++++
2 files changed, 12 insertions(+)
diff --git a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected
index 0e0d34153e7..9e3733d8480 100644
--- a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected
+++ b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected
@@ -109,6 +109,8 @@ test_VariableTypes
| tst.ts:142:33:142:35 | msg | msg | tst.ts:142:39:142:44 | string |
| tst.ts:148:25:148:27 | val | val | tst.ts:148:30:148:32 | any |
| tst.ts:157:32:157:34 | arr | arr | tst.ts:157:37:157:56 | readonly [any, ...T] |
+| tst.ts:164:47:164:50 | arr1 | arr1 | tst.ts:164:53:164:53 | T |
+| tst.ts:164:56:164:59 | arr2 | arr2 | tst.ts:164:62:164:62 | U |
test_QualifiedTypeAccess
| tst.ts:63:19:63:21 | N.I | tst.ts:63:19:63:19 | N | tst.ts:63:21:63:21 | I |
| tst.ts:64:20:64:24 | N.M.I | tst.ts:64:20:64:22 | N.M | tst.ts:64:24:64:24 | I |
@@ -201,6 +203,8 @@ test_TupleTypeExpr
| tst.ts:136:39:136:68 | [number ... mber[]] | 2 | 3 | tst.ts:136:57:136:67 | ...number[] |
| tst.ts:157:46:157:56 | [any, ...T] | 0 | 2 | tst.ts:157:47:157:49 | any |
| tst.ts:157:46:157:56 | [any, ...T] | 1 | 2 | tst.ts:157:52:157:55 | ...T |
+| tst.ts:164:66:164:77 | [...T, ...U] | 0 | 2 | tst.ts:164:67:164:70 | ...T |
+| tst.ts:164:66:164:77 | [...T, ...U] | 1 | 2 | tst.ts:164:73:164:76 | ...U |
test_FieldTypes
| tst.ts:15:3:15:22 | numberField: number; | tst.ts:15:16:15:21 | number |
| tst.ts:16:3:16:22 | stringField: string; | tst.ts:16:16:16:21 | string |
@@ -223,6 +227,7 @@ test_ArrayTypeExpr
| tst.ts:135:39:135:46 | string[] | tst.ts:135:39:135:44 | string |
| tst.ts:136:60:136:67 | number[] | tst.ts:136:60:136:65 | number |
| tst.ts:157:25:157:29 | any[] | tst.ts:157:25:157:27 | any |
+| tst.ts:163:21:163:25 | any[] | tst.ts:163:21:163:23 | any |
test_TypeAccessHelpers
| tst.ts:65:18:65:24 | Generic | 1 | 0 | tst.ts:65:26:65:31 | number |
| tst.ts:66:18:66:32 | N.InnerGeneric1 | 1 | 0 | tst.ts:66:34:66:39 | number |
@@ -274,6 +279,7 @@ test_ReturnTypes
| tst.ts:96:1:96:52 | functio ... rn x; } | function f3 | tst.ts:96:38:96:38 | S |
| tst.ts:142:1:146:1 | functio ... )\\n }\\n} | function assert | tst.ts:142:48:142:64 | asserts condition |
| tst.ts:148:1:152:1 | functio ... ;\\n }\\n} | function assertIsString | tst.ts:148:36:148:56 | asserts ... string |
+| tst.ts:164:1:166:1 | functio ... rr2];\\n} | function concat | tst.ts:164:66:164:77 | [...T, ...U] |
test_KeyofTypeExpr
| tst.ts:49:16:49:30 | keyof Interface | tst.ts:49:22:49:30 | Interface |
| tst.ts:113:26:113:35 | keyof Node | tst.ts:113:32:113:35 | Node |
diff --git a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tst.ts b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tst.ts
index 975432f4576..fb9539e35e8 100644
--- a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tst.ts
+++ b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tst.ts
@@ -157,4 +157,10 @@ function assertIsString(val: any): asserts val is string {
function tail(arr: readonly [any, ...T]) {
const [_ignored, ...rest] = arr;
return rest;
+}
+
+// spread in tuple in non-last position
+type Arr = readonly any[];
+function concat(arr1: T, arr2: U): [...T, ...U] {
+ return [...arr1, ...arr2];
}
\ No newline at end of file
From ea583fe86242e6e676d83c59e659296aef489171 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Tue, 11 Aug 2020 11:47:28 +0200
Subject: [PATCH 018/146] add basic support for named tuple elements
---
.../src/com/semmle/js/parser/TypeScriptASTConverter.java | 6 ++++++
.../library-tests/TypeScript/TypeAnnotations/tests.expected | 4 ++++
.../ql/test/library-tests/TypeScript/TypeAnnotations/tst.ts | 6 ++++++
3 files changed, 16 insertions(+)
diff --git a/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java b/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java
index 8331d91326e..e4d60d2b0f7 100644
--- a/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java
+++ b/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java
@@ -585,6 +585,8 @@ public class TypeScriptASTConverter {
return convertTryStatement(node, loc);
case "TupleType":
return convertTupleType(node, loc);
+ case "NamedTupleMember":
+ return convertNamedTupleMember(node, loc);
case "TypeAliasDeclaration":
return convertTypeAliasDeclaration(node, loc);
case "TypeAssertionExpression":
@@ -2180,6 +2182,10 @@ public class TypeScriptASTConverter {
return new TupleTypeExpr(loc, convertChildrenAsTypes(node, "elements"));
}
+ private Node convertNamedTupleMember(JsonObject node, SourceLocation loc) throws ParseError {
+ return convertChild(node, "type");
+ }
+
private Node convertTypeAliasDeclaration(JsonObject node, SourceLocation loc) throws ParseError {
TypeAliasDeclaration typeAlias =
new TypeAliasDeclaration(
diff --git a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected
index 9e3733d8480..f7eda7865e7 100644
--- a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected
+++ b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected
@@ -111,6 +111,7 @@ test_VariableTypes
| tst.ts:157:32:157:34 | arr | arr | tst.ts:157:37:157:56 | readonly [any, ...T] |
| tst.ts:164:47:164:50 | arr1 | arr1 | tst.ts:164:53:164:53 | T |
| tst.ts:164:56:164:59 | arr2 | arr2 | tst.ts:164:62:164:62 | U |
+| tst.ts:169:31:169:31 | x | x | tst.ts:169:34:169:64 | [first: ... number] |
test_QualifiedTypeAccess
| tst.ts:63:19:63:21 | N.I | tst.ts:63:19:63:19 | N | tst.ts:63:21:63:21 | I |
| tst.ts:64:20:64:24 | N.M.I | tst.ts:64:20:64:22 | N.M | tst.ts:64:24:64:24 | I |
@@ -205,6 +206,8 @@ test_TupleTypeExpr
| tst.ts:157:46:157:56 | [any, ...T] | 1 | 2 | tst.ts:157:52:157:55 | ...T |
| tst.ts:164:66:164:77 | [...T, ...U] | 0 | 2 | tst.ts:164:67:164:70 | ...T |
| tst.ts:164:66:164:77 | [...T, ...U] | 1 | 2 | tst.ts:164:73:164:76 | ...U |
+| tst.ts:169:34:169:64 | [first: ... number] | 0 | 2 | tst.ts:169:42:169:47 | number |
+| tst.ts:169:34:169:64 | [first: ... number] | 1 | 2 | tst.ts:169:58:169:63 | number |
test_FieldTypes
| tst.ts:15:3:15:22 | numberField: number; | tst.ts:15:16:15:21 | number |
| tst.ts:16:3:16:22 | stringField: string; | tst.ts:16:16:16:21 | string |
@@ -280,6 +283,7 @@ test_ReturnTypes
| tst.ts:142:1:146:1 | functio ... )\\n }\\n} | function assert | tst.ts:142:48:142:64 | asserts condition |
| tst.ts:148:1:152:1 | functio ... ;\\n }\\n} | function assertIsString | tst.ts:148:36:148:56 | asserts ... string |
| tst.ts:164:1:166:1 | functio ... rr2];\\n} | function concat | tst.ts:164:66:164:77 | [...T, ...U] |
+| tst.ts:169:1:172:1 | functio ... + b;\\n} | function labelOnTupleElements | tst.ts:169:68:169:73 | number |
test_KeyofTypeExpr
| tst.ts:49:16:49:30 | keyof Interface | tst.ts:49:22:49:30 | Interface |
| tst.ts:113:26:113:35 | keyof Node | tst.ts:113:32:113:35 | Node |
diff --git a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tst.ts b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tst.ts
index fb9539e35e8..d5b02b4911b 100644
--- a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tst.ts
+++ b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tst.ts
@@ -163,4 +163,10 @@ function tail(arr: readonly [any, ...T]) {
type Arr = readonly any[];
function concat(arr1: T, arr2: U): [...T, ...U] {
return [...arr1, ...arr2];
+}
+
+// labelled tuple elements
+function labelOnTupleElements(x: [first: number, second: number]): number {
+ let [a, b] = x;
+ return a + b;
}
\ No newline at end of file
From 2f34990ae6ae700a033847ac2ba20519b36d597f Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Tue, 11 Aug 2020 11:49:39 +0200
Subject: [PATCH 019/146] add another test for spread elements in tuple types
---
.../TypeScript/TypeAnnotations/tests.expected | 7 +++++++
.../library-tests/TypeScript/TypeAnnotations/tst.ts | 10 +++++++++-
2 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected
index f7eda7865e7..23cd84a8b55 100644
--- a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected
+++ b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected
@@ -112,6 +112,7 @@ test_VariableTypes
| tst.ts:164:47:164:50 | arr1 | arr1 | tst.ts:164:53:164:53 | T |
| tst.ts:164:56:164:59 | arr2 | arr2 | tst.ts:164:62:164:62 | U |
| tst.ts:169:31:169:31 | x | x | tst.ts:169:34:169:64 | [first: ... number] |
+| tst.ts:180:5:180:7 | foo | foo | tst.ts:180:10:180:21 | StrStrNumNum |
test_QualifiedTypeAccess
| tst.ts:63:19:63:21 | N.I | tst.ts:63:19:63:19 | N | tst.ts:63:21:63:21 | I |
| tst.ts:64:20:64:24 | N.M.I | tst.ts:64:20:64:22 | N.M | tst.ts:64:24:64:24 | I |
@@ -208,6 +209,12 @@ test_TupleTypeExpr
| tst.ts:164:66:164:77 | [...T, ...U] | 1 | 2 | tst.ts:164:73:164:76 | ...U |
| tst.ts:169:34:169:64 | [first: ... number] | 0 | 2 | tst.ts:169:42:169:47 | number |
| tst.ts:169:34:169:64 | [first: ... number] | 1 | 2 | tst.ts:169:58:169:63 | number |
+| tst.ts:175:16:175:31 | [string, string] | 0 | 2 | tst.ts:175:17:175:22 | string |
+| tst.ts:175:16:175:31 | [string, string] | 1 | 2 | tst.ts:175:25:175:30 | string |
+| tst.ts:176:16:176:31 | [number, number] | 0 | 2 | tst.ts:176:17:176:22 | number |
+| tst.ts:176:16:176:31 | [number, number] | 1 | 2 | tst.ts:176:25:176:30 | number |
+| tst.ts:179:21:179:44 | [...Str ... umbers] | 0 | 2 | tst.ts:179:22:179:31 | ...Strings |
+| tst.ts:179:21:179:44 | [...Str ... umbers] | 1 | 2 | tst.ts:179:34:179:43 | ...Numbers |
test_FieldTypes
| tst.ts:15:3:15:22 | numberField: number; | tst.ts:15:16:15:21 | number |
| tst.ts:16:3:16:22 | stringField: string; | tst.ts:16:16:16:21 | string |
diff --git a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tst.ts b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tst.ts
index d5b02b4911b..4b4ec925f08 100644
--- a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tst.ts
+++ b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tst.ts
@@ -169,4 +169,12 @@ function concat(arr1: T, arr2: U): [...T, ...U] {
function labelOnTupleElements(x: [first: number, second: number]): number {
let [a, b] = x;
return a + b;
-}
\ No newline at end of file
+}
+
+// spread elements can occur anywhere in a tuple – not just at the end!
+type Strings = [string, string];
+type Numbers = [number, number];
+
+// [string, string, number, number]
+type StrStrNumNum = [...Strings, ...Numbers];
+var foo: StrStrNumNum;
\ No newline at end of file
From d2c87d0a2ee5c5079be78a26c1cbd6e43f27efde Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Tue, 11 Aug 2020 13:55:01 +0200
Subject: [PATCH 020/146] add support for the new assign expression in
TypeScript 4
---
.../com/semmle/js/extractor/ExprKinds.java | 3 ++
javascript/ql/src/semmle/javascript/Expr.qll | 35 ++++++++++++++++++-
.../ql/src/semmlecode.javascript.dbscheme | 5 ++-
.../TypeScript/TypeAnnotations/tests.expected | 3 ++
.../TypeScript/TypeAnnotations/tst.ts | 11 +++++-
5 files changed, 54 insertions(+), 3 deletions(-)
diff --git a/javascript/extractor/src/com/semmle/js/extractor/ExprKinds.java b/javascript/extractor/src/com/semmle/js/extractor/ExprKinds.java
index d88f341bc0e..c470d1df804 100644
--- a/javascript/extractor/src/com/semmle/js/extractor/ExprKinds.java
+++ b/javascript/extractor/src/com/semmle/js/extractor/ExprKinds.java
@@ -77,6 +77,9 @@ public class ExprKinds {
binOpKinds.put("**", 87);
binOpKinds.put("**=", 88);
binOpKinds.put("??", 107);
+ binOpKinds.put("&&=", 116);
+ binOpKinds.put("||=", 117);
+ binOpKinds.put("??=", 118);
}
private static final Map unOpKinds = new LinkedHashMap();
diff --git a/javascript/ql/src/semmle/javascript/Expr.qll b/javascript/ql/src/semmle/javascript/Expr.qll
index cba8be4241f..2aefcd2ffd9 100644
--- a/javascript/ql/src/semmle/javascript/Expr.qll
+++ b/javascript/ql/src/semmle/javascript/Expr.qll
@@ -1851,7 +1851,7 @@ class AssignExpr extends @assignexpr, Assignment {
private class TCompoundAssignExpr =
@assignaddexpr or @assignsubexpr or @assignmulexpr or @assigndivexpr or @assignmodexpr or
@assignexpexpr or @assignlshiftexpr or @assignrshiftexpr or @assignurshiftexpr or
- @assignorexpr or @assignxorexpr or @assignandexpr;
+ @assignorexpr or @assignxorexpr or @assignandexpr or @assignlogandexpr or @assignlogorexpr or @assignnullishcoalescingexpr;
/**
* A compound assign expression.
@@ -1997,6 +1997,39 @@ class AssignXOrExpr extends @assignxorexpr, CompoundAssignExpr { }
*/
class AssignAndExpr extends @assignandexpr, CompoundAssignExpr { }
+/**
+ * A logical-'or'-assign expression.
+ *
+ * Example:
+ *
+ * ```
+ * x ||= y
+ * ```
+ */
+class AssignLogOrExpr extends @assignlogandexpr, CompoundAssignExpr { }
+
+/**
+ * A logical-'and'-assign expression.
+ *
+ * Example:
+ *
+ * ```
+ * x &&= y
+ * ```
+ */
+class AssignLogAndExpr extends @assignlogorexpr, CompoundAssignExpr { }
+
+/**
+ * A 'nullish-coalescing'-assign expression.
+ *
+ * Example:
+ *
+ * ```
+ * x ??= y
+ * ```
+ */
+class AssignNullishCoalescingExpr extends @assignnullishcoalescingexpr, CompoundAssignExpr { }
+
/**
* An update expression, that is, an increment or decrement expression.
*
diff --git a/javascript/ql/src/semmlecode.javascript.dbscheme b/javascript/ql/src/semmlecode.javascript.dbscheme
index 2dc7a038982..c73fbfca57f 100644
--- a/javascript/ql/src/semmlecode.javascript.dbscheme
+++ b/javascript/ql/src/semmlecode.javascript.dbscheme
@@ -351,6 +351,9 @@ case @expr.kind of
| 113 = @e4x_xml_dynamic_qualident
| 114 = @e4x_xml_dotdotexpr
| 115 = @importmetaexpr
+| 116 = @assignlogandexpr
+| 117 = @assignlogorexpr
+| 118 = @assignnullishcoalescingexpr
;
@varaccess = @proper_varaccess | @export_varaccess;
@@ -372,7 +375,7 @@ case @expr.kind of
@binaryexpr = @comparison | @lshiftexpr | @rshiftexpr | @urshiftexpr | @addexpr | @subexpr | @mulexpr | @divexpr | @modexpr | @expexpr | @bitorexpr | @xorexpr | @bitandexpr | @inexpr | @instanceofexpr | @logandexpr | @logorexpr | @nullishcoalescingexpr;
-@assignment = @assignexpr | @assignaddexpr | @assignsubexpr | @assignmulexpr | @assigndivexpr | @assignmodexpr | @assignexpexpr | @assignlshiftexpr | @assignrshiftexpr | @assignurshiftexpr | @assignorexpr | @assignxorexpr | @assignandexpr;
+@assignment = @assignexpr | @assignaddexpr | @assignsubexpr | @assignmulexpr | @assigndivexpr | @assignmodexpr | @assignexpexpr | @assignlshiftexpr | @assignrshiftexpr | @assignurshiftexpr | @assignorexpr | @assignxorexpr | @assignandexpr | @assignlogandexpr | @assignlogorexpr | @assignnullishcoalescingexpr;
@updateexpr = @preincexpr | @postincexpr | @predecexpr | @postdecexpr;
diff --git a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected
index 23cd84a8b55..08738d8786f 100644
--- a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected
+++ b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected
@@ -113,6 +113,9 @@ test_VariableTypes
| tst.ts:164:56:164:59 | arr2 | arr2 | tst.ts:164:62:164:62 | U |
| tst.ts:169:31:169:31 | x | x | tst.ts:169:34:169:64 | [first: ... number] |
| tst.ts:180:5:180:7 | foo | foo | tst.ts:180:10:180:21 | StrStrNumNum |
+| tst.ts:184:7:184:8 | a1 | a1 | tst.ts:184:12:184:17 | number |
+| tst.ts:185:7:185:8 | a2 | a2 | tst.ts:185:12:185:17 | number |
+| tst.ts:186:7:186:8 | a3 | a3 | tst.ts:186:12:186:17 | number |
test_QualifiedTypeAccess
| tst.ts:63:19:63:21 | N.I | tst.ts:63:19:63:19 | N | tst.ts:63:21:63:21 | I |
| tst.ts:64:20:64:24 | N.M.I | tst.ts:64:20:64:22 | N.M | tst.ts:64:24:64:24 | I |
diff --git a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tst.ts b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tst.ts
index 4b4ec925f08..138576c4b4f 100644
--- a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tst.ts
+++ b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tst.ts
@@ -177,4 +177,13 @@ type Numbers = [number, number];
// [string, string, number, number]
type StrStrNumNum = [...Strings, ...Numbers];
-var foo: StrStrNumNum;
\ No newline at end of file
+var foo: StrStrNumNum;
+
+// Short-Circuiting Assignment Operators
+function shortAssignment() {
+ let a1 : number = parseInt("foo");
+ let a2 : number = parseInt("bar");
+ let a3 : number = a1 ||= a2;
+ let a4 = a2 &&= a3;
+ let a5 = a3 ??= a4;
+}
\ No newline at end of file
From b10130524806768455bfd8d23cceb54e850426b4 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 12 Aug 2020 09:27:43 +0200
Subject: [PATCH 021/146] autoformat
---
javascript/ql/src/semmle/javascript/Expr.qll | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/javascript/ql/src/semmle/javascript/Expr.qll b/javascript/ql/src/semmle/javascript/Expr.qll
index 2aefcd2ffd9..53546033e87 100644
--- a/javascript/ql/src/semmle/javascript/Expr.qll
+++ b/javascript/ql/src/semmle/javascript/Expr.qll
@@ -1851,7 +1851,8 @@ class AssignExpr extends @assignexpr, Assignment {
private class TCompoundAssignExpr =
@assignaddexpr or @assignsubexpr or @assignmulexpr or @assigndivexpr or @assignmodexpr or
@assignexpexpr or @assignlshiftexpr or @assignrshiftexpr or @assignurshiftexpr or
- @assignorexpr or @assignxorexpr or @assignandexpr or @assignlogandexpr or @assignlogorexpr or @assignnullishcoalescingexpr;
+ @assignorexpr or @assignxorexpr or @assignandexpr or @assignlogandexpr or @assignlogorexpr or
+ @assignnullishcoalescingexpr;
/**
* A compound assign expression.
From 211ef610399a253512246e5574d3f2e34f9255be Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 12 Aug 2020 09:29:34 +0200
Subject: [PATCH 022/146] add change note
---
change-notes/1.25/analysis-javascript.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/change-notes/1.25/analysis-javascript.md b/change-notes/1.25/analysis-javascript.md
index 33d9055b839..9491ecb657f 100644
--- a/change-notes/1.25/analysis-javascript.md
+++ b/change-notes/1.25/analysis-javascript.md
@@ -30,7 +30,7 @@
- [yargs](https://www.npmjs.com/package/yargs)
- [webpack-dev-server](https://www.npmjs.com/package/webpack-dev-server)
-* TypeScript 3.9 is now supported.
+* TypeScript 4.0 is now supported.
* TypeScript code embedded in HTML and Vue files is now extracted and analyzed.
From 26dcd2faaedf19936be62428912d82920f0b0ec4 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 12 Aug 2020 10:33:49 +0200
Subject: [PATCH 023/146] add support for getting the name from named tuple
elements
---
.../src/com/semmle/js/ast/NodeCopier.java | 2 +-
.../com/semmle/js/extractor/ASTExtractor.java | 5 +++++
.../js/parser/TypeScriptASTConverter.java | 18 +++++++++++++++++-
.../src/com/semmle/ts/ast/TupleTypeExpr.java | 8 +++++++-
.../ql/src/semmle/javascript/TypeScript.qll | 3 +++
.../ql/src/semmlecode.javascript.dbscheme | 7 +++++++
.../TypeAnnotations/TupleTypeExpr.qll | 4 ++++
.../TypeScript/TypeAnnotations/tests.expected | 3 +++
8 files changed, 47 insertions(+), 3 deletions(-)
diff --git a/javascript/extractor/src/com/semmle/js/ast/NodeCopier.java b/javascript/extractor/src/com/semmle/js/ast/NodeCopier.java
index 18a3e55b8c9..ddf57b32cff 100644
--- a/javascript/extractor/src/com/semmle/js/ast/NodeCopier.java
+++ b/javascript/extractor/src/com/semmle/js/ast/NodeCopier.java
@@ -705,7 +705,7 @@ public class NodeCopier implements Visitor {
@Override
public INode visit(TupleTypeExpr nd, Void c) {
- return new TupleTypeExpr(visit(nd.getLoc()), copy(nd.getElementTypes()));
+ return new TupleTypeExpr(visit(nd.getLoc()), copy(nd.getElementTypes()), nd.getElementNames());
}
@Override
diff --git a/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java
index 8dcc6d3a5c5..4bf57075b0b 100644
--- a/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java
+++ b/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java
@@ -1819,6 +1819,11 @@ public class ASTExtractor {
@Override
public Label visit(TupleTypeExpr nd, Context c) {
Label key = super.visit(nd, c);
+ if (nd.getElementNames() != null) {
+ for (int i = 0; i < nd.getElementNames().size(); i++) {
+ trapwriter.addTuple("tuple_element_name", key, i, nd.getElementNames().get(i));
+ }
+ }
visitAll(nd.getElementTypes(), key, IdContext.typeBind, 0);
return key;
}
diff --git a/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java b/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java
index e4d60d2b0f7..83529f8b1b4 100644
--- a/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java
+++ b/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java
@@ -5,6 +5,7 @@ import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
@@ -2179,9 +2180,24 @@ public class TypeScriptASTConverter {
}
private Node convertTupleType(JsonObject node, SourceLocation loc) throws ParseError {
- return new TupleTypeExpr(loc, convertChildrenAsTypes(node, "elements"));
+ List elements = new ArrayList<>();
+ ((JsonArray)node.get("elements")).iterator().forEachRemaining(elements::add);
+
+ List elementNames = elements.stream()
+ .filter(n -> getKind(n).equals("NamedTupleMember"))
+ .map(n -> n.getAsJsonObject().get("name"))
+ .map(n -> n.getAsJsonObject().get("escapedText"))
+ .map(n -> n.getAsString())
+ .collect(Collectors.toList());
+
+ if (elementNames.size() == 0) {
+ elementNames = null;
+ }
+
+ return new TupleTypeExpr(loc, convertChildrenAsTypes(node, "elements"), elementNames);
}
+ // This method just does a trivial forward to the type. The names have already been extracted in `convertTupleType`.
private Node convertNamedTupleMember(JsonObject node, SourceLocation loc) throws ParseError {
return convertChild(node, "type");
}
diff --git a/javascript/extractor/src/com/semmle/ts/ast/TupleTypeExpr.java b/javascript/extractor/src/com/semmle/ts/ast/TupleTypeExpr.java
index 5b9044cd63f..9d49899fafd 100644
--- a/javascript/extractor/src/com/semmle/ts/ast/TupleTypeExpr.java
+++ b/javascript/extractor/src/com/semmle/ts/ast/TupleTypeExpr.java
@@ -7,16 +7,22 @@ import java.util.List;
/** A tuple type, such as [number, string]. */
public class TupleTypeExpr extends TypeExpression {
private final List elementTypes;
+ private final List elementNames;
- public TupleTypeExpr(SourceLocation loc, List elementTypes) {
+ public TupleTypeExpr(SourceLocation loc, List elementTypes, List elementNames) {
super("TupleTypeExpr", loc);
this.elementTypes = elementTypes;
+ this.elementNames = elementNames;
}
public List getElementTypes() {
return elementTypes;
}
+ public List getElementNames() {
+ return elementNames;
+ }
+
@Override
public R accept(Visitor v, C c) {
return v.visit(this, c);
diff --git a/javascript/ql/src/semmle/javascript/TypeScript.qll b/javascript/ql/src/semmle/javascript/TypeScript.qll
index 72536ea5db4..82088638a60 100644
--- a/javascript/ql/src/semmle/javascript/TypeScript.qll
+++ b/javascript/ql/src/semmle/javascript/TypeScript.qll
@@ -853,6 +853,9 @@ class TupleTypeExpr extends @tupletypeexpr, TypeExpr {
/** Gets the number of elements in the tuple type. */
int getNumElementType() { result = count(getAnElementType()) }
+
+ /** Gets the name of the `n`th tuple member if the tuple members are named. */
+ string getElementName(int n) { tuple_element_name(this, n, result) }
}
/**
diff --git a/javascript/ql/src/semmlecode.javascript.dbscheme b/javascript/ql/src/semmlecode.javascript.dbscheme
index c73fbfca57f..3f291931cb8 100644
--- a/javascript/ql/src/semmlecode.javascript.dbscheme
+++ b/javascript/ql/src/semmlecode.javascript.dbscheme
@@ -790,6 +790,13 @@ tuple_type_rest(
unique int typ: @type ref
);
+#keyset[tuple, index]
+tuple_element_name (
+ int tuple: @tupletypeexpr ref,
+ int index: int ref,
+ varchar(900) name: string ref
+);
+
// comments
comments (unique int id: @comment,
int kind: int ref,
diff --git a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/TupleTypeExpr.qll b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/TupleTypeExpr.qll
index b8cbe0ab2e3..488138a10e4 100644
--- a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/TupleTypeExpr.qll
+++ b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/TupleTypeExpr.qll
@@ -3,3 +3,7 @@ import javascript
query predicate test_TupleTypeExpr(TupleTypeExpr type, int n, int res0, TypeExpr res1) {
res0 = type.getNumElementType() and res1 = type.getElementType(n)
}
+
+query predicate test_TupleTypeElementName(TupleTypeExpr type, int n, string name) {
+ name = type.getElementName(n)
+}
\ No newline at end of file
diff --git a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected
index 08738d8786f..00bda23ab3f 100644
--- a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected
+++ b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected
@@ -218,6 +218,9 @@ test_TupleTypeExpr
| tst.ts:176:16:176:31 | [number, number] | 1 | 2 | tst.ts:176:25:176:30 | number |
| tst.ts:179:21:179:44 | [...Str ... umbers] | 0 | 2 | tst.ts:179:22:179:31 | ...Strings |
| tst.ts:179:21:179:44 | [...Str ... umbers] | 1 | 2 | tst.ts:179:34:179:43 | ...Numbers |
+test_TupleTypeElementName
+| tst.ts:169:34:169:64 | [first: ... number] | 0 | first |
+| tst.ts:169:34:169:64 | [first: ... number] | 1 | second |
test_FieldTypes
| tst.ts:15:3:15:22 | numberField: number; | tst.ts:15:16:15:21 | number |
| tst.ts:16:3:16:22 | stringField: string; | tst.ts:16:16:16:21 | string |
From 0e33eae960c71daeee64f064bb770bdd72df0eb8 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 12 Aug 2020 10:58:18 +0200
Subject: [PATCH 024/146] add dbscheme upgrade script
---
.../TypeAnnotations/TupleTypeExpr.qll | 2 +-
.../old.dbscheme | 1190 ++++++++++++++++
.../semmlecode.javascript.dbscheme | 1200 +++++++++++++++++
.../upgrade.properties | 2 +
4 files changed, 2393 insertions(+), 1 deletion(-)
create mode 100644 javascript/upgrades/2dc7a0389827d235763a3748aba73c2d4a677b15/old.dbscheme
create mode 100644 javascript/upgrades/2dc7a0389827d235763a3748aba73c2d4a677b15/semmlecode.javascript.dbscheme
create mode 100644 javascript/upgrades/2dc7a0389827d235763a3748aba73c2d4a677b15/upgrade.properties
diff --git a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/TupleTypeExpr.qll b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/TupleTypeExpr.qll
index 488138a10e4..03fe4325786 100644
--- a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/TupleTypeExpr.qll
+++ b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/TupleTypeExpr.qll
@@ -6,4 +6,4 @@ query predicate test_TupleTypeExpr(TupleTypeExpr type, int n, int res0, TypeExpr
query predicate test_TupleTypeElementName(TupleTypeExpr type, int n, string name) {
name = type.getElementName(n)
-}
\ No newline at end of file
+}
diff --git a/javascript/upgrades/2dc7a0389827d235763a3748aba73c2d4a677b15/old.dbscheme b/javascript/upgrades/2dc7a0389827d235763a3748aba73c2d4a677b15/old.dbscheme
new file mode 100644
index 00000000000..2dc7a038982
--- /dev/null
+++ b/javascript/upgrades/2dc7a0389827d235763a3748aba73c2d4a677b15/old.dbscheme
@@ -0,0 +1,1190 @@
+/*** Standard fragments ***/
+
+/** Files and folders **/
+
+@location = @location_default;
+
+locations_default(unique int id: @location_default,
+ int file: @file ref,
+ int beginLine: int ref,
+ int beginColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref
+ );
+
+@sourceline = @locatable;
+
+numlines(int element_id: @sourceline ref,
+ int num_lines: int ref,
+ int num_code: int ref,
+ int num_comment: int ref
+ );
+
+
+/*
+ fromSource(0) = unknown,
+ fromSource(1) = from source,
+ fromSource(2) = from library
+*/
+files(unique int id: @file,
+ varchar(900) name: string ref,
+ varchar(900) simple: string ref,
+ varchar(900) ext: string ref,
+ int fromSource: int ref);
+
+folders(unique int id: @folder,
+ varchar(900) name: string ref,
+ varchar(900) simple: string ref);
+
+
+@container = @folder | @file ;
+
+
+containerparent(int parent: @container ref,
+ unique int child: @container ref);
+
+/** Duplicate code **/
+
+duplicateCode(
+ unique int id : @duplication,
+ varchar(900) relativePath : string ref,
+ int equivClass : int ref);
+
+similarCode(
+ unique int id : @similarity,
+ varchar(900) relativePath : string ref,
+ int equivClass : int ref);
+
+@duplication_or_similarity = @duplication | @similarity;
+
+tokens(
+ int id : @duplication_or_similarity ref,
+ int offset : int ref,
+ int beginLine : int ref,
+ int beginColumn : int ref,
+ int endLine : int ref,
+ int endColumn : int ref);
+
+/** External data **/
+
+externalData(
+ int id : @externalDataElement,
+ varchar(900) path : string ref,
+ int column: int ref,
+ varchar(900) value : string ref
+);
+
+snapshotDate(unique date snapshotDate : date ref);
+
+sourceLocationPrefix(varchar(900) prefix : string ref);
+
+/** Version control data **/
+
+svnentries(
+ int id : @svnentry,
+ varchar(500) revision : string ref,
+ varchar(500) author : string ref,
+ date revisionDate : date ref,
+ int changeSize : int ref
+);
+
+svnaffectedfiles(
+ int id : @svnentry ref,
+ int file : @file ref,
+ varchar(500) action : string ref
+);
+
+svnentrymsg(
+ int id : @svnentry ref,
+ varchar(500) message : string ref
+);
+
+svnchurn(
+ int commit : @svnentry ref,
+ int file : @file ref,
+ int addedLines : int ref,
+ int deletedLines : int ref
+);
+
+
+/*** JavaScript-specific part ***/
+
+filetype(
+ int file: @file ref,
+ string filetype: string ref
+)
+
+// top-level code fragments
+toplevels (unique int id: @toplevel,
+ int kind: int ref);
+
+isExterns (int toplevel: @toplevel ref);
+
+case @toplevel.kind of
+ 0 = @script
+| 1 = @inline_script
+| 2 = @event_handler
+| 3 = @javascript_url;
+
+isModule (int tl: @toplevel ref);
+isNodejs (int tl: @toplevel ref);
+isES2015Module (int tl: @toplevel ref);
+isClosureModule (int tl: @toplevel ref);
+
+// statements
+#keyset[parent, idx]
+stmts (unique int id: @stmt,
+ int kind: int ref,
+ int parent: @stmtparent ref,
+ int idx: int ref,
+ varchar(900) tostring: string ref);
+
+stmtContainers (unique int stmt: @stmt ref,
+ int container: @stmt_container ref);
+
+jumpTargets (unique int jump: @stmt ref,
+ int target: @stmt ref);
+
+@stmtparent = @stmt | @toplevel | @functionexpr | @arrowfunctionexpr;
+@stmt_container = @toplevel | @function | @namespacedeclaration | @externalmoduledeclaration | @globalaugmentationdeclaration;
+
+case @stmt.kind of
+ 0 = @emptystmt
+| 1 = @blockstmt
+| 2 = @exprstmt
+| 3 = @ifstmt
+| 4 = @labeledstmt
+| 5 = @breakstmt
+| 6 = @continuestmt
+| 7 = @withstmt
+| 8 = @switchstmt
+| 9 = @returnstmt
+| 10 = @throwstmt
+| 11 = @trystmt
+| 12 = @whilestmt
+| 13 = @dowhilestmt
+| 14 = @forstmt
+| 15 = @forinstmt
+| 16 = @debuggerstmt
+| 17 = @functiondeclstmt
+| 18 = @vardeclstmt
+| 19 = @case
+| 20 = @catchclause
+| 21 = @forofstmt
+| 22 = @constdeclstmt
+| 23 = @letstmt
+| 24 = @legacy_letstmt
+| 25 = @foreachstmt
+| 26 = @classdeclstmt
+| 27 = @importdeclaration
+| 28 = @exportalldeclaration
+| 29 = @exportdefaultdeclaration
+| 30 = @exportnameddeclaration
+| 31 = @namespacedeclaration
+| 32 = @importequalsdeclaration
+| 33 = @exportassigndeclaration
+| 34 = @interfacedeclaration
+| 35 = @typealiasdeclaration
+| 36 = @enumdeclaration
+| 37 = @externalmoduledeclaration
+| 38 = @exportasnamespacedeclaration
+| 39 = @globalaugmentationdeclaration
+;
+
+@declstmt = @vardeclstmt | @constdeclstmt | @letstmt | @legacy_letstmt;
+
+@exportdeclaration = @exportalldeclaration | @exportdefaultdeclaration | @exportnameddeclaration;
+
+@namespacedefinition = @namespacedeclaration | @enumdeclaration;
+@typedefinition = @classdefinition | @interfacedeclaration | @enumdeclaration | @typealiasdeclaration | @enum_member;
+
+isInstantiated(unique int decl: @namespacedeclaration ref);
+
+@declarablenode = @declstmt | @namespacedeclaration | @classdeclstmt | @functiondeclstmt | @enumdeclaration | @externalmoduledeclaration | @globalaugmentationdeclaration | @field;
+hasDeclareKeyword(unique int stmt: @declarablenode ref);
+
+isForAwaitOf(unique int forof: @forofstmt ref);
+
+// expressions
+#keyset[parent, idx]
+exprs (unique int id: @expr,
+ int kind: int ref,
+ int parent: @exprparent ref,
+ int idx: int ref,
+ varchar(900) tostring: string ref);
+
+literals (varchar(900) value: string ref,
+ varchar(900) raw: string ref,
+ unique int expr: @exprortype ref);
+
+enclosingStmt (unique int expr: @exprortype ref,
+ int stmt: @stmt ref);
+
+exprContainers (unique int expr: @exprortype ref,
+ int container: @stmt_container ref);
+
+arraySize (unique int ae: @arraylike ref,
+ int sz: int ref);
+
+isDelegating (int yield: @yieldexpr ref);
+
+@exprorstmt = @expr | @stmt;
+@exprortype = @expr | @typeexpr;
+@exprparent = @exprorstmt | @property | @functiontypeexpr;
+@arraylike = @arrayexpr | @arraypattern;
+@type_annotation = @typeexpr | @jsdoc_type_expr;
+@node_in_stmt_container = @cfg_node | @type_annotation | @toplevel;
+
+case @expr.kind of
+ 0 = @label
+| 1 = @nullliteral
+| 2 = @booleanliteral
+| 3 = @numberliteral
+| 4 = @stringliteral
+| 5 = @regexpliteral
+| 6 = @thisexpr
+| 7 = @arrayexpr
+| 8 = @objexpr
+| 9 = @functionexpr
+| 10 = @seqexpr
+| 11 = @conditionalexpr
+| 12 = @newexpr
+| 13 = @callexpr
+| 14 = @dotexpr
+| 15 = @indexexpr
+| 16 = @negexpr
+| 17 = @plusexpr
+| 18 = @lognotexpr
+| 19 = @bitnotexpr
+| 20 = @typeofexpr
+| 21 = @voidexpr
+| 22 = @deleteexpr
+| 23 = @eqexpr
+| 24 = @neqexpr
+| 25 = @eqqexpr
+| 26 = @neqqexpr
+| 27 = @ltexpr
+| 28 = @leexpr
+| 29 = @gtexpr
+| 30 = @geexpr
+| 31 = @lshiftexpr
+| 32 = @rshiftexpr
+| 33 = @urshiftexpr
+| 34 = @addexpr
+| 35 = @subexpr
+| 36 = @mulexpr
+| 37 = @divexpr
+| 38 = @modexpr
+| 39 = @bitorexpr
+| 40 = @xorexpr
+| 41 = @bitandexpr
+| 42 = @inexpr
+| 43 = @instanceofexpr
+| 44 = @logandexpr
+| 45 = @logorexpr
+| 47 = @assignexpr
+| 48 = @assignaddexpr
+| 49 = @assignsubexpr
+| 50 = @assignmulexpr
+| 51 = @assigndivexpr
+| 52 = @assignmodexpr
+| 53 = @assignlshiftexpr
+| 54 = @assignrshiftexpr
+| 55 = @assignurshiftexpr
+| 56 = @assignorexpr
+| 57 = @assignxorexpr
+| 58 = @assignandexpr
+| 59 = @preincexpr
+| 60 = @postincexpr
+| 61 = @predecexpr
+| 62 = @postdecexpr
+| 63 = @parexpr
+| 64 = @vardeclarator
+| 65 = @arrowfunctionexpr
+| 66 = @spreadelement
+| 67 = @arraypattern
+| 68 = @objectpattern
+| 69 = @yieldexpr
+| 70 = @taggedtemplateexpr
+| 71 = @templateliteral
+| 72 = @templateelement
+| 73 = @arraycomprehensionexpr
+| 74 = @generatorexpr
+| 75 = @forincomprehensionblock
+| 76 = @forofcomprehensionblock
+| 77 = @legacy_letexpr
+| 78 = @vardecl
+| 79 = @proper_varaccess
+| 80 = @classexpr
+| 81 = @superexpr
+| 82 = @newtargetexpr
+| 83 = @namedimportspecifier
+| 84 = @importdefaultspecifier
+| 85 = @importnamespacespecifier
+| 86 = @namedexportspecifier
+| 87 = @expexpr
+| 88 = @assignexpexpr
+| 89 = @jsxelement
+| 90 = @jsxqualifiedname
+| 91 = @jsxemptyexpr
+| 92 = @awaitexpr
+| 93 = @functionsentexpr
+| 94 = @decorator
+| 95 = @exportdefaultspecifier
+| 96 = @exportnamespacespecifier
+| 97 = @bindexpr
+| 98 = @externalmodulereference
+| 99 = @dynamicimport
+| 100 = @expressionwithtypearguments
+| 101 = @prefixtypeassertion
+| 102 = @astypeassertion
+| 103 = @export_varaccess
+| 104 = @decorator_list
+| 105 = @non_null_assertion
+| 106 = @bigintliteral
+| 107 = @nullishcoalescingexpr
+| 108 = @e4x_xml_anyname
+| 109 = @e4x_xml_static_attribute_selector
+| 110 = @e4x_xml_dynamic_attribute_selector
+| 111 = @e4x_xml_filter_expression
+| 112 = @e4x_xml_static_qualident
+| 113 = @e4x_xml_dynamic_qualident
+| 114 = @e4x_xml_dotdotexpr
+| 115 = @importmetaexpr
+;
+
+@varaccess = @proper_varaccess | @export_varaccess;
+@varref = @vardecl | @varaccess;
+
+@identifier = @label | @varref | @typeidentifier;
+
+@literal = @nullliteral | @booleanliteral | @numberliteral | @stringliteral | @regexpliteral | @bigintliteral;
+
+@propaccess = @dotexpr | @indexexpr;
+
+@invokeexpr = @newexpr | @callexpr;
+
+@unaryexpr = @negexpr | @plusexpr | @lognotexpr | @bitnotexpr | @typeofexpr | @voidexpr | @deleteexpr | @spreadelement;
+
+@equalitytest = @eqexpr | @neqexpr | @eqqexpr | @neqqexpr;
+
+@comparison = @equalitytest | @ltexpr | @leexpr | @gtexpr | @geexpr;
+
+@binaryexpr = @comparison | @lshiftexpr | @rshiftexpr | @urshiftexpr | @addexpr | @subexpr | @mulexpr | @divexpr | @modexpr | @expexpr | @bitorexpr | @xorexpr | @bitandexpr | @inexpr | @instanceofexpr | @logandexpr | @logorexpr | @nullishcoalescingexpr;
+
+@assignment = @assignexpr | @assignaddexpr | @assignsubexpr | @assignmulexpr | @assigndivexpr | @assignmodexpr | @assignexpexpr | @assignlshiftexpr | @assignrshiftexpr | @assignurshiftexpr | @assignorexpr | @assignxorexpr | @assignandexpr;
+
+@updateexpr = @preincexpr | @postincexpr | @predecexpr | @postdecexpr;
+
+@pattern = @varref | @arraypattern | @objectpattern;
+
+@comprehensionexpr = @arraycomprehensionexpr | @generatorexpr;
+
+@comprehensionblock = @forincomprehensionblock | @forofcomprehensionblock;
+
+@importspecifier = @namedimportspecifier | @importdefaultspecifier | @importnamespacespecifier;
+
+@exportspecifier = @namedexportspecifier | @exportdefaultspecifier | @exportnamespacespecifier;
+
+@import_or_export_declaration = @importdeclaration | @exportdeclaration;
+
+@typeassertion = @astypeassertion | @prefixtypeassertion;
+
+@classdefinition = @classdeclstmt | @classexpr;
+@interfacedefinition = @interfacedeclaration | @interfacetypeexpr;
+@classorinterface = @classdefinition | @interfacedefinition;
+
+@lexical_decl = @vardecl | @typedecl;
+@lexical_access = @varaccess | @localtypeaccess | @localvartypeaccess | @localnamespaceaccess;
+@lexical_ref = @lexical_decl | @lexical_access;
+
+@e4x_xml_attribute_selector = @e4x_xml_static_attribute_selector | @e4x_xml_dynamic_attribute_selector;
+@e4x_xml_qualident = @e4x_xml_static_qualident | @e4x_xml_dynamic_qualident;
+
+// scopes
+scopes (unique int id: @scope,
+ int kind: int ref);
+
+case @scope.kind of
+ 0 = @globalscope
+| 1 = @functionscope
+| 2 = @catchscope
+| 3 = @modulescope
+| 4 = @blockscope
+| 5 = @forscope
+| 6 = @forinscope // for-of scopes work the same as for-in scopes
+| 7 = @comprehensionblockscope
+| 8 = @classexprscope
+| 9 = @namespacescope
+| 10 = @classdeclscope
+| 11 = @interfacescope
+| 12 = @typealiasscope
+| 13 = @mappedtypescope
+| 14 = @enumscope
+| 15 = @externalmodulescope
+| 16 = @conditionaltypescope;
+
+scopenodes (unique int node: @ast_node ref,
+ int scope: @scope ref);
+
+scopenesting (unique int inner: @scope ref,
+ int outer: @scope ref);
+
+// functions
+@function = @functiondeclstmt | @functionexpr | @arrowfunctionexpr;
+
+@parameterized = @function | @catchclause;
+@type_parameterized = @function | @classorinterface | @typealiasdeclaration | @mappedtypeexpr | @infertypeexpr;
+
+isGenerator (int fun: @function ref);
+hasRestParameter (int fun: @function ref);
+isAsync (int fun: @function ref);
+
+// variables and lexically scoped type names
+#keyset[scope, name]
+variables (unique int id: @variable,
+ varchar(900) name: string ref,
+ int scope: @scope ref);
+
+#keyset[scope, name]
+local_type_names (unique int id: @local_type_name,
+ varchar(900) name: string ref,
+ int scope: @scope ref);
+
+#keyset[scope, name]
+local_namespace_names (unique int id: @local_namespace_name,
+ varchar(900) name: string ref,
+ int scope: @scope ref);
+
+isArgumentsObject (int id: @variable ref);
+
+@lexical_name = @variable | @local_type_name | @local_namespace_name;
+
+@bind_id = @varaccess | @localvartypeaccess;
+bind (unique int id: @bind_id ref,
+ int decl: @variable ref);
+
+decl (unique int id: @vardecl ref,
+ int decl: @variable ref);
+
+@typebind_id = @localtypeaccess | @export_varaccess;
+typebind (unique int id: @typebind_id ref,
+ int decl: @local_type_name ref);
+
+@typedecl_id = @typedecl | @vardecl;
+typedecl (unique int id: @typedecl_id ref,
+ int decl: @local_type_name ref);
+
+namespacedecl (unique int id: @vardecl ref,
+ int decl: @local_namespace_name ref);
+
+@namespacebind_id = @localnamespaceaccess | @export_varaccess;
+namespacebind (unique int id: @namespacebind_id ref,
+ int decl: @local_namespace_name ref);
+
+
+// properties in object literals, property patterns in object patterns, and method declarations in classes
+#keyset[parent, index]
+properties (unique int id: @property,
+ int parent: @property_parent ref,
+ int index: int ref,
+ int kind: int ref,
+ varchar(900) tostring: string ref);
+
+case @property.kind of
+ 0 = @value_property
+| 1 = @property_getter
+| 2 = @property_setter
+| 3 = @jsx_attribute
+| 4 = @function_call_signature
+| 5 = @constructor_call_signature
+| 6 = @index_signature
+| 7 = @enum_member
+| 8 = @proper_field
+| 9 = @parameter_field
+;
+
+@property_parent = @objexpr | @objectpattern | @classdefinition | @jsxelement | @interfacedefinition | @enumdeclaration;
+@property_accessor = @property_getter | @property_setter;
+@call_signature = @function_call_signature | @constructor_call_signature;
+@field = @proper_field | @parameter_field;
+@field_or_vardeclarator = @field | @vardeclarator;
+
+isComputed (int id: @property ref);
+isMethod (int id: @property ref);
+isStatic (int id: @property ref);
+isAbstractMember (int id: @property ref);
+isConstEnum (int id: @enumdeclaration ref);
+isAbstractClass (int id: @classdeclstmt ref);
+
+hasPublicKeyword (int id: @property ref);
+hasPrivateKeyword (int id: @property ref);
+hasProtectedKeyword (int id: @property ref);
+hasReadonlyKeyword (int id: @property ref);
+hasTypeKeyword (int id: @import_or_export_declaration ref);
+isOptionalMember (int id: @property ref);
+hasDefiniteAssignmentAssertion (int id: @field_or_vardeclarator ref);
+isOptionalParameterDeclaration (unique int parameter: @pattern ref);
+
+#keyset[constructor, param_index]
+parameter_fields(
+ unique int field: @parameter_field ref,
+ int constructor: @functionexpr ref,
+ int param_index: int ref
+);
+
+// types
+#keyset[parent, idx]
+typeexprs (
+ unique int id: @typeexpr,
+ int kind: int ref,
+ int parent: @typeexpr_parent ref,
+ int idx: int ref,
+ varchar(900) tostring: string ref
+);
+
+case @typeexpr.kind of
+ 0 = @localtypeaccess
+| 1 = @typedecl
+| 2 = @keywordtypeexpr
+| 3 = @stringliteraltypeexpr
+| 4 = @numberliteraltypeexpr
+| 5 = @booleanliteraltypeexpr
+| 6 = @arraytypeexpr
+| 7 = @uniontypeexpr
+| 8 = @indexedaccesstypeexpr
+| 9 = @intersectiontypeexpr
+| 10 = @parenthesizedtypeexpr
+| 11 = @tupletypeexpr
+| 12 = @keyoftypeexpr
+| 13 = @qualifiedtypeaccess
+| 14 = @generictypeexpr
+| 15 = @typelabel
+| 16 = @typeoftypeexpr
+| 17 = @localvartypeaccess
+| 18 = @qualifiedvartypeaccess
+| 19 = @thisvartypeaccess
+| 20 = @predicatetypeexpr
+| 21 = @interfacetypeexpr
+| 22 = @typeparameter
+| 23 = @plainfunctiontypeexpr
+| 24 = @constructortypeexpr
+| 25 = @localnamespaceaccess
+| 26 = @qualifiednamespaceaccess
+| 27 = @mappedtypeexpr
+| 28 = @conditionaltypeexpr
+| 29 = @infertypeexpr
+| 30 = @importtypeaccess
+| 31 = @importnamespaceaccess
+| 32 = @importvartypeaccess
+| 33 = @optionaltypeexpr
+| 34 = @resttypeexpr
+| 35 = @bigintliteraltypeexpr
+| 36 = @readonlytypeexpr
+;
+
+@typeref = @typeaccess | @typedecl;
+@typeidentifier = @typedecl | @localtypeaccess | @typelabel | @localvartypeaccess | @localnamespaceaccess;
+@typeexpr_parent = @expr | @stmt | @property | @typeexpr;
+@literaltypeexpr = @stringliteraltypeexpr | @numberliteraltypeexpr | @booleanliteraltypeexpr | @bigintliteraltypeexpr;
+@typeaccess = @localtypeaccess | @qualifiedtypeaccess | @importtypeaccess;
+@vartypeaccess = @localvartypeaccess | @qualifiedvartypeaccess | @thisvartypeaccess | @importvartypeaccess;
+@namespaceaccess = @localnamespaceaccess | @qualifiednamespaceaccess | @importnamespaceaccess;
+@importtypeexpr = @importtypeaccess | @importnamespaceaccess | @importvartypeaccess;
+
+@functiontypeexpr = @plainfunctiontypeexpr | @constructortypeexpr;
+
+// types
+types (
+ unique int id: @type,
+ int kind: int ref,
+ varchar(900) tostring: string ref
+);
+
+#keyset[parent, idx]
+type_child (
+ int child: @type ref,
+ int parent: @type ref,
+ int idx: int ref
+);
+
+case @type.kind of
+ 0 = @anytype
+| 1 = @stringtype
+| 2 = @numbertype
+| 3 = @uniontype
+| 4 = @truetype
+| 5 = @falsetype
+| 6 = @typereference
+| 7 = @objecttype
+| 8 = @canonicaltypevariabletype
+| 9 = @typeoftype
+| 10 = @voidtype
+| 11 = @undefinedtype
+| 12 = @nulltype
+| 13 = @nevertype
+| 14 = @plainsymboltype
+| 15 = @uniquesymboltype
+| 16 = @objectkeywordtype
+| 17 = @intersectiontype
+| 18 = @tupletype
+| 19 = @lexicaltypevariabletype
+| 20 = @thistype
+| 21 = @numberliteraltype
+| 22 = @stringliteraltype
+| 23 = @unknowntype
+| 24 = @biginttype
+| 25 = @bigintliteraltype
+;
+
+@booleanliteraltype = @truetype | @falsetype;
+@symboltype = @plainsymboltype | @uniquesymboltype;
+@unionorintersectiontype = @uniontype | @intersectiontype;
+@typevariabletype = @canonicaltypevariabletype | @lexicaltypevariabletype;
+
+hasAssertsKeyword(int node: @predicatetypeexpr ref);
+
+@typed_ast_node = @expr | @typeexpr | @function;
+ast_node_type(
+ unique int node: @typed_ast_node ref,
+ int typ: @type ref);
+
+declared_function_signature(
+ unique int node: @function ref,
+ int sig: @signature_type ref
+);
+
+invoke_expr_signature(
+ unique int node: @invokeexpr ref,
+ int sig: @signature_type ref
+);
+
+invoke_expr_overload_index(
+ unique int node: @invokeexpr ref,
+ int index: int ref
+);
+
+symbols (
+ unique int id: @symbol,
+ int kind: int ref,
+ varchar(900) name: string ref
+);
+
+symbol_parent (
+ unique int symbol: @symbol ref,
+ int parent: @symbol ref
+);
+
+symbol_module (
+ int symbol: @symbol ref,
+ varchar(900) moduleName: string ref
+);
+
+symbol_global (
+ int symbol: @symbol ref,
+ varchar(900) globalName: string ref
+);
+
+case @symbol.kind of
+ 0 = @root_symbol
+| 1 = @member_symbol
+| 2 = @other_symbol
+;
+
+@type_with_symbol = @typereference | @typevariabletype | @typeoftype | @uniquesymboltype;
+@ast_node_with_symbol = @typedefinition | @namespacedefinition | @toplevel | @typeaccess | @namespaceaccess | @vardecl | @function | @invokeexpr | @importdeclaration | @externalmodulereference;
+
+ast_node_symbol(
+ unique int node: @ast_node_with_symbol ref,
+ int symbol: @symbol ref);
+
+type_symbol(
+ unique int typ: @type_with_symbol ref,
+ int symbol: @symbol ref);
+
+#keyset[typ, name]
+type_property(
+ int typ: @type ref,
+ varchar(900) name: string ref,
+ int propertyType: @type ref);
+
+type_alias(
+ unique int aliasType: @type ref,
+ int underlyingType: @type ref);
+
+@literaltype = @stringliteraltype | @numberliteraltype | @booleanliteraltype | @bigintliteraltype;
+@type_with_literal_value = @stringliteraltype | @numberliteraltype | @bigintliteraltype;
+type_literal_value(
+ unique int typ: @type_with_literal_value ref,
+ varchar(900) value: string ref);
+
+signature_types (
+ unique int id: @signature_type,
+ int kind: int ref,
+ varchar(900) tostring: string ref,
+ int type_parameters: int ref,
+ int required_params: int ref
+);
+
+signature_rest_parameter(
+ unique int sig: @signature_type ref,
+ int rest_param_arra_type: @type ref
+);
+
+case @signature_type.kind of
+ 0 = @function_signature_type
+| 1 = @constructor_signature_type
+;
+
+#keyset[typ, kind, index]
+type_contains_signature (
+ int typ: @type ref,
+ int kind: int ref, // constructor/call/index
+ int index: int ref, // ordering of overloaded signatures
+ int sig: @signature_type ref
+);
+
+#keyset[parent, index]
+signature_contains_type (
+ int child: @type ref,
+ int parent: @signature_type ref,
+ int index: int ref
+);
+
+#keyset[sig, index]
+signature_parameter_name (
+ int sig: @signature_type ref,
+ int index: int ref,
+ varchar(900) name: string ref
+);
+
+number_index_type (
+ unique int baseType: @type ref,
+ int propertyType: @type ref
+);
+
+string_index_type (
+ unique int baseType: @type ref,
+ int propertyType: @type ref
+);
+
+base_type_names(
+ int typeName: @symbol ref,
+ int baseTypeName: @symbol ref
+);
+
+self_types(
+ int typeName: @symbol ref,
+ int selfType: @typereference ref
+);
+
+tuple_type_min_length(
+ unique int typ: @type ref,
+ int minLength: int ref
+);
+
+tuple_type_rest(
+ unique int typ: @type ref
+);
+
+// comments
+comments (unique int id: @comment,
+ int kind: int ref,
+ int toplevel: @toplevel ref,
+ varchar(900) text: string ref,
+ varchar(900) tostring: string ref);
+
+case @comment.kind of
+ 0 = @slashslashcomment
+| 1 = @slashstarcomment
+| 2 = @doccomment
+| 3 = @htmlcommentstart
+| 4 = @htmlcommentend;
+
+@htmlcomment = @htmlcommentstart | @htmlcommentend;
+@linecomment = @slashslashcomment | @htmlcomment;
+@blockcomment = @slashstarcomment | @doccomment;
+
+// source lines
+lines (unique int id: @line,
+ int toplevel: @toplevel ref,
+ varchar(900) text: string ref,
+ varchar(2) terminator: string ref);
+indentation (int file: @file ref,
+ int lineno: int ref,
+ varchar(1) indentChar: string ref,
+ int indentDepth: int ref);
+
+// JavaScript parse errors
+jsParseErrors (unique int id: @js_parse_error,
+ int toplevel: @toplevel ref,
+ varchar(900) message: string ref,
+ varchar(900) line: string ref);
+
+// regular expressions
+#keyset[parent, idx]
+regexpterm (unique int id: @regexpterm,
+ int kind: int ref,
+ int parent: @regexpparent ref,
+ int idx: int ref,
+ varchar(900) tostring: string ref);
+
+@regexpparent = @regexpterm | @regexpliteral | @stringliteral;
+
+case @regexpterm.kind of
+ 0 = @regexp_alt
+| 1 = @regexp_seq
+| 2 = @regexp_caret
+| 3 = @regexp_dollar
+| 4 = @regexp_wordboundary
+| 5 = @regexp_nonwordboundary
+| 6 = @regexp_positive_lookahead
+| 7 = @regexp_negative_lookahead
+| 8 = @regexp_star
+| 9 = @regexp_plus
+| 10 = @regexp_opt
+| 11 = @regexp_range
+| 12 = @regexp_dot
+| 13 = @regexp_group
+| 14 = @regexp_normal_constant
+| 15 = @regexp_hex_escape
+| 16 = @regexp_unicode_escape
+| 17 = @regexp_dec_escape
+| 18 = @regexp_oct_escape
+| 19 = @regexp_ctrl_escape
+| 20 = @regexp_char_class_escape
+| 21 = @regexp_id_escape
+| 22 = @regexp_backref
+| 23 = @regexp_char_class
+| 24 = @regexp_char_range
+| 25 = @regexp_positive_lookbehind
+| 26 = @regexp_negative_lookbehind
+| 27 = @regexp_unicode_property_escape;
+
+regexpParseErrors (unique int id: @regexp_parse_error,
+ int regexp: @regexpterm ref,
+ varchar(900) message: string ref);
+
+@regexp_quantifier = @regexp_star | @regexp_plus | @regexp_opt | @regexp_range;
+@regexp_escape = @regexp_char_escape | @regexp_char_class_escape | @regexp_unicode_property_escape;
+@regexp_char_escape = @regexp_hex_escape | @regexp_unicode_escape | @regexp_dec_escape | @regexp_oct_escape | @regexp_ctrl_escape | @regexp_id_escape;
+@regexp_constant = @regexp_normal_constant | @regexp_char_escape;
+@regexp_lookahead = @regexp_positive_lookahead | @regexp_negative_lookahead;
+@regexp_lookbehind = @regexp_positive_lookbehind | @regexp_negative_lookbehind;
+@regexp_subpattern = @regexp_lookahead | @regexp_lookbehind;
+@regexp_anchor = @regexp_dollar | @regexp_caret;
+
+isGreedy (int id: @regexp_quantifier ref);
+rangeQuantifierLowerBound (unique int id: @regexp_range ref, int lo: int ref);
+rangeQuantifierUpperBound (unique int id: @regexp_range ref, int hi: int ref);
+isCapture (unique int id: @regexp_group ref, int number: int ref);
+isNamedCapture (unique int id: @regexp_group ref, string name: string ref);
+isInverted (int id: @regexp_char_class ref);
+regexpConstValue (unique int id: @regexp_constant ref, varchar(1) value: string ref);
+charClassEscape (unique int id: @regexp_char_class_escape ref, varchar(1) value: string ref);
+backref (unique int id: @regexp_backref ref, int value: int ref);
+namedBackref (unique int id: @regexp_backref ref, string name: string ref);
+unicodePropertyEscapeName (unique int id: @regexp_unicode_property_escape ref, string name: string ref);
+unicodePropertyEscapeValue (unique int id: @regexp_unicode_property_escape ref, string value: string ref);
+
+// tokens
+#keyset[toplevel, idx]
+tokeninfo (unique int id: @token,
+ int kind: int ref,
+ int toplevel: @toplevel ref,
+ int idx: int ref,
+ varchar(900) value: string ref);
+
+case @token.kind of
+ 0 = @token_eof
+| 1 = @token_null_literal
+| 2 = @token_boolean_literal
+| 3 = @token_numeric_literal
+| 4 = @token_string_literal
+| 5 = @token_regular_expression
+| 6 = @token_identifier
+| 7 = @token_keyword
+| 8 = @token_punctuator;
+
+// associate comments with the token immediately following them (which may be EOF)
+next_token (int comment: @comment ref, int token: @token ref);
+
+// JSON
+#keyset[parent, idx]
+json (unique int id: @json_value,
+ int kind: int ref,
+ int parent: @json_parent ref,
+ int idx: int ref,
+ varchar(900) tostring: string ref);
+
+json_literals (varchar(900) value: string ref,
+ varchar(900) raw: string ref,
+ unique int expr: @json_value ref);
+
+json_properties (int obj: @json_object ref,
+ varchar(900) property: string ref,
+ int value: @json_value ref);
+
+json_errors (unique int id: @json_parse_error,
+ varchar(900) message: string ref);
+
+json_locations(unique int locatable: @json_locatable ref,
+ int location: @location_default ref);
+
+case @json_value.kind of
+ 0 = @json_null
+| 1 = @json_boolean
+| 2 = @json_number
+| 3 = @json_string
+| 4 = @json_array
+| 5 = @json_object;
+
+@json_parent = @json_object | @json_array | @file;
+
+@json_locatable = @json_value | @json_parse_error;
+
+// locations
+@ast_node = @toplevel | @stmt | @expr | @property | @typeexpr;
+
+@locatable = @file
+ | @ast_node
+ | @comment
+ | @line
+ | @js_parse_error | @regexp_parse_error
+ | @regexpterm
+ | @json_locatable
+ | @token
+ | @cfg_node
+ | @jsdoc | @jsdoc_type_expr | @jsdoc_tag
+ | @yaml_locatable
+ | @xmllocatable
+ | @configLocatable;
+
+hasLocation (unique int locatable: @locatable ref,
+ int location: @location ref);
+
+// CFG
+entry_cfg_node (unique int id: @entry_node, int container: @stmt_container ref);
+exit_cfg_node (unique int id: @exit_node, int container: @stmt_container ref);
+guard_node (unique int id: @guard_node, int kind: int ref, int test: @expr ref);
+case @guard_node.kind of
+ 0 = @falsy_guard
+| 1 = @truthy_guard;
+@condition_guard = @falsy_guard | @truthy_guard;
+
+@synthetic_cfg_node = @entry_node | @exit_node | @guard_node;
+@cfg_node = @synthetic_cfg_node | @exprparent;
+
+successor (int pred: @cfg_node ref, int succ: @cfg_node ref);
+
+// JSDoc comments
+jsdoc (unique int id: @jsdoc, varchar(900) description: string ref, int comment: @comment ref);
+#keyset[parent, idx]
+jsdoc_tags (unique int id: @jsdoc_tag, varchar(900) title: string ref,
+ int parent: @jsdoc ref, int idx: int ref, varchar(900) tostring: string ref);
+jsdoc_tag_descriptions (unique int tag: @jsdoc_tag ref, varchar(900) text: string ref);
+jsdoc_tag_names (unique int tag: @jsdoc_tag ref, varchar(900) text: string ref);
+
+#keyset[parent, idx]
+jsdoc_type_exprs (unique int id: @jsdoc_type_expr,
+ int kind: int ref,
+ int parent: @jsdoc_type_expr_parent ref,
+ int idx: int ref,
+ varchar(900) tostring: string ref);
+case @jsdoc_type_expr.kind of
+ 0 = @jsdoc_any_type_expr
+| 1 = @jsdoc_null_type_expr
+| 2 = @jsdoc_undefined_type_expr
+| 3 = @jsdoc_unknown_type_expr
+| 4 = @jsdoc_void_type_expr
+| 5 = @jsdoc_named_type_expr
+| 6 = @jsdoc_applied_type_expr
+| 7 = @jsdoc_nullable_type_expr
+| 8 = @jsdoc_non_nullable_type_expr
+| 9 = @jsdoc_record_type_expr
+| 10 = @jsdoc_array_type_expr
+| 11 = @jsdoc_union_type_expr
+| 12 = @jsdoc_function_type_expr
+| 13 = @jsdoc_optional_type_expr
+| 14 = @jsdoc_rest_type_expr
+;
+
+#keyset[id, idx]
+jsdoc_record_field_name (int id: @jsdoc_record_type_expr ref, int idx: int ref, varchar(900) name: string ref);
+jsdoc_prefix_qualifier (int id: @jsdoc_type_expr ref);
+jsdoc_has_new_parameter (int fn: @jsdoc_function_type_expr ref);
+
+@jsdoc_type_expr_parent = @jsdoc_type_expr | @jsdoc_tag;
+
+jsdoc_errors (unique int id: @jsdoc_error, int tag: @jsdoc_tag ref, varchar(900) message: string ref, varchar(900) tostring: string ref);
+
+// YAML
+#keyset[parent, idx]
+yaml (unique int id: @yaml_node,
+ int kind: int ref,
+ int parent: @yaml_node_parent ref,
+ int idx: int ref,
+ varchar(900) tag: string ref,
+ varchar(900) tostring: string ref);
+
+case @yaml_node.kind of
+ 0 = @yaml_scalar_node
+| 1 = @yaml_mapping_node
+| 2 = @yaml_sequence_node
+| 3 = @yaml_alias_node
+;
+
+@yaml_collection_node = @yaml_mapping_node | @yaml_sequence_node;
+
+@yaml_node_parent = @yaml_collection_node | @file;
+
+yaml_anchors (unique int node: @yaml_node ref,
+ varchar(900) anchor: string ref);
+
+yaml_aliases (unique int alias: @yaml_alias_node ref,
+ varchar(900) target: string ref);
+
+yaml_scalars (unique int scalar: @yaml_scalar_node ref,
+ int style: int ref,
+ varchar(900) value: string ref);
+
+yaml_errors (unique int id: @yaml_error,
+ varchar(900) message: string ref);
+
+yaml_locations(unique int locatable: @yaml_locatable ref,
+ int location: @location_default ref);
+
+@yaml_locatable = @yaml_node | @yaml_error;
+
+/* XML Files */
+
+xmlEncoding(
+ unique int id: @file ref,
+ varchar(900) encoding: string ref
+);
+
+xmlDTDs(
+ unique int id: @xmldtd,
+ varchar(900) root: string ref,
+ varchar(900) publicId: string ref,
+ varchar(900) systemId: string ref,
+ int fileid: @file ref
+);
+
+xmlElements(
+ unique int id: @xmlelement,
+ varchar(900) name: string ref,
+ int parentid: @xmlparent ref,
+ int idx: int ref,
+ int fileid: @file ref
+);
+
+xmlAttrs(
+ unique int id: @xmlattribute,
+ int elementid: @xmlelement ref,
+ varchar(900) name: string ref,
+ varchar(3600) value: string ref,
+ int idx: int ref,
+ int fileid: @file ref
+);
+
+xmlNs(
+ int id: @xmlnamespace,
+ varchar(900) prefixName: string ref,
+ varchar(900) URI: string ref,
+ int fileid: @file ref
+);
+
+xmlHasNs(
+ int elementId: @xmlnamespaceable ref,
+ int nsId: @xmlnamespace ref,
+ int fileid: @file ref
+);
+
+xmlComments(
+ unique int id: @xmlcomment,
+ varchar(3600) text: string ref,
+ int parentid: @xmlparent ref,
+ int fileid: @file ref
+);
+
+xmlChars(
+ unique int id: @xmlcharacters,
+ varchar(3600) text: string ref,
+ int parentid: @xmlparent ref,
+ int idx: int ref,
+ int isCDATA: int ref,
+ int fileid: @file ref
+);
+
+@xmlparent = @file | @xmlelement;
+@xmlnamespaceable = @xmlelement | @xmlattribute;
+
+xmllocations(
+ int xmlElement: @xmllocatable ref,
+ int location: @location_default ref
+);
+
+@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace;
+
+@dataflownode = @expr | @functiondeclstmt | @classdeclstmt | @namespacedeclaration | @enumdeclaration | @property;
+
+@optionalchainable = @callexpr | @propaccess;
+
+isOptionalChaining(int id: @optionalchainable ref);
+
+/*
+ * configuration files with key value pairs
+ */
+
+configs(
+ unique int id: @config
+);
+
+configNames(
+ unique int id: @configName,
+ int config: @config ref,
+ string name: string ref
+);
+
+configValues(
+ unique int id: @configValue,
+ int config: @config ref,
+ string value: string ref
+);
+
+configLocations(
+ int locatable: @configLocatable ref,
+ int location: @location_default ref
+);
+
+@configLocatable = @config | @configName | @configValue;
+
+/**
+ * The time taken for the extraction of a file.
+ * This table contains non-deterministic content.
+ *
+ * The sum of the `time` column for each (`file`, `timerKind`) pair
+ * is the total time taken for extraction of `file`. The `extractionPhase`
+ * column provides a granular view of the extraction time of the file.
+ */
+extraction_time(
+ int file : @file ref,
+ // see `com.semmle.js.extractor.ExtractionMetrics.ExtractionPhase`.
+ int extractionPhase: int ref,
+ // 0 for the elapsed CPU time in nanoseconds, 1 for the elapsed wallclock time in nanoseconds
+ int timerKind: int ref,
+ float time: float ref
+)
+
+/**
+ * Non-timing related data for the extraction of a single file.
+ * This table contains non-deterministic content.
+ */
+extraction_data(
+ int file : @file ref,
+ // the absolute path to the cache file
+ varchar(900) cacheFile: string ref,
+ boolean fromCache: boolean ref,
+ int length: int ref
+)
diff --git a/javascript/upgrades/2dc7a0389827d235763a3748aba73c2d4a677b15/semmlecode.javascript.dbscheme b/javascript/upgrades/2dc7a0389827d235763a3748aba73c2d4a677b15/semmlecode.javascript.dbscheme
new file mode 100644
index 00000000000..3f291931cb8
--- /dev/null
+++ b/javascript/upgrades/2dc7a0389827d235763a3748aba73c2d4a677b15/semmlecode.javascript.dbscheme
@@ -0,0 +1,1200 @@
+/*** Standard fragments ***/
+
+/** Files and folders **/
+
+@location = @location_default;
+
+locations_default(unique int id: @location_default,
+ int file: @file ref,
+ int beginLine: int ref,
+ int beginColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref
+ );
+
+@sourceline = @locatable;
+
+numlines(int element_id: @sourceline ref,
+ int num_lines: int ref,
+ int num_code: int ref,
+ int num_comment: int ref
+ );
+
+
+/*
+ fromSource(0) = unknown,
+ fromSource(1) = from source,
+ fromSource(2) = from library
+*/
+files(unique int id: @file,
+ varchar(900) name: string ref,
+ varchar(900) simple: string ref,
+ varchar(900) ext: string ref,
+ int fromSource: int ref);
+
+folders(unique int id: @folder,
+ varchar(900) name: string ref,
+ varchar(900) simple: string ref);
+
+
+@container = @folder | @file ;
+
+
+containerparent(int parent: @container ref,
+ unique int child: @container ref);
+
+/** Duplicate code **/
+
+duplicateCode(
+ unique int id : @duplication,
+ varchar(900) relativePath : string ref,
+ int equivClass : int ref);
+
+similarCode(
+ unique int id : @similarity,
+ varchar(900) relativePath : string ref,
+ int equivClass : int ref);
+
+@duplication_or_similarity = @duplication | @similarity;
+
+tokens(
+ int id : @duplication_or_similarity ref,
+ int offset : int ref,
+ int beginLine : int ref,
+ int beginColumn : int ref,
+ int endLine : int ref,
+ int endColumn : int ref);
+
+/** External data **/
+
+externalData(
+ int id : @externalDataElement,
+ varchar(900) path : string ref,
+ int column: int ref,
+ varchar(900) value : string ref
+);
+
+snapshotDate(unique date snapshotDate : date ref);
+
+sourceLocationPrefix(varchar(900) prefix : string ref);
+
+/** Version control data **/
+
+svnentries(
+ int id : @svnentry,
+ varchar(500) revision : string ref,
+ varchar(500) author : string ref,
+ date revisionDate : date ref,
+ int changeSize : int ref
+);
+
+svnaffectedfiles(
+ int id : @svnentry ref,
+ int file : @file ref,
+ varchar(500) action : string ref
+);
+
+svnentrymsg(
+ int id : @svnentry ref,
+ varchar(500) message : string ref
+);
+
+svnchurn(
+ int commit : @svnentry ref,
+ int file : @file ref,
+ int addedLines : int ref,
+ int deletedLines : int ref
+);
+
+
+/*** JavaScript-specific part ***/
+
+filetype(
+ int file: @file ref,
+ string filetype: string ref
+)
+
+// top-level code fragments
+toplevels (unique int id: @toplevel,
+ int kind: int ref);
+
+isExterns (int toplevel: @toplevel ref);
+
+case @toplevel.kind of
+ 0 = @script
+| 1 = @inline_script
+| 2 = @event_handler
+| 3 = @javascript_url;
+
+isModule (int tl: @toplevel ref);
+isNodejs (int tl: @toplevel ref);
+isES2015Module (int tl: @toplevel ref);
+isClosureModule (int tl: @toplevel ref);
+
+// statements
+#keyset[parent, idx]
+stmts (unique int id: @stmt,
+ int kind: int ref,
+ int parent: @stmtparent ref,
+ int idx: int ref,
+ varchar(900) tostring: string ref);
+
+stmtContainers (unique int stmt: @stmt ref,
+ int container: @stmt_container ref);
+
+jumpTargets (unique int jump: @stmt ref,
+ int target: @stmt ref);
+
+@stmtparent = @stmt | @toplevel | @functionexpr | @arrowfunctionexpr;
+@stmt_container = @toplevel | @function | @namespacedeclaration | @externalmoduledeclaration | @globalaugmentationdeclaration;
+
+case @stmt.kind of
+ 0 = @emptystmt
+| 1 = @blockstmt
+| 2 = @exprstmt
+| 3 = @ifstmt
+| 4 = @labeledstmt
+| 5 = @breakstmt
+| 6 = @continuestmt
+| 7 = @withstmt
+| 8 = @switchstmt
+| 9 = @returnstmt
+| 10 = @throwstmt
+| 11 = @trystmt
+| 12 = @whilestmt
+| 13 = @dowhilestmt
+| 14 = @forstmt
+| 15 = @forinstmt
+| 16 = @debuggerstmt
+| 17 = @functiondeclstmt
+| 18 = @vardeclstmt
+| 19 = @case
+| 20 = @catchclause
+| 21 = @forofstmt
+| 22 = @constdeclstmt
+| 23 = @letstmt
+| 24 = @legacy_letstmt
+| 25 = @foreachstmt
+| 26 = @classdeclstmt
+| 27 = @importdeclaration
+| 28 = @exportalldeclaration
+| 29 = @exportdefaultdeclaration
+| 30 = @exportnameddeclaration
+| 31 = @namespacedeclaration
+| 32 = @importequalsdeclaration
+| 33 = @exportassigndeclaration
+| 34 = @interfacedeclaration
+| 35 = @typealiasdeclaration
+| 36 = @enumdeclaration
+| 37 = @externalmoduledeclaration
+| 38 = @exportasnamespacedeclaration
+| 39 = @globalaugmentationdeclaration
+;
+
+@declstmt = @vardeclstmt | @constdeclstmt | @letstmt | @legacy_letstmt;
+
+@exportdeclaration = @exportalldeclaration | @exportdefaultdeclaration | @exportnameddeclaration;
+
+@namespacedefinition = @namespacedeclaration | @enumdeclaration;
+@typedefinition = @classdefinition | @interfacedeclaration | @enumdeclaration | @typealiasdeclaration | @enum_member;
+
+isInstantiated(unique int decl: @namespacedeclaration ref);
+
+@declarablenode = @declstmt | @namespacedeclaration | @classdeclstmt | @functiondeclstmt | @enumdeclaration | @externalmoduledeclaration | @globalaugmentationdeclaration | @field;
+hasDeclareKeyword(unique int stmt: @declarablenode ref);
+
+isForAwaitOf(unique int forof: @forofstmt ref);
+
+// expressions
+#keyset[parent, idx]
+exprs (unique int id: @expr,
+ int kind: int ref,
+ int parent: @exprparent ref,
+ int idx: int ref,
+ varchar(900) tostring: string ref);
+
+literals (varchar(900) value: string ref,
+ varchar(900) raw: string ref,
+ unique int expr: @exprortype ref);
+
+enclosingStmt (unique int expr: @exprortype ref,
+ int stmt: @stmt ref);
+
+exprContainers (unique int expr: @exprortype ref,
+ int container: @stmt_container ref);
+
+arraySize (unique int ae: @arraylike ref,
+ int sz: int ref);
+
+isDelegating (int yield: @yieldexpr ref);
+
+@exprorstmt = @expr | @stmt;
+@exprortype = @expr | @typeexpr;
+@exprparent = @exprorstmt | @property | @functiontypeexpr;
+@arraylike = @arrayexpr | @arraypattern;
+@type_annotation = @typeexpr | @jsdoc_type_expr;
+@node_in_stmt_container = @cfg_node | @type_annotation | @toplevel;
+
+case @expr.kind of
+ 0 = @label
+| 1 = @nullliteral
+| 2 = @booleanliteral
+| 3 = @numberliteral
+| 4 = @stringliteral
+| 5 = @regexpliteral
+| 6 = @thisexpr
+| 7 = @arrayexpr
+| 8 = @objexpr
+| 9 = @functionexpr
+| 10 = @seqexpr
+| 11 = @conditionalexpr
+| 12 = @newexpr
+| 13 = @callexpr
+| 14 = @dotexpr
+| 15 = @indexexpr
+| 16 = @negexpr
+| 17 = @plusexpr
+| 18 = @lognotexpr
+| 19 = @bitnotexpr
+| 20 = @typeofexpr
+| 21 = @voidexpr
+| 22 = @deleteexpr
+| 23 = @eqexpr
+| 24 = @neqexpr
+| 25 = @eqqexpr
+| 26 = @neqqexpr
+| 27 = @ltexpr
+| 28 = @leexpr
+| 29 = @gtexpr
+| 30 = @geexpr
+| 31 = @lshiftexpr
+| 32 = @rshiftexpr
+| 33 = @urshiftexpr
+| 34 = @addexpr
+| 35 = @subexpr
+| 36 = @mulexpr
+| 37 = @divexpr
+| 38 = @modexpr
+| 39 = @bitorexpr
+| 40 = @xorexpr
+| 41 = @bitandexpr
+| 42 = @inexpr
+| 43 = @instanceofexpr
+| 44 = @logandexpr
+| 45 = @logorexpr
+| 47 = @assignexpr
+| 48 = @assignaddexpr
+| 49 = @assignsubexpr
+| 50 = @assignmulexpr
+| 51 = @assigndivexpr
+| 52 = @assignmodexpr
+| 53 = @assignlshiftexpr
+| 54 = @assignrshiftexpr
+| 55 = @assignurshiftexpr
+| 56 = @assignorexpr
+| 57 = @assignxorexpr
+| 58 = @assignandexpr
+| 59 = @preincexpr
+| 60 = @postincexpr
+| 61 = @predecexpr
+| 62 = @postdecexpr
+| 63 = @parexpr
+| 64 = @vardeclarator
+| 65 = @arrowfunctionexpr
+| 66 = @spreadelement
+| 67 = @arraypattern
+| 68 = @objectpattern
+| 69 = @yieldexpr
+| 70 = @taggedtemplateexpr
+| 71 = @templateliteral
+| 72 = @templateelement
+| 73 = @arraycomprehensionexpr
+| 74 = @generatorexpr
+| 75 = @forincomprehensionblock
+| 76 = @forofcomprehensionblock
+| 77 = @legacy_letexpr
+| 78 = @vardecl
+| 79 = @proper_varaccess
+| 80 = @classexpr
+| 81 = @superexpr
+| 82 = @newtargetexpr
+| 83 = @namedimportspecifier
+| 84 = @importdefaultspecifier
+| 85 = @importnamespacespecifier
+| 86 = @namedexportspecifier
+| 87 = @expexpr
+| 88 = @assignexpexpr
+| 89 = @jsxelement
+| 90 = @jsxqualifiedname
+| 91 = @jsxemptyexpr
+| 92 = @awaitexpr
+| 93 = @functionsentexpr
+| 94 = @decorator
+| 95 = @exportdefaultspecifier
+| 96 = @exportnamespacespecifier
+| 97 = @bindexpr
+| 98 = @externalmodulereference
+| 99 = @dynamicimport
+| 100 = @expressionwithtypearguments
+| 101 = @prefixtypeassertion
+| 102 = @astypeassertion
+| 103 = @export_varaccess
+| 104 = @decorator_list
+| 105 = @non_null_assertion
+| 106 = @bigintliteral
+| 107 = @nullishcoalescingexpr
+| 108 = @e4x_xml_anyname
+| 109 = @e4x_xml_static_attribute_selector
+| 110 = @e4x_xml_dynamic_attribute_selector
+| 111 = @e4x_xml_filter_expression
+| 112 = @e4x_xml_static_qualident
+| 113 = @e4x_xml_dynamic_qualident
+| 114 = @e4x_xml_dotdotexpr
+| 115 = @importmetaexpr
+| 116 = @assignlogandexpr
+| 117 = @assignlogorexpr
+| 118 = @assignnullishcoalescingexpr
+;
+
+@varaccess = @proper_varaccess | @export_varaccess;
+@varref = @vardecl | @varaccess;
+
+@identifier = @label | @varref | @typeidentifier;
+
+@literal = @nullliteral | @booleanliteral | @numberliteral | @stringliteral | @regexpliteral | @bigintliteral;
+
+@propaccess = @dotexpr | @indexexpr;
+
+@invokeexpr = @newexpr | @callexpr;
+
+@unaryexpr = @negexpr | @plusexpr | @lognotexpr | @bitnotexpr | @typeofexpr | @voidexpr | @deleteexpr | @spreadelement;
+
+@equalitytest = @eqexpr | @neqexpr | @eqqexpr | @neqqexpr;
+
+@comparison = @equalitytest | @ltexpr | @leexpr | @gtexpr | @geexpr;
+
+@binaryexpr = @comparison | @lshiftexpr | @rshiftexpr | @urshiftexpr | @addexpr | @subexpr | @mulexpr | @divexpr | @modexpr | @expexpr | @bitorexpr | @xorexpr | @bitandexpr | @inexpr | @instanceofexpr | @logandexpr | @logorexpr | @nullishcoalescingexpr;
+
+@assignment = @assignexpr | @assignaddexpr | @assignsubexpr | @assignmulexpr | @assigndivexpr | @assignmodexpr | @assignexpexpr | @assignlshiftexpr | @assignrshiftexpr | @assignurshiftexpr | @assignorexpr | @assignxorexpr | @assignandexpr | @assignlogandexpr | @assignlogorexpr | @assignnullishcoalescingexpr;
+
+@updateexpr = @preincexpr | @postincexpr | @predecexpr | @postdecexpr;
+
+@pattern = @varref | @arraypattern | @objectpattern;
+
+@comprehensionexpr = @arraycomprehensionexpr | @generatorexpr;
+
+@comprehensionblock = @forincomprehensionblock | @forofcomprehensionblock;
+
+@importspecifier = @namedimportspecifier | @importdefaultspecifier | @importnamespacespecifier;
+
+@exportspecifier = @namedexportspecifier | @exportdefaultspecifier | @exportnamespacespecifier;
+
+@import_or_export_declaration = @importdeclaration | @exportdeclaration;
+
+@typeassertion = @astypeassertion | @prefixtypeassertion;
+
+@classdefinition = @classdeclstmt | @classexpr;
+@interfacedefinition = @interfacedeclaration | @interfacetypeexpr;
+@classorinterface = @classdefinition | @interfacedefinition;
+
+@lexical_decl = @vardecl | @typedecl;
+@lexical_access = @varaccess | @localtypeaccess | @localvartypeaccess | @localnamespaceaccess;
+@lexical_ref = @lexical_decl | @lexical_access;
+
+@e4x_xml_attribute_selector = @e4x_xml_static_attribute_selector | @e4x_xml_dynamic_attribute_selector;
+@e4x_xml_qualident = @e4x_xml_static_qualident | @e4x_xml_dynamic_qualident;
+
+// scopes
+scopes (unique int id: @scope,
+ int kind: int ref);
+
+case @scope.kind of
+ 0 = @globalscope
+| 1 = @functionscope
+| 2 = @catchscope
+| 3 = @modulescope
+| 4 = @blockscope
+| 5 = @forscope
+| 6 = @forinscope // for-of scopes work the same as for-in scopes
+| 7 = @comprehensionblockscope
+| 8 = @classexprscope
+| 9 = @namespacescope
+| 10 = @classdeclscope
+| 11 = @interfacescope
+| 12 = @typealiasscope
+| 13 = @mappedtypescope
+| 14 = @enumscope
+| 15 = @externalmodulescope
+| 16 = @conditionaltypescope;
+
+scopenodes (unique int node: @ast_node ref,
+ int scope: @scope ref);
+
+scopenesting (unique int inner: @scope ref,
+ int outer: @scope ref);
+
+// functions
+@function = @functiondeclstmt | @functionexpr | @arrowfunctionexpr;
+
+@parameterized = @function | @catchclause;
+@type_parameterized = @function | @classorinterface | @typealiasdeclaration | @mappedtypeexpr | @infertypeexpr;
+
+isGenerator (int fun: @function ref);
+hasRestParameter (int fun: @function ref);
+isAsync (int fun: @function ref);
+
+// variables and lexically scoped type names
+#keyset[scope, name]
+variables (unique int id: @variable,
+ varchar(900) name: string ref,
+ int scope: @scope ref);
+
+#keyset[scope, name]
+local_type_names (unique int id: @local_type_name,
+ varchar(900) name: string ref,
+ int scope: @scope ref);
+
+#keyset[scope, name]
+local_namespace_names (unique int id: @local_namespace_name,
+ varchar(900) name: string ref,
+ int scope: @scope ref);
+
+isArgumentsObject (int id: @variable ref);
+
+@lexical_name = @variable | @local_type_name | @local_namespace_name;
+
+@bind_id = @varaccess | @localvartypeaccess;
+bind (unique int id: @bind_id ref,
+ int decl: @variable ref);
+
+decl (unique int id: @vardecl ref,
+ int decl: @variable ref);
+
+@typebind_id = @localtypeaccess | @export_varaccess;
+typebind (unique int id: @typebind_id ref,
+ int decl: @local_type_name ref);
+
+@typedecl_id = @typedecl | @vardecl;
+typedecl (unique int id: @typedecl_id ref,
+ int decl: @local_type_name ref);
+
+namespacedecl (unique int id: @vardecl ref,
+ int decl: @local_namespace_name ref);
+
+@namespacebind_id = @localnamespaceaccess | @export_varaccess;
+namespacebind (unique int id: @namespacebind_id ref,
+ int decl: @local_namespace_name ref);
+
+
+// properties in object literals, property patterns in object patterns, and method declarations in classes
+#keyset[parent, index]
+properties (unique int id: @property,
+ int parent: @property_parent ref,
+ int index: int ref,
+ int kind: int ref,
+ varchar(900) tostring: string ref);
+
+case @property.kind of
+ 0 = @value_property
+| 1 = @property_getter
+| 2 = @property_setter
+| 3 = @jsx_attribute
+| 4 = @function_call_signature
+| 5 = @constructor_call_signature
+| 6 = @index_signature
+| 7 = @enum_member
+| 8 = @proper_field
+| 9 = @parameter_field
+;
+
+@property_parent = @objexpr | @objectpattern | @classdefinition | @jsxelement | @interfacedefinition | @enumdeclaration;
+@property_accessor = @property_getter | @property_setter;
+@call_signature = @function_call_signature | @constructor_call_signature;
+@field = @proper_field | @parameter_field;
+@field_or_vardeclarator = @field | @vardeclarator;
+
+isComputed (int id: @property ref);
+isMethod (int id: @property ref);
+isStatic (int id: @property ref);
+isAbstractMember (int id: @property ref);
+isConstEnum (int id: @enumdeclaration ref);
+isAbstractClass (int id: @classdeclstmt ref);
+
+hasPublicKeyword (int id: @property ref);
+hasPrivateKeyword (int id: @property ref);
+hasProtectedKeyword (int id: @property ref);
+hasReadonlyKeyword (int id: @property ref);
+hasTypeKeyword (int id: @import_or_export_declaration ref);
+isOptionalMember (int id: @property ref);
+hasDefiniteAssignmentAssertion (int id: @field_or_vardeclarator ref);
+isOptionalParameterDeclaration (unique int parameter: @pattern ref);
+
+#keyset[constructor, param_index]
+parameter_fields(
+ unique int field: @parameter_field ref,
+ int constructor: @functionexpr ref,
+ int param_index: int ref
+);
+
+// types
+#keyset[parent, idx]
+typeexprs (
+ unique int id: @typeexpr,
+ int kind: int ref,
+ int parent: @typeexpr_parent ref,
+ int idx: int ref,
+ varchar(900) tostring: string ref
+);
+
+case @typeexpr.kind of
+ 0 = @localtypeaccess
+| 1 = @typedecl
+| 2 = @keywordtypeexpr
+| 3 = @stringliteraltypeexpr
+| 4 = @numberliteraltypeexpr
+| 5 = @booleanliteraltypeexpr
+| 6 = @arraytypeexpr
+| 7 = @uniontypeexpr
+| 8 = @indexedaccesstypeexpr
+| 9 = @intersectiontypeexpr
+| 10 = @parenthesizedtypeexpr
+| 11 = @tupletypeexpr
+| 12 = @keyoftypeexpr
+| 13 = @qualifiedtypeaccess
+| 14 = @generictypeexpr
+| 15 = @typelabel
+| 16 = @typeoftypeexpr
+| 17 = @localvartypeaccess
+| 18 = @qualifiedvartypeaccess
+| 19 = @thisvartypeaccess
+| 20 = @predicatetypeexpr
+| 21 = @interfacetypeexpr
+| 22 = @typeparameter
+| 23 = @plainfunctiontypeexpr
+| 24 = @constructortypeexpr
+| 25 = @localnamespaceaccess
+| 26 = @qualifiednamespaceaccess
+| 27 = @mappedtypeexpr
+| 28 = @conditionaltypeexpr
+| 29 = @infertypeexpr
+| 30 = @importtypeaccess
+| 31 = @importnamespaceaccess
+| 32 = @importvartypeaccess
+| 33 = @optionaltypeexpr
+| 34 = @resttypeexpr
+| 35 = @bigintliteraltypeexpr
+| 36 = @readonlytypeexpr
+;
+
+@typeref = @typeaccess | @typedecl;
+@typeidentifier = @typedecl | @localtypeaccess | @typelabel | @localvartypeaccess | @localnamespaceaccess;
+@typeexpr_parent = @expr | @stmt | @property | @typeexpr;
+@literaltypeexpr = @stringliteraltypeexpr | @numberliteraltypeexpr | @booleanliteraltypeexpr | @bigintliteraltypeexpr;
+@typeaccess = @localtypeaccess | @qualifiedtypeaccess | @importtypeaccess;
+@vartypeaccess = @localvartypeaccess | @qualifiedvartypeaccess | @thisvartypeaccess | @importvartypeaccess;
+@namespaceaccess = @localnamespaceaccess | @qualifiednamespaceaccess | @importnamespaceaccess;
+@importtypeexpr = @importtypeaccess | @importnamespaceaccess | @importvartypeaccess;
+
+@functiontypeexpr = @plainfunctiontypeexpr | @constructortypeexpr;
+
+// types
+types (
+ unique int id: @type,
+ int kind: int ref,
+ varchar(900) tostring: string ref
+);
+
+#keyset[parent, idx]
+type_child (
+ int child: @type ref,
+ int parent: @type ref,
+ int idx: int ref
+);
+
+case @type.kind of
+ 0 = @anytype
+| 1 = @stringtype
+| 2 = @numbertype
+| 3 = @uniontype
+| 4 = @truetype
+| 5 = @falsetype
+| 6 = @typereference
+| 7 = @objecttype
+| 8 = @canonicaltypevariabletype
+| 9 = @typeoftype
+| 10 = @voidtype
+| 11 = @undefinedtype
+| 12 = @nulltype
+| 13 = @nevertype
+| 14 = @plainsymboltype
+| 15 = @uniquesymboltype
+| 16 = @objectkeywordtype
+| 17 = @intersectiontype
+| 18 = @tupletype
+| 19 = @lexicaltypevariabletype
+| 20 = @thistype
+| 21 = @numberliteraltype
+| 22 = @stringliteraltype
+| 23 = @unknowntype
+| 24 = @biginttype
+| 25 = @bigintliteraltype
+;
+
+@booleanliteraltype = @truetype | @falsetype;
+@symboltype = @plainsymboltype | @uniquesymboltype;
+@unionorintersectiontype = @uniontype | @intersectiontype;
+@typevariabletype = @canonicaltypevariabletype | @lexicaltypevariabletype;
+
+hasAssertsKeyword(int node: @predicatetypeexpr ref);
+
+@typed_ast_node = @expr | @typeexpr | @function;
+ast_node_type(
+ unique int node: @typed_ast_node ref,
+ int typ: @type ref);
+
+declared_function_signature(
+ unique int node: @function ref,
+ int sig: @signature_type ref
+);
+
+invoke_expr_signature(
+ unique int node: @invokeexpr ref,
+ int sig: @signature_type ref
+);
+
+invoke_expr_overload_index(
+ unique int node: @invokeexpr ref,
+ int index: int ref
+);
+
+symbols (
+ unique int id: @symbol,
+ int kind: int ref,
+ varchar(900) name: string ref
+);
+
+symbol_parent (
+ unique int symbol: @symbol ref,
+ int parent: @symbol ref
+);
+
+symbol_module (
+ int symbol: @symbol ref,
+ varchar(900) moduleName: string ref
+);
+
+symbol_global (
+ int symbol: @symbol ref,
+ varchar(900) globalName: string ref
+);
+
+case @symbol.kind of
+ 0 = @root_symbol
+| 1 = @member_symbol
+| 2 = @other_symbol
+;
+
+@type_with_symbol = @typereference | @typevariabletype | @typeoftype | @uniquesymboltype;
+@ast_node_with_symbol = @typedefinition | @namespacedefinition | @toplevel | @typeaccess | @namespaceaccess | @vardecl | @function | @invokeexpr | @importdeclaration | @externalmodulereference;
+
+ast_node_symbol(
+ unique int node: @ast_node_with_symbol ref,
+ int symbol: @symbol ref);
+
+type_symbol(
+ unique int typ: @type_with_symbol ref,
+ int symbol: @symbol ref);
+
+#keyset[typ, name]
+type_property(
+ int typ: @type ref,
+ varchar(900) name: string ref,
+ int propertyType: @type ref);
+
+type_alias(
+ unique int aliasType: @type ref,
+ int underlyingType: @type ref);
+
+@literaltype = @stringliteraltype | @numberliteraltype | @booleanliteraltype | @bigintliteraltype;
+@type_with_literal_value = @stringliteraltype | @numberliteraltype | @bigintliteraltype;
+type_literal_value(
+ unique int typ: @type_with_literal_value ref,
+ varchar(900) value: string ref);
+
+signature_types (
+ unique int id: @signature_type,
+ int kind: int ref,
+ varchar(900) tostring: string ref,
+ int type_parameters: int ref,
+ int required_params: int ref
+);
+
+signature_rest_parameter(
+ unique int sig: @signature_type ref,
+ int rest_param_arra_type: @type ref
+);
+
+case @signature_type.kind of
+ 0 = @function_signature_type
+| 1 = @constructor_signature_type
+;
+
+#keyset[typ, kind, index]
+type_contains_signature (
+ int typ: @type ref,
+ int kind: int ref, // constructor/call/index
+ int index: int ref, // ordering of overloaded signatures
+ int sig: @signature_type ref
+);
+
+#keyset[parent, index]
+signature_contains_type (
+ int child: @type ref,
+ int parent: @signature_type ref,
+ int index: int ref
+);
+
+#keyset[sig, index]
+signature_parameter_name (
+ int sig: @signature_type ref,
+ int index: int ref,
+ varchar(900) name: string ref
+);
+
+number_index_type (
+ unique int baseType: @type ref,
+ int propertyType: @type ref
+);
+
+string_index_type (
+ unique int baseType: @type ref,
+ int propertyType: @type ref
+);
+
+base_type_names(
+ int typeName: @symbol ref,
+ int baseTypeName: @symbol ref
+);
+
+self_types(
+ int typeName: @symbol ref,
+ int selfType: @typereference ref
+);
+
+tuple_type_min_length(
+ unique int typ: @type ref,
+ int minLength: int ref
+);
+
+tuple_type_rest(
+ unique int typ: @type ref
+);
+
+#keyset[tuple, index]
+tuple_element_name (
+ int tuple: @tupletypeexpr ref,
+ int index: int ref,
+ varchar(900) name: string ref
+);
+
+// comments
+comments (unique int id: @comment,
+ int kind: int ref,
+ int toplevel: @toplevel ref,
+ varchar(900) text: string ref,
+ varchar(900) tostring: string ref);
+
+case @comment.kind of
+ 0 = @slashslashcomment
+| 1 = @slashstarcomment
+| 2 = @doccomment
+| 3 = @htmlcommentstart
+| 4 = @htmlcommentend;
+
+@htmlcomment = @htmlcommentstart | @htmlcommentend;
+@linecomment = @slashslashcomment | @htmlcomment;
+@blockcomment = @slashstarcomment | @doccomment;
+
+// source lines
+lines (unique int id: @line,
+ int toplevel: @toplevel ref,
+ varchar(900) text: string ref,
+ varchar(2) terminator: string ref);
+indentation (int file: @file ref,
+ int lineno: int ref,
+ varchar(1) indentChar: string ref,
+ int indentDepth: int ref);
+
+// JavaScript parse errors
+jsParseErrors (unique int id: @js_parse_error,
+ int toplevel: @toplevel ref,
+ varchar(900) message: string ref,
+ varchar(900) line: string ref);
+
+// regular expressions
+#keyset[parent, idx]
+regexpterm (unique int id: @regexpterm,
+ int kind: int ref,
+ int parent: @regexpparent ref,
+ int idx: int ref,
+ varchar(900) tostring: string ref);
+
+@regexpparent = @regexpterm | @regexpliteral | @stringliteral;
+
+case @regexpterm.kind of
+ 0 = @regexp_alt
+| 1 = @regexp_seq
+| 2 = @regexp_caret
+| 3 = @regexp_dollar
+| 4 = @regexp_wordboundary
+| 5 = @regexp_nonwordboundary
+| 6 = @regexp_positive_lookahead
+| 7 = @regexp_negative_lookahead
+| 8 = @regexp_star
+| 9 = @regexp_plus
+| 10 = @regexp_opt
+| 11 = @regexp_range
+| 12 = @regexp_dot
+| 13 = @regexp_group
+| 14 = @regexp_normal_constant
+| 15 = @regexp_hex_escape
+| 16 = @regexp_unicode_escape
+| 17 = @regexp_dec_escape
+| 18 = @regexp_oct_escape
+| 19 = @regexp_ctrl_escape
+| 20 = @regexp_char_class_escape
+| 21 = @regexp_id_escape
+| 22 = @regexp_backref
+| 23 = @regexp_char_class
+| 24 = @regexp_char_range
+| 25 = @regexp_positive_lookbehind
+| 26 = @regexp_negative_lookbehind
+| 27 = @regexp_unicode_property_escape;
+
+regexpParseErrors (unique int id: @regexp_parse_error,
+ int regexp: @regexpterm ref,
+ varchar(900) message: string ref);
+
+@regexp_quantifier = @regexp_star | @regexp_plus | @regexp_opt | @regexp_range;
+@regexp_escape = @regexp_char_escape | @regexp_char_class_escape | @regexp_unicode_property_escape;
+@regexp_char_escape = @regexp_hex_escape | @regexp_unicode_escape | @regexp_dec_escape | @regexp_oct_escape | @regexp_ctrl_escape | @regexp_id_escape;
+@regexp_constant = @regexp_normal_constant | @regexp_char_escape;
+@regexp_lookahead = @regexp_positive_lookahead | @regexp_negative_lookahead;
+@regexp_lookbehind = @regexp_positive_lookbehind | @regexp_negative_lookbehind;
+@regexp_subpattern = @regexp_lookahead | @regexp_lookbehind;
+@regexp_anchor = @regexp_dollar | @regexp_caret;
+
+isGreedy (int id: @regexp_quantifier ref);
+rangeQuantifierLowerBound (unique int id: @regexp_range ref, int lo: int ref);
+rangeQuantifierUpperBound (unique int id: @regexp_range ref, int hi: int ref);
+isCapture (unique int id: @regexp_group ref, int number: int ref);
+isNamedCapture (unique int id: @regexp_group ref, string name: string ref);
+isInverted (int id: @regexp_char_class ref);
+regexpConstValue (unique int id: @regexp_constant ref, varchar(1) value: string ref);
+charClassEscape (unique int id: @regexp_char_class_escape ref, varchar(1) value: string ref);
+backref (unique int id: @regexp_backref ref, int value: int ref);
+namedBackref (unique int id: @regexp_backref ref, string name: string ref);
+unicodePropertyEscapeName (unique int id: @regexp_unicode_property_escape ref, string name: string ref);
+unicodePropertyEscapeValue (unique int id: @regexp_unicode_property_escape ref, string value: string ref);
+
+// tokens
+#keyset[toplevel, idx]
+tokeninfo (unique int id: @token,
+ int kind: int ref,
+ int toplevel: @toplevel ref,
+ int idx: int ref,
+ varchar(900) value: string ref);
+
+case @token.kind of
+ 0 = @token_eof
+| 1 = @token_null_literal
+| 2 = @token_boolean_literal
+| 3 = @token_numeric_literal
+| 4 = @token_string_literal
+| 5 = @token_regular_expression
+| 6 = @token_identifier
+| 7 = @token_keyword
+| 8 = @token_punctuator;
+
+// associate comments with the token immediately following them (which may be EOF)
+next_token (int comment: @comment ref, int token: @token ref);
+
+// JSON
+#keyset[parent, idx]
+json (unique int id: @json_value,
+ int kind: int ref,
+ int parent: @json_parent ref,
+ int idx: int ref,
+ varchar(900) tostring: string ref);
+
+json_literals (varchar(900) value: string ref,
+ varchar(900) raw: string ref,
+ unique int expr: @json_value ref);
+
+json_properties (int obj: @json_object ref,
+ varchar(900) property: string ref,
+ int value: @json_value ref);
+
+json_errors (unique int id: @json_parse_error,
+ varchar(900) message: string ref);
+
+json_locations(unique int locatable: @json_locatable ref,
+ int location: @location_default ref);
+
+case @json_value.kind of
+ 0 = @json_null
+| 1 = @json_boolean
+| 2 = @json_number
+| 3 = @json_string
+| 4 = @json_array
+| 5 = @json_object;
+
+@json_parent = @json_object | @json_array | @file;
+
+@json_locatable = @json_value | @json_parse_error;
+
+// locations
+@ast_node = @toplevel | @stmt | @expr | @property | @typeexpr;
+
+@locatable = @file
+ | @ast_node
+ | @comment
+ | @line
+ | @js_parse_error | @regexp_parse_error
+ | @regexpterm
+ | @json_locatable
+ | @token
+ | @cfg_node
+ | @jsdoc | @jsdoc_type_expr | @jsdoc_tag
+ | @yaml_locatable
+ | @xmllocatable
+ | @configLocatable;
+
+hasLocation (unique int locatable: @locatable ref,
+ int location: @location ref);
+
+// CFG
+entry_cfg_node (unique int id: @entry_node, int container: @stmt_container ref);
+exit_cfg_node (unique int id: @exit_node, int container: @stmt_container ref);
+guard_node (unique int id: @guard_node, int kind: int ref, int test: @expr ref);
+case @guard_node.kind of
+ 0 = @falsy_guard
+| 1 = @truthy_guard;
+@condition_guard = @falsy_guard | @truthy_guard;
+
+@synthetic_cfg_node = @entry_node | @exit_node | @guard_node;
+@cfg_node = @synthetic_cfg_node | @exprparent;
+
+successor (int pred: @cfg_node ref, int succ: @cfg_node ref);
+
+// JSDoc comments
+jsdoc (unique int id: @jsdoc, varchar(900) description: string ref, int comment: @comment ref);
+#keyset[parent, idx]
+jsdoc_tags (unique int id: @jsdoc_tag, varchar(900) title: string ref,
+ int parent: @jsdoc ref, int idx: int ref, varchar(900) tostring: string ref);
+jsdoc_tag_descriptions (unique int tag: @jsdoc_tag ref, varchar(900) text: string ref);
+jsdoc_tag_names (unique int tag: @jsdoc_tag ref, varchar(900) text: string ref);
+
+#keyset[parent, idx]
+jsdoc_type_exprs (unique int id: @jsdoc_type_expr,
+ int kind: int ref,
+ int parent: @jsdoc_type_expr_parent ref,
+ int idx: int ref,
+ varchar(900) tostring: string ref);
+case @jsdoc_type_expr.kind of
+ 0 = @jsdoc_any_type_expr
+| 1 = @jsdoc_null_type_expr
+| 2 = @jsdoc_undefined_type_expr
+| 3 = @jsdoc_unknown_type_expr
+| 4 = @jsdoc_void_type_expr
+| 5 = @jsdoc_named_type_expr
+| 6 = @jsdoc_applied_type_expr
+| 7 = @jsdoc_nullable_type_expr
+| 8 = @jsdoc_non_nullable_type_expr
+| 9 = @jsdoc_record_type_expr
+| 10 = @jsdoc_array_type_expr
+| 11 = @jsdoc_union_type_expr
+| 12 = @jsdoc_function_type_expr
+| 13 = @jsdoc_optional_type_expr
+| 14 = @jsdoc_rest_type_expr
+;
+
+#keyset[id, idx]
+jsdoc_record_field_name (int id: @jsdoc_record_type_expr ref, int idx: int ref, varchar(900) name: string ref);
+jsdoc_prefix_qualifier (int id: @jsdoc_type_expr ref);
+jsdoc_has_new_parameter (int fn: @jsdoc_function_type_expr ref);
+
+@jsdoc_type_expr_parent = @jsdoc_type_expr | @jsdoc_tag;
+
+jsdoc_errors (unique int id: @jsdoc_error, int tag: @jsdoc_tag ref, varchar(900) message: string ref, varchar(900) tostring: string ref);
+
+// YAML
+#keyset[parent, idx]
+yaml (unique int id: @yaml_node,
+ int kind: int ref,
+ int parent: @yaml_node_parent ref,
+ int idx: int ref,
+ varchar(900) tag: string ref,
+ varchar(900) tostring: string ref);
+
+case @yaml_node.kind of
+ 0 = @yaml_scalar_node
+| 1 = @yaml_mapping_node
+| 2 = @yaml_sequence_node
+| 3 = @yaml_alias_node
+;
+
+@yaml_collection_node = @yaml_mapping_node | @yaml_sequence_node;
+
+@yaml_node_parent = @yaml_collection_node | @file;
+
+yaml_anchors (unique int node: @yaml_node ref,
+ varchar(900) anchor: string ref);
+
+yaml_aliases (unique int alias: @yaml_alias_node ref,
+ varchar(900) target: string ref);
+
+yaml_scalars (unique int scalar: @yaml_scalar_node ref,
+ int style: int ref,
+ varchar(900) value: string ref);
+
+yaml_errors (unique int id: @yaml_error,
+ varchar(900) message: string ref);
+
+yaml_locations(unique int locatable: @yaml_locatable ref,
+ int location: @location_default ref);
+
+@yaml_locatable = @yaml_node | @yaml_error;
+
+/* XML Files */
+
+xmlEncoding(
+ unique int id: @file ref,
+ varchar(900) encoding: string ref
+);
+
+xmlDTDs(
+ unique int id: @xmldtd,
+ varchar(900) root: string ref,
+ varchar(900) publicId: string ref,
+ varchar(900) systemId: string ref,
+ int fileid: @file ref
+);
+
+xmlElements(
+ unique int id: @xmlelement,
+ varchar(900) name: string ref,
+ int parentid: @xmlparent ref,
+ int idx: int ref,
+ int fileid: @file ref
+);
+
+xmlAttrs(
+ unique int id: @xmlattribute,
+ int elementid: @xmlelement ref,
+ varchar(900) name: string ref,
+ varchar(3600) value: string ref,
+ int idx: int ref,
+ int fileid: @file ref
+);
+
+xmlNs(
+ int id: @xmlnamespace,
+ varchar(900) prefixName: string ref,
+ varchar(900) URI: string ref,
+ int fileid: @file ref
+);
+
+xmlHasNs(
+ int elementId: @xmlnamespaceable ref,
+ int nsId: @xmlnamespace ref,
+ int fileid: @file ref
+);
+
+xmlComments(
+ unique int id: @xmlcomment,
+ varchar(3600) text: string ref,
+ int parentid: @xmlparent ref,
+ int fileid: @file ref
+);
+
+xmlChars(
+ unique int id: @xmlcharacters,
+ varchar(3600) text: string ref,
+ int parentid: @xmlparent ref,
+ int idx: int ref,
+ int isCDATA: int ref,
+ int fileid: @file ref
+);
+
+@xmlparent = @file | @xmlelement;
+@xmlnamespaceable = @xmlelement | @xmlattribute;
+
+xmllocations(
+ int xmlElement: @xmllocatable ref,
+ int location: @location_default ref
+);
+
+@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace;
+
+@dataflownode = @expr | @functiondeclstmt | @classdeclstmt | @namespacedeclaration | @enumdeclaration | @property;
+
+@optionalchainable = @callexpr | @propaccess;
+
+isOptionalChaining(int id: @optionalchainable ref);
+
+/*
+ * configuration files with key value pairs
+ */
+
+configs(
+ unique int id: @config
+);
+
+configNames(
+ unique int id: @configName,
+ int config: @config ref,
+ string name: string ref
+);
+
+configValues(
+ unique int id: @configValue,
+ int config: @config ref,
+ string value: string ref
+);
+
+configLocations(
+ int locatable: @configLocatable ref,
+ int location: @location_default ref
+);
+
+@configLocatable = @config | @configName | @configValue;
+
+/**
+ * The time taken for the extraction of a file.
+ * This table contains non-deterministic content.
+ *
+ * The sum of the `time` column for each (`file`, `timerKind`) pair
+ * is the total time taken for extraction of `file`. The `extractionPhase`
+ * column provides a granular view of the extraction time of the file.
+ */
+extraction_time(
+ int file : @file ref,
+ // see `com.semmle.js.extractor.ExtractionMetrics.ExtractionPhase`.
+ int extractionPhase: int ref,
+ // 0 for the elapsed CPU time in nanoseconds, 1 for the elapsed wallclock time in nanoseconds
+ int timerKind: int ref,
+ float time: float ref
+)
+
+/**
+ * Non-timing related data for the extraction of a single file.
+ * This table contains non-deterministic content.
+ */
+extraction_data(
+ int file : @file ref,
+ // the absolute path to the cache file
+ varchar(900) cacheFile: string ref,
+ boolean fromCache: boolean ref,
+ int length: int ref
+)
diff --git a/javascript/upgrades/2dc7a0389827d235763a3748aba73c2d4a677b15/upgrade.properties b/javascript/upgrades/2dc7a0389827d235763a3748aba73c2d4a677b15/upgrade.properties
new file mode 100644
index 00000000000..dc0d1654262
--- /dev/null
+++ b/javascript/upgrades/2dc7a0389827d235763a3748aba73c2d4a677b15/upgrade.properties
@@ -0,0 +1,2 @@
+description: add support for TypeScript 4.0
+compatibility: backwards
From a7a016c5df3ee65c489464fd618fc03077e25f34 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 12 Aug 2020 13:29:43 +0200
Subject: [PATCH 025/146] update expected output
---
.../ql/test/library-tests/TypeScript/Types/GetExprType.expected | 2 --
1 file changed, 2 deletions(-)
diff --git a/javascript/ql/test/library-tests/TypeScript/Types/GetExprType.expected b/javascript/ql/test/library-tests/TypeScript/Types/GetExprType.expected
index 30eb614d5c3..f5b8bf8eaa9 100644
--- a/javascript/ql/test/library-tests/TypeScript/Types/GetExprType.expected
+++ b/javascript/ql/test/library-tests/TypeScript/Types/GetExprType.expected
@@ -67,12 +67,10 @@
| tst.ts:26:15:26:24 | () => void | () => void |
| tst.ts:27:5:27:17 | undefinedType | undefined |
| tst.ts:28:5:28:12 | nullType | null |
-| tst.ts:28:22:28:25 | null | null |
| tst.ts:29:5:29:13 | neverType | () => never |
| tst.ts:29:16:29:26 | () => never | () => never |
| tst.ts:30:5:30:14 | symbolType | symbol |
| tst.ts:31:7:31:22 | uniqueSymbolType | typeof uniqueSymbolType |
-| tst.ts:31:41:31:44 | null | null |
| tst.ts:32:5:32:14 | objectType | object |
| tst.ts:33:5:33:16 | intersection | string & { x: string; } |
| tst.ts:33:29:33:29 | x | string |
From fd9eb1d40b82e593aa43749101c9f34e92449ada Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 12 Aug 2020 16:55:55 +0200
Subject: [PATCH 026/146] use Identifier instead of just a plain string when
getting tuple-element-names
---
.../extractor/src/com/semmle/js/ast/NodeCopier.java | 2 +-
.../src/com/semmle/js/extractor/ASTExtractor.java | 5 ++---
.../semmle/js/parser/TypeScriptASTConverter.java | 13 ++++---------
.../src/com/semmle/ts/ast/TupleTypeExpr.java | 7 ++++---
javascript/ql/src/semmle/javascript/TypeScript.qll | 11 ++++++++---
javascript/ql/src/semmlecode.javascript.dbscheme | 7 -------
.../TypeScript/TypeAnnotations/TupleTypeExpr.qll | 2 +-
.../TypeScript/TypeAnnotations/tests.expected | 4 ++--
.../semmlecode.javascript.dbscheme | 7 -------
9 files changed, 22 insertions(+), 36 deletions(-)
diff --git a/javascript/extractor/src/com/semmle/js/ast/NodeCopier.java b/javascript/extractor/src/com/semmle/js/ast/NodeCopier.java
index ddf57b32cff..00939503eeb 100644
--- a/javascript/extractor/src/com/semmle/js/ast/NodeCopier.java
+++ b/javascript/extractor/src/com/semmle/js/ast/NodeCopier.java
@@ -705,7 +705,7 @@ public class NodeCopier implements Visitor {
@Override
public INode visit(TupleTypeExpr nd, Void c) {
- return new TupleTypeExpr(visit(nd.getLoc()), copy(nd.getElementTypes()), nd.getElementNames());
+ return new TupleTypeExpr(visit(nd.getLoc()), copy(nd.getElementTypes()), copy(nd.getElementNames()));
}
@Override
diff --git a/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java
index 4bf57075b0b..1050590f14d 100644
--- a/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java
+++ b/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java
@@ -1820,9 +1820,8 @@ public class ASTExtractor {
public Label visit(TupleTypeExpr nd, Context c) {
Label key = super.visit(nd, c);
if (nd.getElementNames() != null) {
- for (int i = 0; i < nd.getElementNames().size(); i++) {
- trapwriter.addTuple("tuple_element_name", key, i, nd.getElementNames().get(i));
- }
+ // Element names are index -1, -2, -3...
+ visitAll(nd.getElementNames(), key, IdContext.typeDecl, -1, -1);
}
visitAll(nd.getElementTypes(), key, IdContext.typeBind, 0);
return key;
diff --git a/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java b/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java
index 83529f8b1b4..546739d7f28 100644
--- a/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java
+++ b/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java
@@ -2183,18 +2183,13 @@ public class TypeScriptASTConverter {
List elements = new ArrayList<>();
((JsonArray)node.get("elements")).iterator().forEachRemaining(elements::add);
- List elementNames = elements.stream()
+ List names = convertNodes(elements.stream()
.filter(n -> getKind(n).equals("NamedTupleMember"))
.map(n -> n.getAsJsonObject().get("name"))
- .map(n -> n.getAsJsonObject().get("escapedText"))
- .map(n -> n.getAsString())
- .collect(Collectors.toList());
+ .collect(Collectors.toList())
+ );
- if (elementNames.size() == 0) {
- elementNames = null;
- }
-
- return new TupleTypeExpr(loc, convertChildrenAsTypes(node, "elements"), elementNames);
+ return new TupleTypeExpr(loc, convertChildrenAsTypes(node, "elements"), names);
}
// This method just does a trivial forward to the type. The names have already been extracted in `convertTupleType`.
diff --git a/javascript/extractor/src/com/semmle/ts/ast/TupleTypeExpr.java b/javascript/extractor/src/com/semmle/ts/ast/TupleTypeExpr.java
index 9d49899fafd..a42df41847e 100644
--- a/javascript/extractor/src/com/semmle/ts/ast/TupleTypeExpr.java
+++ b/javascript/extractor/src/com/semmle/ts/ast/TupleTypeExpr.java
@@ -1,5 +1,6 @@
package com.semmle.ts.ast;
+import com.semmle.js.ast.Identifier;
import com.semmle.js.ast.SourceLocation;
import com.semmle.js.ast.Visitor;
import java.util.List;
@@ -7,9 +8,9 @@ import java.util.List;
/** A tuple type, such as [number, string]. */
public class TupleTypeExpr extends TypeExpression {
private final List elementTypes;
- private final List elementNames;
+ private final List elementNames;
- public TupleTypeExpr(SourceLocation loc, List elementTypes, List elementNames) {
+ public TupleTypeExpr(SourceLocation loc, List elementTypes, List elementNames) {
super("TupleTypeExpr", loc);
this.elementTypes = elementTypes;
this.elementNames = elementNames;
@@ -19,7 +20,7 @@ public class TupleTypeExpr extends TypeExpression {
return elementTypes;
}
- public List getElementNames() {
+ public List getElementNames() {
return elementNames;
}
diff --git a/javascript/ql/src/semmle/javascript/TypeScript.qll b/javascript/ql/src/semmle/javascript/TypeScript.qll
index 82088638a60..799f386e2a0 100644
--- a/javascript/ql/src/semmle/javascript/TypeScript.qll
+++ b/javascript/ql/src/semmle/javascript/TypeScript.qll
@@ -846,7 +846,7 @@ class ParenthesizedTypeExpr extends @parenthesizedtypeexpr, TypeExpr {
*/
class TupleTypeExpr extends @tupletypeexpr, TypeExpr {
/** Gets the `n`th element type in the tuple, starting at 0. */
- TypeExpr getElementType(int n) { result = getChildTypeExpr(n) }
+ TypeExpr getElementType(int n) { result = getChildTypeExpr(n) and n >= 0 }
/** Gets any of the element types in the tuple. */
TypeExpr getAnElementType() { result = getElementType(_) }
@@ -854,8 +854,13 @@ class TupleTypeExpr extends @tupletypeexpr, TypeExpr {
/** Gets the number of elements in the tuple type. */
int getNumElementType() { result = count(getAnElementType()) }
- /** Gets the name of the `n`th tuple member if the tuple members are named. */
- string getElementName(int n) { tuple_element_name(this, n, result) }
+ /**
+ * Gets the name of the `n`th tuple member, starting at 0.
+ * Only has a result if the tuple members are named.
+ *
+ * Type element names are at indices -1, -2, -3, ...
+ */
+ Identifier getElementName(int n) { result = getChild(-(n + 1)) and n >= 0 }
}
/**
diff --git a/javascript/ql/src/semmlecode.javascript.dbscheme b/javascript/ql/src/semmlecode.javascript.dbscheme
index 3f291931cb8..c73fbfca57f 100644
--- a/javascript/ql/src/semmlecode.javascript.dbscheme
+++ b/javascript/ql/src/semmlecode.javascript.dbscheme
@@ -790,13 +790,6 @@ tuple_type_rest(
unique int typ: @type ref
);
-#keyset[tuple, index]
-tuple_element_name (
- int tuple: @tupletypeexpr ref,
- int index: int ref,
- varchar(900) name: string ref
-);
-
// comments
comments (unique int id: @comment,
int kind: int ref,
diff --git a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/TupleTypeExpr.qll b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/TupleTypeExpr.qll
index 03fe4325786..adabff94060 100644
--- a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/TupleTypeExpr.qll
+++ b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/TupleTypeExpr.qll
@@ -4,6 +4,6 @@ query predicate test_TupleTypeExpr(TupleTypeExpr type, int n, int res0, TypeExpr
res0 = type.getNumElementType() and res1 = type.getElementType(n)
}
-query predicate test_TupleTypeElementName(TupleTypeExpr type, int n, string name) {
+query predicate test_TupleTypeElementName(TupleTypeExpr type, int n, Identifier name) {
name = type.getElementName(n)
}
diff --git a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected
index 00bda23ab3f..9ae9bdfe456 100644
--- a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected
+++ b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected
@@ -219,8 +219,8 @@ test_TupleTypeExpr
| tst.ts:179:21:179:44 | [...Str ... umbers] | 0 | 2 | tst.ts:179:22:179:31 | ...Strings |
| tst.ts:179:21:179:44 | [...Str ... umbers] | 1 | 2 | tst.ts:179:34:179:43 | ...Numbers |
test_TupleTypeElementName
-| tst.ts:169:34:169:64 | [first: ... number] | 0 | first |
-| tst.ts:169:34:169:64 | [first: ... number] | 1 | second |
+| tst.ts:169:34:169:64 | [first: ... number] | 0 | tst.ts:169:35:169:39 | first |
+| tst.ts:169:34:169:64 | [first: ... number] | 1 | tst.ts:169:50:169:55 | second |
test_FieldTypes
| tst.ts:15:3:15:22 | numberField: number; | tst.ts:15:16:15:21 | number |
| tst.ts:16:3:16:22 | stringField: string; | tst.ts:16:16:16:21 | string |
diff --git a/javascript/upgrades/2dc7a0389827d235763a3748aba73c2d4a677b15/semmlecode.javascript.dbscheme b/javascript/upgrades/2dc7a0389827d235763a3748aba73c2d4a677b15/semmlecode.javascript.dbscheme
index 3f291931cb8..c73fbfca57f 100644
--- a/javascript/upgrades/2dc7a0389827d235763a3748aba73c2d4a677b15/semmlecode.javascript.dbscheme
+++ b/javascript/upgrades/2dc7a0389827d235763a3748aba73c2d4a677b15/semmlecode.javascript.dbscheme
@@ -790,13 +790,6 @@ tuple_type_rest(
unique int typ: @type ref
);
-#keyset[tuple, index]
-tuple_element_name (
- int tuple: @tupletypeexpr ref,
- int index: int ref,
- varchar(900) name: string ref
-);
-
// comments
comments (unique int id: @comment,
int kind: int ref,
From d95d427c5b6019792cf53902df771bcd0b9068dd Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 13 Aug 2020 09:22:32 +0200
Subject: [PATCH 027/146] better support for the `&&=`, `||=`, and `??=`
operators
---
.../com/semmle/js/extractor/CFGExtractor.java | 23 +-
.../js/parser/TypeScriptASTConverter.java | 3 +
.../ql/test/library-tests/Expr/assignment2.ts | 9 +
.../ql/test/library-tests/Expr/tests.expected | 120 ++++
.../CWE-022/TaintedPath/TaintedPath.expected | 541 ++++++++++++++++++
.../CWE-022/TaintedPath/typescript.ts | 34 ++
6 files changed, 728 insertions(+), 2 deletions(-)
create mode 100644 javascript/ql/test/library-tests/Expr/assignment2.ts
create mode 100644 javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/typescript.ts
diff --git a/javascript/extractor/src/com/semmle/js/extractor/CFGExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/CFGExtractor.java
index 2aeba574bae..a43bd82f33b 100644
--- a/javascript/extractor/src/com/semmle/js/extractor/CFGExtractor.java
+++ b/javascript/extractor/src/com/semmle/js/extractor/CFGExtractor.java
@@ -1551,8 +1551,27 @@ public class CFGExtractor {
@Override
public Void visit(AssignmentExpression nd, SuccessorInfo i) {
- visitAssign(nd, nd.getLeft(), nd.getRight());
- succ(nd, i.getGuardedSuccessors(nd));
+ // `a &&= b` expands to `a || (a = b);`
+ // The CFG is a conditional assignment, so we go through the assignment `nd` last.
+ if ("&&=".equals(nd.getOperator()) || "||=".equals(nd.getOperator()) || "??=".equals(nd.getOperator())) {
+ if ("&&=".equals(nd.getOperator())) {
+ // from lhs to rhs on truthy. from lhs to false-branch on falsy.
+ visit(nd.getLeft(), First.of(nd.getRight()), i.getSuccessors(false));
+ } else if ("||=".equals(nd.getOperator())) {
+ // from lhs to true-branch on truthy. from lhs to rhs on falsy.
+ visit(nd.getLeft(), i.getSuccessors(true), First.of(nd.getRight()));
+ } else { // "??="
+ // the union of the above - truthyness is unknown.
+ visit(nd.getLeft(), union(First.of(nd.getRight()), i.getAllSuccessors()), null);
+ }
+
+ visit(nd.getRight(), First.of(nd), null); // from right to assignment.
+
+ succ(nd, i.getGuardedSuccessors(nd));
+ } else {
+ visitAssign(nd, nd.getLeft(), nd.getRight());
+ succ(nd, i.getGuardedSuccessors(nd));
+ }
return null;
}
diff --git a/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java b/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java
index 546739d7f28..8d9079a0b69 100644
--- a/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java
+++ b/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java
@@ -852,6 +852,9 @@ public class TypeScriptASTConverter {
case ">>=":
case "<<=":
case ">>>=":
+ case "??=":
+ case "&&=":
+ case "||=":
return new AssignmentExpression(loc, operator, convertLValue(left), right);
default:
diff --git a/javascript/ql/test/library-tests/Expr/assignment2.ts b/javascript/ql/test/library-tests/Expr/assignment2.ts
new file mode 100644
index 00000000000..3300fd2bf74
--- /dev/null
+++ b/javascript/ql/test/library-tests/Expr/assignment2.ts
@@ -0,0 +1,9 @@
+let a = 2;
+let b = 3;
+
+a = 23;
+a += 19;
+
+a &&= 4;
+a ||= 5;
+a ??= 6;
\ No newline at end of file
diff --git a/javascript/ql/test/library-tests/Expr/tests.expected b/javascript/ql/test/library-tests/Expr/tests.expected
index 1c3963b50ea..fc0734b34c2 100644
--- a/javascript/ql/test/library-tests/Expr/tests.expected
+++ b/javascript/ql/test/library-tests/Expr/tests.expected
@@ -1,4 +1,25 @@
test_getParent
+| assignment2.ts:1:5:1:5 | a | assignment2.ts:1:5:1:9 | a = 2 |
+| assignment2.ts:1:5:1:9 | a = 2 | assignment2.ts:1:1:1:10 | let a = 2; |
+| assignment2.ts:1:9:1:9 | 2 | assignment2.ts:1:5:1:9 | a = 2 |
+| assignment2.ts:2:5:2:5 | b | assignment2.ts:2:5:2:9 | b = 3 |
+| assignment2.ts:2:5:2:9 | b = 3 | assignment2.ts:2:1:2:10 | let b = 3; |
+| assignment2.ts:2:9:2:9 | 3 | assignment2.ts:2:5:2:9 | b = 3 |
+| assignment2.ts:4:1:4:1 | a | assignment2.ts:4:1:4:6 | a = 23 |
+| assignment2.ts:4:1:4:6 | a = 23 | assignment2.ts:4:1:4:7 | a = 23; |
+| assignment2.ts:4:5:4:6 | 23 | assignment2.ts:4:1:4:6 | a = 23 |
+| assignment2.ts:5:1:5:1 | a | assignment2.ts:5:1:5:7 | a += 19 |
+| assignment2.ts:5:1:5:7 | a += 19 | assignment2.ts:5:1:5:8 | a += 19; |
+| assignment2.ts:5:6:5:7 | 19 | assignment2.ts:5:1:5:7 | a += 19 |
+| assignment2.ts:7:1:7:1 | a | assignment2.ts:7:1:7:7 | a &&= 4 |
+| assignment2.ts:7:1:7:7 | a &&= 4 | assignment2.ts:7:1:7:8 | a &&= 4; |
+| assignment2.ts:7:7:7:7 | 4 | assignment2.ts:7:1:7:7 | a &&= 4 |
+| assignment2.ts:8:1:8:1 | a | assignment2.ts:8:1:8:7 | a \|\|= 5 |
+| assignment2.ts:8:1:8:7 | a \|\|= 5 | assignment2.ts:8:1:8:8 | a \|\|= 5; |
+| assignment2.ts:8:7:8:7 | 5 | assignment2.ts:8:1:8:7 | a \|\|= 5 |
+| assignment2.ts:9:1:9:1 | a | assignment2.ts:9:1:9:7 | a ??= 6 |
+| assignment2.ts:9:1:9:7 | a ??= 6 | assignment2.ts:9:1:9:8 | a ??= 6; |
+| assignment2.ts:9:7:9:7 | 6 | assignment2.ts:9:1:9:7 | a ??= 6 |
| assignment.js:1:1:1:1 | a | assignment.js:1:1:1:6 | a = 23 |
| assignment.js:1:1:1:6 | a = 23 | assignment.js:1:1:1:7 | a = 23; |
| assignment.js:1:5:1:6 | 23 | assignment.js:1:1:1:6 | a = 23 |
@@ -468,6 +489,27 @@ test_getParent
| update.js:4:1:4:1 | b | update.js:4:1:4:3 | b-- |
| update.js:4:1:4:3 | b-- | update.js:4:1:4:4 | b--; |
test_getTopLevel
+| assignment2.ts:1:5:1:5 | a | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:1:5:1:9 | a = 2 | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:1:9:1:9 | 2 | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:2:5:2:5 | b | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:2:5:2:9 | b = 3 | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:2:9:2:9 | 3 | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:4:1:4:1 | a | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:4:1:4:6 | a = 23 | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:4:5:4:6 | 23 | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:5:1:5:1 | a | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:5:1:5:7 | a += 19 | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:5:6:5:7 | 19 | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:7:1:7:1 | a | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:7:1:7:7 | a &&= 4 | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:7:7:7:7 | 4 | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:8:1:8:1 | a | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:8:1:8:7 | a \|\|= 5 | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:8:7:8:7 | 5 | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:9:1:9:1 | a | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:9:1:9:7 | a ??= 6 | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:9:7:9:7 | 6 | assignment2.ts:1:1:9:8 | |
| assignment.js:1:1:1:1 | a | assignment.js:1:1:12:7 | |
| assignment.js:1:1:1:6 | a = 23 | assignment.js:1:1:12:7 | |
| assignment.js:1:5:1:6 | 23 | assignment.js:1:1:12:7 | |
@@ -937,6 +979,20 @@ test_getTopLevel
| update.js:4:1:4:1 | b | update.js:1:1:5:0 | |
| update.js:4:1:4:3 | b-- | update.js:1:1:5:0 | |
test_getChild
+| assignment2.ts:1:5:1:9 | a = 2 | 0 | assignment2.ts:1:5:1:5 | a |
+| assignment2.ts:1:5:1:9 | a = 2 | 1 | assignment2.ts:1:9:1:9 | 2 |
+| assignment2.ts:2:5:2:9 | b = 3 | 0 | assignment2.ts:2:5:2:5 | b |
+| assignment2.ts:2:5:2:9 | b = 3 | 1 | assignment2.ts:2:9:2:9 | 3 |
+| assignment2.ts:4:1:4:6 | a = 23 | 0 | assignment2.ts:4:1:4:1 | a |
+| assignment2.ts:4:1:4:6 | a = 23 | 1 | assignment2.ts:4:5:4:6 | 23 |
+| assignment2.ts:5:1:5:7 | a += 19 | 0 | assignment2.ts:5:1:5:1 | a |
+| assignment2.ts:5:1:5:7 | a += 19 | 1 | assignment2.ts:5:6:5:7 | 19 |
+| assignment2.ts:7:1:7:7 | a &&= 4 | 0 | assignment2.ts:7:1:7:1 | a |
+| assignment2.ts:7:1:7:7 | a &&= 4 | 1 | assignment2.ts:7:7:7:7 | 4 |
+| assignment2.ts:8:1:8:7 | a \|\|= 5 | 0 | assignment2.ts:8:1:8:1 | a |
+| assignment2.ts:8:1:8:7 | a \|\|= 5 | 1 | assignment2.ts:8:7:8:7 | 5 |
+| assignment2.ts:9:1:9:7 | a ??= 6 | 0 | assignment2.ts:9:1:9:1 | a |
+| assignment2.ts:9:1:9:7 | a ??= 6 | 1 | assignment2.ts:9:7:9:7 | 6 |
| assignment.js:1:1:1:6 | a = 23 | 0 | assignment.js:1:1:1:1 | a |
| assignment.js:1:1:1:6 | a = 23 | 1 | assignment.js:1:5:1:6 | 23 |
| assignment.js:2:1:2:7 | a += 19 | 0 | assignment.js:2:1:2:1 | a |
@@ -1213,6 +1269,20 @@ test_getChild
| update.js:3:1:3:3 | --b | 0 | update.js:3:3:3:3 | b |
| update.js:4:1:4:3 | b-- | 0 | update.js:4:1:4:1 | b |
test_isPure
+| assignment2.ts:1:5:1:5 | a |
+| assignment2.ts:1:9:1:9 | 2 |
+| assignment2.ts:2:5:2:5 | b |
+| assignment2.ts:2:9:2:9 | 3 |
+| assignment2.ts:4:1:4:1 | a |
+| assignment2.ts:4:5:4:6 | 23 |
+| assignment2.ts:5:1:5:1 | a |
+| assignment2.ts:5:6:5:7 | 19 |
+| assignment2.ts:7:1:7:1 | a |
+| assignment2.ts:7:7:7:7 | 4 |
+| assignment2.ts:8:1:8:1 | a |
+| assignment2.ts:8:7:8:7 | 5 |
+| assignment2.ts:9:1:9:1 | a |
+| assignment2.ts:9:7:9:7 | 6 |
| assignment.js:1:1:1:1 | a |
| assignment.js:1:5:1:6 | 23 |
| assignment.js:2:1:2:1 | a |
@@ -1708,6 +1778,27 @@ test_stripParens
| primaries.js:28:2:28:5 | (42) | primaries.js:28:3:28:4 | 42 |
| unary.js:6:5:6:7 | (0) | unary.js:6:6:6:6 | 0 |
test_getContainer
+| assignment2.ts:1:5:1:5 | a | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:1:5:1:9 | a = 2 | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:1:9:1:9 | 2 | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:2:5:2:5 | b | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:2:5:2:9 | b = 3 | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:2:9:2:9 | 3 | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:4:1:4:1 | a | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:4:1:4:6 | a = 23 | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:4:5:4:6 | 23 | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:5:1:5:1 | a | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:5:1:5:7 | a += 19 | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:5:6:5:7 | 19 | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:7:1:7:1 | a | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:7:1:7:7 | a &&= 4 | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:7:7:7:7 | 4 | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:8:1:8:1 | a | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:8:1:8:7 | a \|\|= 5 | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:8:7:8:7 | 5 | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:9:1:9:1 | a | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:9:1:9:7 | a ??= 6 | assignment2.ts:1:1:9:8 | |
+| assignment2.ts:9:7:9:7 | 6 | assignment2.ts:1:1:9:8 | |
| assignment.js:1:1:1:1 | a | assignment.js:1:1:12:7 | |
| assignment.js:1:1:1:6 | a = 23 | assignment.js:1:1:12:7 | |
| assignment.js:1:5:1:6 | 23 | assignment.js:1:1:12:7 | |
@@ -2177,6 +2268,27 @@ test_getContainer
| update.js:4:1:4:1 | b | update.js:1:1:5:0 | |
| update.js:4:1:4:3 | b-- | update.js:1:1:5:0 | |
test_getEnclosingStmt
+| assignment2.ts:1:5:1:5 | a | assignment2.ts:1:1:1:10 | let a = 2; |
+| assignment2.ts:1:5:1:9 | a = 2 | assignment2.ts:1:1:1:10 | let a = 2; |
+| assignment2.ts:1:9:1:9 | 2 | assignment2.ts:1:1:1:10 | let a = 2; |
+| assignment2.ts:2:5:2:5 | b | assignment2.ts:2:1:2:10 | let b = 3; |
+| assignment2.ts:2:5:2:9 | b = 3 | assignment2.ts:2:1:2:10 | let b = 3; |
+| assignment2.ts:2:9:2:9 | 3 | assignment2.ts:2:1:2:10 | let b = 3; |
+| assignment2.ts:4:1:4:1 | a | assignment2.ts:4:1:4:7 | a = 23; |
+| assignment2.ts:4:1:4:6 | a = 23 | assignment2.ts:4:1:4:7 | a = 23; |
+| assignment2.ts:4:5:4:6 | 23 | assignment2.ts:4:1:4:7 | a = 23; |
+| assignment2.ts:5:1:5:1 | a | assignment2.ts:5:1:5:8 | a += 19; |
+| assignment2.ts:5:1:5:7 | a += 19 | assignment2.ts:5:1:5:8 | a += 19; |
+| assignment2.ts:5:6:5:7 | 19 | assignment2.ts:5:1:5:8 | a += 19; |
+| assignment2.ts:7:1:7:1 | a | assignment2.ts:7:1:7:8 | a &&= 4; |
+| assignment2.ts:7:1:7:7 | a &&= 4 | assignment2.ts:7:1:7:8 | a &&= 4; |
+| assignment2.ts:7:7:7:7 | 4 | assignment2.ts:7:1:7:8 | a &&= 4; |
+| assignment2.ts:8:1:8:1 | a | assignment2.ts:8:1:8:8 | a \|\|= 5; |
+| assignment2.ts:8:1:8:7 | a \|\|= 5 | assignment2.ts:8:1:8:8 | a \|\|= 5; |
+| assignment2.ts:8:7:8:7 | 5 | assignment2.ts:8:1:8:8 | a \|\|= 5; |
+| assignment2.ts:9:1:9:1 | a | assignment2.ts:9:1:9:8 | a ??= 6; |
+| assignment2.ts:9:1:9:7 | a ??= 6 | assignment2.ts:9:1:9:8 | a ??= 6; |
+| assignment2.ts:9:7:9:7 | 6 | assignment2.ts:9:1:9:8 | a ??= 6; |
| assignment.js:1:1:1:1 | a | assignment.js:1:1:1:7 | a = 23; |
| assignment.js:1:1:1:6 | a = 23 | assignment.js:1:1:1:7 | a = 23; |
| assignment.js:1:5:1:6 | 23 | assignment.js:1:1:1:7 | a = 23; |
@@ -2612,6 +2724,14 @@ test_getEnclosingStmt
| update.js:4:1:4:1 | b | update.js:4:1:4:4 | b--; |
| update.js:4:1:4:3 | b-- | update.js:4:1:4:4 | b--; |
test_inNullSensitiveContext
+| assignment2.ts:5:1:5:1 | a |
+| assignment2.ts:5:6:5:7 | 19 |
+| assignment2.ts:7:1:7:1 | a |
+| assignment2.ts:7:7:7:7 | 4 |
+| assignment2.ts:8:1:8:1 | a |
+| assignment2.ts:8:7:8:7 | 5 |
+| assignment2.ts:9:1:9:1 | a |
+| assignment2.ts:9:7:9:7 | 6 |
| assignment.js:2:1:2:1 | a |
| assignment.js:2:6:2:7 | 19 |
| assignment.js:3:1:3:1 | a |
diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected
index 7f6ca553576..7e4ab1a0427 100644
--- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected
@@ -3048,6 +3048,239 @@ nodes
| torrents.js:7:25:7:27 | loc |
| torrents.js:7:25:7:27 | loc |
| torrents.js:7:25:7:27 | loc |
+| typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:24:9:30 | req.url |
+| typescript.ts:9:24:9:30 | req.url |
+| typescript.ts:9:24:9:30 | req.url |
+| typescript.ts:9:24:9:30 | req.url |
+| typescript.ts:9:24:9:30 | req.url |
+| typescript.ts:12:29:12:32 | path |
+| typescript.ts:12:29:12:32 | path |
+| typescript.ts:12:29:12:32 | path |
+| typescript.ts:12:29:12:32 | path |
+| typescript.ts:12:29:12:32 | path |
+| typescript.ts:12:29:12:32 | path |
+| typescript.ts:12:29:12:32 | path |
+| typescript.ts:12:29:12:32 | path |
+| typescript.ts:12:29:12:32 | path |
+| typescript.ts:12:29:12:32 | path |
+| typescript.ts:12:29:12:32 | path |
+| typescript.ts:12:29:12:32 | path |
+| typescript.ts:12:29:12:32 | path |
+| typescript.ts:12:29:12:32 | path |
+| typescript.ts:12:29:12:32 | path |
+| typescript.ts:12:29:12:32 | path |
+| typescript.ts:12:29:12:32 | path |
+| typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:15:20:18 | path |
+| typescript.ts:20:15:20:18 | path |
+| typescript.ts:20:15:20:18 | path |
+| typescript.ts:20:15:20:18 | path |
+| typescript.ts:20:15:20:18 | path |
+| typescript.ts:20:15:20:18 | path |
+| typescript.ts:20:15:20:18 | path |
+| typescript.ts:20:15:20:18 | path |
+| typescript.ts:20:15:20:18 | path |
+| typescript.ts:20:15:20:18 | path |
+| typescript.ts:20:15:20:18 | path |
+| typescript.ts:20:15:20:18 | path |
+| typescript.ts:20:15:20:18 | path |
+| typescript.ts:20:15:20:18 | path |
+| typescript.ts:20:15:20:18 | path |
+| typescript.ts:20:15:20:18 | path |
+| typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:15:23:18 | path |
+| typescript.ts:23:15:23:18 | path |
+| typescript.ts:23:15:23:18 | path |
+| typescript.ts:23:15:23:18 | path |
+| typescript.ts:23:15:23:18 | path |
+| typescript.ts:23:15:23:18 | path |
+| typescript.ts:23:15:23:18 | path |
+| typescript.ts:23:15:23:18 | path |
+| typescript.ts:23:15:23:18 | path |
+| typescript.ts:23:15:23:18 | path |
+| typescript.ts:23:15:23:18 | path |
+| typescript.ts:23:15:23:18 | path |
+| typescript.ts:23:15:23:18 | path |
+| typescript.ts:23:15:23:18 | path |
+| typescript.ts:23:15:23:18 | path |
+| typescript.ts:23:15:23:18 | path |
+| typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:15:30:18 | path |
+| typescript.ts:30:15:30:18 | path |
+| typescript.ts:30:15:30:18 | path |
+| typescript.ts:30:15:30:18 | path |
+| typescript.ts:30:15:30:18 | path |
+| typescript.ts:30:15:30:18 | path |
+| typescript.ts:30:15:30:18 | path |
+| typescript.ts:30:15:30:18 | path |
+| typescript.ts:30:15:30:18 | path |
+| typescript.ts:30:15:30:18 | path |
+| typescript.ts:30:15:30:18 | path |
+| typescript.ts:30:15:30:18 | path |
+| typescript.ts:30:15:30:18 | path |
+| typescript.ts:30:15:30:18 | path |
+| typescript.ts:30:15:30:18 | path |
+| typescript.ts:30:15:30:18 | path |
+| typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:32:29:32:33 | path6 |
| views.js:1:43:1:55 | req.params[0] |
| views.js:1:43:1:55 | req.params[0] |
| views.js:1:43:1:55 | req.params[0] |
@@ -7362,6 +7595,310 @@ edges
| torrents.js:6:24:6:27 | name | torrents.js:6:12:6:45 | dir + " ... t.data" |
| torrents.js:6:24:6:27 | name | torrents.js:6:12:6:45 | dir + " ... t.data" |
| torrents.js:6:24:6:27 | name | torrents.js:6:12:6:45 | dir + " ... t.data" |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:12:29:12:32 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:20:15:20:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:20:15:20:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:20:15:20:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:20:15:20:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:20:15:20:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:20:15:20:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:20:15:20:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:20:15:20:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:20:15:20:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:20:15:20:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:20:15:20:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:20:15:20:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:20:15:20:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:20:15:20:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:20:15:20:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:20:15:20:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:23:15:23:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:23:15:23:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:23:15:23:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:23:15:23:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:23:15:23:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:23:15:23:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:23:15:23:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:23:15:23:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:23:15:23:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:23:15:23:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:23:15:23:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:23:15:23:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:23:15:23:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:23:15:23:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:23:15:23:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:23:15:23:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:30:15:30:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:30:15:30:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:30:15:30:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:30:15:30:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:30:15:30:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:30:15:30:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:30:15:30:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:30:15:30:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:30:15:30:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:30:15:30:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:30:15:30:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:30:15:30:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:30:15:30:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:30:15:30:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:30:15:30:18 | path |
+| typescript.ts:9:7:9:48 | path | typescript.ts:30:15:30:18 | path |
+| typescript.ts:9:14:9:37 | url.par ... , true) | typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:37 | url.par ... , true) | typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:37 | url.par ... , true) | typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:37 | url.par ... , true) | typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:37 | url.par ... , true) | typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:37 | url.par ... , true) | typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:37 | url.par ... , true) | typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:37 | url.par ... , true) | typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:37 | url.par ... , true) | typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:37 | url.par ... , true) | typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:37 | url.par ... , true) | typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:37 | url.par ... , true) | typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:37 | url.par ... , true) | typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:37 | url.par ... , true) | typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:37 | url.par ... , true) | typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:37 | url.par ... , true) | typescript.ts:9:14:9:43 | url.par ... ).query |
+| typescript.ts:9:14:9:43 | url.par ... ).query | typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:43 | url.par ... ).query | typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:43 | url.par ... ).query | typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:43 | url.par ... ).query | typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:43 | url.par ... ).query | typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:43 | url.par ... ).query | typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:43 | url.par ... ).query | typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:43 | url.par ... ).query | typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:43 | url.par ... ).query | typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:43 | url.par ... ).query | typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:43 | url.par ... ).query | typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:43 | url.par ... ).query | typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:43 | url.par ... ).query | typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:43 | url.par ... ).query | typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:43 | url.par ... ).query | typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:43 | url.par ... ).query | typescript.ts:9:14:9:48 | url.par ... ry.path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path | typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path | typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path | typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path | typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path | typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path | typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path | typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path | typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path | typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path | typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path | typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path | typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path | typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path | typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path | typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:14:9:48 | url.par ... ry.path | typescript.ts:9:7:9:48 | path |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:9:24:9:30 | req.url | typescript.ts:9:14:9:37 | url.par ... , true) |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:7:20:18 | path3 | typescript.ts:21:39:21:43 | path3 |
+| typescript.ts:20:15:20:18 | path | typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:15:20:18 | path | typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:15:20:18 | path | typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:15:20:18 | path | typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:15:20:18 | path | typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:15:20:18 | path | typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:15:20:18 | path | typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:15:20:18 | path | typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:15:20:18 | path | typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:15:20:18 | path | typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:15:20:18 | path | typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:15:20:18 | path | typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:15:20:18 | path | typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:15:20:18 | path | typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:15:20:18 | path | typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:20:15:20:18 | path | typescript.ts:20:7:20:18 | path3 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:7:23:18 | path4 | typescript.ts:24:39:24:43 | path4 |
+| typescript.ts:23:15:23:18 | path | typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:15:23:18 | path | typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:15:23:18 | path | typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:15:23:18 | path | typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:15:23:18 | path | typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:15:23:18 | path | typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:15:23:18 | path | typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:15:23:18 | path | typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:15:23:18 | path | typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:15:23:18 | path | typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:15:23:18 | path | typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:15:23:18 | path | typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:15:23:18 | path | typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:15:23:18 | path | typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:15:23:18 | path | typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:23:15:23:18 | path | typescript.ts:23:7:23:18 | path4 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:7:30:18 | path6 | typescript.ts:32:29:32:33 | path6 |
+| typescript.ts:30:15:30:18 | path | typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:15:30:18 | path | typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:15:30:18 | path | typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:15:30:18 | path | typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:15:30:18 | path | typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:15:30:18 | path | typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:15:30:18 | path | typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:15:30:18 | path | typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:15:30:18 | path | typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:15:30:18 | path | typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:15:30:18 | path | typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:15:30:18 | path | typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:15:30:18 | path | typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:15:30:18 | path | typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:15:30:18 | path | typescript.ts:30:7:30:18 | path6 |
+| typescript.ts:30:15:30:18 | path | typescript.ts:30:7:30:18 | path6 |
| views.js:1:43:1:55 | req.params[0] | views.js:1:43:1:55 | req.params[0] |
#select
| TaintedPath-es6.js:10:26:10:45 | join("public", path) | TaintedPath-es6.js:7:20:7:26 | req.url | TaintedPath-es6.js:10:26:10:45 | join("public", path) | This path depends on $@. | TaintedPath-es6.js:7:20:7:26 | req.url | a user-provided value |
@@ -7497,4 +8034,8 @@ edges
| tainted-string-steps.js:26:18:26:45 | path.sp ... hatever | tainted-string-steps.js:6:24:6:30 | req.url | tainted-string-steps.js:26:18:26:45 | path.sp ... hatever | This path depends on $@. | tainted-string-steps.js:6:24:6:30 | req.url | a user-provided value |
| tainted-string-steps.js:27:18:27:36 | path.split(unknown) | tainted-string-steps.js:6:24:6:30 | req.url | tainted-string-steps.js:27:18:27:36 | path.split(unknown) | This path depends on $@. | tainted-string-steps.js:6:24:6:30 | req.url | a user-provided value |
| torrents.js:7:25:7:27 | loc | torrents.js:5:13:5:38 | parseTo ... t).name | torrents.js:7:25:7:27 | loc | This path depends on $@. | torrents.js:5:13:5:38 | parseTo ... t).name | a user-provided value |
+| typescript.ts:12:29:12:32 | path | typescript.ts:9:24:9:30 | req.url | typescript.ts:12:29:12:32 | path | This path depends on $@. | typescript.ts:9:24:9:30 | req.url | a user-provided value |
+| typescript.ts:21:39:21:43 | path3 | typescript.ts:9:24:9:30 | req.url | typescript.ts:21:39:21:43 | path3 | This path depends on $@. | typescript.ts:9:24:9:30 | req.url | a user-provided value |
+| typescript.ts:24:39:24:43 | path4 | typescript.ts:9:24:9:30 | req.url | typescript.ts:24:39:24:43 | path4 | This path depends on $@. | typescript.ts:9:24:9:30 | req.url | a user-provided value |
+| typescript.ts:32:29:32:33 | path6 | typescript.ts:9:24:9:30 | req.url | typescript.ts:32:29:32:33 | path6 | This path depends on $@. | typescript.ts:9:24:9:30 | req.url | a user-provided value |
| views.js:1:43:1:55 | req.params[0] | views.js:1:43:1:55 | req.params[0] | views.js:1:43:1:55 | req.params[0] | This path depends on $@. | views.js:1:43:1:55 | req.params[0] | a user-provided value |
diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/typescript.ts b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/typescript.ts
new file mode 100644
index 00000000000..f5fd62b2ee0
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/typescript.ts
@@ -0,0 +1,34 @@
+var fs = require('fs'),
+ http = require('http'),
+ url = require('url'),
+ sanitize = require('sanitize-filename'),
+ pathModule = require('path')
+ ;
+
+var server = http.createServer(function(req, res) {
+ let path = url.parse(req.url, true).query.path;
+
+ // BAD: This could read any file on the file system
+ res.write(fs.readFileSync(path));
+
+ if (path === 'foo.txt')
+ res.write(fs.readFileSync(path)); // GOOD: Path is compared to white-list
+
+ let path2 = path;
+ path2 ||= res.write(fs.readFileSync(path2)); // GOOD: path is falsy
+
+ let path3 = path;
+ path3 &&= res.write(fs.readFileSync(path3)); // BAD: path is truthy
+
+ let path4 = path;
+ path4 ??= res.write(fs.readFileSync(path4)); // GOOD - path is null or undefined - but we don't capture that. [INCONSISTENCY]
+
+ let path5 = path;
+ path5 &&= "clean";
+ res.write(fs.readFileSync(path5)); // GOOD: path is either falsy or "clean";
+
+ let path6 = path;
+ path6 ||= "clean";
+ res.write(fs.readFileSync(path6)); // BAD: path can still be tainted
+
+});
From d35d3f4271dabf653f4c3f9f910f5c274c77383c Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 13 Aug 2020 09:37:55 +0200
Subject: [PATCH 028/146] add test for catch with type `unknown`
---
.../library-tests/TypeScript/Types/GetExprType.expected | 7 +++++++
.../TypeScript/Types/GetTypeExprType.expected | 1 +
.../library-tests/TypeScript/Types/UnknownType.expected | 2 ++
javascript/ql/test/library-tests/TypeScript/Types/tst.ts | 8 ++++++++
4 files changed, 18 insertions(+)
diff --git a/javascript/ql/test/library-tests/TypeScript/Types/GetExprType.expected b/javascript/ql/test/library-tests/TypeScript/Types/GetExprType.expected
index f5b8bf8eaa9..f5e0d3d6748 100644
--- a/javascript/ql/test/library-tests/TypeScript/Types/GetExprType.expected
+++ b/javascript/ql/test/library-tests/TypeScript/Types/GetExprType.expected
@@ -90,6 +90,13 @@
| tst.ts:43:26:43:48 | { foo: ... s const | { readonly foo: "foo"; } |
| tst.ts:43:28:43:30 | foo | "foo" |
| tst.ts:43:33:43:37 | "foo" | "foo" |
+| tst.ts:47:8:47:8 | e | unknown |
+| tst.ts:48:7:48:14 | typeof e | "string" \| "number" \| "bigint" \| "boolean" \| "s... |
+| tst.ts:48:7:48:27 | typeof ... string" | boolean |
+| tst.ts:48:14:48:14 | e | unknown |
+| tst.ts:48:20:48:27 | "string" | "string" |
+| tst.ts:49:11:49:11 | b | string |
+| tst.ts:49:24:49:24 | e | string |
| type_alias.ts:3:5:3:5 | b | boolean |
| type_alias.ts:7:5:7:5 | c | ValueOrArray |
| type_alias.ts:14:9:14:32 | [proper ... ]: Json | any |
diff --git a/javascript/ql/test/library-tests/TypeScript/Types/GetTypeExprType.expected b/javascript/ql/test/library-tests/TypeScript/Types/GetTypeExprType.expected
index 9254cf1febd..080d309f153 100644
--- a/javascript/ql/test/library-tests/TypeScript/Types/GetTypeExprType.expected
+++ b/javascript/ql/test/library-tests/TypeScript/Types/GetTypeExprType.expected
@@ -68,6 +68,7 @@
| tst.ts:39:60:39:65 | number | number |
| tst.ts:39:60:39:67 | number[] | number[] |
| tst.ts:40:18:40:24 | unknown | unknown |
+| tst.ts:49:15:49:20 | string | string |
| type_alias.ts:1:6:1:6 | B | boolean |
| type_alias.ts:1:10:1:16 | boolean | boolean |
| type_alias.ts:3:8:3:8 | B | boolean |
diff --git a/javascript/ql/test/library-tests/TypeScript/Types/UnknownType.expected b/javascript/ql/test/library-tests/TypeScript/Types/UnknownType.expected
index dfc3f2808ed..abc98c3ecce 100644
--- a/javascript/ql/test/library-tests/TypeScript/Types/UnknownType.expected
+++ b/javascript/ql/test/library-tests/TypeScript/Types/UnknownType.expected
@@ -1 +1,3 @@
| tst.ts:40:5:40:15 | unknownType | unknown |
+| tst.ts:47:8:47:8 | e | unknown |
+| tst.ts:48:14:48:14 | e | unknown |
diff --git a/javascript/ql/test/library-tests/TypeScript/Types/tst.ts b/javascript/ql/test/library-tests/TypeScript/Types/tst.ts
index 7e426a30faa..04968386c99 100644
--- a/javascript/ql/test/library-tests/TypeScript/Types/tst.ts
+++ b/javascript/ql/test/library-tests/TypeScript/Types/tst.ts
@@ -41,3 +41,11 @@ let unknownType: unknown;
let constArrayLiteral = [1, 2] as const;
let constObjectLiteral = { foo: "foo" } as const;
+
+
+try { }
+catch (e: unknown) {
+ if (typeof e === "string") {
+ let b : string = e;
+ }
+}
\ No newline at end of file
From 5ed31070452beabb70ff8d1dadbae32c02f8fd9a Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Fri, 14 Aug 2020 11:12:23 +0200
Subject: [PATCH 029/146] Python: Start scaffold for magic methods
---
python/ql/src/semmle/python/Magic.qll | 36 +++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
create mode 100644 python/ql/src/semmle/python/Magic.qll
diff --git a/python/ql/src/semmle/python/Magic.qll b/python/ql/src/semmle/python/Magic.qll
new file mode 100644
index 00000000000..bbea1f90d1d
--- /dev/null
+++ b/python/ql/src/semmle/python/Magic.qll
@@ -0,0 +1,36 @@
+import python
+
+module MagicMethod {
+ abstract class Potential extends ControlFlowNode {
+ abstract string getMagicMethodName();
+ abstract ControlFlowNode getArg(int n);
+ ControlFlowNode getSelf() { result = this.getArg(1) }
+ }
+
+ class Actual extends ControlFlowNode {
+ Object resolvedMagicMethod;
+
+ Actual() {
+ exists(Potential pot |
+ this.(Potential) = pot and
+ pot.getSelf().(ClassObject).lookupAttribute(pot.getMagicMethodName()) = resolvedMagicMethod
+ )
+ }
+ }
+}
+
+class MagicBinOp extends MagicMethod::Potential, BinaryExprNode {
+ Operator operator;
+
+ MagicBinOp() { this.getOp() = operator}
+
+ override string getMagicMethodName() {
+ result = operator.getSpecialMethodName()
+ }
+
+ override ControlFlowNode getArg(int n) {
+ n = 1 and result = this.getLeft()
+ or
+ n = 2 and result = this.getRight()
+ }
+}
From 360ddc6314439036a312d33615fa515a6717611f Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Fri, 14 Aug 2020 13:25:17 +0200
Subject: [PATCH 030/146] Python: better charPred
---
python/ql/src/semmle/python/Magic.qll | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/python/ql/src/semmle/python/Magic.qll b/python/ql/src/semmle/python/Magic.qll
index bbea1f90d1d..d277599b58d 100644
--- a/python/ql/src/semmle/python/Magic.qll
+++ b/python/ql/src/semmle/python/Magic.qll
@@ -8,14 +8,16 @@ module MagicMethod {
}
class Actual extends ControlFlowNode {
- Object resolvedMagicMethod;
+ Value resolvedMagicMethod;
Actual() {
exists(Potential pot |
this.(Potential) = pot and
- pot.getSelf().(ClassObject).lookupAttribute(pot.getMagicMethodName()) = resolvedMagicMethod
+ pot.getSelf().pointsTo().getClass().lookup(pot.getMagicMethodName()) = resolvedMagicMethod
)
}
+
+ Value getResolvedMagicMethod() { result = resolvedMagicMethod }
}
}
From e808d3033acfa0de5d4827c7e5eac53010f42d2f Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Fri, 14 Aug 2020 14:19:18 +0200
Subject: [PATCH 031/146] Python: Add magic to DataFlowCall
---
.../dataflow/internal/DataFlowPrivate.qll | 65 +++++++++++++++++--
python/ql/src/semmle/python/Magic.qll | 6 +-
.../coverage/classesCallGraph.expected | 26 ++++++++
3 files changed, 87 insertions(+), 10 deletions(-)
diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
index 1f46fd341e9..319bf69a414 100644
--- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
+++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
@@ -1,5 +1,6 @@
private import python
private import DataFlowPublic
+import semmle.python.Magic
//--------
// Data flow graph
@@ -157,17 +158,67 @@ class DataFlowClassValue extends DataFlowCallable, TClassValue {
override string getName() { result = c.getName() }
}
-/** Represents a call to a callable */
-class DataFlowCall extends CallNode {
- DataFlowCallable callable;
+newtype TDataFlowCall =
+ TCallNode(CallNode call) or
+ TMagicCall(MagicMethod::Actual magic)
- DataFlowCall() { this = callable.getACall() }
+abstract class DataFlowCall extends TDataFlowCall {
+ /** Gets a textual representation of this element. */
+ abstract string toString();
/** Get the callable to which this call goes. */
- DataFlowCallable getCallable() { result = callable }
+ abstract DataFlowCallable getCallable();
+
+ /** Get the specified arguemnt to this call. */
+ abstract ControlFlowNode getArg(int n);
+
+ /** Get the control flow node representing this call. */
+ abstract ControlFlowNode getNode();
/** Gets the enclosing callable of this call. */
- DataFlowCallable getEnclosingCallable() { result.getScope() = this.getNode().getScope() }
+ abstract DataFlowCallable getEnclosingCallable();
+}
+
+
+/** Represents a call to a callable */
+class CallNodeCall extends DataFlowCall, TCallNode {
+ CallNode call;
+ DataFlowCallable callable;
+
+ CallNodeCall() {
+ this = TCallNode(call) and
+ call = callable.getACall()
+ }
+
+ override string toString() { result = call.toString() }
+
+ override ControlFlowNode getArg(int n) {
+ result = call.getArg(n)
+ }
+
+ override ControlFlowNode getNode() { result = call }
+
+ override DataFlowCallable getCallable() { result = callable }
+
+ override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getNode().getScope() }
+}
+
+class MagicCall extends DataFlowCall, TMagicCall {
+ MagicMethod::Actual magic;
+
+ MagicCall() { this = TMagicCall(magic) }
+
+ override string toString() { result = magic.toString() }
+
+ override ControlFlowNode getArg(int n) {
+ result = magic.(MagicMethod::Potential).getArg(n)
+ }
+
+ override ControlFlowNode getNode() { result = magic }
+
+ override DataFlowCallable getCallable() { result = TCallableValue(magic.getResolvedMagicMethod()) }
+
+ override DataFlowCallable getEnclosingCallable() { result.getScope() = magic.getNode().getScope() }
}
/** A data flow node that represents a call argument. */
@@ -220,7 +271,7 @@ class OutNode extends CfgNode {
* `kind`.
*/
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
- call = result.getNode() and
+ call.getNode() = result.getNode() and
kind = TNormalReturnKind()
}
diff --git a/python/ql/src/semmle/python/Magic.qll b/python/ql/src/semmle/python/Magic.qll
index d277599b58d..e60b16c16ab 100644
--- a/python/ql/src/semmle/python/Magic.qll
+++ b/python/ql/src/semmle/python/Magic.qll
@@ -4,7 +4,7 @@ module MagicMethod {
abstract class Potential extends ControlFlowNode {
abstract string getMagicMethodName();
abstract ControlFlowNode getArg(int n);
- ControlFlowNode getSelf() { result = this.getArg(1) }
+ ControlFlowNode getSelf() { result = this.getArg(0) }
}
class Actual extends ControlFlowNode {
@@ -31,8 +31,8 @@ class MagicBinOp extends MagicMethod::Potential, BinaryExprNode {
}
override ControlFlowNode getArg(int n) {
- n = 1 and result = this.getLeft()
+ n = 0 and result = this.getLeft()
or
- n = 2 and result = this.getRight()
+ n = 1 and result = this.getRight()
}
}
diff --git a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected
index 4baf0b077ab..3a15759e3bd 100644
--- a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected
+++ b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected
@@ -4,3 +4,29 @@
| classes.py:182:7:182:26 | ControlFlowNode for dict() | classes.py:182:7:182:26 | ControlFlowNode for dict() |
| classes.py:303:28:303:51 | ControlFlowNode for dict() | classes.py:303:28:303:51 | ControlFlowNode for dict() |
| classes.py:466:12:466:24 | ControlFlowNode for Attribute() | classes.py:466:12:466:24 | ControlFlowNode for Attribute() |
+| classes.py:505:3:505:10 | ControlFlowNode for with_add | classes.py:499:15:499:18 | SSA variable self |
+| classes.py:505:14:505:21 | ControlFlowNode for with_add | classes.py:499:21:499:25 | SSA variable other |
+| classes.py:516:3:516:10 | ControlFlowNode for with_sub | classes.py:510:15:510:18 | SSA variable self |
+| classes.py:516:14:516:21 | ControlFlowNode for with_sub | classes.py:510:21:510:25 | SSA variable other |
+| classes.py:527:3:527:10 | ControlFlowNode for with_mul | classes.py:521:15:521:18 | SSA variable self |
+| classes.py:527:14:527:21 | ControlFlowNode for with_mul | classes.py:521:21:521:25 | SSA variable other |
+| classes.py:538:3:538:13 | ControlFlowNode for with_matmul | classes.py:532:18:532:21 | SSA variable self |
+| classes.py:538:17:538:27 | ControlFlowNode for with_matmul | classes.py:532:24:532:28 | SSA variable other |
+| classes.py:549:3:549:14 | ControlFlowNode for with_truediv | classes.py:543:19:543:22 | SSA variable self |
+| classes.py:549:18:549:29 | ControlFlowNode for with_truediv | classes.py:543:25:543:29 | SSA variable other |
+| classes.py:560:3:560:15 | ControlFlowNode for with_floordiv | classes.py:554:20:554:23 | SSA variable self |
+| classes.py:560:20:560:32 | ControlFlowNode for with_floordiv | classes.py:554:26:554:30 | SSA variable other |
+| classes.py:571:3:571:10 | ControlFlowNode for with_mod | classes.py:565:15:565:18 | SSA variable self |
+| classes.py:571:14:571:21 | ControlFlowNode for with_mod | classes.py:565:21:565:25 | SSA variable other |
+| classes.py:597:3:597:10 | ControlFlowNode for with_pow | classes.py:587:15:587:18 | SSA variable self |
+| classes.py:597:15:597:22 | ControlFlowNode for with_pow | classes.py:587:21:587:25 | SSA variable other |
+| classes.py:608:3:608:13 | ControlFlowNode for with_lshift | classes.py:602:18:602:21 | SSA variable self |
+| classes.py:608:18:608:28 | ControlFlowNode for with_lshift | classes.py:602:24:602:28 | SSA variable other |
+| classes.py:619:3:619:13 | ControlFlowNode for with_rshift | classes.py:613:18:613:21 | SSA variable self |
+| classes.py:619:18:619:28 | ControlFlowNode for with_rshift | classes.py:613:24:613:28 | SSA variable other |
+| classes.py:630:3:630:10 | ControlFlowNode for with_and | classes.py:624:15:624:18 | SSA variable self |
+| classes.py:630:14:630:21 | ControlFlowNode for with_and | classes.py:624:21:624:25 | SSA variable other |
+| classes.py:641:3:641:10 | ControlFlowNode for with_xor | classes.py:635:15:635:18 | SSA variable self |
+| classes.py:641:14:641:21 | ControlFlowNode for with_xor | classes.py:635:21:635:25 | SSA variable other |
+| classes.py:652:3:652:9 | ControlFlowNode for with_or | classes.py:646:14:646:17 | SSA variable self |
+| classes.py:652:13:652:19 | ControlFlowNode for with_or | classes.py:646:20:646:24 | SSA variable other |
From 4bc04486cbcc637c38d420375beb25c4add97795 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Fri, 14 Aug 2020 14:41:35 +0200
Subject: [PATCH 032/146] Python: Annotate tests (as before the new feature)
---
.../experimental/dataflow/coverage/classes.py | 200 +++++++++---------
1 file changed, 100 insertions(+), 100 deletions(-)
diff --git a/python/ql/test/experimental/dataflow/coverage/classes.py b/python/ql/test/experimental/dataflow/coverage/classes.py
index de06d284361..255bacf092a 100644
--- a/python/ql/test/experimental/dataflow/coverage/classes.py
+++ b/python/ql/test/experimental/dataflow/coverage/classes.py
@@ -8,14 +8,14 @@
# All functions starting with "test_" should run and print `"OK"`.
# This can be checked by running validTest.py.
-def OK():
+def OK() # Call not found:
print("OK")
# object.__new__(cls[, ...])
class With_new:
def __new__(cls):
- OK()
+ OK() # Call not found
return super().__new__(cls)
def test_new():
@@ -25,7 +25,7 @@ def test_new():
class With_init:
def __init__(self):
- OK()
+ OK() # Call not found
def test_init():
with_init = With_init()
@@ -34,7 +34,7 @@ def test_init():
class With_del:
def __del__(self):
- OK()
+ OK() # Call not found
def test_del():
with_del = With_del()
@@ -44,7 +44,7 @@ def test_del():
class With_repr:
def __repr__(self):
- OK()
+ OK() # Call not found
return "With_repr()"
def test_repr():
@@ -55,7 +55,7 @@ def test_repr():
class With_str:
def __str__(self):
- OK()
+ OK() # Call not found
return "Awesome"
def test_str():
@@ -66,7 +66,7 @@ def test_str():
class With_bytes:
def __bytes__(self):
- OK()
+ OK() # Call not found
return b"Awesome"
def test_bytes():
@@ -77,7 +77,7 @@ def test_bytes():
class With_format:
def __format__(self, format_spec):
- OK()
+ OK() # Call not found
return "Awesome"
def test_format():
@@ -96,7 +96,7 @@ def test_format_fstr():
class With_lt:
def __lt__(self, other):
- OK()
+ OK() # Call not found
return ""
def test_lt():
@@ -107,7 +107,7 @@ def test_lt():
class With_le:
def __le__(self, other):
- OK()
+ OK() # Call not found
return ""
def test_le():
@@ -118,7 +118,7 @@ def test_le():
class With_eq:
def __eq__(self, other):
- OK()
+ OK() # Call not found
return ""
def test_eq():
@@ -129,7 +129,7 @@ def test_eq():
class With_ne:
def __ne__(self, other):
- OK()
+ OK() # Call not found
return ""
def test_ne():
@@ -140,7 +140,7 @@ def test_ne():
class With_gt:
def __gt__(self, other):
- OK()
+ OK() # Call not found
return ""
def test_gt():
@@ -151,7 +151,7 @@ def test_gt():
class With_ge:
def __ge__(self, other):
- OK()
+ OK() # Call not found
return ""
def test_ge():
@@ -162,7 +162,7 @@ def test_ge():
class With_hash:
def __hash__(self):
- OK()
+ OK() # Call not found
return 0
def test_hash():
@@ -185,7 +185,7 @@ def test_hash_dict():
class With_bool:
def __bool__(self):
- OK()
+ OK() # Call not found
return True
def test_bool():
@@ -202,7 +202,7 @@ def test_bool_if():
class With_getattr:
def __getattr__(self, name):
- OK()
+ OK() # Call not found
return ""
def test_getattr():
@@ -213,7 +213,7 @@ def test_getattr():
class With_getattribute:
def __getattribute__(self, name):
- OK()
+ OK() # Call not found
return ""
def test_getattribute():
@@ -224,7 +224,7 @@ def test_getattribute():
class With_setattr:
def __setattr__(self, name, value):
- OK()
+ OK() # Call not found
def test_setattr():
with_setattr = With_setattr()
@@ -234,7 +234,7 @@ def test_setattr():
class With_delattr:
def __delattr__(self, name):
- OK()
+ OK() # Call not found
def test_delattr():
with_delattr = With_delattr()
@@ -244,7 +244,7 @@ def test_delattr():
class With_dir:
def __dir__(self):
- OK()
+ OK() # Call not found
return []
def test_dir():
@@ -260,7 +260,7 @@ class Owner:
class With_get:
def __get__(self, instance, owner=None):
- OK()
+ OK() # Call not found
return ""
def test_get():
@@ -272,7 +272,7 @@ def test_get():
class With_set:
def __set__(self, instance, value):
- OK()
+ OK() # Call not found
def test_set():
with_set = With_set()
@@ -284,7 +284,7 @@ def test_set():
class With_delete:
def __delete__(self, instance):
- OK()
+ OK() # Call not found
def test_delete():
with_delete = With_delete()
@@ -296,7 +296,7 @@ def test_delete():
class With_set_name:
def __set_name__(self, owner, name):
- OK()
+ OK() # Call not found
def test_set_name():
with_set_name = With_set_name()
@@ -312,7 +312,7 @@ def test_set_name():
class With_init_subclass:
def __init_subclass__(cls):
- OK()
+ OK() # Call not found
def test_init_subclass():
type("Subclass", (With_init_subclass,), {})
@@ -328,7 +328,7 @@ def test_init_subclass():
class With_prepare(type):
def __prepare__(name, bases, **kwds):
- OK()
+ OK() # Call not found
return kwds
@@ -341,7 +341,7 @@ def test_prepare():
class With_instancecheck:
def __instancecheck__(self, instance):
- OK()
+ OK() # Call not found
return True
def test_instancecheck():
@@ -352,7 +352,7 @@ def test_instancecheck():
class With_subclasscheck:
def __subclasscheck__(self, subclass):
- OK()
+ OK() # Call not found
return True
def test_subclasscheck():
@@ -365,7 +365,7 @@ def test_subclasscheck():
class With_class_getitem:
def __class_getitem__(cls, key):
- OK()
+ OK() # Call not found
return object
def test_class_getitem():
@@ -377,7 +377,7 @@ def test_class_getitem():
class With_call:
def __call__(self):
- OK()
+ OK() # Call not found
def test_call():
with_call = With_call()
@@ -388,7 +388,7 @@ def test_call():
class With_len:
def __len__(self):
- OK()
+ OK() # Call not found
return 0
def test_len():
@@ -408,7 +408,7 @@ def test_len_if():
class With_length_hint:
def __length_hint__(self):
- OK()
+ OK() # Call not found
return 0
def test_length_hint():
@@ -420,7 +420,7 @@ def test_length_hint():
class With_getitem:
def __getitem__(self, key):
- OK()
+ OK() # Call not found
return ""
def test_getitem():
@@ -431,7 +431,7 @@ def test_getitem():
class With_setitem:
def __setitem__(self, key, value):
- OK()
+ OK() # Call not found
def test_setitem():
with_setitem = With_setitem()
@@ -441,7 +441,7 @@ def test_setitem():
class With_delitem:
def __delitem__(self, key):
- OK()
+ OK() # Call not found
def test_delitem():
with_delitem = With_delitem()
@@ -451,7 +451,7 @@ def test_delitem():
class With_missing(dict):
def __missing__(self, key):
- OK()
+ OK() # Call not found
return ""
def test_missing():
@@ -462,7 +462,7 @@ def test_missing():
class With_iter:
def __iter__(self):
- OK()
+ OK() # Call not found
return [].__iter__()
def test_iter():
@@ -473,7 +473,7 @@ def test_iter():
class With_reversed:
def __reversed__(self):
- OK()
+ OK() # Call not found
return [].__iter__
def test_reversed():
@@ -484,7 +484,7 @@ def test_reversed():
class With_contains:
def __contains__(self, item):
- OK()
+ OK() # Call not found
return True
def test_contains():
@@ -497,7 +497,7 @@ def test_contains():
class With_add:
def __add__(self, other):
- OK()
+ OK() # Call not found
return self
def test_add():
@@ -508,7 +508,7 @@ def test_add():
class With_sub:
def __sub__(self, other):
- OK()
+ OK() # Call not found
return self
def test_sub():
@@ -519,7 +519,7 @@ def test_sub():
class With_mul:
def __mul__(self, other):
- OK()
+ OK() # Call not found
return self
def test_mul():
@@ -530,7 +530,7 @@ def test_mul():
class With_matmul:
def __matmul__(self, other):
- OK()
+ OK() # Call not found
return self
def test_matmul():
@@ -541,7 +541,7 @@ def test_matmul():
class With_truediv:
def __truediv__(self, other):
- OK()
+ OK() # Call not found
return self
def test_truediv():
@@ -552,7 +552,7 @@ def test_truediv():
class With_floordiv:
def __floordiv__(self, other):
- OK()
+ OK() # Call not found
return self
def test_floordiv():
@@ -563,7 +563,7 @@ def test_floordiv():
class With_mod:
def __mod__(self, other):
- OK()
+ OK() # Call not found
return self
def test_mod():
@@ -574,7 +574,7 @@ def test_mod():
class With_divmod:
def __divmod__(self, other):
- OK()
+ OK() # Call not found
return self
def test_divmod():
@@ -585,7 +585,7 @@ def test_divmod():
class With_pow:
def __pow__(self, other):
- OK()
+ OK() # Call not found
return self
def test_pow():
@@ -600,7 +600,7 @@ def test_pow_op():
class With_lshift:
def __lshift__(self, other):
- OK()
+ OK() # Call not found
return self
def test_lshift():
@@ -611,7 +611,7 @@ def test_lshift():
class With_rshift:
def __rshift__(self, other):
- OK()
+ OK() # Call not found
return self
def test_rshift():
@@ -622,7 +622,7 @@ def test_rshift():
class With_and:
def __and__(self, other):
- OK()
+ OK() # Call not found
return self
def test_and():
@@ -633,7 +633,7 @@ def test_and():
class With_xor:
def __xor__(self, other):
- OK()
+ OK() # Call not found
return self
def test_xor():
@@ -644,7 +644,7 @@ def test_xor():
class With_or:
def __or__(self, other):
- OK()
+ OK() # Call not found
return self
def test_or():
@@ -655,7 +655,7 @@ def test_or():
class With_radd:
def __radd__(self, other):
- OK()
+ OK() # Call not found
return self
def test_radd():
@@ -666,7 +666,7 @@ def test_radd():
class With_rsub:
def __rsub__(self, other):
- OK()
+ OK() # Call not found
return self
def test_rsub():
@@ -677,7 +677,7 @@ def test_rsub():
class With_rmul:
def __rmul__(self, other):
- OK()
+ OK() # Call not found
return self
def test_rmul():
@@ -688,7 +688,7 @@ def test_rmul():
class With_rmatmul:
def __rmatmul__(self, other):
- OK()
+ OK() # Call not found
return self
def test_rmatmul():
@@ -699,7 +699,7 @@ def test_rmatmul():
class With_rtruediv:
def __rtruediv__(self, other):
- OK()
+ OK() # Call not found
return self
def test_rtruediv():
@@ -710,7 +710,7 @@ def test_rtruediv():
class With_rfloordiv:
def __rfloordiv__(self, other):
- OK()
+ OK() # Call not found
return self
def test_rfloordiv():
@@ -721,7 +721,7 @@ def test_rfloordiv():
class With_rmod:
def __rmod__(self, other):
- OK()
+ OK() # Call not found
return self
def test_rmod():
@@ -732,7 +732,7 @@ def test_rmod():
class With_rdivmod:
def __rdivmod__(self, other):
- OK()
+ OK() # Call not found
return self
def test_rdivmod():
@@ -743,7 +743,7 @@ def test_rdivmod():
class With_rpow:
def __rpow__(self, other):
- OK()
+ OK() # Call not found
return self
def test_rpow():
@@ -758,7 +758,7 @@ def test_rpow_op():
class With_rlshift:
def __rlshift__(self, other):
- OK()
+ OK() # Call not found
return self
def test_rlshift():
@@ -769,7 +769,7 @@ def test_rlshift():
class With_rrshift:
def __rrshift__(self, other):
- OK()
+ OK() # Call not found
return self
def test_rrshift():
@@ -780,7 +780,7 @@ def test_rrshift():
class With_rand:
def __rand__(self, other):
- OK()
+ OK() # Call not found
return self
def test_rand():
@@ -791,7 +791,7 @@ def test_rand():
class With_rxor:
def __rxor__(self, other):
- OK()
+ OK() # Call not found
return self
def test_rxor():
@@ -802,7 +802,7 @@ def test_rxor():
class With_ror:
def __ror__(self, other):
- OK()
+ OK() # Call not found
return self
def test_ror():
@@ -813,7 +813,7 @@ def test_ror():
class With_iadd:
def __iadd__(self, other):
- OK()
+ OK() # Call not found
return self
def test_iadd():
@@ -824,7 +824,7 @@ def test_iadd():
class With_isub:
def __isub__(self, other):
- OK()
+ OK() # Call not found
return self
def test_isub():
@@ -835,7 +835,7 @@ def test_isub():
class With_imul:
def __imul__(self, other):
- OK()
+ OK() # Call not found
return self
def test_imul():
@@ -846,7 +846,7 @@ def test_imul():
class With_imatmul:
def __imatmul__(self, other):
- OK()
+ OK() # Call not found
return self
def test_imatmul():
@@ -857,7 +857,7 @@ def test_imatmul():
class With_itruediv:
def __itruediv__(self, other):
- OK()
+ OK() # Call not found
return self
def test_itruediv():
@@ -868,7 +868,7 @@ def test_itruediv():
class With_ifloordiv:
def __ifloordiv__(self, other):
- OK()
+ OK() # Call not found
return self
def test_ifloordiv():
@@ -879,7 +879,7 @@ def test_ifloordiv():
class With_imod:
def __imod__(self, other):
- OK()
+ OK() # Call not found
return self
def test_imod():
@@ -890,7 +890,7 @@ def test_imod():
class With_ipow:
def __ipow__(self, other):
- OK()
+ OK() # Call not found
return self
def test_ipow():
@@ -901,7 +901,7 @@ def test_ipow():
class With_ilshift:
def __ilshift__(self, other):
- OK()
+ OK() # Call not found
return self
def test_ilshift():
@@ -912,7 +912,7 @@ def test_ilshift():
class With_irshift:
def __irshift__(self, other):
- OK()
+ OK() # Call not found
return self
def test_irshift():
@@ -923,7 +923,7 @@ def test_irshift():
class With_iand:
def __iand__(self, other):
- OK()
+ OK() # Call not found
return self
def test_iand():
@@ -934,7 +934,7 @@ def test_iand():
class With_ixor:
def __ixor__(self, other):
- OK()
+ OK() # Call not found
return self
def test_ixor():
@@ -945,7 +945,7 @@ def test_ixor():
class With_ior:
def __ior__(self, other):
- OK()
+ OK() # Call not found
return self
def test_ior():
@@ -956,7 +956,7 @@ def test_ior():
class With_neg:
def __neg__(self):
- OK()
+ OK() # Call not found
return self
def test_neg():
@@ -967,7 +967,7 @@ def test_neg():
class With_pos:
def __pos__(self):
- OK()
+ OK() # Call not found
return self
def test_pos():
@@ -978,7 +978,7 @@ def test_pos():
class With_abs:
def __abs__(self):
- OK()
+ OK() # Call not found
return self
def test_abs():
@@ -989,7 +989,7 @@ def test_abs():
class With_invert:
def __invert__(self):
- OK()
+ OK() # Call not found
return self
def test_invert():
@@ -1000,7 +1000,7 @@ def test_invert():
class With_complex:
def __complex__(self):
- OK()
+ OK() # Call not found
return 0j
def test_complex():
@@ -1011,7 +1011,7 @@ def test_complex():
class With_int:
def __int__(self):
- OK()
+ OK() # Call not found
return 0
def test_int():
@@ -1022,7 +1022,7 @@ def test_int():
class With_float:
def __float__(self):
- OK()
+ OK() # Call not found
return 0.0
def test_float():
@@ -1033,7 +1033,7 @@ def test_float():
class With_index:
def __index__(self):
- OK()
+ OK() # Call not found
return 0
def test_index():
@@ -1073,7 +1073,7 @@ def test_index_complex():
class With_round:
def __round__(self):
- OK()
+ OK() # Call not found
return 0
def test_round():
@@ -1084,7 +1084,7 @@ def test_round():
class With_trunc:
def __trunc__(self):
- OK()
+ OK() # Call not found
return 0
def test_trunc():
@@ -1096,7 +1096,7 @@ def test_trunc():
class With_floor:
def __floor__(self):
- OK()
+ OK() # Call not found
return 0
def test_floor():
@@ -1108,7 +1108,7 @@ def test_floor():
class With_ceil:
def __ceil__(self):
- OK()
+ OK() # Call not found
return 0
def test_ceil():
@@ -1122,7 +1122,7 @@ def test_ceil():
class With_enter:
def __enter__(self):
- OK()
+ OK() # Call not found
return
def __exit__(self, exc_type, exc_value, traceback):
@@ -1139,7 +1139,7 @@ class With_exit:
return
def __exit__(self, exc_type, exc_value, traceback):
- OK()
+ OK() # Call not found
return
def test_exit():
@@ -1153,7 +1153,7 @@ import asyncio
class With_await:
def __await__(self):
- OK()
+ OK() # Call not found
return (yield from asyncio.coroutine(lambda: "")())
async def atest_await():
@@ -1171,7 +1171,7 @@ async def atest_await():
class With_aiter:
def __aiter__(self):
- OK()
+ OK() # Call not found
return self
async def __anext__(self):
@@ -1189,7 +1189,7 @@ class With_anext:
return self
async def __anext__(self):
- OK()
+ OK() # Call not found
raise StopAsyncIteration
async def atest_anext():
@@ -1203,7 +1203,7 @@ async def atest_anext():
class With_aenter:
async def __aenter__(self):
- OK()
+ OK() # Call not found
async def __aexit__(self, exc_type, exc_value, traceback):
pass
@@ -1220,7 +1220,7 @@ class With_aexit:
pass
async def __aexit__(self, exc_type, exc_value, traceback):
- OK()
+ OK() # Call not found
async def atest_aexit():
with_aexit = With_aexit()
From 7ea3fc04c82e405e977e96f0dd979218ce527f31 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Fri, 14 Aug 2020 14:46:39 +0200
Subject: [PATCH 033/146] Python: adjust test annotation (for after feature)
---
.../experimental/dataflow/coverage/classes.py | 28 +++++++++----------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/python/ql/test/experimental/dataflow/coverage/classes.py b/python/ql/test/experimental/dataflow/coverage/classes.py
index 255bacf092a..75abe0c82e2 100644
--- a/python/ql/test/experimental/dataflow/coverage/classes.py
+++ b/python/ql/test/experimental/dataflow/coverage/classes.py
@@ -497,7 +497,7 @@ def test_contains():
class With_add:
def __add__(self, other):
- OK() # Call not found
+ OK()
return self
def test_add():
@@ -508,7 +508,7 @@ def test_add():
class With_sub:
def __sub__(self, other):
- OK() # Call not found
+ OK()
return self
def test_sub():
@@ -519,7 +519,7 @@ def test_sub():
class With_mul:
def __mul__(self, other):
- OK() # Call not found
+ OK()
return self
def test_mul():
@@ -530,7 +530,7 @@ def test_mul():
class With_matmul:
def __matmul__(self, other):
- OK() # Call not found
+ OK()
return self
def test_matmul():
@@ -541,7 +541,7 @@ def test_matmul():
class With_truediv:
def __truediv__(self, other):
- OK() # Call not found
+ OK()
return self
def test_truediv():
@@ -552,7 +552,7 @@ def test_truediv():
class With_floordiv:
def __floordiv__(self, other):
- OK() # Call not found
+ OK()
return self
def test_floordiv():
@@ -563,7 +563,7 @@ def test_floordiv():
class With_mod:
def __mod__(self, other):
- OK() # Call not found
+ OK()
return self
def test_mod():
@@ -585,12 +585,12 @@ def test_divmod():
class With_pow:
def __pow__(self, other):
- OK() # Call not found
+ OK()
return self
def test_pow():
with_pow = With_pow()
- pow(with_pow, with_pow)
+ pow(with_pow, with_pow) # Call not found
def test_pow_op():
with_pow = With_pow()
@@ -600,7 +600,7 @@ def test_pow_op():
class With_lshift:
def __lshift__(self, other):
- OK() # Call not found
+ OK()
return self
def test_lshift():
@@ -611,7 +611,7 @@ def test_lshift():
class With_rshift:
def __rshift__(self, other):
- OK() # Call not found
+ OK()
return self
def test_rshift():
@@ -622,7 +622,7 @@ def test_rshift():
class With_and:
def __and__(self, other):
- OK() # Call not found
+ OK()
return self
def test_and():
@@ -633,7 +633,7 @@ def test_and():
class With_xor:
def __xor__(self, other):
- OK() # Call not found
+ OK()
return self
def test_xor():
@@ -644,7 +644,7 @@ def test_xor():
class With_or:
def __or__(self, other):
- OK() # Call not found
+ OK()
return self
def test_or():
From 67fccac8a97f7902504eb079fa22a2a1c90cf8e6 Mon Sep 17 00:00:00 2001
From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 16 Aug 2020 14:13:03 +0200
Subject: [PATCH 034/146] Update
javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
Co-authored-by: Esben Sparre Andreasen
---
.../ql/src/experimental/Security/CWE-614/InsecureCookie.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
index 82deffa60ca..01ff6ad34b2 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
@@ -16,7 +16,7 @@ module InsecureCookie {
*/
abstract class InsecureCookies extends DataFlow::Node {
/**
- * The name of the middleware/library used to set the cookie.
+ * Gets the name of the middleware/library used to set the cookie.
*/
abstract string getKind();
From 0c121062b6b2e6ceafff652403b8dbb0b58fd071 Mon Sep 17 00:00:00 2001
From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 16 Aug 2020 14:13:54 +0200
Subject: [PATCH 035/146] Update
javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
Co-authored-by: Esben Sparre Andreasen
---
.../ql/src/experimental/Security/CWE-614/InsecureCookie.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
index 01ff6ad34b2..ad56e75186c 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
@@ -21,7 +21,7 @@ module InsecureCookie {
abstract string getKind();
/**
- * The `cookie` options.
+ * Gets the options used to set this cookie, if any.
*/
abstract DataFlow::Node getCookieOptionsArgument();
From 8d26b810eee5f8e2340998ea5a35fca33fb8d382 Mon Sep 17 00:00:00 2001
From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 16 Aug 2020 14:17:16 +0200
Subject: [PATCH 036/146] Update
javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
Co-authored-by: Esben Sparre Andreasen
---
.../ql/src/experimental/Security/CWE-614/InsecureCookie.qll | 1 -
1 file changed, 1 deletion(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
index ad56e75186c..5f47f636dc7 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
@@ -33,7 +33,6 @@ module InsecureCookie {
/**
* A cookie set using the `express` module `cookie-session` (https://github.com/expressjs/cookie-session).
- * The flag `secure` is set to `false` by default for HTTP, `true` by default for HTTPS (https://github.com/expressjs/cookie-session#cookie-options).
*/
class InsecureCookieSession extends ExpressLibraries::CookieSession::MiddlewareInstance,
InsecureCookies {
From 10bd745740e76ddb09fec19429f2e900d9680dda Mon Sep 17 00:00:00 2001
From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 16 Aug 2020 14:18:54 +0200
Subject: [PATCH 037/146] Update
javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
Co-authored-by: Esben Sparre Andreasen
---
.../ql/src/experimental/Security/CWE-614/InsecureCookie.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
index 5f47f636dc7..85d36daa6bd 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
@@ -41,7 +41,7 @@ module InsecureCookie {
override string getKind() { result = "cookie-session" }
override DataFlow::SourceNode getCookieOptionsArgument() {
- result = this.getOption("cookie").(DataFlow::SourceNode)
+ result = this.getOption("cookie")
}
DataFlow::Node getCookieFlagValue(string flag) {
From 5cae3005f39b73ec67363eae15f1119b27ceb917 Mon Sep 17 00:00:00 2001
From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 16 Aug 2020 14:20:22 +0200
Subject: [PATCH 038/146] Update
javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
Co-authored-by: Esben Sparre Andreasen
---
.../ql/src/experimental/Security/CWE-614/InsecureCookie.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
index 85d36daa6bd..f5abeb1aab4 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
@@ -44,7 +44,7 @@ module InsecureCookie {
result = this.getOption("cookie")
}
- DataFlow::Node getCookieFlagValue(string flag) {
+ private DataFlow::Node getCookieFlagValue(string flag) {
result = this.getCookieOptionsArgument().getAPropertyWrite(flag).getRhs()
}
From e46301475915102e52443e9a67abbb3ba617fd01 Mon Sep 17 00:00:00 2001
From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 16 Aug 2020 14:21:56 +0200
Subject: [PATCH 039/146] Update
javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
Co-authored-by: Esben Sparre Andreasen
---
.../ql/src/experimental/Security/CWE-614/InsecureCookie.qll | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
index f5abeb1aab4..6c3188208e4 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
@@ -48,8 +48,10 @@ module InsecureCookie {
result = this.getCookieOptionsArgument().getAPropertyWrite(flag).getRhs()
}
- // A cookie is insecure if the `secure` flag is explicitly set to `false`.
- override predicate isInsecure() { getCookieFlagValue(flag()).mayHaveBooleanValue(false) }
+ override predicate isInsecure() {
+ // A cookie is insecure if the `secure` flag is explicitly set to `false`.
+ getCookieFlagValue(flag()).mayHaveBooleanValue(false)
+ }
}
/**
From fb3ffb895a841e55693804ed34032b99eb54d695 Mon Sep 17 00:00:00 2001
From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 16 Aug 2020 14:23:17 +0200
Subject: [PATCH 040/146] Update
javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
Co-authored-by: Esben Sparre Andreasen
---
.../ql/src/experimental/Security/CWE-614/InsecureCookie.qll | 2 --
1 file changed, 2 deletions(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
index 6c3188208e4..23a6f83cb18 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
@@ -56,8 +56,6 @@ module InsecureCookie {
/**
* A cookie set using the `express` module `express-session` (https://github.com/expressjs/session).
- * The flag `secure` is not set by default (https://github.com/expressjs/session#cookiesecure).
- * The default value for cookie options is { path: '/', httpOnly: true, secure: false, maxAge: null }.
*/
class InsecureExpressSessionCookie extends ExpressLibraries::ExpressSession::MiddlewareInstance,
InsecureCookies {
From 97f039af3adc79c220a5fff032d84c4d360be5e2 Mon Sep 17 00:00:00 2001
From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 16 Aug 2020 14:25:11 +0200
Subject: [PATCH 041/146] Update
javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
Co-authored-by: Esben Sparre Andreasen
---
.../ql/src/experimental/Security/CWE-614/InsecureCookie.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
index 23a6f83cb18..71be1311cc5 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
@@ -62,7 +62,7 @@ module InsecureCookie {
override string getKind() { result = "express-session" }
override DataFlow::SourceNode getCookieOptionsArgument() {
- result = this.getOption("cookie").(DataFlow::SourceNode)
+ result = this.getOption("cookie")
}
DataFlow::Node getCookieFlagValue(string flag) {
From 40e101de5af10c36853484fa895728f382bd0948 Mon Sep 17 00:00:00 2001
From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 16 Aug 2020 14:26:15 +0200
Subject: [PATCH 042/146] Update
javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
Co-authored-by: Esben Sparre Andreasen
---
.../ql/src/experimental/Security/CWE-614/InsecureCookie.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
index 71be1311cc5..7c9168f02cc 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
@@ -65,7 +65,7 @@ module InsecureCookie {
result = this.getOption("cookie")
}
- DataFlow::Node getCookieFlagValue(string flag) {
+ private DataFlow::Node getCookieFlagValue(string flag) {
result = this.getCookieOptionsArgument().getAPropertyWrite(flag).getRhs()
}
From ab128f71728a353d832c13f9a4e96e267febf3cf Mon Sep 17 00:00:00 2001
From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 16 Aug 2020 14:27:26 +0200
Subject: [PATCH 043/146] Update
javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
Co-authored-by: Esben Sparre Andreasen
---
.../ql/src/experimental/Security/CWE-614/InsecureCookie.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
index 7c9168f02cc..e4aca8fac32 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
@@ -69,8 +69,8 @@ module InsecureCookie {
result = this.getCookieOptionsArgument().getAPropertyWrite(flag).getRhs()
}
- // A cookie is insecure if there are not cookie options with the `secure` flag set to `true`.
override predicate isInsecure() {
+ // A cookie is insecure if there are not cookie options with the `secure` flag set to `true`.
not exists(DataFlow::SourceNode cookieOptions |
cookieOptions = this.getCookieOptionsArgument() and
getCookieFlagValue(flag()).mayHaveBooleanValue(true)
From 9292e3b80e0e1c81cd30da93d0772994a54e738b Mon Sep 17 00:00:00 2001
From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 16 Aug 2020 14:28:39 +0200
Subject: [PATCH 044/146] Update
javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
Co-authored-by: Esben Sparre Andreasen
---
.../ql/src/experimental/Security/CWE-614/InsecureCookie.qll | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
index e4aca8fac32..e4edf7351f1 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
@@ -71,10 +71,7 @@ module InsecureCookie {
override predicate isInsecure() {
// A cookie is insecure if there are not cookie options with the `secure` flag set to `true`.
- not exists(DataFlow::SourceNode cookieOptions |
- cookieOptions = this.getCookieOptionsArgument() and
- getCookieFlagValue(flag()).mayHaveBooleanValue(true)
- ) and
+ not getCookieFlagValue(flag()).mayHaveBooleanValue(true) and
not getCookieFlagValue(flag()).mayHaveStringValue("auto")
}
}
From 275b8dfda2e16047b31251c7317f2442df0cc630 Mon Sep 17 00:00:00 2001
From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 16 Aug 2020 14:29:36 +0200
Subject: [PATCH 045/146] Update
javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
Co-authored-by: Esben Sparre Andreasen
---
.../ql/src/experimental/Security/CWE-614/InsecureCookie.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
index e4edf7351f1..b84a2500f5c 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
@@ -81,7 +81,7 @@ module InsecureCookie {
*/
class InsecureExpressCookieResponse extends InsecureCookies {
InsecureExpressCookieResponse() {
- this = any(Express::ResponseExpr response).flow().getALocalSource().getAMemberCall("cookie")
+ this = any(Express::ResponseExpr response).flow().getALocalSource().getAMethodCall("cookie")
}
override string getKind() { result = "response.cookie" }
From 14c8e4ce768fc48c5578084b01c731537ce5e4d3 Mon Sep 17 00:00:00 2001
From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 16 Aug 2020 14:30:45 +0200
Subject: [PATCH 046/146] Update
javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
Co-authored-by: Esben Sparre Andreasen
---
.../ql/src/experimental/Security/CWE-614/InsecureCookie.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
index b84a2500f5c..3ca117df3c0 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
@@ -90,7 +90,7 @@ module InsecureCookie {
result = this.(DataFlow::InvokeNode).getLastArgument().getALocalSource()
}
- DataFlow::Node getCookieFlagValue(string flag) {
+ private DataFlow::Node getCookieFlagValue(string flag) {
result = this.getCookieOptionsArgument().getAPropertyWrite(flag).getRhs()
}
From a2e945645022db47e7c152a97d46f6bdedba1ce1 Mon Sep 17 00:00:00 2001
From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 16 Aug 2020 14:31:21 +0200
Subject: [PATCH 047/146] Update
javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
Co-authored-by: Esben Sparre Andreasen
---
.../ql/src/experimental/Security/CWE-614/InsecureCookie.qll | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
index 3ca117df3c0..0a9170fabf8 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
@@ -94,8 +94,9 @@ module InsecureCookie {
result = this.getCookieOptionsArgument().getAPropertyWrite(flag).getRhs()
}
- // A cookie is insecure if there are not cookie options with the `secure` flag set to `true`.
override predicate isInsecure() {
+ // A cookie is insecure if there are not cookie options with the `secure` flag set to `true`.
+
not exists(DataFlow::SourceNode cookieOptions |
cookieOptions = this.getCookieOptionsArgument() and
getCookieFlagValue(flag()).mayHaveBooleanValue(true)
From bfef84e1b57983ef7fc950ba72af6a737d3fad81 Mon Sep 17 00:00:00 2001
From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 16 Aug 2020 14:32:05 +0200
Subject: [PATCH 048/146] Update
javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
Co-authored-by: Esben Sparre Andreasen
---
.../ql/src/experimental/Security/CWE-614/InsecureCookie.qll | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
index 0a9170fabf8..36dfd34f1e6 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
@@ -97,10 +97,7 @@ module InsecureCookie {
override predicate isInsecure() {
// A cookie is insecure if there are not cookie options with the `secure` flag set to `true`.
- not exists(DataFlow::SourceNode cookieOptions |
- cookieOptions = this.getCookieOptionsArgument() and
- getCookieFlagValue(flag()).mayHaveBooleanValue(true)
- )
+ not getCookieFlagValue(flag()).mayHaveBooleanValue(true)
}
}
From ab20beba565c5de0ac2a2d9dc09eecd82b3d0e77 Mon Sep 17 00:00:00 2001
From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 16 Aug 2020 14:32:51 +0200
Subject: [PATCH 049/146] Update
javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
Co-authored-by: Esben Sparre Andreasen
---
.../ql/src/experimental/Security/CWE-614/InsecureCookie.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
index 36dfd34f1e6..79b604f5d63 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
@@ -115,8 +115,8 @@ module InsecureCookie {
result.asExpr() = this.asExpr().(ArrayExpr).getAnElement()
}
- // A cookie is insecure if the 'secure' flag is not specified in the cookie definition.
override predicate isInsecure() {
+ // A cookie is insecure if the 'secure' flag is not specified in the cookie definition.
not exists(string s |
getCookieOptionsArgument().mayHaveStringValue(s) and
s.matches("%; secure%")
From 05ffd672d70635d2b07330996d814f14ee005945 Mon Sep 17 00:00:00 2001
From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 16 Aug 2020 14:33:38 +0200
Subject: [PATCH 050/146] Update
javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
Co-authored-by: Esben Sparre Andreasen
---
.../ql/src/experimental/Security/CWE-614/InsecureCookie.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
index 79b604f5d63..389137f17a3 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
@@ -144,8 +144,8 @@ module InsecureCookie {
result = this.getCookieOptionsArgument().getAPropertyWrite(flag).getRhs()
}
- // A cookie is insecure if there are not cookie options with the `secure` flag set to `true`.
override predicate isInsecure() {
+ // A cookie is insecure if there are not cookie options with the `secure` flag set to `true`.
not exists(DataFlow::SourceNode cookieOptions |
cookieOptions = this.getCookieOptionsArgument() and
getCookieFlagValue(flag()).mayHaveBooleanValue(true)
From 1ba39e4130e6d1fc190f4e11d19161ad8f638e72 Mon Sep 17 00:00:00 2001
From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 16 Aug 2020 14:34:19 +0200
Subject: [PATCH 051/146] Update
javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
Co-authored-by: Esben Sparre Andreasen
---
.../ql/src/experimental/Security/CWE-614/InsecureCookie.qll | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
index 389137f17a3..31f8dc9e93b 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
@@ -146,10 +146,7 @@ module InsecureCookie {
override predicate isInsecure() {
// A cookie is insecure if there are not cookie options with the `secure` flag set to `true`.
- not exists(DataFlow::SourceNode cookieOptions |
- cookieOptions = this.getCookieOptionsArgument() and
- getCookieFlagValue(flag()).mayHaveBooleanValue(true)
- )
+ not getCookieFlagValue(flag()).mayHaveBooleanValue(true)
}
}
}
From e2908026c556bb2ec89c25b4c21d0f51bd45410d Mon Sep 17 00:00:00 2001
From: ubuntu <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 16 Aug 2020 14:41:55 +0200
Subject: [PATCH 052/146] Remove redundancy
---
.../Security/CWE-614/InsecureCookie.qll | 27 +++++++------------
1 file changed, 10 insertions(+), 17 deletions(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
index 31f8dc9e93b..fa7a149b518 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
@@ -36,21 +36,17 @@ module InsecureCookie {
*/
class InsecureCookieSession extends ExpressLibraries::CookieSession::MiddlewareInstance,
InsecureCookies {
- InsecureCookieSession() { this instanceof ExpressLibraries::CookieSession::MiddlewareInstance }
-
override string getKind() { result = "cookie-session" }
- override DataFlow::SourceNode getCookieOptionsArgument() {
- result = this.getOption("cookie")
- }
+ override DataFlow::SourceNode getCookieOptionsArgument() { result = this.getOption("cookie") }
private DataFlow::Node getCookieFlagValue(string flag) {
result = this.getCookieOptionsArgument().getAPropertyWrite(flag).getRhs()
}
- override predicate isInsecure() {
- // A cookie is insecure if the `secure` flag is explicitly set to `false`.
- getCookieFlagValue(flag()).mayHaveBooleanValue(false)
+ override predicate isInsecure() {
+ // A cookie is insecure if the `secure` flag is explicitly set to `false`.
+ getCookieFlagValue(flag()).mayHaveBooleanValue(false)
}
}
@@ -61,16 +57,14 @@ module InsecureCookie {
InsecureCookies {
override string getKind() { result = "express-session" }
- override DataFlow::SourceNode getCookieOptionsArgument() {
- result = this.getOption("cookie")
- }
+ override DataFlow::SourceNode getCookieOptionsArgument() { result = this.getOption("cookie") }
private DataFlow::Node getCookieFlagValue(string flag) {
result = this.getCookieOptionsArgument().getAPropertyWrite(flag).getRhs()
}
override predicate isInsecure() {
- // A cookie is insecure if there are not cookie options with the `secure` flag set to `true`.
+ // A cookie is insecure if there are not cookie options with the `secure` flag set to `true`.
not getCookieFlagValue(flag()).mayHaveBooleanValue(true) and
not getCookieFlagValue(flag()).mayHaveStringValue("auto")
}
@@ -95,9 +89,8 @@ module InsecureCookie {
}
override predicate isInsecure() {
- // A cookie is insecure if there are not cookie options with the `secure` flag set to `true`.
-
- not getCookieFlagValue(flag()).mayHaveBooleanValue(true)
+ // A cookie is insecure if there are not cookie options with the `secure` flag set to `true`.
+ not getCookieFlagValue(flag()).mayHaveBooleanValue(true)
}
}
@@ -116,7 +109,7 @@ module InsecureCookie {
}
override predicate isInsecure() {
- // A cookie is insecure if the 'secure' flag is not specified in the cookie definition.
+ // A cookie is insecure if the 'secure' flag is not specified in the cookie definition.
not exists(string s |
getCookieOptionsArgument().mayHaveStringValue(s) and
s.matches("%; secure%")
@@ -145,7 +138,7 @@ module InsecureCookie {
}
override predicate isInsecure() {
- // A cookie is insecure if there are not cookie options with the `secure` flag set to `true`.
+ // A cookie is insecure if there are not cookie options with the `secure` flag set to `true`.
not getCookieFlagValue(flag()).mayHaveBooleanValue(true)
}
}
From d4b231b86796ba2ed1572aaf9a07b082832ce357 Mon Sep 17 00:00:00 2001
From: ubuntu <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 16 Aug 2020 14:48:26 +0200
Subject: [PATCH 053/146] Replace regex
---
.../ql/src/experimental/Security/CWE-614/InsecureCookie.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
index fa7a149b518..72d1c1bb951 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
@@ -112,7 +112,7 @@ module InsecureCookie {
// A cookie is insecure if the 'secure' flag is not specified in the cookie definition.
not exists(string s |
getCookieOptionsArgument().mayHaveStringValue(s) and
- s.matches("%; secure%")
+ s.regexpMatch("(.*;)?\\s*secure.*")
)
}
}
From 91d44854c07c42a666e7996fae85618bb4241f05 Mon Sep 17 00:00:00 2001
From: ubuntu <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 16 Aug 2020 14:53:31 +0200
Subject: [PATCH 054/146] Replace class and module name
---
.../Security/CWE-614/InsecureCookie.ql | 4 ++--
.../Security/CWE-614/InsecureCookie.qll | 15 +++++++--------
2 files changed, 9 insertions(+), 10 deletions(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.ql b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.ql
index 228a38d25fa..e45777405f2 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.ql
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.ql
@@ -11,9 +11,9 @@
*/
import javascript
-import InsecureCookie::InsecureCookie
+import InsecureCookie::Cookie
-from InsecureCookies insecureCookies
+from Cookie insecureCookies
where insecureCookies.isInsecure()
select "Cookie is added to response without the 'secure' flag being set to true (using " +
insecureCookies.getKind() + ").", insecureCookies
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
index 72d1c1bb951..3bedfbcea40 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
@@ -5,7 +5,7 @@
import javascript
-module InsecureCookie {
+module Cookie {
/**
* `secure` property of the cookie options.
*/
@@ -14,7 +14,7 @@ module InsecureCookie {
/**
* Abstract class to represent different cases of insecure cookie settings.
*/
- abstract class InsecureCookies extends DataFlow::Node {
+ abstract class Cookie extends DataFlow::Node {
/**
* Gets the name of the middleware/library used to set the cookie.
*/
@@ -34,8 +34,7 @@ module InsecureCookie {
/**
* A cookie set using the `express` module `cookie-session` (https://github.com/expressjs/cookie-session).
*/
- class InsecureCookieSession extends ExpressLibraries::CookieSession::MiddlewareInstance,
- InsecureCookies {
+ class InsecureCookieSession extends ExpressLibraries::CookieSession::MiddlewareInstance, Cookie {
override string getKind() { result = "cookie-session" }
override DataFlow::SourceNode getCookieOptionsArgument() { result = this.getOption("cookie") }
@@ -54,7 +53,7 @@ module InsecureCookie {
* A cookie set using the `express` module `express-session` (https://github.com/expressjs/session).
*/
class InsecureExpressSessionCookie extends ExpressLibraries::ExpressSession::MiddlewareInstance,
- InsecureCookies {
+ Cookie {
override string getKind() { result = "express-session" }
override DataFlow::SourceNode getCookieOptionsArgument() { result = this.getOption("cookie") }
@@ -73,7 +72,7 @@ module InsecureCookie {
/**
* A cookie set using `response.cookie` from `express` module (https://expressjs.com/en/api.html#res.cookie).
*/
- class InsecureExpressCookieResponse extends InsecureCookies {
+ class InsecureExpressCookieResponse extends Cookie {
InsecureExpressCookieResponse() {
this = any(Express::ResponseExpr response).flow().getALocalSource().getAMethodCall("cookie")
}
@@ -97,7 +96,7 @@ module InsecureCookie {
/**
* A cookie set using `Set-Cookie` header of an `HTTP` response (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie).
*/
- class InsecureSetCookieHeader extends InsecureCookies {
+ class InsecureSetCookieHeader extends Cookie {
InsecureSetCookieHeader() {
this.asExpr() = any(HTTP::SetCookieHeader setCookie).getHeaderArgument()
}
@@ -120,7 +119,7 @@ module InsecureCookie {
/**
* A cookie set using `js-cookie` library (https://github.com/js-cookie/js-cookie).
*/
- class InsecureJsCookie extends InsecureCookies {
+ class InsecureJsCookie extends Cookie {
InsecureJsCookie() {
this = DataFlow::globalVarRef("Cookie").getAMemberCall("set") or
this = DataFlow::globalVarRef("Cookie").getAMemberCall("noConflict").getAMemberCall("set") or
From 2a322976c62a45a9f5f68337bb25ab395236288d Mon Sep 17 00:00:00 2001
From: ubuntu <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 16 Aug 2020 14:57:04 +0200
Subject: [PATCH 055/146] Changed .qhelp
---
.../Security/CWE-614/InsecureCookie.qhelp | 58 -------------------
1 file changed, 58 deletions(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qhelp b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qhelp
index cff9e878e59..06e920b44bf 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qhelp
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qhelp
@@ -12,64 +12,6 @@ This makes it easier for an attacker to intercept.
to an HTTP response (if the default value is `false`).
-
-
-In the first example the `secure` flag is set to `false` using the express middleware `cookie-session`.
-In the second example the `secure` flag is set to `true` (it is set `false` by default for HTTP, `true` by default for HTTPS).
-
-
-
-
-
-
-
-
-The first four examples show four ways of adding a cookie using the express middleware `express-session`.
-Since the default value for the flag `secure` is false, each example shows a possible scenario where a cookie is set with
-the `secure` to `false`.
-In the last example the `secure` flag is set to `true`.
-
-
-
-
-
-
-
-
-
-
-
-The first two examples show two ways of adding a cookie using the method `response.cookie`.
-In both cases the `secure` flag is to `false`.
-In the last example the `secure` flag is set to `true`.
-
-
-
-
-
-
-
-
-
-
-The first example shows when the `secure` flag is set using the method `Set-Cookie` header of an `HTTP` response.
-In this case the `secure` flag is not set.
-In the last example the `secure` flag is set.
-
-
-
-
-
-
-
-
-In the first example the `secure` flag is set to `false` using the `js-cookie` library.
-In the second example the `secure` flag is set to `true`.
-
-
-
-
-
From 3e9142bf7187c6ecfff8182c78c443ca0840784c Mon Sep 17 00:00:00 2001
From: ubuntu <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 16 Aug 2020 14:58:37 +0200
Subject: [PATCH 056/146] Remove examples
---
.../CWE-614/examples/cookie-session_bad.js | 17 -----------------
.../CWE-614/examples/cookie-session_good.js | 17 -----------------
.../examples/express-session_bad1_false.js | 7 -------
.../examples/express-session_bad2_notSet.js | 7 -------
.../examples/express-session_bad3_setEmpty.js | 7 -------
.../CWE-614/examples/express-session_bad4.js | 9 ---------
.../CWE-614/examples/express-session_good.js | 9 ---------
.../examples/express_response-cookie_bad1.js | 12 ------------
.../examples/express_response-cookie_bad2.js | 12 ------------
.../examples/express_response-cookie_good1.js | 12 ------------
.../Security/CWE-614/examples/httpserver_bad.js | 8 --------
.../CWE-614/examples/httpserver_good.js | 8 --------
.../Security/CWE-614/examples/jsCookie_bad.js | 2 --
.../Security/CWE-614/examples/jsCookie_good.js | 2 --
14 files changed, 129 deletions(-)
delete mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/cookie-session_bad.js
delete mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/cookie-session_good.js
delete mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad1_false.js
delete mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad2_notSet.js
delete mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad3_setEmpty.js
delete mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad4.js
delete mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/express-session_good.js
delete mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/express_response-cookie_bad1.js
delete mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/express_response-cookie_bad2.js
delete mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/express_response-cookie_good1.js
delete mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/httpserver_bad.js
delete mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/httpserver_good.js
delete mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/jsCookie_bad.js
delete mode 100644 javascript/ql/src/experimental/Security/CWE-614/examples/jsCookie_good.js
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/cookie-session_bad.js b/javascript/ql/src/experimental/Security/CWE-614/examples/cookie-session_bad.js
deleted file mode 100644
index 009ef60fde3..00000000000
--- a/javascript/ql/src/experimental/Security/CWE-614/examples/cookie-session_bad.js
+++ /dev/null
@@ -1,17 +0,0 @@
-const session = require('cookie-session')
-const express = require('express')
-const app = express()
-
-const expiryDate = new Date(Date.now() + 60 * 60 * 1000)
-
-app.use(session({
- name: 'session',
- keys: ['key1', 'key2'],
- cookie: {
- secure: false, // BAD
- httpOnly: true,
- domain: 'example.com',
- path: 'foo/bar',
- expires: expiryDate
- }
-}))
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/cookie-session_good.js b/javascript/ql/src/experimental/Security/CWE-614/examples/cookie-session_good.js
deleted file mode 100644
index b16458d2edf..00000000000
--- a/javascript/ql/src/experimental/Security/CWE-614/examples/cookie-session_good.js
+++ /dev/null
@@ -1,17 +0,0 @@
-const session = require('cookie-session')
-const express = require('express')
-const app = express()
-
-const expiryDate = new Date(Date.now() + 60 * 60 * 1000)
-
-app.use(session({
- name: 'session',
- keys: ['key1', 'key2'],
- cookie: {
- secure: true, // GOOD: false by default for HTTP, true by default for HTTPS
- httpOnly: true,
- domain: 'example.com',
- path: 'foo/bar',
- expires: expiryDate
- }
-}))
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad1_false.js b/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad1_false.js
deleted file mode 100644
index 02868dee748..00000000000
--- a/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad1_false.js
+++ /dev/null
@@ -1,7 +0,0 @@
-const app = express()
-const session = require('express-session')
-
-app.use(session({
- secret: 'secret',
- cookie: { secure: false } // BAD
-}))
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad2_notSet.js b/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad2_notSet.js
deleted file mode 100644
index d7b1841b542..00000000000
--- a/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad2_notSet.js
+++ /dev/null
@@ -1,7 +0,0 @@
-const app = express()
-const session = require('express-session')
-
-app.use(session({
- secret: 'secret'
- // BAD: in this case the default value of `secure` flag is `false`
-}))
\ No newline at end of file
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad3_setEmpty.js b/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad3_setEmpty.js
deleted file mode 100644
index 0ec001ec4fb..00000000000
--- a/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad3_setEmpty.js
+++ /dev/null
@@ -1,7 +0,0 @@
-const app = express()
-const session = require('express-session')
-
-app.use(session({
- secret: 'secret',
- cookie: {} // BAD: in this case the default value of `secure` flag is `false`
-}))
\ No newline at end of file
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad4.js b/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad4.js
deleted file mode 100644
index 0b0f5cd328d..00000000000
--- a/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_bad4.js
+++ /dev/null
@@ -1,9 +0,0 @@
-const app = express()
-const session = require('express-session')
-
-const sess = {
- secret: 'secret',
- cookie: { secure: false } // BAD
-}
-
-app.use(session(sess))
\ No newline at end of file
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_good.js b/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_good.js
deleted file mode 100644
index ae2a7f41f55..00000000000
--- a/javascript/ql/src/experimental/Security/CWE-614/examples/express-session_good.js
+++ /dev/null
@@ -1,9 +0,0 @@
-const app = express()
-const session = require('express-session')
-
-app.set('trust proxy', 1)
-
-app.use(session({
- secret: 'secret',
- cookie: { secure: true } // GOOD
-}))
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/express_response-cookie_bad1.js b/javascript/ql/src/experimental/Security/CWE-614/examples/express_response-cookie_bad1.js
deleted file mode 100644
index 1a762c27a2c..00000000000
--- a/javascript/ql/src/experimental/Security/CWE-614/examples/express_response-cookie_bad1.js
+++ /dev/null
@@ -1,12 +0,0 @@
-const express = require('express')
-const app = express()
-
-app.get('/', function (req, res, next) {
- res.cookie('name', 'value',
- {
- maxAge: 9000000000,
- httpOnly: true,
- secure: false // BAD
- });
- res.end('ok')
-})
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/express_response-cookie_bad2.js b/javascript/ql/src/experimental/Security/CWE-614/examples/express_response-cookie_bad2.js
deleted file mode 100644
index ba67049f167..00000000000
--- a/javascript/ql/src/experimental/Security/CWE-614/examples/express_response-cookie_bad2.js
+++ /dev/null
@@ -1,12 +0,0 @@
-const express = require('express')
-const app = express()
-
-app.get('/', function (req, res, next) {
- let options = {
- maxAge: 9000000000,
- httpOnly: true,
- secure: false // BAD
- }
- res.cookie('name', 'value', options);
- res.end('ok')
-})
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/express_response-cookie_good1.js b/javascript/ql/src/experimental/Security/CWE-614/examples/express_response-cookie_good1.js
deleted file mode 100644
index ee5fe440b93..00000000000
--- a/javascript/ql/src/experimental/Security/CWE-614/examples/express_response-cookie_good1.js
+++ /dev/null
@@ -1,12 +0,0 @@
-const express = require('express')
-const app = express()
-
-app.get('/', function (req, res, next) {
- res.cookie('name', 'value',
- {
- maxAge: 9000000000,
- httpOnly: true,
- secure: true // GOOD
- });
- res.end('ok')
-})
\ No newline at end of file
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/httpserver_bad.js b/javascript/ql/src/experimental/Security/CWE-614/examples/httpserver_bad.js
deleted file mode 100644
index dfd2c4bb409..00000000000
--- a/javascript/ql/src/experimental/Security/CWE-614/examples/httpserver_bad.js
+++ /dev/null
@@ -1,8 +0,0 @@
-const http = require('http');
-const server = http.createServer((req, res) => {
- res.setHeader('Content-Type', 'text/html');
- // BAD
- res.setHeader("Set-Cookie", ["type=ninja", "language=javascript"]);
- res.writeHead(200, { 'Content-Type': 'text/plain' });
- res.end('ok');
-});
\ No newline at end of file
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/httpserver_good.js b/javascript/ql/src/experimental/Security/CWE-614/examples/httpserver_good.js
deleted file mode 100644
index 8f607aaf972..00000000000
--- a/javascript/ql/src/experimental/Security/CWE-614/examples/httpserver_good.js
+++ /dev/null
@@ -1,8 +0,0 @@
-const http = require('http');
-const server = http.createServer((req, res) => {
- res.setHeader('Content-Type', 'text/html');
- // GOOD
- res.setHeader("Set-Cookie", ["type=ninja; Secure", "language=javascript; secure"]);
- res.writeHead(200, { 'Content-Type': 'text/plain' });
- res.end('ok');
-});
\ No newline at end of file
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/jsCookie_bad.js b/javascript/ql/src/experimental/Security/CWE-614/examples/jsCookie_bad.js
deleted file mode 100644
index 6ab10c64e26..00000000000
--- a/javascript/ql/src/experimental/Security/CWE-614/examples/jsCookie_bad.js
+++ /dev/null
@@ -1,2 +0,0 @@
-const js_cookie = require('js-cookie')
-js_cookie.set('key', 'value', { secure: false }); // BAD
diff --git a/javascript/ql/src/experimental/Security/CWE-614/examples/jsCookie_good.js b/javascript/ql/src/experimental/Security/CWE-614/examples/jsCookie_good.js
deleted file mode 100644
index b18c476b2f5..00000000000
--- a/javascript/ql/src/experimental/Security/CWE-614/examples/jsCookie_good.js
+++ /dev/null
@@ -1,2 +0,0 @@
-const js_cookie = require('js-cookie')
-js_cookie.set('key', 'value', { secure: true });
From 5d6e6be4e45033c5e90770ba489350417085bc1e Mon Sep 17 00:00:00 2001
From: ubuntu <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 16 Aug 2020 15:02:52 +0200
Subject: [PATCH 057/146] Add query-tests
---
.../Security/CWE-614/InsecureCookies.expected | 9 +++++
.../Security/CWE-614/InsecureCookies.qlref | 1 +
.../Security/CWE-614/test_cookie-session.js | 28 ++++++++++++++++
.../Security/CWE-614/test_express-session.js | 33 +++++++++++++++++++
.../Security/CWE-614/test_httpserver.js | 22 +++++++++++++
.../Security/CWE-614/test_jscookie.js | 3 ++
.../Security/CWE-614/test_responseCookie.js | 33 +++++++++++++++++++
7 files changed, 129 insertions(+)
create mode 100644 javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.expected
create mode 100644 javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.qlref
create mode 100644 javascript/ql/test/query-tests/Security/CWE-614/test_cookie-session.js
create mode 100644 javascript/ql/test/query-tests/Security/CWE-614/test_express-session.js
create mode 100644 javascript/ql/test/query-tests/Security/CWE-614/test_httpserver.js
create mode 100644 javascript/ql/test/query-tests/Security/CWE-614/test_jscookie.js
create mode 100644 javascript/ql/test/query-tests/Security/CWE-614/test_responseCookie.js
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.expected b/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.expected
new file mode 100644
index 00000000000..de25e9b58ed
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.expected
@@ -0,0 +1,9 @@
+| Cookie is added to response without the 'secure' flag being set to true (using cookie-session). | test_cookie-session.js:18:9:28:2 | session ... }\\n}) |
+| Cookie is added to response without the 'secure' flag being set to true (using express-session). | test_express-session.js:5:9:8:2 | session ... T OK\\n}) |
+| Cookie is added to response without the 'secure' flag being set to true (using express-session). | test_express-session.js:10:9:13:2 | session ... T OK\\n}) |
+| Cookie is added to response without the 'secure' flag being set to true (using express-session). | test_express-session.js:15:9:18:2 | session ... T OK\\n}) |
+| Cookie is added to response without the 'secure' flag being set to true (using express-session). | test_express-session.js:25:9:25:21 | session(sess) |
+| Cookie is added to response without the 'secure' flag being set to true (using js-cookie). | test_jscookie.js:2:1:2:48 | js_cook ... alse }) |
+| Cookie is added to response without the 'secure' flag being set to true (using response.cookie). | test_responseCookie.js:5:5:10:10 | res.coo ... }) |
+| Cookie is added to response without the 'secure' flag being set to true (using response.cookie). | test_responseCookie.js:20:5:20:40 | res.coo ... ptions) |
+| Cookie is added to response without the 'secure' flag being set to true (using set-cookie header). | test_httpserver.js:7:37:7:73 | ["type= ... cript"] |
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.qlref b/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.qlref
new file mode 100644
index 00000000000..378d5dcae1a
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE-614/InsecureCookie.ql
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/test_cookie-session.js b/javascript/ql/test/query-tests/Security/CWE-614/test_cookie-session.js
new file mode 100644
index 00000000000..d8d80b0059e
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-614/test_cookie-session.js
@@ -0,0 +1,28 @@
+const express = require('express')
+const app = express()
+const session = require('cookie-session')
+const expiryDate = new Date(Date.now() + 60 * 60 * 1000)
+
+app.use(session({
+ name: 'session',
+ keys: ['key1', 'key2'],
+ cookie: {
+ secure: true, // OK
+ httpOnly: true,
+ domain: 'example.com',
+ path: 'foo/bar',
+ expires: expiryDate
+ }
+}))
+
+app.use(session({
+ name: 'session',
+ keys: ['key1', 'key2'],
+ cookie: {
+ secure: false, // NOT OK
+ httpOnly: true,
+ domain: 'example.com',
+ path: 'foo/bar',
+ expires: expiryDate
+ }
+}))
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/test_express-session.js b/javascript/ql/test/query-tests/Security/CWE-614/test_express-session.js
new file mode 100644
index 00000000000..873fe2162ca
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-614/test_express-session.js
@@ -0,0 +1,33 @@
+const express = require('express')
+const app = express()
+const session = require('express-session')
+
+app.use(session({
+ secret: 'secret',
+ cookie: { secure: false } // NOT OK
+}))
+
+app.use(session({
+ secret: 'secret'
+ // NOT OK
+}))
+
+app.use(session({
+ secret: 'secret',
+ cookie: {} // NOT OK
+}))
+
+const sess = {
+ secret: 'secret',
+ cookie: { secure: false } // NOT OK
+}
+
+app.use(session(sess))
+
+
+app.set('trust proxy', 1)
+app.use(session({
+ secret: 'secret',
+ cookie: { secure: true } // OK
+}))
+
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/test_httpserver.js b/javascript/ql/test/query-tests/Security/CWE-614/test_httpserver.js
new file mode 100644
index 00000000000..fb9fd386850
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-614/test_httpserver.js
@@ -0,0 +1,22 @@
+const http = require('http');
+
+function test1() {
+ const server = http.createServer((req, res) => {
+ res.setHeader('Content-Type', 'text/html');
+ // NOT OK
+ res.setHeader("Set-Cookie", ["type=ninja", "language=javascript"]);
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end('ok');
+ });
+}
+
+
+function test2() {
+ const server = http.createServer((req, res) => {
+ res.setHeader('Content-Type', 'text/html');
+ // OK
+ res.setHeader("Set-Cookie", ["type=ninja; Secure", "language=javascript; secure"]);
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end('ok');
+ });
+}
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/test_jscookie.js b/javascript/ql/test/query-tests/Security/CWE-614/test_jscookie.js
new file mode 100644
index 00000000000..d53fcecf84b
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-614/test_jscookie.js
@@ -0,0 +1,3 @@
+const js_cookie = require('js-cookie')
+js_cookie.set('key', 'value', { secure: false }); // NOT OK
+js_cookie.set('key', 'value', { secure: true }); // OK
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/test_responseCookie.js b/javascript/ql/test/query-tests/Security/CWE-614/test_responseCookie.js
new file mode 100644
index 00000000000..8d002d594f3
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-614/test_responseCookie.js
@@ -0,0 +1,33 @@
+const express = require('express')
+const app = express()
+
+app.get('/a', function (req, res, next) {
+ res.cookie('name', 'value',
+ {
+ maxAge: 9000000000,
+ httpOnly: true,
+ secure: false // NOT OK
+ });
+ res.end('ok')
+})
+
+app.get('/b', function (req, res, next) {
+ let options = {
+ maxAge: 9000000000,
+ httpOnly: true,
+ secure: false // NOT OK
+ }
+ res.cookie('name', 'value', options);
+ res.end('ok')
+})
+
+app.get('/c', function (req, res, next) {
+ res.cookie('name', 'value',
+ {
+ maxAge: 9000000000,
+ httpOnly: true,
+ secure: true // OK
+ });
+ res.end('ok')
+})
+
From 8ec91ef0c69e7fa51562395a9f6150b6c865bb02 Mon Sep 17 00:00:00 2001
From: ubuntu <43420907+dellalibera@users.noreply.github.com>
Date: Sun, 16 Aug 2020 15:23:29 +0200
Subject: [PATCH 058/146] Change polarity predicate isInsecure
---
.../Security/CWE-614/InsecureCookie.ql | 6 +--
.../Security/CWE-614/InsecureCookie.qll | 41 ++++++++++---------
2 files changed, 25 insertions(+), 22 deletions(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.ql b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.ql
index e45777405f2..d08548df993 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.ql
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.ql
@@ -13,7 +13,7 @@
import javascript
import InsecureCookie::Cookie
-from Cookie insecureCookies
-where insecureCookies.isInsecure()
+from Cookie cookie
+where not cookie.isSecure()
select "Cookie is added to response without the 'secure' flag being set to true (using " +
- insecureCookies.getKind() + ").", insecureCookies
+cookie.getKind() + ").", cookie
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
index 3bedfbcea40..e43eb61d5f7 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qll
@@ -26,9 +26,9 @@ module Cookie {
abstract DataFlow::Node getCookieOptionsArgument();
/**
- * Predicate that determines if a cookie is insecure.
+ * Holds if this cookie is secure.
*/
- abstract predicate isInsecure();
+ abstract predicate isSecure();
}
/**
@@ -43,9 +43,10 @@ module Cookie {
result = this.getCookieOptionsArgument().getAPropertyWrite(flag).getRhs()
}
- override predicate isInsecure() {
- // A cookie is insecure if the `secure` flag is explicitly set to `false`.
- getCookieFlagValue(flag()).mayHaveBooleanValue(false)
+ override predicate isSecure() {
+ // The flag `secure` is set to `false` by default for HTTP, `true` by default for HTTPS (https://github.com/expressjs/cookie-session#cookie-options).
+ // A cookie is secure if the `secure` flag is not explicitly set to `false`.
+ not getCookieFlagValue(flag()).mayHaveBooleanValue(false)
}
}
@@ -62,10 +63,12 @@ module Cookie {
result = this.getCookieOptionsArgument().getAPropertyWrite(flag).getRhs()
}
- override predicate isInsecure() {
- // A cookie is insecure if there are not cookie options with the `secure` flag set to `true`.
- not getCookieFlagValue(flag()).mayHaveBooleanValue(true) and
- not getCookieFlagValue(flag()).mayHaveStringValue("auto")
+ override predicate isSecure() {
+ // The flag `secure` is not set by default (https://github.com/expressjs/session#Cookieecure).
+ // The default value for cookie options is { path: '/', httpOnly: true, secure: false, maxAge: null }.
+ // A cookie is secure if there are the cookie options with the `secure` flag set to `true` or to `auto`.
+ getCookieFlagValue(flag()).mayHaveBooleanValue(true) or
+ getCookieFlagValue(flag()).mayHaveStringValue("auto")
}
}
@@ -87,9 +90,9 @@ module Cookie {
result = this.getCookieOptionsArgument().getAPropertyWrite(flag).getRhs()
}
- override predicate isInsecure() {
- // A cookie is insecure if there are not cookie options with the `secure` flag set to `true`.
- not getCookieFlagValue(flag()).mayHaveBooleanValue(true)
+ override predicate isSecure() {
+ // A cookie is secure if there are cookie options with the `secure` flag set to `true`.
+ getCookieFlagValue(flag()).mayHaveBooleanValue(true)
}
}
@@ -107,9 +110,9 @@ module Cookie {
result.asExpr() = this.asExpr().(ArrayExpr).getAnElement()
}
- override predicate isInsecure() {
- // A cookie is insecure if the 'secure' flag is not specified in the cookie definition.
- not exists(string s |
+ override predicate isSecure() {
+ // A cookie is secure if the 'secure' flag is specified in the cookie definition.
+ exists(string s |
getCookieOptionsArgument().mayHaveStringValue(s) and
s.regexpMatch("(.*;)?\\s*secure.*")
)
@@ -129,16 +132,16 @@ module Cookie {
override string getKind() { result = "js-cookie" }
override DataFlow::SourceNode getCookieOptionsArgument() {
- result = this.(DataFlow::CallNode).getArgument(2).getALocalSource()
+ result = this.(DataFlow::CallNode).getAnArgument().getALocalSource()
}
DataFlow::Node getCookieFlagValue(string flag) {
result = this.getCookieOptionsArgument().getAPropertyWrite(flag).getRhs()
}
- override predicate isInsecure() {
- // A cookie is insecure if there are not cookie options with the `secure` flag set to `true`.
- not getCookieFlagValue(flag()).mayHaveBooleanValue(true)
+ override predicate isSecure() {
+ // A cookie is secure if there are cookie options with the `secure` flag set to `true`.
+ getCookieFlagValue(flag()).mayHaveBooleanValue(true)
}
}
}
From 518459c0f7e29320aaa219923c433d07f20d00d6 Mon Sep 17 00:00:00 2001
From: Remco Vermeulen
Date: Mon, 17 Aug 2020 10:31:44 +0200
Subject: [PATCH 059/146] Abstract Xss sanitizer
Turn the Xss sanitizer into an abstract class to support customizations
and provide a default implementation.
---
java/ql/src/Security/CWE/CWE-079/XSS.ql | 4 +---
java/ql/src/semmle/code/java/security/XSS.qll | 8 ++++++++
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-079/XSS.ql b/java/ql/src/Security/CWE/CWE-079/XSS.ql
index 1a1996c5c16..5c29ff51994 100644
--- a/java/ql/src/Security/CWE/CWE-079/XSS.ql
+++ b/java/ql/src/Security/CWE/CWE-079/XSS.ql
@@ -22,9 +22,7 @@ class XSSConfig extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) { sink instanceof XssSink }
- override predicate isSanitizer(DataFlow::Node node) {
- node.getType() instanceof NumericType or node.getType() instanceof BooleanType
- }
+ override predicate isSanitizer(DataFlow::Node node) { node instanceof XssSanitizer }
}
from DataFlow::PathNode source, DataFlow::PathNode sink, XSSConfig conf
diff --git a/java/ql/src/semmle/code/java/security/XSS.qll b/java/ql/src/semmle/code/java/security/XSS.qll
index 84f06f7b41d..34b5e8ba7f4 100644
--- a/java/ql/src/semmle/code/java/security/XSS.qll
+++ b/java/ql/src/semmle/code/java/security/XSS.qll
@@ -12,6 +12,8 @@ import semmle.code.java.dataflow.TaintTracking2
abstract class XssSink extends DataFlow::Node { }
+abstract class XssSanitizer extends DataFlow::Node { }
+
private class DefaultXssSink extends XssSink {
DefaultXssSink() {
exists(HttpServletResponseSendErrorMethod m, MethodAccess ma |
@@ -80,6 +82,12 @@ private class DefaultXssSink extends XssSink {
}
}
+private class DefaultXSSSanitizer extends XssSanitizer {
+ DefaultXSSSanitizer() {
+ this.getType() instanceof NumericType or this.getType() instanceof BooleanType
+ }
+}
+
private class ServletWriterSourceToWritingMethodFlowConfig extends TaintTracking2::Configuration {
ServletWriterSourceToWritingMethodFlowConfig() {
this = "XSS::ServletWriterSourceToWritingMethodFlowConfig"
From 8db5c4f2e2f283c912c0eeee7c9acba615e10118 Mon Sep 17 00:00:00 2001
From: Remco Vermeulen
Date: Mon, 17 Aug 2020 10:41:27 +0200
Subject: [PATCH 060/146] Abstract additional taint step
---
java/ql/src/Security/CWE/CWE-079/XSS.ql | 4 ++++
java/ql/src/semmle/code/java/security/XSS.qll | 14 ++++++++++++++
2 files changed, 18 insertions(+)
diff --git a/java/ql/src/Security/CWE/CWE-079/XSS.ql b/java/ql/src/Security/CWE/CWE-079/XSS.ql
index 5c29ff51994..ae7cec3277d 100644
--- a/java/ql/src/Security/CWE/CWE-079/XSS.ql
+++ b/java/ql/src/Security/CWE/CWE-079/XSS.ql
@@ -23,6 +23,10 @@ class XSSConfig extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) { sink instanceof XssSink }
override predicate isSanitizer(DataFlow::Node node) { node instanceof XssSanitizer }
+
+ override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
+ any(XssAdditionalTaintStep s).step(node1, node2)
+ }
}
from DataFlow::PathNode source, DataFlow::PathNode sink, XSSConfig conf
diff --git a/java/ql/src/semmle/code/java/security/XSS.qll b/java/ql/src/semmle/code/java/security/XSS.qll
index 34b5e8ba7f4..ca83f6c4b88 100644
--- a/java/ql/src/semmle/code/java/security/XSS.qll
+++ b/java/ql/src/semmle/code/java/security/XSS.qll
@@ -14,6 +14,20 @@ abstract class XssSink extends DataFlow::Node { }
abstract class XssSanitizer extends DataFlow::Node { }
+/**
+ * A unit class for adding additional taint steps.
+ *
+ * Extend this class to add additional taint steps that should apply to the XSS
+ * taint configuration.
+ */
+abstract class XssAdditionalTaintStep extends TaintTracking2::Unit {
+ /**
+ * Holds if the step from `node1` to `node2` should be considered a taint
+ * step for all configurations.
+ */
+ abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
+}
+
private class DefaultXssSink extends XssSink {
DefaultXssSink() {
exists(HttpServletResponseSendErrorMethod m, MethodAccess ma |
From bfdb580206d2e00933488f39cefd493f246ed943 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Mon, 17 Aug 2020 11:37:52 +0200
Subject: [PATCH 061/146] Python: Experiemntal cleanup strategy
---
.../dataflow/internal/DataFlowPublic.qll | 14 +-
.../dataflow/basic/global.expected | 29 ----
.../dataflow/basic/globalStep.expected | 32 ----
.../dataflow/basic/local.expected | 18 +--
.../dataflow/basic/localStep.expected | 4 -
.../dataflow/basic/sinks.expected | 12 +-
.../dataflow/basic/sources.expected | 12 +-
.../consistency/dataflow-consistency.expected | 153 +++++++++++++-----
.../dataflow/coverage/localFlow.expected | 3 -
9 files changed, 146 insertions(+), 131 deletions(-)
diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll
index 0f950dd6052..d03ba150711 100644
--- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll
+++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll
@@ -68,7 +68,7 @@ class EssaNode extends Node, TEssaNode {
override Location getLocation() { result = var.getDefinition().getLocation() }
}
-class CfgNode extends Node, TCfgNode {
+abstract class CfgNode extends Node, TCfgNode {
ControlFlowNode node;
CfgNode() { this = TCfgNode(node) }
@@ -83,6 +83,10 @@ class CfgNode extends Node, TCfgNode {
override Location getLocation() { result = node.getLocation() }
}
+class CfgLiteralNode extends CfgNode {
+ CfgLiteralNode() { node.isLiteral() }
+}
+
/**
* An expression, viewed as a node in a data flow graph.
*
@@ -90,7 +94,13 @@ class CfgNode extends Node, TCfgNode {
* to multiple `ExprNode`s, just like it may correspond to multiple
* `ControlFlow::Node`s.
*/
-class ExprNode extends Node { }
+class ExprNode extends CfgNode {
+ ExprNode() {
+ // node.isAttribute()
+ // or
+ node instanceof NameNode
+ }
+}
/** Gets a node corresponding to expression `e`. */
ExprNode exprNode(DataFlowExpr e) { none() }
diff --git a/python/ql/test/experimental/dataflow/basic/global.expected b/python/ql/test/experimental/dataflow/basic/global.expected
index f42042f7871..73e5e22c98d 100644
--- a/python/ql/test/experimental/dataflow/basic/global.expected
+++ b/python/ql/test/experimental/dataflow/basic/global.expected
@@ -1,23 +1,12 @@
-| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:0:0:0:0 | Exit node for Module test |
| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:7:1:7:1 | GSSA Variable b |
| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
-| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:0:0:0:0 | Exit node for Module test |
| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:7:1:7:1 | GSSA Variable b |
| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
-| test.py:0:0:0:0 | GSSA Variable b | test.py:0:0:0:0 | Exit node for Module test |
| test.py:0:0:0:0 | GSSA Variable b | test.py:7:1:7:1 | GSSA Variable b |
| test.py:0:0:0:0 | GSSA Variable b | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
-| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:0:0:0:0 | Exit node for Module test |
-| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable obfuscated_id |
-| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:7:1:7:1 | GSSA Variable b |
-| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
-| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
-| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:0:0:0:0 | Exit node for Module test |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:1:7:1 | GSSA Variable b |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
-| test.py:1:19:1:19 | SSA variable x | test.py:0:0:0:0 | Exit node for Module test |
-| test.py:1:19:1:19 | SSA variable x | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:1:19:1:19 | SSA variable x | test.py:2:3:2:3 | SSA variable y |
| test.py:1:19:1:19 | SSA variable x | test.py:2:7:2:7 | ControlFlowNode for x |
| test.py:1:19:1:19 | SSA variable x | test.py:3:3:3:3 | SSA variable z |
@@ -25,37 +14,26 @@
| test.py:1:19:1:19 | SSA variable x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:1:19:1:19 | SSA variable x | test.py:7:1:7:1 | GSSA Variable b |
| test.py:1:19:1:19 | SSA variable x | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
-| test.py:2:3:2:3 | SSA variable y | test.py:0:0:0:0 | Exit node for Module test |
-| test.py:2:3:2:3 | SSA variable y | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:2:3:2:3 | SSA variable y | test.py:3:3:3:3 | SSA variable z |
| test.py:2:3:2:3 | SSA variable y | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:2:3:2:3 | SSA variable y | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:2:3:2:3 | SSA variable y | test.py:7:1:7:1 | GSSA Variable b |
| test.py:2:3:2:3 | SSA variable y | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
-| test.py:2:7:2:7 | ControlFlowNode for x | test.py:0:0:0:0 | Exit node for Module test |
-| test.py:2:7:2:7 | ControlFlowNode for x | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:3:2:3 | SSA variable y |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:3:3:3 | SSA variable z |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:7:1:7:1 | GSSA Variable b |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
-| test.py:3:3:3:3 | SSA variable z | test.py:0:0:0:0 | Exit node for Module test |
-| test.py:3:3:3:3 | SSA variable z | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:3:3:3:3 | SSA variable z | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:3:3:3:3 | SSA variable z | test.py:7:1:7:1 | GSSA Variable b |
| test.py:3:3:3:3 | SSA variable z | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
-| test.py:3:7:3:7 | ControlFlowNode for y | test.py:0:0:0:0 | Exit node for Module test |
-| test.py:3:7:3:7 | ControlFlowNode for y | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:3:3:3 | SSA variable z |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:7:1:7:1 | GSSA Variable b |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
-| test.py:4:10:4:10 | ControlFlowNode for z | test.py:0:0:0:0 | Exit node for Module test |
| test.py:4:10:4:10 | ControlFlowNode for z | test.py:7:1:7:1 | GSSA Variable b |
| test.py:4:10:4:10 | ControlFlowNode for z | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
-| test.py:6:1:6:1 | GSSA Variable a | test.py:0:0:0:0 | Exit node for Module test |
-| test.py:6:1:6:1 | GSSA Variable a | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:6:1:6:1 | GSSA Variable a | test.py:1:19:1:19 | SSA variable x |
| test.py:6:1:6:1 | GSSA Variable a | test.py:2:3:2:3 | SSA variable y |
| test.py:6:1:6:1 | GSSA Variable a | test.py:2:7:2:7 | ControlFlowNode for x |
@@ -66,8 +44,6 @@
| test.py:6:1:6:1 | GSSA Variable a | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:6:1:6:1 | GSSA Variable a | test.py:7:5:7:20 | GSSA Variable a |
| test.py:6:1:6:1 | GSSA Variable a | test.py:7:19:7:19 | ControlFlowNode for a |
-| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:0:0:0:0 | Exit node for Module test |
-| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:1:19:1:19 | SSA variable x |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:2:3:2:3 | SSA variable y |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:2:7:2:7 | ControlFlowNode for x |
@@ -79,12 +55,7 @@
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:7:5:7:20 | GSSA Variable a |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:7:19:7:19 | ControlFlowNode for a |
-| test.py:7:1:7:1 | GSSA Variable b | test.py:0:0:0:0 | Exit node for Module test |
-| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:0:0:0:0 | Exit node for Module test |
| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:7:1:7:1 | GSSA Variable b |
-| test.py:7:5:7:20 | GSSA Variable a | test.py:0:0:0:0 | Exit node for Module test |
-| test.py:7:19:7:19 | ControlFlowNode for a | test.py:0:0:0:0 | Exit node for Module test |
-| test.py:7:19:7:19 | ControlFlowNode for a | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:7:19:7:19 | ControlFlowNode for a | test.py:1:19:1:19 | SSA variable x |
| test.py:7:19:7:19 | ControlFlowNode for a | test.py:2:3:2:3 | SSA variable y |
| test.py:7:19:7:19 | ControlFlowNode for a | test.py:2:7:2:7 | ControlFlowNode for x |
diff --git a/python/ql/test/experimental/dataflow/basic/globalStep.expected b/python/ql/test/experimental/dataflow/basic/globalStep.expected
index 9cf3a8c4d89..7e15cd93025 100644
--- a/python/ql/test/experimental/dataflow/basic/globalStep.expected
+++ b/python/ql/test/experimental/dataflow/basic/globalStep.expected
@@ -1,25 +1,13 @@
-| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:0:0:0:0 | Exit node for Module test |
-| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:0:0:0:0 | Exit node for Module test |
| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
-| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:0:0:0:0 | Exit node for Module test |
-| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:0:0:0:0 | Exit node for Module test |
| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:0:0:0:0 | GSSA Variable b | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:0:0:0:0 | GSSA Variable b | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
-| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable obfuscated_id |
-| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable obfuscated_id |
-| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:0:0:0:0 | Exit node for Module test |
-| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:0:0:0:0 | Exit node for Module test |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
-| test.py:1:19:1:19 | SSA variable x | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
-| test.py:1:19:1:19 | SSA variable x | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
-| test.py:1:19:1:19 | SSA variable x | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
-| test.py:1:19:1:19 | SSA variable x | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:1:19:1:19 | SSA variable x | test.py:2:3:2:3 | SSA variable y |
| test.py:1:19:1:19 | SSA variable x | test.py:2:3:2:3 | SSA variable y |
| test.py:1:19:1:19 | SSA variable x | test.py:2:3:2:3 | SSA variable y |
@@ -40,10 +28,6 @@
| test.py:1:19:1:19 | SSA variable x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:1:19:1:19 | SSA variable x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:1:19:1:19 | SSA variable x | test.py:4:10:4:10 | ControlFlowNode for z |
-| test.py:2:3:2:3 | SSA variable y | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
-| test.py:2:3:2:3 | SSA variable y | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
-| test.py:2:3:2:3 | SSA variable y | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
-| test.py:2:3:2:3 | SSA variable y | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:2:3:2:3 | SSA variable y | test.py:3:3:3:3 | SSA variable z |
| test.py:2:3:2:3 | SSA variable y | test.py:3:3:3:3 | SSA variable z |
| test.py:2:3:2:3 | SSA variable y | test.py:3:3:3:3 | SSA variable z |
@@ -56,10 +40,6 @@
| test.py:2:3:2:3 | SSA variable y | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:2:3:2:3 | SSA variable y | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:2:3:2:3 | SSA variable y | test.py:4:10:4:10 | ControlFlowNode for z |
-| test.py:2:7:2:7 | ControlFlowNode for x | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
-| test.py:2:7:2:7 | ControlFlowNode for x | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
-| test.py:2:7:2:7 | ControlFlowNode for x | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
-| test.py:2:7:2:7 | ControlFlowNode for x | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:3:2:3 | SSA variable y |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:3:2:3 | SSA variable y |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:3:2:3 | SSA variable y |
@@ -76,18 +56,10 @@
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z |
-| test.py:3:3:3:3 | SSA variable z | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
-| test.py:3:3:3:3 | SSA variable z | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
-| test.py:3:3:3:3 | SSA variable z | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
-| test.py:3:3:3:3 | SSA variable z | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:3:3:3:3 | SSA variable z | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:3:3:3:3 | SSA variable z | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:3:3:3:3 | SSA variable z | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:3:3:3:3 | SSA variable z | test.py:4:10:4:10 | ControlFlowNode for z |
-| test.py:3:7:3:7 | ControlFlowNode for y | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
-| test.py:3:7:3:7 | ControlFlowNode for y | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
-| test.py:3:7:3:7 | ControlFlowNode for y | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
-| test.py:3:7:3:7 | ControlFlowNode for y | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:3:3:3 | SSA variable z |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:3:3:3 | SSA variable z |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:3:3:3 | SSA variable z |
@@ -106,12 +78,8 @@
| test.py:6:1:6:1 | GSSA Variable a | test.py:7:19:7:19 | ControlFlowNode for a |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:6:1:6:1 | GSSA Variable a |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:6:1:6:1 | GSSA Variable a |
-| test.py:7:1:7:1 | GSSA Variable b | test.py:0:0:0:0 | Exit node for Module test |
-| test.py:7:1:7:1 | GSSA Variable b | test.py:0:0:0:0 | Exit node for Module test |
| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:7:1:7:1 | GSSA Variable b |
| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:7:1:7:1 | GSSA Variable b |
-| test.py:7:5:7:20 | GSSA Variable a | test.py:0:0:0:0 | Exit node for Module test |
-| test.py:7:5:7:20 | GSSA Variable a | test.py:0:0:0:0 | Exit node for Module test |
| test.py:7:19:7:19 | ControlFlowNode for a | test.py:1:19:1:19 | SSA variable x |
| test.py:7:19:7:19 | ControlFlowNode for a | test.py:1:19:1:19 | SSA variable x |
| test.py:7:19:7:19 | ControlFlowNode for a | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
diff --git a/python/ql/test/experimental/dataflow/basic/local.expected b/python/ql/test/experimental/dataflow/basic/local.expected
index 052c52f90fc..60c9928ebfd 100644
--- a/python/ql/test/experimental/dataflow/basic/local.expected
+++ b/python/ql/test/experimental/dataflow/basic/local.expected
@@ -1,17 +1,16 @@
-| test.py:0:0:0:0 | Entry node for Module test | test.py:0:0:0:0 | Entry node for Module test |
-| test.py:0:0:0:0 | Exit node for Module test | test.py:0:0:0:0 | Exit node for Module test |
+| file://:0:0:0:0 | Data flow node | file://:0:0:0:0 | Data flow node |
+| file://:0:0:0:0 | Data flow node | file://:0:0:0:0 | Data flow node |
+| file://:0:0:0:0 | Data flow node | file://:0:0:0:0 | Data flow node |
+| file://:0:0:0:0 | Data flow node | file://:0:0:0:0 | Data flow node |
+| file://:0:0:0:0 | Data flow node | file://:0:0:0:0 | Data flow node |
+| file://:0:0:0:0 | Data flow node | file://:0:0:0:0 | Data flow node |
| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:0:0:0:0 | GSSA Variable __name__ |
| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:0:0:0:0 | GSSA Variable __package__ |
| test.py:0:0:0:0 | GSSA Variable b | test.py:0:0:0:0 | GSSA Variable b |
-| test.py:0:0:0:0 | SSA variable $ | test.py:0:0:0:0 | Exit node for Module test |
| test.py:0:0:0:0 | SSA variable $ | test.py:0:0:0:0 | SSA variable $ |
-| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:1:1:21 | ControlFlowNode for FunctionExpr |
-| test.py:1:1:1:21 | Entry node for Function obfuscated_id | test.py:1:1:1:21 | Entry node for Function obfuscated_id |
-| test.py:1:1:1:21 | Exit node for Function obfuscated_id | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:1:5:1:17 | ControlFlowNode for obfuscated_id | test.py:1:5:1:17 | ControlFlowNode for obfuscated_id |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:1:5:1:17 | GSSA Variable obfuscated_id |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:1:19:1:19 | ControlFlowNode for x |
-| test.py:1:19:1:19 | SSA variable x | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:1:19:1:19 | SSA variable x | test.py:1:19:1:19 | SSA variable x |
| test.py:1:19:1:19 | SSA variable x | test.py:2:3:2:3 | SSA variable y |
| test.py:1:19:1:19 | SSA variable x | test.py:2:7:2:7 | ControlFlowNode for x |
@@ -19,26 +18,21 @@
| test.py:1:19:1:19 | SSA variable x | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:1:19:1:19 | SSA variable x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:2:3:2:3 | ControlFlowNode for y | test.py:2:3:2:3 | ControlFlowNode for y |
-| test.py:2:3:2:3 | SSA variable y | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:2:3:2:3 | SSA variable y | test.py:2:3:2:3 | SSA variable y |
| test.py:2:3:2:3 | SSA variable y | test.py:3:3:3:3 | SSA variable z |
| test.py:2:3:2:3 | SSA variable y | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:2:3:2:3 | SSA variable y | test.py:4:10:4:10 | ControlFlowNode for z |
-| test.py:2:7:2:7 | ControlFlowNode for x | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:3:2:3 | SSA variable y |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:7:2:7 | ControlFlowNode for x |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:3:3:3 | SSA variable z |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:3:3:3:3 | ControlFlowNode for z | test.py:3:3:3:3 | ControlFlowNode for z |
-| test.py:3:3:3:3 | SSA variable z | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:3:3:3:3 | SSA variable z | test.py:3:3:3:3 | SSA variable z |
| test.py:3:3:3:3 | SSA variable z | test.py:4:10:4:10 | ControlFlowNode for z |
-| test.py:3:7:3:7 | ControlFlowNode for y | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:3:3:3 | SSA variable z |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:4:10:4:10 | ControlFlowNode for z |
-| test.py:4:3:4:10 | ControlFlowNode for Return | test.py:4:3:4:10 | ControlFlowNode for Return |
| test.py:4:10:4:10 | ControlFlowNode for z | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:6:1:6:1 | ControlFlowNode for a | test.py:6:1:6:1 | ControlFlowNode for a |
| test.py:6:1:6:1 | GSSA Variable a | test.py:6:1:6:1 | GSSA Variable a |
diff --git a/python/ql/test/experimental/dataflow/basic/localStep.expected b/python/ql/test/experimental/dataflow/basic/localStep.expected
index 24509950e88..29510fa4de1 100644
--- a/python/ql/test/experimental/dataflow/basic/localStep.expected
+++ b/python/ql/test/experimental/dataflow/basic/localStep.expected
@@ -1,9 +1,5 @@
-| test.py:0:0:0:0 | SSA variable $ | test.py:0:0:0:0 | Exit node for Module test |
-| test.py:1:19:1:19 | SSA variable x | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:1:19:1:19 | SSA variable x | test.py:2:7:2:7 | ControlFlowNode for x |
-| test.py:2:3:2:3 | SSA variable y | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:2:3:2:3 | SSA variable y | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:3:2:3 | SSA variable y |
-| test.py:3:3:3:3 | SSA variable z | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:3:3:3:3 | SSA variable z | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:3:3:3 | SSA variable z |
diff --git a/python/ql/test/experimental/dataflow/basic/sinks.expected b/python/ql/test/experimental/dataflow/basic/sinks.expected
index bcf55bee27c..9ff2ae53247 100644
--- a/python/ql/test/experimental/dataflow/basic/sinks.expected
+++ b/python/ql/test/experimental/dataflow/basic/sinks.expected
@@ -1,12 +1,13 @@
-| test.py:0:0:0:0 | Entry node for Module test |
-| test.py:0:0:0:0 | Exit node for Module test |
+| file://:0:0:0:0 | Data flow node |
+| file://:0:0:0:0 | Data flow node |
+| file://:0:0:0:0 | Data flow node |
+| file://:0:0:0:0 | Data flow node |
+| file://:0:0:0:0 | Data flow node |
+| file://:0:0:0:0 | Data flow node |
| test.py:0:0:0:0 | GSSA Variable __name__ |
| test.py:0:0:0:0 | GSSA Variable __package__ |
| test.py:0:0:0:0 | GSSA Variable b |
| test.py:0:0:0:0 | SSA variable $ |
-| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr |
-| test.py:1:1:1:21 | Entry node for Function obfuscated_id |
-| test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:1:5:1:17 | ControlFlowNode for obfuscated_id |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id |
| test.py:1:19:1:19 | ControlFlowNode for x |
@@ -17,7 +18,6 @@
| test.py:3:3:3:3 | ControlFlowNode for z |
| test.py:3:3:3:3 | SSA variable z |
| test.py:3:7:3:7 | ControlFlowNode for y |
-| test.py:4:3:4:10 | ControlFlowNode for Return |
| test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:6:1:6:1 | ControlFlowNode for a |
| test.py:6:1:6:1 | GSSA Variable a |
diff --git a/python/ql/test/experimental/dataflow/basic/sources.expected b/python/ql/test/experimental/dataflow/basic/sources.expected
index bcf55bee27c..9ff2ae53247 100644
--- a/python/ql/test/experimental/dataflow/basic/sources.expected
+++ b/python/ql/test/experimental/dataflow/basic/sources.expected
@@ -1,12 +1,13 @@
-| test.py:0:0:0:0 | Entry node for Module test |
-| test.py:0:0:0:0 | Exit node for Module test |
+| file://:0:0:0:0 | Data flow node |
+| file://:0:0:0:0 | Data flow node |
+| file://:0:0:0:0 | Data flow node |
+| file://:0:0:0:0 | Data flow node |
+| file://:0:0:0:0 | Data flow node |
+| file://:0:0:0:0 | Data flow node |
| test.py:0:0:0:0 | GSSA Variable __name__ |
| test.py:0:0:0:0 | GSSA Variable __package__ |
| test.py:0:0:0:0 | GSSA Variable b |
| test.py:0:0:0:0 | SSA variable $ |
-| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr |
-| test.py:1:1:1:21 | Entry node for Function obfuscated_id |
-| test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:1:5:1:17 | ControlFlowNode for obfuscated_id |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id |
| test.py:1:19:1:19 | ControlFlowNode for x |
@@ -17,7 +18,6 @@
| test.py:3:3:3:3 | ControlFlowNode for z |
| test.py:3:3:3:3 | SSA variable z |
| test.py:3:7:3:7 | ControlFlowNode for y |
-| test.py:4:3:4:10 | ControlFlowNode for Return |
| test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:6:1:6:1 | ControlFlowNode for a |
| test.py:6:1:6:1 | GSSA Variable a |
diff --git a/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected
index 2ae31eb1126..759c20be6e6 100644
--- a/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected
+++ b/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected
@@ -1,80 +1,159 @@
uniqueEnclosingCallable
-| test.py:0:0:0:0 | Exit node for Module test | Node should have one enclosing callable but has 0. |
| test.py:0:0:0:0 | GSSA Variable __name__ | Node should have one enclosing callable but has 0. |
| test.py:0:0:0:0 | GSSA Variable __package__ | Node should have one enclosing callable but has 0. |
| test.py:0:0:0:0 | GSSA Variable test23 | Node should have one enclosing callable but has 0. |
| test.py:0:0:0:0 | GSSA Variable test24 | Node should have one enclosing callable but has 0. |
| test.py:0:0:0:0 | GSSA Variable test_truth | Node should have one enclosing callable but has 0. |
| test.py:0:0:0:0 | GSSA Variable test_update_extend | Node should have one enclosing callable but has 0. |
-| test.py:0:0:0:0 | SSA variable $ | Node should have one enclosing callable but has 0. |
-| test.py:6:1:6:12 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:6:5:6:9 | GSSA Variable test1 | Node should have one enclosing callable but has 0. |
-| test.py:9:1:9:12 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:9:5:9:9 | GSSA Variable test2 | Node should have one enclosing callable but has 0. |
-| test.py:13:1:13:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:13:5:13:10 | GSSA Variable source | Node should have one enclosing callable but has 0. |
-| test.py:16:1:16:14 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:16:5:16:8 | GSSA Variable sink | Node should have one enclosing callable but has 0. |
-| test.py:19:1:19:12 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:19:5:19:9 | GSSA Variable test3 | Node should have one enclosing callable but has 0. |
-| test.py:23:1:23:12 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:23:5:23:9 | GSSA Variable test4 | Node should have one enclosing callable but has 0. |
-| test.py:27:1:27:12 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:27:5:27:9 | GSSA Variable test5 | Node should have one enclosing callable but has 0. |
-| test.py:31:1:31:16 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:31:5:31:9 | GSSA Variable test6 | Node should have one enclosing callable but has 0. |
-| test.py:39:1:39:16 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:39:5:39:9 | GSSA Variable test7 | Node should have one enclosing callable but has 0. |
-| test.py:47:1:47:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:47:5:47:11 | GSSA Variable source2 | Node should have one enclosing callable but has 0. |
-| test.py:50:1:50:15 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:50:5:50:9 | GSSA Variable sink2 | Node should have one enclosing callable but has 0. |
-| test.py:53:1:53:21 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:53:5:53:9 | GSSA Variable sink3 | Node should have one enclosing callable but has 0. |
-| test.py:57:1:57:16 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:57:5:57:9 | GSSA Variable test8 | Node should have one enclosing callable but has 0. |
-| test.py:62:1:62:16 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:62:5:62:9 | GSSA Variable test9 | Node should have one enclosing callable but has 0. |
-| test.py:69:1:69:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:69:5:69:10 | GSSA Variable test10 | Node should have one enclosing callable but has 0. |
-| test.py:76:1:76:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:76:5:76:7 | GSSA Variable hub | Node should have one enclosing callable but has 0. |
-| test.py:79:1:79:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:79:5:79:10 | GSSA Variable test11 | Node should have one enclosing callable but has 0. |
-| test.py:84:1:84:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:84:5:84:10 | GSSA Variable test12 | Node should have one enclosing callable but has 0. |
-| test.py:89:8:89:13 | ControlFlowNode for ImportExpr | Node should have one enclosing callable but has 0. |
| test.py:89:8:89:13 | GSSA Variable module | Node should have one enclosing callable but has 0. |
-| test.py:91:1:91:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:91:5:91:10 | GSSA Variable test13 | Node should have one enclosing callable but has 0. |
-| test.py:95:1:95:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:95:5:95:10 | GSSA Variable test14 | Node should have one enclosing callable but has 0. |
-| test.py:99:1:99:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:99:5:99:10 | GSSA Variable test15 | Node should have one enclosing callable but has 0. |
-| test.py:103:1:103:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:103:5:103:10 | GSSA Variable test16 | Node should have one enclosing callable but has 0. |
-| test.py:108:1:108:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:108:5:108:10 | GSSA Variable test20 | Node should have one enclosing callable but has 0. |
-| test.py:118:1:118:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:118:5:118:10 | GSSA Variable test21 | Node should have one enclosing callable but has 0. |
-| test.py:128:1:128:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:128:5:128:10 | GSSA Variable test22 | Node should have one enclosing callable but has 0. |
-| test.py:139:20:139:38 | ControlFlowNode for ImportMember | Node should have one enclosing callable but has 0. |
| test.py:139:33:139:38 | GSSA Variable unsafe | Node should have one enclosing callable but has 0. |
| test.py:140:1:140:12 | ControlFlowNode for SINK() | Node should have one enclosing callable but has 0. |
| test.py:140:1:140:12 | GSSA Variable unsafe | Node should have one enclosing callable but has 0. |
| test.py:140:6:140:11 | ControlFlowNode for unsafe | Node should have one enclosing callable but has 0. |
-| test.py:142:1:142:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
-| test.py:142:5:142:10 | GSSA Variable test23 | Node should have one enclosing callable but has 0. |
-| test.py:146:1:146:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
-| test.py:146:5:146:10 | GSSA Variable test24 | Node should have one enclosing callable but has 0. |
-| test.py:151:1:151:29 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
-| test.py:151:5:151:22 | GSSA Variable test_update_extend | Node should have one enclosing callable but has 0. |
-| test.py:161:1:161:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
-| test.py:161:5:161:14 | GSSA Variable test_truth | Node should have one enclosing callable but has 0. |
uniqueType
uniqueNodeLocation
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
+| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
missingLocation
+| Nodes without location: 115 |
uniqueNodeToString
missingToString
parameterCallable
diff --git a/python/ql/test/experimental/dataflow/coverage/localFlow.expected b/python/ql/test/experimental/dataflow/coverage/localFlow.expected
index 9e4dc40eafc..54e4a36a7fe 100644
--- a/python/ql/test/experimental/dataflow/coverage/localFlow.expected
+++ b/python/ql/test/experimental/dataflow/coverage/localFlow.expected
@@ -1,7 +1,4 @@
-| test.py:32:5:32:5 | SSA variable x | test.py:31:1:31:33 | Exit node for Function test_tuple_with_local_flow |
| test.py:32:5:32:5 | SSA variable x | test.py:33:9:33:9 | ControlFlowNode for x |
| test.py:32:10:32:26 | ControlFlowNode for Tuple | test.py:32:5:32:5 | SSA variable x |
| test.py:33:5:33:5 | SSA variable y | test.py:34:5:34:11 | SSA variable y |
| test.py:33:5:33:5 | SSA variable y | test.py:34:10:34:10 | ControlFlowNode for y |
-| test.py:33:9:33:12 | ControlFlowNode for Subscript | test.py:33:5:33:5 | SSA variable y |
-| test.py:34:5:34:11 | SSA variable y | test.py:31:1:31:33 | Exit node for Function test_tuple_with_local_flow |
From 894b3f2cd4002d8c5cfef20bf6e5eacc1532f193 Mon Sep 17 00:00:00 2001
From: Remco Vermeulen
Date: Mon, 17 Aug 2020 11:40:08 +0200
Subject: [PATCH 062/146] Add or change qldocs
---
java/ql/src/semmle/code/java/security/XSS.qll | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/java/ql/src/semmle/code/java/security/XSS.qll b/java/ql/src/semmle/code/java/security/XSS.qll
index ca83f6c4b88..df1a1606f12 100644
--- a/java/ql/src/semmle/code/java/security/XSS.qll
+++ b/java/ql/src/semmle/code/java/security/XSS.qll
@@ -1,3 +1,5 @@
+/** Provides classes to reason about Cross-site scripting (XSS) vulnerabilities. */
+
import java
import semmle.code.java.frameworks.Servlets
import semmle.code.java.frameworks.android.WebView
@@ -6,12 +8,10 @@ import semmle.code.java.frameworks.spring.SpringHttp
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.TaintTracking2
-/*
- * Definitions for XSS sinks
- */
-
+/** A sink that represent a method that outputs data without applying contextual output encoding. */
abstract class XssSink extends DataFlow::Node { }
+/** A sanitizer that neutralizes dangerous characters that can be used to perform a XSS attack. */
abstract class XssSanitizer extends DataFlow::Node { }
/**
@@ -28,6 +28,7 @@ abstract class XssAdditionalTaintStep extends TaintTracking2::Unit {
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
}
+/** A default sink representing methods susceptible to XSS attacks. */
private class DefaultXssSink extends XssSink {
DefaultXssSink() {
exists(HttpServletResponseSendErrorMethod m, MethodAccess ma |
@@ -96,12 +97,14 @@ private class DefaultXssSink extends XssSink {
}
}
+/** A default sanitizer that considers numeric and boolean typed data safe for writing to output. */
private class DefaultXSSSanitizer extends XssSanitizer {
DefaultXSSSanitizer() {
this.getType() instanceof NumericType or this.getType() instanceof BooleanType
}
}
+/** A configuration that tracks data from a servlet writer to an output method. */
private class ServletWriterSourceToWritingMethodFlowConfig extends TaintTracking2::Configuration {
ServletWriterSourceToWritingMethodFlowConfig() {
this = "XSS::ServletWriterSourceToWritingMethodFlowConfig"
@@ -116,6 +119,7 @@ private class ServletWriterSourceToWritingMethodFlowConfig extends TaintTracking
}
}
+/** A class representing methods that can be used to output data. */
private class WritingMethod extends Method {
WritingMethod() {
getDeclaringType().getASupertype*().hasQualifiedName("java.io", _) and
@@ -127,6 +131,7 @@ private class WritingMethod extends Method {
}
}
+/** A class representing methods that provides access to an output stream or writer. */
class ServletWriterSource extends MethodAccess {
ServletWriterSource() {
this.getMethod() instanceof ServletResponseGetWriterMethod
From 8eacef34678eea61a4bdfbf23652f10e52be26dc Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Mon, 17 Aug 2020 12:01:36 +0200
Subject: [PATCH 063/146] Python: Add QL doc
---
python/ql/src/semmle/python/Magic.qll | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/python/ql/src/semmle/python/Magic.qll b/python/ql/src/semmle/python/Magic.qll
index e60b16c16ab..11767254ef0 100644
--- a/python/ql/src/semmle/python/Magic.qll
+++ b/python/ql/src/semmle/python/Magic.qll
@@ -1,12 +1,33 @@
+/**
+ * Provides support for magic methods.
+ * This is done in two steps:
+ * - A subset of `ControlFlowNode`s are labelled as potentially corresponding to
+ * a magic method call (by being an instance of `MagicMethod::Potential`).
+ * - A subset of the potential magic method calls are labelled as being actual
+ * magic method calls (`MagicMethod::Actual`) if the appropriate method is defined.
+ */
+
import python
+/**
+ * Machinery for detecting magic method calls.
+ * Extend `MagicMethod::Potential` to capture more cases.
+ */
module MagicMethod {
+ /** A control flow node which might correpsond to a magic method call. */
abstract class Potential extends ControlFlowNode {
+ /** Gets the name of the method that would be called */
abstract string getMagicMethodName();
+
+ /** Gets the controlflow node that would be passed as the specified argument. */
abstract ControlFlowNode getArg(int n);
+
+ /** Gets the control flow node corresponding to the instance
+ * that would define the magic method. */
ControlFlowNode getSelf() { result = this.getArg(0) }
}
+ /** A control flow node corresponding to a magic method call. */
class Actual extends ControlFlowNode {
Value resolvedMagicMethod;
@@ -17,10 +38,12 @@ module MagicMethod {
)
}
+ /** The method that is called. */
Value getResolvedMagicMethod() { result = resolvedMagicMethod }
}
}
+/** A binary expression node that might correspond to a magic method call. */
class MagicBinOp extends MagicMethod::Potential, BinaryExprNode {
Operator operator;
From a5701db3fafe5fd47201188f067f148e2e9bdfeb Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Mon, 17 Aug 2020 15:01:48 +0200
Subject: [PATCH 064/146] Java: Support String.formatted in the format string
queries.
---
java/ql/src/semmle/code/java/StringFormat.qll | 12 ++++++++++--
java/ql/test/query-tests/StringFormat/A.java | 5 +++++
.../StringFormat/MissingFormatArg.expected | 1 +
.../StringFormat/UnusedFormatArg.expected | 1 +
java/ql/test/query-tests/StringFormat/options | 1 +
5 files changed, 18 insertions(+), 2 deletions(-)
create mode 100644 java/ql/test/query-tests/StringFormat/options
diff --git a/java/ql/src/semmle/code/java/StringFormat.qll b/java/ql/src/semmle/code/java/StringFormat.qll
index fac84c5c8af..69e6d4b056e 100644
--- a/java/ql/src/semmle/code/java/StringFormat.qll
+++ b/java/ql/src/semmle/code/java/StringFormat.qll
@@ -22,6 +22,7 @@ class StringFormatMethod extends FormatMethod {
StringFormatMethod() {
(
this.hasName("format") or
+ this.hasName("formatted") or
this.hasName("printf") or
this.hasName("readLine") or
this.hasName("readPassword")
@@ -38,6 +39,8 @@ class StringFormatMethod extends FormatMethod {
override int getFormatStringIndex() {
result = 0 and this.getSignature() = "format(java.lang.String,java.lang.Object[])"
or
+ result = -1 and this.getSignature() = "formatted(java.lang.Object[])"
+ or
result = 0 and this.getSignature() = "printf(java.lang.String,java.lang.Object[])"
or
result = 1 and
@@ -91,6 +94,11 @@ class FmtSyntax extends TFmtSyntax {
predicate isLogger() { this = TFmtLogger() }
}
+private Expr getArgumentOrQualifier(Call c, int i) {
+ result = c.getArgument(i) or
+ result = c.getQualifier() and i = -1
+}
+
/**
* Holds if `c` wraps a call to a `StringFormatMethod`, such that `fmtix` is
* the index of the format string argument to `c` and the following and final
@@ -111,7 +119,7 @@ private predicate formatWrapper(Callable c, int fmtix, FmtSyntax syntax) {
or
fmtcall.getCallee().(LoggerFormatMethod).getFormatStringIndex() = i and syntax = TFmtLogger()
) and
- fmtcall.getArgument(i) = fmt.getAnAccess() and
+ getArgumentOrQualifier(fmtcall, i) = fmt.getAnAccess() and
fmtcall.getArgument(i + 1) = args.getAnAccess()
)
}
@@ -155,7 +163,7 @@ class FormattingCall extends Call {
}
/** Gets the argument to this call in the position of the format string */
- Expr getFormatArgument() { result = this.getArgument(this.getFormatStringIndex()) }
+ Expr getFormatArgument() { result = getArgumentOrQualifier(this, this.getFormatStringIndex()) }
/** Gets an argument to be formatted. */
Expr getAnArgumentToBeFormatted() {
diff --git a/java/ql/test/query-tests/StringFormat/A.java b/java/ql/test/query-tests/StringFormat/A.java
index 31cba68a622..ff87290bcc9 100644
--- a/java/ql/test/query-tests/StringFormat/A.java
+++ b/java/ql/test/query-tests/StringFormat/A.java
@@ -85,4 +85,9 @@ public class A {
String.format("%s%s", a2); // ok
String.format("%s", a2); // unused
}
+
+ void formatted() {
+ "%s%s".formatted(""); // missing
+ "%s".formatted("", ""); // unused
+ }
}
diff --git a/java/ql/test/query-tests/StringFormat/MissingFormatArg.expected b/java/ql/test/query-tests/StringFormat/MissingFormatArg.expected
index c88a74338cf..fe2e76dd8fa 100644
--- a/java/ql/test/query-tests/StringFormat/MissingFormatArg.expected
+++ b/java/ql/test/query-tests/StringFormat/MissingFormatArg.expected
@@ -17,3 +17,4 @@
| A.java:74:5:74:47 | format(...) | This format call refers to 2 argument(s) but only supplies 1 argument(s). |
| A.java:79:5:79:31 | format(...) | This format call refers to 3 argument(s) but only supplies 2 argument(s). |
| A.java:84:5:84:31 | format(...) | This format call refers to 3 argument(s) but only supplies 2 argument(s). |
+| A.java:90:5:90:24 | formatted(...) | This format call refers to 2 argument(s) but only supplies 1 argument(s). |
diff --git a/java/ql/test/query-tests/StringFormat/UnusedFormatArg.expected b/java/ql/test/query-tests/StringFormat/UnusedFormatArg.expected
index 2cbe4cc2eb1..b301df54abf 100644
--- a/java/ql/test/query-tests/StringFormat/UnusedFormatArg.expected
+++ b/java/ql/test/query-tests/StringFormat/UnusedFormatArg.expected
@@ -9,3 +9,4 @@
| A.java:76:5:76:57 | format(...) | This format call refers to 2 argument(s) but supplies 3 argument(s). |
| A.java:81:5:81:27 | format(...) | This format call refers to 1 argument(s) but supplies 2 argument(s). |
| A.java:86:5:86:27 | format(...) | This format call refers to 1 argument(s) but supplies 2 argument(s). |
+| A.java:91:5:91:26 | formatted(...) | This format call refers to 1 argument(s) but supplies 2 argument(s). |
diff --git a/java/ql/test/query-tests/StringFormat/options b/java/ql/test/query-tests/StringFormat/options
new file mode 100644
index 00000000000..266b0eadc5e
--- /dev/null
+++ b/java/ql/test/query-tests/StringFormat/options
@@ -0,0 +1 @@
+//semmle-extractor-options: --javac-args --enable-preview -source 14 -target 14
From 0f87a89fd11782b091a184b795c35978dcd3dc91 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Mon, 17 Aug 2020 15:31:41 +0200
Subject: [PATCH 065/146] use typeLabel instead of typeDecl
Co-authored-by: Asger F
---
.../extractor/src/com/semmle/js/extractor/ASTExtractor.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java
index 1050590f14d..9d1822dce10 100644
--- a/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java
+++ b/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java
@@ -1821,7 +1821,7 @@ public class ASTExtractor {
Label key = super.visit(nd, c);
if (nd.getElementNames() != null) {
// Element names are index -1, -2, -3...
- visitAll(nd.getElementNames(), key, IdContext.typeDecl, -1, -1);
+ visitAll(nd.getElementNames(), key, IdContext.typeLabel, -1, -1);
}
visitAll(nd.getElementTypes(), key, IdContext.typeBind, 0);
return key;
From 83ed41b24744c98c2dcc696cc40c35038ba9a89c Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Mon, 17 Aug 2020 15:43:43 +0200
Subject: [PATCH 066/146] move indices comment into plain comment
---
javascript/ql/src/semmle/javascript/TypeScript.qll | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/javascript/ql/src/semmle/javascript/TypeScript.qll b/javascript/ql/src/semmle/javascript/TypeScript.qll
index 799f386e2a0..02d1c29511a 100644
--- a/javascript/ql/src/semmle/javascript/TypeScript.qll
+++ b/javascript/ql/src/semmle/javascript/TypeScript.qll
@@ -857,10 +857,11 @@ class TupleTypeExpr extends @tupletypeexpr, TypeExpr {
/**
* Gets the name of the `n`th tuple member, starting at 0.
* Only has a result if the tuple members are named.
- *
- * Type element names are at indices -1, -2, -3, ...
*/
- Identifier getElementName(int n) { result = getChild(-(n + 1)) and n >= 0 }
+ Identifier getElementName(int n) {
+ // Type element names are at indices -1, -2, -3, ...
+ result = getChild(-(n + 1)) and n >= 0
+ }
}
/**
From 73d1fac88ec574644239a4259d6edbd16af32cee Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Mon, 17 Aug 2020 16:20:26 +0200
Subject: [PATCH 067/146] support named tuples where not all tuple elements are
named
---
.../semmle/js/parser/TypeScriptASTConverter.java | 15 ++++++++-------
.../TypeScript/TypeAnnotations/tests.expected | 8 ++++++++
.../TypeScript/TypeAnnotations/tst.ts | 5 +++++
3 files changed, 21 insertions(+), 7 deletions(-)
diff --git a/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java b/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java
index 8d9079a0b69..91eb2f28646 100644
--- a/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java
+++ b/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java
@@ -2183,14 +2183,15 @@ public class TypeScriptASTConverter {
}
private Node convertTupleType(JsonObject node, SourceLocation loc) throws ParseError {
- List elements = new ArrayList<>();
- ((JsonArray)node.get("elements")).iterator().forEachRemaining(elements::add);
+ List names = new ArrayList<>();
- List names = convertNodes(elements.stream()
- .filter(n -> getKind(n).equals("NamedTupleMember"))
- .map(n -> n.getAsJsonObject().get("name"))
- .collect(Collectors.toList())
- );
+ for (JsonElement element : node.get("elements").getAsJsonArray()) {
+ Identifier id = null;
+ if (getKind(element).equals("NamedTupleMember")) {
+ id = (Identifier)convertNode(element.getAsJsonObject().get("name").getAsJsonObject());
+ }
+ names.add(id);
+ }
return new TupleTypeExpr(loc, convertChildrenAsTypes(node, "elements"), names);
}
diff --git a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected
index 9ae9bdfe456..5a0098961bc 100644
--- a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected
+++ b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected
@@ -116,6 +116,7 @@ test_VariableTypes
| tst.ts:184:7:184:8 | a1 | a1 | tst.ts:184:12:184:17 | number |
| tst.ts:185:7:185:8 | a2 | a2 | tst.ts:185:12:185:17 | number |
| tst.ts:186:7:186:8 | a3 | a3 | tst.ts:186:12:186:17 | number |
+| tst.ts:192:18:192:18 | x | x | tst.ts:192:21:192:43 | [first: ... number] |
test_QualifiedTypeAccess
| tst.ts:63:19:63:21 | N.I | tst.ts:63:19:63:19 | N | tst.ts:63:21:63:21 | I |
| tst.ts:64:20:64:24 | N.M.I | tst.ts:64:20:64:22 | N.M | tst.ts:64:24:64:24 | I |
@@ -218,9 +219,15 @@ test_TupleTypeExpr
| tst.ts:176:16:176:31 | [number, number] | 1 | 2 | tst.ts:176:25:176:30 | number |
| tst.ts:179:21:179:44 | [...Str ... umbers] | 0 | 2 | tst.ts:179:22:179:31 | ...Strings |
| tst.ts:179:21:179:44 | [...Str ... umbers] | 1 | 2 | tst.ts:179:34:179:43 | ...Numbers |
+| tst.ts:192:21:192:43 | [first: ... number] | 0 | 2 | tst.ts:192:29:192:34 | number |
+| tst.ts:192:21:192:43 | [first: ... number] | 1 | 2 | tst.ts:192:37:192:42 | number |
+| tst.ts:192:47:192:70 | [number ... number] | 0 | 2 | tst.ts:192:48:192:53 | number |
+| tst.ts:192:47:192:70 | [number ... number] | 1 | 2 | tst.ts:192:64:192:69 | number |
test_TupleTypeElementName
| tst.ts:169:34:169:64 | [first: ... number] | 0 | tst.ts:169:35:169:39 | first |
| tst.ts:169:34:169:64 | [first: ... number] | 1 | tst.ts:169:50:169:55 | second |
+| tst.ts:192:21:192:43 | [first: ... number] | 0 | tst.ts:192:22:192:26 | first |
+| tst.ts:192:47:192:70 | [number ... number] | 1 | tst.ts:192:56:192:61 | second |
test_FieldTypes
| tst.ts:15:3:15:22 | numberField: number; | tst.ts:15:16:15:21 | number |
| tst.ts:16:3:16:22 | stringField: string; | tst.ts:16:16:16:21 | string |
@@ -297,6 +304,7 @@ test_ReturnTypes
| tst.ts:148:1:152:1 | functio ... ;\\n }\\n} | function assertIsString | tst.ts:148:36:148:56 | asserts ... string |
| tst.ts:164:1:166:1 | functio ... rr2];\\n} | function concat | tst.ts:164:66:164:77 | [...T, ...U] |
| tst.ts:169:1:172:1 | functio ... + b;\\n} | function labelOnTupleElements | tst.ts:169:68:169:73 | number |
+| tst.ts:192:1:194:1 | functio ... rn x;\\n} | function weirdId | tst.ts:192:47:192:70 | [number ... number] |
test_KeyofTypeExpr
| tst.ts:49:16:49:30 | keyof Interface | tst.ts:49:22:49:30 | Interface |
| tst.ts:113:26:113:35 | keyof Node | tst.ts:113:32:113:35 | Node |
diff --git a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tst.ts b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tst.ts
index 138576c4b4f..24a216bb443 100644
--- a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tst.ts
+++ b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tst.ts
@@ -186,4 +186,9 @@ function shortAssignment() {
let a3 : number = a1 ||= a2;
let a4 = a2 &&= a3;
let a5 = a3 ??= a4;
+}
+
+// only label on some tuple elements (is a type-error)
+function weirdId(x: [first: number, number]): [number, second: number] {
+ return x;
}
\ No newline at end of file
From ca7c045d312b2b1e926800c4afe21429757566ec Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Mon, 17 Aug 2020 16:24:00 +0200
Subject: [PATCH 068/146] Python: bad re match made the tests fail..
---
python/ql/test/experimental/dataflow/coverage/classes.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/test/experimental/dataflow/coverage/classes.py b/python/ql/test/experimental/dataflow/coverage/classes.py
index 75abe0c82e2..4cc78145fe0 100644
--- a/python/ql/test/experimental/dataflow/coverage/classes.py
+++ b/python/ql/test/experimental/dataflow/coverage/classes.py
@@ -8,7 +8,7 @@
# All functions starting with "test_" should run and print `"OK"`.
# This can be checked by running validTest.py.
-def OK() # Call not found:
+def OK():
print("OK")
# object.__new__(cls[, ...])
From bbf925fcc4993f16bdfdd4d3cf31a0796af2a060 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Tue, 18 Aug 2020 12:56:15 +0200
Subject: [PATCH 069/146] Python: Magic subscript and format (this in
preparation for addressing reviews)
---
python/ql/src/semmle/python/Magic.qll | 62 +++++++++++++++++--
.../coverage/classesCallGraph.expected | 7 +++
2 files changed, 63 insertions(+), 6 deletions(-)
diff --git a/python/ql/src/semmle/python/Magic.qll b/python/ql/src/semmle/python/Magic.qll
index 11767254ef0..76d4349c42a 100644
--- a/python/ql/src/semmle/python/Magic.qll
+++ b/python/ql/src/semmle/python/Magic.qll
@@ -22,8 +22,10 @@ module MagicMethod {
/** Gets the controlflow node that would be passed as the specified argument. */
abstract ControlFlowNode getArg(int n);
- /** Gets the control flow node corresponding to the instance
- * that would define the magic method. */
+ /**
+ * Gets the control flow node corresponding to the instance
+ * that would define the magic method.
+ */
ControlFlowNode getSelf() { result = this.getArg(0) }
}
@@ -47,11 +49,9 @@ module MagicMethod {
class MagicBinOp extends MagicMethod::Potential, BinaryExprNode {
Operator operator;
- MagicBinOp() { this.getOp() = operator}
+ MagicBinOp() { this.getOp() = operator }
- override string getMagicMethodName() {
- result = operator.getSpecialMethodName()
- }
+ override string getMagicMethodName() { result = operator.getSpecialMethodName() }
override ControlFlowNode getArg(int n) {
n = 0 and result = this.getLeft()
@@ -59,3 +59,53 @@ class MagicBinOp extends MagicMethod::Potential, BinaryExprNode {
n = 1 and result = this.getRight()
}
}
+
+/** A subscript expression node that might correspond to a magic method call. */
+abstract class MagicSubscript extends MagicMethod::Potential, SubscriptNode {
+ override ControlFlowNode getArg(int n) {
+ n = 0 and result = this.getObject()
+ or
+ n = 1 and result = this.getIndex()
+ }
+}
+
+/** A subscript expression node that might correspond to a call to __getitem__. */
+class MagicGetItem extends MagicSubscript {
+ MagicGetItem() { this.isLoad() }
+
+ override string getMagicMethodName() { result = "__getitem__" }
+}
+
+/** A subscript expression node that might correspond to a call to __setitem__. */
+class MagicSetItem extends MagicSubscript {
+ MagicSetItem() { this.isStore() }
+
+ override string getMagicMethodName() { result = "__setitem__" }
+
+ override ControlFlowNode getArg(int n) {
+ n = 0 and result = this.getObject()
+ or
+ n = 1 and result = this.getIndex()
+ or
+ n = 2 and result = this.getValueNode()
+ }
+
+ private ControlFlowNode getValueNode() {
+ exists(AssignStmt a |
+ a.getATarget() = this.getNode() and
+ result.getNode() = a.getValue()
+ )
+ or
+ exists(AugAssign a |
+ a.getTarget() = this.getNode() and
+ result.getNode() = a.getValue()
+ )
+ }
+}
+
+/** A subscript expression node that might correspond to a call to __delitem__. */
+class MagicDelItem extends MagicSubscript {
+ MagicDelItem() { this.isDelete() }
+
+ override string getMagicMethodName() { result = "__delitem__" }
+}
diff --git a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected
index 3a15759e3bd..1c54afef81e 100644
--- a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected
+++ b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected
@@ -3,6 +3,13 @@
| classes.py:178:7:178:28 | ControlFlowNode for frozenset() | classes.py:178:7:178:28 | ControlFlowNode for frozenset() |
| classes.py:182:7:182:26 | ControlFlowNode for dict() | classes.py:182:7:182:26 | ControlFlowNode for dict() |
| classes.py:303:28:303:51 | ControlFlowNode for dict() | classes.py:303:28:303:51 | ControlFlowNode for dict() |
+| classes.py:428:3:428:14 | ControlFlowNode for with_getitem | classes.py:422:19:422:22 | SSA variable self |
+| classes.py:428:16:428:16 | ControlFlowNode for IntegerLiteral | classes.py:422:25:422:27 | SSA variable key |
+| classes.py:438:3:438:14 | ControlFlowNode for with_setitem | classes.py:433:19:433:22 | SSA variable self |
+| classes.py:438:16:438:16 | ControlFlowNode for IntegerLiteral | classes.py:433:25:433:27 | SSA variable key |
+| classes.py:438:21:438:22 | ControlFlowNode for Str | classes.py:433:30:433:34 | SSA variable value |
+| classes.py:448:7:448:18 | ControlFlowNode for with_delitem | classes.py:443:19:443:22 | SSA variable self |
+| classes.py:448:20:448:20 | ControlFlowNode for IntegerLiteral | classes.py:443:25:443:27 | SSA variable key |
| classes.py:466:12:466:24 | ControlFlowNode for Attribute() | classes.py:466:12:466:24 | ControlFlowNode for Attribute() |
| classes.py:505:3:505:10 | ControlFlowNode for with_add | classes.py:499:15:499:18 | SSA variable self |
| classes.py:505:14:505:21 | ControlFlowNode for with_add | classes.py:499:21:499:25 | SSA variable other |
From 59cee284b5b027647cc23b3a5f557eacad810412 Mon Sep 17 00:00:00 2001
From: yoff
Date: Tue, 18 Aug 2020 12:59:04 +0200
Subject: [PATCH 070/146] Update
python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
Co-authored-by: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com>
---
.../ql/src/experimental/dataflow/internal/DataFlowPrivate.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
index 319bf69a414..790d9de60f1 100644
--- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
+++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
@@ -169,7 +169,7 @@ abstract class DataFlowCall extends TDataFlowCall {
/** Get the callable to which this call goes. */
abstract DataFlowCallable getCallable();
- /** Get the specified arguemnt to this call. */
+ /** Get the specified argument to this call. */
abstract ControlFlowNode getArg(int n);
/** Get the control flow node representing this call. */
From 571520602dfd2ddce58f5a5312a36e41c1def3e0 Mon Sep 17 00:00:00 2001
From: yoff
Date: Tue, 18 Aug 2020 12:59:20 +0200
Subject: [PATCH 071/146] Update
python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
Co-authored-by: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com>
---
.../ql/src/experimental/dataflow/internal/DataFlowPrivate.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
index 790d9de60f1..443774b0963 100644
--- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
+++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
@@ -180,7 +180,7 @@ abstract class DataFlowCall extends TDataFlowCall {
}
-/** Represents a call to a callable */
+/** Represents a call to a callable. */
class CallNodeCall extends DataFlowCall, TCallNode {
CallNode call;
DataFlowCallable callable;
From b9bf11adb49739f1c5f5a889aa19806ce5b3e46c Mon Sep 17 00:00:00 2001
From: yoff
Date: Tue, 18 Aug 2020 12:59:57 +0200
Subject: [PATCH 072/146] Update python/ql/src/semmle/python/Magic.qll
Co-authored-by: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com>
---
python/ql/src/semmle/python/Magic.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/src/semmle/python/Magic.qll b/python/ql/src/semmle/python/Magic.qll
index 76d4349c42a..5ee05f9c622 100644
--- a/python/ql/src/semmle/python/Magic.qll
+++ b/python/ql/src/semmle/python/Magic.qll
@@ -16,7 +16,7 @@ import python
module MagicMethod {
/** A control flow node which might correpsond to a magic method call. */
abstract class Potential extends ControlFlowNode {
- /** Gets the name of the method that would be called */
+ /** Gets the name of the method that would be called. */
abstract string getMagicMethodName();
/** Gets the controlflow node that would be passed as the specified argument. */
From d0eaa139740252814270cb0021adab46f50a45b7 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Tue, 18 Aug 2020 14:14:38 +0200
Subject: [PATCH 073/146] Python: Magic -> Special and reaarange classes
---
.../dataflow/internal/DataFlowPrivate.qll | 33 +++--
python/ql/src/semmle/python/Magic.qll | 111 -----------------
.../ql/src/semmle/python/SpecialMethods.qll | 117 ++++++++++++++++++
3 files changed, 133 insertions(+), 128 deletions(-)
delete mode 100644 python/ql/src/semmle/python/Magic.qll
create mode 100644 python/ql/src/semmle/python/SpecialMethods.qll
diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
index 443774b0963..be29cfb07f1 100644
--- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
+++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
@@ -1,6 +1,6 @@
private import python
private import DataFlowPublic
-import semmle.python.Magic
+import semmle.python.SpecialMethods
//--------
// Data flow graph
@@ -160,7 +160,7 @@ class DataFlowClassValue extends DataFlowCallable, TClassValue {
newtype TDataFlowCall =
TCallNode(CallNode call) or
- TMagicCall(MagicMethod::Actual magic)
+ TSpecialCall(SpecialMethodCallNode special)
abstract class DataFlowCall extends TDataFlowCall {
/** Gets a textual representation of this element. */
@@ -179,7 +179,6 @@ abstract class DataFlowCall extends TDataFlowCall {
abstract DataFlowCallable getEnclosingCallable();
}
-
/** Represents a call to a callable. */
class CallNodeCall extends DataFlowCall, TCallNode {
CallNode call;
@@ -192,9 +191,7 @@ class CallNodeCall extends DataFlowCall, TCallNode {
override string toString() { result = call.toString() }
- override ControlFlowNode getArg(int n) {
- result = call.getArg(n)
- }
+ override ControlFlowNode getArg(int n) { result = call.getArg(n) }
override ControlFlowNode getNode() { result = call }
@@ -203,22 +200,24 @@ class CallNodeCall extends DataFlowCall, TCallNode {
override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getNode().getScope() }
}
-class MagicCall extends DataFlowCall, TMagicCall {
- MagicMethod::Actual magic;
+class SpecialCall extends DataFlowCall, TSpecialCall {
+ SpecialMethodCallNode special;
- MagicCall() { this = TMagicCall(magic) }
+ SpecialCall() { this = TSpecialCall(special) }
- override string toString() { result = magic.toString() }
+ override string toString() { result = special.toString() }
- override ControlFlowNode getArg(int n) {
- result = magic.(MagicMethod::Potential).getArg(n)
+ override ControlFlowNode getArg(int n) { result = special.(SpecialMethod::Potential).getArg(n) }
+
+ override ControlFlowNode getNode() { result = special }
+
+ override DataFlowCallable getCallable() {
+ result = TCallableValue(special.getResolvedSpecialMethod())
}
- override ControlFlowNode getNode() { result = magic }
-
- override DataFlowCallable getCallable() { result = TCallableValue(magic.getResolvedMagicMethod()) }
-
- override DataFlowCallable getEnclosingCallable() { result.getScope() = magic.getNode().getScope() }
+ override DataFlowCallable getEnclosingCallable() {
+ result.getScope() = special.getNode().getScope()
+ }
}
/** A data flow node that represents a call argument. */
diff --git a/python/ql/src/semmle/python/Magic.qll b/python/ql/src/semmle/python/Magic.qll
deleted file mode 100644
index 5ee05f9c622..00000000000
--- a/python/ql/src/semmle/python/Magic.qll
+++ /dev/null
@@ -1,111 +0,0 @@
-/**
- * Provides support for magic methods.
- * This is done in two steps:
- * - A subset of `ControlFlowNode`s are labelled as potentially corresponding to
- * a magic method call (by being an instance of `MagicMethod::Potential`).
- * - A subset of the potential magic method calls are labelled as being actual
- * magic method calls (`MagicMethod::Actual`) if the appropriate method is defined.
- */
-
-import python
-
-/**
- * Machinery for detecting magic method calls.
- * Extend `MagicMethod::Potential` to capture more cases.
- */
-module MagicMethod {
- /** A control flow node which might correpsond to a magic method call. */
- abstract class Potential extends ControlFlowNode {
- /** Gets the name of the method that would be called. */
- abstract string getMagicMethodName();
-
- /** Gets the controlflow node that would be passed as the specified argument. */
- abstract ControlFlowNode getArg(int n);
-
- /**
- * Gets the control flow node corresponding to the instance
- * that would define the magic method.
- */
- ControlFlowNode getSelf() { result = this.getArg(0) }
- }
-
- /** A control flow node corresponding to a magic method call. */
- class Actual extends ControlFlowNode {
- Value resolvedMagicMethod;
-
- Actual() {
- exists(Potential pot |
- this.(Potential) = pot and
- pot.getSelf().pointsTo().getClass().lookup(pot.getMagicMethodName()) = resolvedMagicMethod
- )
- }
-
- /** The method that is called. */
- Value getResolvedMagicMethod() { result = resolvedMagicMethod }
- }
-}
-
-/** A binary expression node that might correspond to a magic method call. */
-class MagicBinOp extends MagicMethod::Potential, BinaryExprNode {
- Operator operator;
-
- MagicBinOp() { this.getOp() = operator }
-
- override string getMagicMethodName() { result = operator.getSpecialMethodName() }
-
- override ControlFlowNode getArg(int n) {
- n = 0 and result = this.getLeft()
- or
- n = 1 and result = this.getRight()
- }
-}
-
-/** A subscript expression node that might correspond to a magic method call. */
-abstract class MagicSubscript extends MagicMethod::Potential, SubscriptNode {
- override ControlFlowNode getArg(int n) {
- n = 0 and result = this.getObject()
- or
- n = 1 and result = this.getIndex()
- }
-}
-
-/** A subscript expression node that might correspond to a call to __getitem__. */
-class MagicGetItem extends MagicSubscript {
- MagicGetItem() { this.isLoad() }
-
- override string getMagicMethodName() { result = "__getitem__" }
-}
-
-/** A subscript expression node that might correspond to a call to __setitem__. */
-class MagicSetItem extends MagicSubscript {
- MagicSetItem() { this.isStore() }
-
- override string getMagicMethodName() { result = "__setitem__" }
-
- override ControlFlowNode getArg(int n) {
- n = 0 and result = this.getObject()
- or
- n = 1 and result = this.getIndex()
- or
- n = 2 and result = this.getValueNode()
- }
-
- private ControlFlowNode getValueNode() {
- exists(AssignStmt a |
- a.getATarget() = this.getNode() and
- result.getNode() = a.getValue()
- )
- or
- exists(AugAssign a |
- a.getTarget() = this.getNode() and
- result.getNode() = a.getValue()
- )
- }
-}
-
-/** A subscript expression node that might correspond to a call to __delitem__. */
-class MagicDelItem extends MagicSubscript {
- MagicDelItem() { this.isDelete() }
-
- override string getMagicMethodName() { result = "__delitem__" }
-}
diff --git a/python/ql/src/semmle/python/SpecialMethods.qll b/python/ql/src/semmle/python/SpecialMethods.qll
new file mode 100644
index 00000000000..53206ca657a
--- /dev/null
+++ b/python/ql/src/semmle/python/SpecialMethods.qll
@@ -0,0 +1,117 @@
+/**
+ * Provides support for special methods.
+ * This is done in two steps:
+ * - A subset of `ControlFlowNode`s are labelled as potentially corresponding to
+ * a special method call (by being an instance of `SpecialMethod::Potential`).
+ * - A subset of the potential special method calls are labelled as being actual
+ * special method calls (`SpecialMethodCallNode`) if the appropriate method is defined.
+ * Extend `SpecialMethod::Potential` to capture more cases.
+ */
+
+import python
+
+
+/** A control flow node which might correpsond to a special method call. */
+class PotentialSpecialMethodCallNode extends ControlFlowNode {
+ PotentialSpecialMethodCallNode() { this instanceof SpecialMethod::Potential}
+}
+
+/**
+ * Machinery for detecting special method calls.
+ * Extend `SpecialMethod::Potential` to capture more cases.
+ */
+module SpecialMethod {
+ /** A control flow node which might correpsond to a special method call. */
+ abstract class Potential extends ControlFlowNode {
+ /** Gets the name of the method that would be called. */
+ abstract string getSpecialMethodName();
+
+ /** Gets the controlflow node that would be passed as the specified argument. */
+ abstract ControlFlowNode getArg(int n);
+
+ /**
+ * Gets the control flow node corresponding to the instance
+ * that would define the special method.
+ */
+ ControlFlowNode getSelf() { result = this.getArg(0) }
+ }
+
+ /** A binary expression node that might correspond to a special method call. */
+ class SpecialBinOp extends Potential, BinaryExprNode {
+ Operator operator;
+
+ SpecialBinOp() { this.getOp() = operator }
+
+ override string getSpecialMethodName() { result = operator.getSpecialMethodName() }
+
+ override ControlFlowNode getArg(int n) {
+ n = 0 and result = this.getLeft()
+ or
+ n = 1 and result = this.getRight()
+ }
+ }
+
+ /** A subscript expression node that might correspond to a special method call. */
+ abstract class SpecialSubscript extends Potential, SubscriptNode {
+ override ControlFlowNode getArg(int n) {
+ n = 0 and result = this.getObject()
+ or
+ n = 1 and result = this.getIndex()
+ }
+ }
+
+ /** A subscript expression node that might correspond to a call to __getitem__. */
+ class SpecialGetItem extends SpecialSubscript {
+ SpecialGetItem() { this.isLoad() }
+
+ override string getSpecialMethodName() { result = "__getitem__" }
+ }
+
+ /** A subscript expression node that might correspond to a call to __setitem__. */
+ class SpecialSetItem extends SpecialSubscript {
+ SpecialSetItem() { this.isStore() }
+
+ override string getSpecialMethodName() { result = "__setitem__" }
+
+ override ControlFlowNode getArg(int n) {
+ n = 0 and result = this.getObject()
+ or
+ n = 1 and result = this.getIndex()
+ or
+ n = 2 and result = this.getValueNode()
+ }
+
+ private ControlFlowNode getValueNode() {
+ exists(AssignStmt a |
+ a.getATarget() = this.getNode() and
+ result.getNode() = a.getValue()
+ )
+ or
+ exists(AugAssign a |
+ a.getTarget() = this.getNode() and
+ result.getNode() = a.getValue()
+ )
+ }
+ }
+
+ /** A subscript expression node that might correspond to a call to __delitem__. */
+ class SpecialDelItem extends SpecialSubscript {
+ SpecialDelItem() { this.isDelete() }
+
+ override string getSpecialMethodName() { result = "__delitem__" }
+ }
+}
+
+class SpecialMethodCallNode extends PotentialSpecialMethodCallNode {
+ Value resolvedSpecialMethod;
+
+ SpecialMethodCallNode() {
+ exists(SpecialMethod::Potential pot |
+ this.(SpecialMethod::Potential) = pot and
+ pot.getSelf().pointsTo().getClass().lookup(pot.getSpecialMethodName()) = resolvedSpecialMethod
+ )
+ }
+
+ /** The method that is called. */
+ Value getResolvedSpecialMethod() { result = resolvedSpecialMethod }
+}
From aab603d26154802a08741e25ec15f82519763702 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Tue, 18 Aug 2020 14:37:59 +0200
Subject: [PATCH 074/146] Python: QL doc
---
python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll | 1 +
1 file changed, 1 insertion(+)
diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
index be29cfb07f1..1ca52983b21 100644
--- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
+++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
@@ -200,6 +200,7 @@ class CallNodeCall extends DataFlowCall, TCallNode {
override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getNode().getScope() }
}
+/** Represents a call to a special method. */
class SpecialCall extends DataFlowCall, TSpecialCall {
SpecialMethodCallNode special;
From f8364dc74b085246f17dd87b5909ee4d16c80cf0 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Tue, 18 Aug 2020 15:11:20 +0200
Subject: [PATCH 075/146] Python: QL doc
---
python/ql/src/semmle/python/SpecialMethods.qll | 1 +
1 file changed, 1 insertion(+)
diff --git a/python/ql/src/semmle/python/SpecialMethods.qll b/python/ql/src/semmle/python/SpecialMethods.qll
index 53206ca657a..a180ee9253d 100644
--- a/python/ql/src/semmle/python/SpecialMethods.qll
+++ b/python/ql/src/semmle/python/SpecialMethods.qll
@@ -102,6 +102,7 @@ module SpecialMethod {
}
}
+/** A control flow node corresponding to a special method call. */
class SpecialMethodCallNode extends PotentialSpecialMethodCallNode {
Value resolvedSpecialMethod;
From de1c75c279a97d7aa6039d208f79522b63f64f7c Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Tue, 18 Aug 2020 16:34:04 +0200
Subject: [PATCH 076/146] Python: QL format
---
python/ql/src/semmle/python/SpecialMethods.qll | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/python/ql/src/semmle/python/SpecialMethods.qll b/python/ql/src/semmle/python/SpecialMethods.qll
index a180ee9253d..d7fd44471f8 100644
--- a/python/ql/src/semmle/python/SpecialMethods.qll
+++ b/python/ql/src/semmle/python/SpecialMethods.qll
@@ -10,10 +10,9 @@
import python
-
/** A control flow node which might correpsond to a special method call. */
class PotentialSpecialMethodCallNode extends ControlFlowNode {
- PotentialSpecialMethodCallNode() { this instanceof SpecialMethod::Potential}
+ PotentialSpecialMethodCallNode() { this instanceof SpecialMethod::Potential }
}
/**
From 28578fd5721b4ccae192d21efdcd723091ad6743 Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Wed, 19 Aug 2020 13:12:24 +0200
Subject: [PATCH 077/146] Java: Autoformat.
---
java/ql/src/semmle/code/java/StringFormat.qll | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/java/ql/src/semmle/code/java/StringFormat.qll b/java/ql/src/semmle/code/java/StringFormat.qll
index 69e6d4b056e..6ac122b58d8 100644
--- a/java/ql/src/semmle/code/java/StringFormat.qll
+++ b/java/ql/src/semmle/code/java/StringFormat.qll
@@ -95,7 +95,8 @@ class FmtSyntax extends TFmtSyntax {
}
private Expr getArgumentOrQualifier(Call c, int i) {
- result = c.getArgument(i) or
+ result = c.getArgument(i)
+ or
result = c.getQualifier() and i = -1
}
From 18e946d4aae17c30367a66a6dfbba6d1b2f0b61a Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Wed, 19 Aug 2020 17:56:02 +0200
Subject: [PATCH 078/146] Python: Small rearrangement
---
.../dataflow/internal/DataFlowPrivate.qll | 18 +++
.../dataflow/internal/DataFlowPublic.qll | 20 +--
.../dataflow/basic/local.expected | 6 -
.../dataflow/basic/sinks.expected | 6 -
.../dataflow/basic/sources.expected | 6 -
.../consistency/dataflow-consistency.expected | 116 ------------------
6 files changed, 23 insertions(+), 149 deletions(-)
diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
index 1f46fd341e9..eeceb673793 100644
--- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
+++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
@@ -7,6 +7,24 @@ private import DataFlowPublic
//--------
// Nodes
//--------
+/** A control flow node which is also a dataflow node */
+abstract class DataFlowCfgNode extends ControlFlowNode { }
+
+/** Literals contribute to dataflow */
+class CfgLiteralNode extends DataFlowCfgNode {
+ CfgLiteralNode() { this.isLiteral() }
+}
+
+/** Variables contribute to dataflow */
+class CfgNameNode extends DataFlowCfgNode {
+ CfgNameNode() { this instanceof NameNode }
+}
+
+/** Calls contribute to dataflow */
+class CfgCallNode extends DataFlowCfgNode {
+ CfgCallNode() { this instanceof CallNode }
+}
+
/**
* A node associated with an object after an operation that might have
* changed its state.
diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll
index d03ba150711..12118f75182 100644
--- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll
+++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll
@@ -20,7 +20,7 @@ newtype TNode =
/** A node corresponding to an SSA variable. */
TEssaNode(EssaVariable var) or
/** A node corresponding to a control flow node. */
- TCfgNode(ControlFlowNode node)
+ TCfgNode(DataFlowCfgNode node)
/**
* An element, viewed as a node in a data flow graph. Either an SSA variable
@@ -68,12 +68,12 @@ class EssaNode extends Node, TEssaNode {
override Location getLocation() { result = var.getDefinition().getLocation() }
}
-abstract class CfgNode extends Node, TCfgNode {
- ControlFlowNode node;
+class CfgNode extends Node, TCfgNode {
+ DataFlowCfgNode node;
CfgNode() { this = TCfgNode(node) }
- ControlFlowNode getNode() { result = node }
+ DataFlowCfgNode getNode() { result = node }
/** Gets a textual representation of this element. */
override string toString() { result = node.toString() }
@@ -83,10 +83,6 @@ abstract class CfgNode extends Node, TCfgNode {
override Location getLocation() { result = node.getLocation() }
}
-class CfgLiteralNode extends CfgNode {
- CfgLiteralNode() { node.isLiteral() }
-}
-
/**
* An expression, viewed as a node in a data flow graph.
*
@@ -94,13 +90,7 @@ class CfgLiteralNode extends CfgNode {
* to multiple `ExprNode`s, just like it may correspond to multiple
* `ControlFlow::Node`s.
*/
-class ExprNode extends CfgNode {
- ExprNode() {
- // node.isAttribute()
- // or
- node instanceof NameNode
- }
-}
+class ExprNode extends CfgNode { }
/** Gets a node corresponding to expression `e`. */
ExprNode exprNode(DataFlowExpr e) { none() }
diff --git a/python/ql/test/experimental/dataflow/basic/local.expected b/python/ql/test/experimental/dataflow/basic/local.expected
index 60c9928ebfd..217afdc3185 100644
--- a/python/ql/test/experimental/dataflow/basic/local.expected
+++ b/python/ql/test/experimental/dataflow/basic/local.expected
@@ -1,9 +1,3 @@
-| file://:0:0:0:0 | Data flow node | file://:0:0:0:0 | Data flow node |
-| file://:0:0:0:0 | Data flow node | file://:0:0:0:0 | Data flow node |
-| file://:0:0:0:0 | Data flow node | file://:0:0:0:0 | Data flow node |
-| file://:0:0:0:0 | Data flow node | file://:0:0:0:0 | Data flow node |
-| file://:0:0:0:0 | Data flow node | file://:0:0:0:0 | Data flow node |
-| file://:0:0:0:0 | Data flow node | file://:0:0:0:0 | Data flow node |
| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:0:0:0:0 | GSSA Variable __name__ |
| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:0:0:0:0 | GSSA Variable __package__ |
| test.py:0:0:0:0 | GSSA Variable b | test.py:0:0:0:0 | GSSA Variable b |
diff --git a/python/ql/test/experimental/dataflow/basic/sinks.expected b/python/ql/test/experimental/dataflow/basic/sinks.expected
index 9ff2ae53247..2cd04d0e82c 100644
--- a/python/ql/test/experimental/dataflow/basic/sinks.expected
+++ b/python/ql/test/experimental/dataflow/basic/sinks.expected
@@ -1,9 +1,3 @@
-| file://:0:0:0:0 | Data flow node |
-| file://:0:0:0:0 | Data flow node |
-| file://:0:0:0:0 | Data flow node |
-| file://:0:0:0:0 | Data flow node |
-| file://:0:0:0:0 | Data flow node |
-| file://:0:0:0:0 | Data flow node |
| test.py:0:0:0:0 | GSSA Variable __name__ |
| test.py:0:0:0:0 | GSSA Variable __package__ |
| test.py:0:0:0:0 | GSSA Variable b |
diff --git a/python/ql/test/experimental/dataflow/basic/sources.expected b/python/ql/test/experimental/dataflow/basic/sources.expected
index 9ff2ae53247..2cd04d0e82c 100644
--- a/python/ql/test/experimental/dataflow/basic/sources.expected
+++ b/python/ql/test/experimental/dataflow/basic/sources.expected
@@ -1,9 +1,3 @@
-| file://:0:0:0:0 | Data flow node |
-| file://:0:0:0:0 | Data flow node |
-| file://:0:0:0:0 | Data flow node |
-| file://:0:0:0:0 | Data flow node |
-| file://:0:0:0:0 | Data flow node |
-| file://:0:0:0:0 | Data flow node |
| test.py:0:0:0:0 | GSSA Variable __name__ |
| test.py:0:0:0:0 | GSSA Variable __package__ |
| test.py:0:0:0:0 | GSSA Variable b |
diff --git a/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected
index 759c20be6e6..f7f7f7a7211 100644
--- a/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected
+++ b/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected
@@ -37,123 +37,7 @@ uniqueEnclosingCallable
| test.py:140:6:140:11 | ControlFlowNode for unsafe | Node should have one enclosing callable but has 0. |
uniqueType
uniqueNodeLocation
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
-| file://:0:0:0:0 | Data flow node | Node should have one location but has 0. |
missingLocation
-| Nodes without location: 115 |
uniqueNodeToString
missingToString
parameterCallable
From 5a734730de1b7a4de1fd479881714a550e9f8869 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Thu, 20 Aug 2020 15:00:42 +0200
Subject: [PATCH 079/146] Python: Control flow nodes are dataflow nodes iff
they are expression nodes We could refine this later, but it seems to work
for now...
---
.../dataflow/internal/DataFlowPrivate.qll | 19 +++-------
.../dataflow/internal/DataFlowPublic.qll | 6 ++--
.../dataflow/basic/global.expected | 4 +++
.../dataflow/basic/globalStep.expected | 2 ++
.../dataflow/basic/local.expected | 1 +
.../dataflow/basic/sinks.expected | 1 +
.../dataflow/basic/sources.expected | 1 +
.../consistency/dataflow-consistency.expected | 35 +++++++++++++++++++
.../dataflow/coverage/localFlow.expected | 1 +
9 files changed, 53 insertions(+), 17 deletions(-)
diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
index eeceb673793..22e08f1f216 100644
--- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
+++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
@@ -7,22 +7,11 @@ private import DataFlowPublic
//--------
// Nodes
//--------
+predicate isExpressionNode(ControlFlowNode node) { node.getNode() instanceof Expr }
+
/** A control flow node which is also a dataflow node */
-abstract class DataFlowCfgNode extends ControlFlowNode { }
-
-/** Literals contribute to dataflow */
-class CfgLiteralNode extends DataFlowCfgNode {
- CfgLiteralNode() { this.isLiteral() }
-}
-
-/** Variables contribute to dataflow */
-class CfgNameNode extends DataFlowCfgNode {
- CfgNameNode() { this instanceof NameNode }
-}
-
-/** Calls contribute to dataflow */
-class CfgCallNode extends DataFlowCfgNode {
- CfgCallNode() { this instanceof CallNode }
+class DataFlowCfgNode extends ControlFlowNode {
+ DataFlowCfgNode() { isExpressionNode(this) }
}
/**
diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll
index 12118f75182..122ea0ca9fd 100644
--- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll
+++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll
@@ -90,10 +90,12 @@ class CfgNode extends Node, TCfgNode {
* to multiple `ExprNode`s, just like it may correspond to multiple
* `ControlFlow::Node`s.
*/
-class ExprNode extends CfgNode { }
+class ExprNode extends CfgNode {
+ ExprNode() { isExpressionNode(node) }
+}
/** Gets a node corresponding to expression `e`. */
-ExprNode exprNode(DataFlowExpr e) { none() }
+ExprNode exprNode(DataFlowExpr e) { result.getNode().getNode() = e }
/**
* The value of a parameter at function entry, viewed as a node in a data
diff --git a/python/ql/test/experimental/dataflow/basic/global.expected b/python/ql/test/experimental/dataflow/basic/global.expected
index 73e5e22c98d..4e616d5b6e4 100644
--- a/python/ql/test/experimental/dataflow/basic/global.expected
+++ b/python/ql/test/experimental/dataflow/basic/global.expected
@@ -4,6 +4,10 @@
| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:0:0:0:0 | GSSA Variable b | test.py:7:1:7:1 | GSSA Variable b |
| test.py:0:0:0:0 | GSSA Variable b | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
+| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable obfuscated_id |
+| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:7:1:7:1 | GSSA Variable b |
+| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
+| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:1:7:1 | GSSA Variable b |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
diff --git a/python/ql/test/experimental/dataflow/basic/globalStep.expected b/python/ql/test/experimental/dataflow/basic/globalStep.expected
index 7e15cd93025..58aa95a3621 100644
--- a/python/ql/test/experimental/dataflow/basic/globalStep.expected
+++ b/python/ql/test/experimental/dataflow/basic/globalStep.expected
@@ -4,6 +4,8 @@
| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:0:0:0:0 | GSSA Variable b | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:0:0:0:0 | GSSA Variable b | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
+| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable obfuscated_id |
+| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable obfuscated_id |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
diff --git a/python/ql/test/experimental/dataflow/basic/local.expected b/python/ql/test/experimental/dataflow/basic/local.expected
index 217afdc3185..4777c30adfd 100644
--- a/python/ql/test/experimental/dataflow/basic/local.expected
+++ b/python/ql/test/experimental/dataflow/basic/local.expected
@@ -2,6 +2,7 @@
| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:0:0:0:0 | GSSA Variable __package__ |
| test.py:0:0:0:0 | GSSA Variable b | test.py:0:0:0:0 | GSSA Variable b |
| test.py:0:0:0:0 | SSA variable $ | test.py:0:0:0:0 | SSA variable $ |
+| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:1:1:21 | ControlFlowNode for FunctionExpr |
| test.py:1:5:1:17 | ControlFlowNode for obfuscated_id | test.py:1:5:1:17 | ControlFlowNode for obfuscated_id |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:1:5:1:17 | GSSA Variable obfuscated_id |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:1:19:1:19 | ControlFlowNode for x |
diff --git a/python/ql/test/experimental/dataflow/basic/sinks.expected b/python/ql/test/experimental/dataflow/basic/sinks.expected
index 2cd04d0e82c..8b292d55b5e 100644
--- a/python/ql/test/experimental/dataflow/basic/sinks.expected
+++ b/python/ql/test/experimental/dataflow/basic/sinks.expected
@@ -2,6 +2,7 @@
| test.py:0:0:0:0 | GSSA Variable __package__ |
| test.py:0:0:0:0 | GSSA Variable b |
| test.py:0:0:0:0 | SSA variable $ |
+| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr |
| test.py:1:5:1:17 | ControlFlowNode for obfuscated_id |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id |
| test.py:1:19:1:19 | ControlFlowNode for x |
diff --git a/python/ql/test/experimental/dataflow/basic/sources.expected b/python/ql/test/experimental/dataflow/basic/sources.expected
index 2cd04d0e82c..8b292d55b5e 100644
--- a/python/ql/test/experimental/dataflow/basic/sources.expected
+++ b/python/ql/test/experimental/dataflow/basic/sources.expected
@@ -2,6 +2,7 @@
| test.py:0:0:0:0 | GSSA Variable __package__ |
| test.py:0:0:0:0 | GSSA Variable b |
| test.py:0:0:0:0 | SSA variable $ |
+| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr |
| test.py:1:5:1:17 | ControlFlowNode for obfuscated_id |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id |
| test.py:1:19:1:19 | ControlFlowNode for x |
diff --git a/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected
index f7f7f7a7211..20054ec34f8 100644
--- a/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected
+++ b/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected
@@ -5,36 +5,71 @@ uniqueEnclosingCallable
| test.py:0:0:0:0 | GSSA Variable test24 | Node should have one enclosing callable but has 0. |
| test.py:0:0:0:0 | GSSA Variable test_truth | Node should have one enclosing callable but has 0. |
| test.py:0:0:0:0 | GSSA Variable test_update_extend | Node should have one enclosing callable but has 0. |
+| test.py:6:1:6:12 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:6:5:6:9 | GSSA Variable test1 | Node should have one enclosing callable but has 0. |
+| test.py:9:1:9:12 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:9:5:9:9 | GSSA Variable test2 | Node should have one enclosing callable but has 0. |
+| test.py:13:1:13:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:13:5:13:10 | GSSA Variable source | Node should have one enclosing callable but has 0. |
+| test.py:16:1:16:14 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:16:5:16:8 | GSSA Variable sink | Node should have one enclosing callable but has 0. |
+| test.py:19:1:19:12 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:19:5:19:9 | GSSA Variable test3 | Node should have one enclosing callable but has 0. |
+| test.py:23:1:23:12 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:23:5:23:9 | GSSA Variable test4 | Node should have one enclosing callable but has 0. |
+| test.py:27:1:27:12 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:27:5:27:9 | GSSA Variable test5 | Node should have one enclosing callable but has 0. |
+| test.py:31:1:31:16 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:31:5:31:9 | GSSA Variable test6 | Node should have one enclosing callable but has 0. |
+| test.py:39:1:39:16 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:39:5:39:9 | GSSA Variable test7 | Node should have one enclosing callable but has 0. |
+| test.py:47:1:47:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:47:5:47:11 | GSSA Variable source2 | Node should have one enclosing callable but has 0. |
+| test.py:50:1:50:15 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:50:5:50:9 | GSSA Variable sink2 | Node should have one enclosing callable but has 0. |
+| test.py:53:1:53:21 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:53:5:53:9 | GSSA Variable sink3 | Node should have one enclosing callable but has 0. |
+| test.py:57:1:57:16 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:57:5:57:9 | GSSA Variable test8 | Node should have one enclosing callable but has 0. |
+| test.py:62:1:62:16 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:62:5:62:9 | GSSA Variable test9 | Node should have one enclosing callable but has 0. |
+| test.py:69:1:69:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:69:5:69:10 | GSSA Variable test10 | Node should have one enclosing callable but has 0. |
+| test.py:76:1:76:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:76:5:76:7 | GSSA Variable hub | Node should have one enclosing callable but has 0. |
+| test.py:79:1:79:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:79:5:79:10 | GSSA Variable test11 | Node should have one enclosing callable but has 0. |
+| test.py:84:1:84:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:84:5:84:10 | GSSA Variable test12 | Node should have one enclosing callable but has 0. |
+| test.py:89:8:89:13 | ControlFlowNode for ImportExpr | Node should have one enclosing callable but has 0. |
| test.py:89:8:89:13 | GSSA Variable module | Node should have one enclosing callable but has 0. |
+| test.py:91:1:91:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:91:5:91:10 | GSSA Variable test13 | Node should have one enclosing callable but has 0. |
+| test.py:95:1:95:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:95:5:95:10 | GSSA Variable test14 | Node should have one enclosing callable but has 0. |
+| test.py:99:1:99:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:99:5:99:10 | GSSA Variable test15 | Node should have one enclosing callable but has 0. |
+| test.py:103:1:103:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:103:5:103:10 | GSSA Variable test16 | Node should have one enclosing callable but has 0. |
+| test.py:108:1:108:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:108:5:108:10 | GSSA Variable test20 | Node should have one enclosing callable but has 0. |
+| test.py:118:1:118:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:118:5:118:10 | GSSA Variable test21 | Node should have one enclosing callable but has 0. |
+| test.py:128:1:128:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:128:5:128:10 | GSSA Variable test22 | Node should have one enclosing callable but has 0. |
+| test.py:139:20:139:38 | ControlFlowNode for ImportMember | Node should have one enclosing callable but has 0. |
| test.py:139:33:139:38 | GSSA Variable unsafe | Node should have one enclosing callable but has 0. |
| test.py:140:1:140:12 | ControlFlowNode for SINK() | Node should have one enclosing callable but has 0. |
| test.py:140:1:140:12 | GSSA Variable unsafe | Node should have one enclosing callable but has 0. |
| test.py:140:6:140:11 | ControlFlowNode for unsafe | Node should have one enclosing callable but has 0. |
+| test.py:142:1:142:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
+| test.py:142:5:142:10 | GSSA Variable test23 | Node should have one enclosing callable but has 0. |
+| test.py:146:1:146:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
+| test.py:146:5:146:10 | GSSA Variable test24 | Node should have one enclosing callable but has 0. |
+| test.py:151:1:151:29 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
+| test.py:151:5:151:22 | GSSA Variable test_update_extend | Node should have one enclosing callable but has 0. |
+| test.py:161:1:161:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
+| test.py:161:5:161:14 | GSSA Variable test_truth | Node should have one enclosing callable but has 0. |
uniqueType
uniqueNodeLocation
missingLocation
diff --git a/python/ql/test/experimental/dataflow/coverage/localFlow.expected b/python/ql/test/experimental/dataflow/coverage/localFlow.expected
index 54e4a36a7fe..d9306fab9f6 100644
--- a/python/ql/test/experimental/dataflow/coverage/localFlow.expected
+++ b/python/ql/test/experimental/dataflow/coverage/localFlow.expected
@@ -2,3 +2,4 @@
| test.py:32:10:32:26 | ControlFlowNode for Tuple | test.py:32:5:32:5 | SSA variable x |
| test.py:33:5:33:5 | SSA variable y | test.py:34:5:34:11 | SSA variable y |
| test.py:33:5:33:5 | SSA variable y | test.py:34:10:34:10 | ControlFlowNode for y |
+| test.py:33:9:33:12 | ControlFlowNode for Subscript | test.py:33:5:33:5 | SSA variable y |
From 94e6fd9199d118c92a1f80107f1be69690062430 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Thu, 20 Aug 2020 15:16:23 +0200
Subject: [PATCH 080/146] Python: Convenience methods asVar, asCfgNode, and
asExpr
---
.../dataflow/internal/DataFlowPublic.qll | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll
index 122ea0ca9fd..55fe94350cc 100644
--- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll
+++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll
@@ -51,6 +51,15 @@ class Node extends TNode {
) {
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
+
+ /** Convenience method for casting to EssaNode and calling getVar. */
+ EssaVariable asVar() { none() }
+
+ /** Convenience method for casting to CfgNode and calling getNode. */
+ DataFlowCfgNode asCfgNode() { none() }
+
+ /** Convenience method for casting to ExprNode and calling getNode and getNode again. */
+ Expr asExpr() { none() }
}
class EssaNode extends Node, TEssaNode {
@@ -60,6 +69,8 @@ class EssaNode extends Node, TEssaNode {
EssaVariable getVar() { result = var }
+ override EssaVariable asVar() { result = var }
+
/** Gets a textual representation of this element. */
override string toString() { result = var.toString() }
@@ -75,6 +86,8 @@ class CfgNode extends Node, TCfgNode {
DataFlowCfgNode getNode() { result = node }
+ override DataFlowCfgNode asCfgNode() { result = node }
+
/** Gets a textual representation of this element. */
override string toString() { result = node.toString() }
@@ -92,6 +105,8 @@ class CfgNode extends Node, TCfgNode {
*/
class ExprNode extends CfgNode {
ExprNode() { isExpressionNode(node) }
+
+ override Expr asExpr() { result = node.getNode() }
}
/** Gets a node corresponding to expression `e`. */
From 9cdee63ed7967f32a585f502caac1674ba316ea7 Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Wed, 19 Aug 2020 14:53:40 +0200
Subject: [PATCH 081/146] C#: Enable nullability checks on
Semmle.Extraction.CIL
---
.../Semmle.Extraction.CIL/CachedFunction.cs | 2 +-
.../Semmle.Extraction.CIL/Context.cs | 23 +-
.../Entities/Assembly.cs | 10 +-
.../Entities/Attribute.cs | 8 +-
.../Semmle.Extraction.CIL/Entities/Event.cs | 2 +-
.../Entities/ExceptionRegion.cs | 4 +-
.../Semmle.Extraction.CIL/Entities/Field.cs | 4 +-
.../Semmle.Extraction.CIL/Entities/File.cs | 10 +-
.../Semmle.Extraction.CIL/Entities/Folder.cs | 2 +-
.../Entities/Instruction.cs | 17 +-
.../Semmle.Extraction.CIL/Entities/Method.cs | 65 ++---
.../Entities/Namespace.cs | 10 +-
.../Entities/Parameter.cs | 2 +-
.../Entities/Property.cs | 2 +-
.../Entities/SourceLocation.cs | 2 +-
.../Semmle.Extraction.CIL/Entities/Type.cs | 252 +++++++++++-------
.../Semmle.Extraction.CIL/Factories.cs | 2 +-
.../PDB/MetadataPdbReader.cs | 8 +-
.../PDB/NativePdbReader.cs | 16 +-
.../Semmle.Extraction.CIL/PDB/PdbReader.cs | 19 +-
.../Semmle.Extraction.CIL.csproj | 1 +
.../Semmle.Util/IEnumerableExtensions.cs | 4 +-
22 files changed, 286 insertions(+), 179 deletions(-)
diff --git a/csharp/extractor/Semmle.Extraction.CIL/CachedFunction.cs b/csharp/extractor/Semmle.Extraction.CIL/CachedFunction.cs
index 3bbc386a691..d127addf899 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/CachedFunction.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/CachedFunction.cs
@@ -9,7 +9,7 @@ namespace Semmle.Extraction.CIL
///
/// The type of the source.
/// The type of the generated object.
- public class CachedFunction
+ public class CachedFunction where SrcType : notnull
{
readonly Func generator;
readonly Dictionary cache;
diff --git a/csharp/extractor/Semmle.Extraction.CIL/Context.cs b/csharp/extractor/Semmle.Extraction.CIL/Context.cs
index 79fb8b7a8d4..618293553f0 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/Context.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/Context.cs
@@ -14,13 +14,19 @@ namespace Semmle.Extraction.CIL
///
partial class Context : IDisposable
{
- public Extraction.Context cx;
readonly FileStream stream;
- public readonly MetadataReader mdReader;
- public readonly PEReader peReader;
- public readonly string assemblyPath;
- public Entities.Assembly assembly;
- public PDB.IPdb pdb;
+ Entities.Assembly? assemblyNull;
+
+ public Extraction.Context cx { get; }
+ public MetadataReader mdReader { get; }
+ public PEReader peReader { get; }
+ public string assemblyPath { get; }
+ public Entities.Assembly assembly
+ {
+ get { return assemblyNull!; }
+ set { assemblyNull = value; }
+ }
+ public PDB.IPdb? pdb { get; }
public Context(Extraction.Context cx, string assemblyPath, bool extractPdbs)
{
@@ -105,7 +111,7 @@ namespace Semmle.Extraction.CIL
///
/// The handle of the method.
/// The debugging information, or null if the information could not be located.
- public PDB.IMethod GetMethodDebugInformation(MethodDefinitionHandle handle)
+ public PDB.IMethod? GetMethodDebugInformation(MethodDefinitionHandle handle)
{
return pdb == null ? null : pdb.GetMethod(handle.ToDebugInformationHandle());
}
@@ -125,7 +131,8 @@ namespace Semmle.Extraction.CIL
}
///
- /// The list of generic type parameters.
+ /// The list of generic type parameters, including type parameters of
+ /// containing types.
///
public abstract IEnumerable TypeParameters { get; }
diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs
index 255d37699f2..0368888caa7 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs
@@ -47,9 +47,9 @@ namespace Semmle.Extraction.CIL.Entities
trapFile.Write(cx.assemblyPath.Replace("\\", "/"));
}
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
- return GetType() == obj.GetType() && Equals(file, ((Assembly)obj).file);
+ return GetType() == obj?.GetType() && Equals(file, ((Assembly)obj).file);
}
public override int GetHashCode() => 7 * file.GetHashCode();
@@ -63,7 +63,7 @@ namespace Semmle.Extraction.CIL.Entities
get
{
yield return file;
- yield return Tuples.assemblies(this, file, FullName, assemblyName.Name, assemblyName.Version.ToString());
+ yield return Tuples.assemblies(this, file, FullName, assemblyName.Name ?? string.Empty, assemblyName.Version?.ToString() ?? string.Empty);
if (cx.pdb != null)
{
@@ -75,7 +75,7 @@ namespace Semmle.Extraction.CIL.Entities
foreach (var handle in cx.mdReader.TypeDefinitions)
{
- IExtractionProduct product = null;
+ IExtractionProduct? product = null;
try
{
product = cx.Create(handle);
@@ -92,7 +92,7 @@ namespace Semmle.Extraction.CIL.Entities
foreach (var handle in cx.mdReader.MethodDefinitions)
{
- IExtractionProduct product = null;
+ IExtractionProduct? product = null;
try
{
product = cx.Create(handle);
diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Attribute.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Attribute.cs
index ed3e866c16f..f1d0a818e07 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Attribute.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Attribute.cs
@@ -27,7 +27,7 @@ namespace Semmle.Extraction.CIL.Entities
this.@object = @object;
}
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
return obj is Attribute attribute && handle.Equals(attribute.handle);
}
@@ -58,13 +58,15 @@ namespace Semmle.Extraction.CIL.Entities
for (int index = 0; index < decoded.FixedArguments.Length; ++index)
{
object value = decoded.FixedArguments[index].Value;
- yield return Tuples.cil_attribute_positional_argument(this, index, value == null ? "null" : value.ToString());
+ var stringValue = value?.ToString();
+ yield return Tuples.cil_attribute_positional_argument(this, index, stringValue ?? "null");
}
foreach (var p in decoded.NamedArguments)
{
object value = p.Value;
- yield return Tuples.cil_attribute_named_argument(this, p.Name, value == null ? "null" : value.ToString());
+ var stringValue = value?.ToString();
+ yield return Tuples.cil_attribute_named_argument(this, p.Name, stringValue ?? "null");
}
}
}
diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Event.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Event.cs
index da33b9a63d5..dc3453a786c 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Event.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Event.cs
@@ -36,7 +36,7 @@ namespace Semmle.Extraction.CIL.Entities
public override string IdSuffix => ";cil-event";
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
return obj is Event e && handle.Equals(e.handle);
}
diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/ExceptionRegion.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/ExceptionRegion.cs
index 57035d993af..260fc3c357f 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/Entities/ExceptionRegion.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/ExceptionRegion.cs
@@ -30,7 +30,7 @@ namespace Semmle.Extraction.CIL.Entities
{
get
{
- IInstruction try_start, try_end, handler_start;
+ IInstruction? try_start, try_end, handler_start;
if (!jump_table.TryGetValue(r.TryOffset, out try_start))
throw new InternalError("Failed to retrieve handler");
@@ -44,7 +44,7 @@ namespace Semmle.Extraction.CIL.Entities
if (r.FilterOffset != -1)
{
- IInstruction filter_start;
+ IInstruction? filter_start;
if (!jump_table.TryGetValue(r.FilterOffset, out filter_start))
throw new InternalError("ExceptionRegion filter clause");
diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Field.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Field.cs
index 400b48fc07e..9e633ef4219 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Field.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Field.cs
@@ -91,7 +91,7 @@ namespace Semmle.Extraction.CIL.Entities
fd = cx.mdReader.GetFieldDefinition(handle);
}
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
return obj is DefinitionField field && handle.Equals(field.handle);
}
@@ -153,7 +153,7 @@ namespace Semmle.Extraction.CIL.Entities
declType = (Type)cx.CreateGeneric(gc, mr.Parent);
}
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
return obj is MemberReferenceField field && Handle.Equals(field.Handle);
}
diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs
index bc8c4c8c76d..0ff31dcc6f1 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs
@@ -25,9 +25,9 @@ namespace Semmle.Extraction.CIL.Entities
trapFile.Write(Semmle.Extraction.Entities.File.PathAsDatabaseId(path));
}
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
- return GetType() == obj.GetType() && path == ((File)obj).path;
+ return GetType() == obj?.GetType() && path == ((File)obj).path;
}
public override int GetHashCode() => 11 * path.GetHashCode();
@@ -36,7 +36,11 @@ namespace Semmle.Extraction.CIL.Entities
{
get
{
- var parent = cx.CreateFolder(System.IO.Path.GetDirectoryName(path));
+ var directoryName = System.IO.Path.GetDirectoryName(path);
+ if (directoryName == null)
+ throw new InternalError($"Directory name for path '{path}' is null.");
+
+ var parent = cx.CreateFolder(directoryName);
yield return parent;
yield return Tuples.containerparent(parent, this);
yield return Tuples.files(this, path, System.IO.Path.GetFileNameWithoutExtension(path), System.IO.Path.GetExtension(path).Substring(1));
diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs
index 48ebe6a19d1..b6597a3eba9 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs
@@ -41,7 +41,7 @@ namespace Semmle.Extraction.CIL.Entities
}
}
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
return obj is Folder folder && path == folder.path;
}
diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Instruction.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Instruction.cs
index 925dea6e711..1c5607a23ab 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Instruction.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Instruction.cs
@@ -365,6 +365,11 @@ namespace Semmle.Extraction.CIL.Entities
{
int offset = Offset;
+ if (Method.Implementation is null)
+ {
+ yield break;
+ }
+
yield return Tuples.cil_instruction(this, (int)OpCode, Index, Method.Implementation);
switch (PayloadType)
@@ -414,11 +419,17 @@ namespace Semmle.Extraction.CIL.Entities
break;
case Payload.Arg8:
case Payload.Arg16:
- yield return Tuples.cil_access(this, Method.Parameters[(int)UnsignedPayloadValue]);
+ if (!(Method.Parameters is null))
+ {
+ yield return Tuples.cil_access(this, Method.Parameters[(int)UnsignedPayloadValue]);
+ }
break;
case Payload.Local8:
case Payload.Local16:
- yield return Tuples.cil_access(this, Method.LocalVariables[(int)UnsignedPayloadValue]);
+ if (!(Method.LocalVariables is null))
+ {
+ yield return Tuples.cil_access(this, Method.LocalVariables[(int)UnsignedPayloadValue]);
+ }
break;
case Payload.None:
case Payload.Target8:
@@ -439,7 +450,7 @@ namespace Semmle.Extraction.CIL.Entities
public IEnumerable JumpContents(Dictionary jump_table)
{
int target;
- IInstruction inst;
+ IInstruction? inst;
switch (PayloadType)
{
diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Method.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Method.cs
index 73fa7712b75..cc9fb49afa9 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Method.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Method.cs
@@ -23,25 +23,29 @@ namespace Semmle.Extraction.CIL.Entities
///
abstract class Method : TypeContainer, IMethod
{
+ protected MethodTypeParameter[]? genericParams;
+ protected GenericContext gc;
+ protected MethodSignature signature;
+
protected Method(GenericContext gc) : base(gc.cx)
{
this.gc = gc;
}
- public override IEnumerable TypeParameters => gc.TypeParameters.Concat(declaringType.TypeParameters);
+ public override IEnumerable TypeParameters => gc.TypeParameters.Concat(DeclaringType.TypeParameters);
public override IEnumerable MethodParameters =>
genericParams == null ? gc.MethodParameters : gc.MethodParameters.Concat(genericParams);
public int GenericParameterCount => signature.GenericParameterCount;
- public virtual Method SourceDeclaration => this;
+ public virtual Method? SourceDeclaration => this;
public abstract Type DeclaringType { get; }
public abstract string Name { get; }
- public virtual IList LocalVariables => throw new NotImplementedException();
- public IList Parameters { get; private set; }
+ public virtual IList? LocalVariables => throw new NotImplementedException();
+ public IList? Parameters { get; private set; }
public override void WriteId(TextWriter trapFile) => WriteMethodId(trapFile, DeclaringType, NameLabel);
@@ -70,12 +74,6 @@ namespace Semmle.Extraction.CIL.Entities
trapFile.Write(')');
}
- protected MethodTypeParameter[] genericParams;
- protected Type declaringType;
- protected GenericContext gc;
- protected MethodSignature signature;
- protected string name;
-
public override string IdSuffix => ";cil-method";
protected void PopulateParameters(IEnumerable parameterTypes)
@@ -145,13 +143,15 @@ namespace Semmle.Extraction.CIL.Entities
{
readonly Handle handle;
readonly MethodDefinition md;
- readonly PDB.IMethod methodDebugInformation;
+ readonly PDB.IMethod? methodDebugInformation;
+ readonly Type declaringType;
- LocalVariable[] locals;
+ string name;
+ LocalVariable[]? locals;
- public MethodImplementation Implementation { get; private set; }
+ public MethodImplementation? Implementation { get; private set; }
- public override IList LocalVariables => locals;
+ public override IList? LocalVariables => locals;
public DefinitionMethod(GenericContext gc, MethodDefinitionHandle handle) : base(gc)
{
@@ -167,7 +167,7 @@ namespace Semmle.Extraction.CIL.Entities
methodDebugInformation = cx.GetMethodDebugInformation(handle);
}
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
return obj is DefinitionMethod method && handle.Equals(method.handle);
}
@@ -208,7 +208,7 @@ namespace Semmle.Extraction.CIL.Entities
PopulateParameters(typeSignature.ParameterTypes);
- foreach (var c in Parameters)
+ foreach (var c in Parameters!)
yield return c;
foreach (var c in PopulateFlags)
@@ -315,8 +315,8 @@ namespace Semmle.Extraction.CIL.Entities
// The sequence point gives the location of each instruction.
// The location of an instruction is given by the sequence point *after* the
// instruction.
- IEnumerator nextSequencePoint = null;
- PdbSourceLocation instructionLocation = null;
+ IEnumerator? nextSequencePoint = null;
+ PdbSourceLocation? instructionLocation = null;
if (methodDebugInformation != null)
{
@@ -401,9 +401,9 @@ namespace Semmle.Extraction.CIL.Entities
{
readonly MemberReferenceHandle handle;
readonly MemberReference mr;
- readonly Type declType;
+ readonly Type declaringType;
readonly GenericContext parent;
- readonly Method sourceDeclaration;
+ readonly Method? sourceDeclaration;
public MemberReferenceMethod(GenericContext gc, MemberReferenceHandle handle) : base(gc)
{
@@ -416,22 +416,24 @@ namespace Semmle.Extraction.CIL.Entities
parent = (GenericContext)cx.CreateGeneric(gc, mr.Parent);
var parentMethod = parent as Method;
- nameLabel = cx.GetString(mr.Name);
- declType = parentMethod == null ? parent as Type : parentMethod.DeclaringType;
+ var declType = parentMethod == null ? parent as Type : parentMethod.DeclaringType;
if (declType is null)
throw new InternalError("Parent context of method is not a type");
- var typeSourceDeclaration = declType.SourceDeclaration;
- sourceDeclaration = typeSourceDeclaration == declType ? (Method)this : typeSourceDeclaration.LookupMethod(mr.Name, mr.Signature);
+ declaringType = declType;
+ nameLabel = cx.GetString(mr.Name);
+
+ var typeSourceDeclaration = declaringType.SourceDeclaration;
+ sourceDeclaration = typeSourceDeclaration == declaringType ? (Method)this : typeSourceDeclaration.LookupMethod(mr.Name, mr.Signature);
}
private readonly string nameLabel;
public override string NameLabel => nameLabel;
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
return obj is MemberReferenceMethod method && handle.Equals(method.handle);
}
@@ -441,11 +443,11 @@ namespace Semmle.Extraction.CIL.Entities
return handle.GetHashCode();
}
- public override Method SourceDeclaration => sourceDeclaration;
+ public override Method? SourceDeclaration => sourceDeclaration;
public override bool IsStatic => !signature.Header.IsInstance;
- public override Type DeclaringType => declType;
+ public override Type DeclaringType => declaringType;
public override string Name => cx.ShortName(mr.Name);
@@ -465,7 +467,7 @@ namespace Semmle.Extraction.CIL.Entities
var typeSignature = mr.DecodeMethodSignature(cx.TypeSignatureDecoder, this);
PopulateParameters(typeSignature.ParameterTypes);
- foreach (var p in Parameters) yield return p;
+ foreach (var p in Parameters!) yield return p;
foreach (var f in PopulateFlags) yield return f;
@@ -486,6 +488,7 @@ namespace Semmle.Extraction.CIL.Entities
readonly MethodSpecification ms;
readonly Method unboundMethod;
readonly ImmutableArray typeParams;
+ readonly Type declaringType;
public MethodSpecificationMethod(GenericContext gc, MethodSpecificationHandle handle) : base(gc)
{
@@ -501,7 +504,7 @@ namespace Semmle.Extraction.CIL.Entities
unboundMethod.WriteId(trapFile);
trapFile.Write('<');
int index = 0;
- foreach(var param in typeParams)
+ foreach (var param in typeParams)
{
trapFile.WriteSeparator(",", ref index);
trapFile.WriteSubId(param);
@@ -511,7 +514,7 @@ namespace Semmle.Extraction.CIL.Entities
public override string NameLabel => throw new NotImplementedException();
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
return obj is MethodSpecificationMethod method && handle.Equals(method.handle) && typeParams.SequenceEqual(method.typeParams);
}
@@ -548,7 +551,7 @@ namespace Semmle.Extraction.CIL.Entities
}
PopulateParameters(constructedTypeSignature.ParameterTypes);
- foreach (var p in Parameters)
+ foreach (var p in Parameters!)
yield return p;
foreach (var f in PopulateFlags)
diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Namespace.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Namespace.cs
index 6a7943c8bd1..421318a757c 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Namespace.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Namespace.cs
@@ -17,7 +17,7 @@ namespace Semmle.Extraction.CIL.Entities
///
public sealed class Namespace : TypeContainer, INamespace
{
- public Namespace ParentNamespace;
+ public Namespace? ParentNamespace;
public readonly string Name;
public bool IsGlobalNamespace => ParentNamespace == null;
@@ -35,7 +35,7 @@ namespace Semmle.Extraction.CIL.Entities
trapFile.Write(Name);
}
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
if (obj is Namespace ns && Name == ns.Name)
{
@@ -63,7 +63,7 @@ namespace Semmle.Extraction.CIL.Entities
return i == -1 ? fqn : fqn.Substring(i + 1);
}
- static Namespace createParentNamespace(Context cx, string fqn)
+ static Namespace? createParentNamespace(Context cx, string fqn)
{
if (fqn == "") return null;
var i = fqn.LastIndexOf('.');
@@ -74,7 +74,7 @@ namespace Semmle.Extraction.CIL.Entities
{
}
- public Namespace(Context cx, string name, Namespace parent) : base(cx)
+ public Namespace(Context cx, string name, Namespace? parent) : base(cx)
{
Name = name;
ParentNamespace = parent;
@@ -86,7 +86,7 @@ namespace Semmle.Extraction.CIL.Entities
{
yield return Tuples.namespaces(this, Name);
if (!IsGlobalNamespace)
- yield return Tuples.parent_namespace(this, ParentNamespace);
+ yield return Tuples.parent_namespace(this, ParentNamespace!);
}
}
}
diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Parameter.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Parameter.cs
index f48a88bcf82..38b1b735f0a 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Parameter.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Parameter.cs
@@ -33,7 +33,7 @@ namespace Semmle.Extraction.CIL.Entities
trapFile.Write(index);
}
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
return obj is Parameter param && method.Equals(param.method) && index == param.index;
}
diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Property.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Property.cs
index 4149baf5e42..1b9c438a17d 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Property.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Property.cs
@@ -48,7 +48,7 @@ namespace Semmle.Extraction.CIL.Entities
trapFile.Write(")");
}
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
return obj is Property property && Equals(handle, property.handle);
}
diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/SourceLocation.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/SourceLocation.cs
index ade644fbb1c..551f2a12bdc 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/Entities/SourceLocation.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/SourceLocation.cs
@@ -32,7 +32,7 @@ namespace Semmle.Extraction.CIL.Entities
trapFile.Write(location.EndColumn);
}
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
return obj is PdbSourceLocation l && location.Equals(l.location);
}
diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs
index 04b25553b6b..3e2f9e23641 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs
@@ -8,6 +8,7 @@ using System.Reflection;
using Semmle.Util;
using System.IO;
using System.Text;
+using System.Diagnostics.CodeAnalysis;
namespace Semmle.Extraction.CIL.Entities
{
@@ -103,7 +104,7 @@ namespace Semmle.Extraction.CIL.Entities
/// shortcut to comparing the signature bytes since handles are unique.
///
/// The method, or 'null' if not found or not supported.
- internal virtual Method LookupMethod(StringHandle methodName, BlobHandle signature)
+ internal virtual Method? LookupMethod(StringHandle methodName, BlobHandle signature)
{
return null;
}
@@ -160,7 +161,7 @@ namespace Semmle.Extraction.CIL.Entities
get;
}
- public virtual TypeContainer Parent => (TypeContainer)ContainingType ?? Namespace;
+ public virtual TypeContainer Parent => (TypeContainer?)ContainingType ?? Namespace!;
public override IEnumerable Contents
{
@@ -177,9 +178,9 @@ namespace Semmle.Extraction.CIL.Entities
public abstract string Name { get; }
- public abstract Namespace Namespace { get; }
+ public abstract Namespace? Namespace { get; }
- public abstract Type ContainingType { get; }
+ public abstract Type? ContainingType { get; }
public abstract Type Construct(IEnumerable typeArguments);
@@ -233,7 +234,7 @@ namespace Semmle.Extraction.CIL.Entities
///
/// The resulting primitive type, or null.
/// True if this type is a primitive type.
- public bool TryGetPrimitiveType(out PrimitiveType t)
+ public bool TryGetPrimitiveType([NotNullWhen(true)] out PrimitiveType? t)
{
if (TryGetPrimitiveTypeCode(out var code))
{
@@ -249,7 +250,7 @@ namespace Semmle.Extraction.CIL.Entities
private bool TryGetPrimitiveTypeCode(out PrimitiveTypeCode code)
{
- if (ContainingType == null && Namespace.Name == cx.SystemNamespace.Name)
+ if (ContainingType == null && Namespace?.Name == cx.SystemNamespace.Name)
{
switch (Name)
{
@@ -341,7 +342,7 @@ namespace Semmle.Extraction.CIL.Entities
typeParams = new Lazy>(MakeTypeParameters);
}
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
return obj is TypeDefinitionType t && handle.Equals(t.handle);
}
@@ -390,9 +391,9 @@ namespace Semmle.Extraction.CIL.Entities
public override Namespace Namespace => cx.Create(td.NamespaceDefinition);
- readonly Type declType;
+ readonly Type? declType;
- public override Type ContainingType => declType;
+ public override Type? ContainingType => declType;
public override int ThisTypeParameters
{
@@ -562,18 +563,14 @@ namespace Semmle.Extraction.CIL.Entities
readonly TypeReference tr;
readonly Lazy typeParams;
- public TypeReferenceType(Context cx, TypeReferenceHandle handle) : this(cx, handle, cx.mdReader.GetTypeReference(handle))
- {
- typeParams = new Lazy(MakeTypeParameters);
- }
-
- public TypeReferenceType(Context cx, TypeReferenceHandle handle, TypeReference tr) : base(cx)
+ public TypeReferenceType(Context cx, TypeReferenceHandle handle) : base(cx)
{
+ this.typeParams = new Lazy(MakeTypeParameters);
this.handle = handle;
- this.tr = tr;
+ this.tr = cx.mdReader.GetTypeReference(handle);
}
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
return obj is TypeReferenceType t && handle.Equals(t.handle);
}
@@ -637,7 +634,7 @@ namespace Semmle.Extraction.CIL.Entities
}
}
- public override Type ContainingType
+ public override Type? ContainingType
{
get
{
@@ -654,7 +651,7 @@ namespace Semmle.Extraction.CIL.Entities
switch (tr.ResolutionScope.Kind)
{
case HandleKind.TypeReference:
- ContainingType.WriteAssemblyPrefix(trapFile);
+ ContainingType!.WriteAssemblyPrefix(trapFile);
break;
case HandleKind.AssemblyReference:
var assemblyDef = cx.mdReader.GetAssemblyReference((AssemblyReferenceHandle)tr.ResolutionScope);
@@ -684,7 +681,7 @@ namespace Semmle.Extraction.CIL.Entities
var ct = ContainingType;
if (ct != null)
{
- ContainingType.GetId(trapFile, inContext);
+ ct.GetId(trapFile, inContext);
}
else
{
@@ -719,9 +716,11 @@ namespace Semmle.Extraction.CIL.Entities
public sealed class ConstructedType : Type
{
readonly Type unboundGenericType;
- readonly Type[] thisTypeArguments;
- public override IEnumerable ThisTypeArguments => thisTypeArguments;
+ // Either null or notEmpty
+ readonly Type[]? thisTypeArguments;
+
+ public override IEnumerable ThisTypeArguments => thisTypeArguments.EnumerateNull();
public override IEnumerable ThisGenericArguments => thisTypeArguments.EnumerateNull();
@@ -751,7 +750,6 @@ namespace Semmle.Extraction.CIL.Entities
unboundGenericType = unboundType;
var thisParams = unboundType.ThisTypeParameters;
- var parentParams = suppliedArgs - thisParams;
if (typeArguments.Count() == thisParams)
{
@@ -760,18 +758,21 @@ namespace Semmle.Extraction.CIL.Entities
}
else if (thisParams == 0)
{
- containingType = unboundType.ContainingType.Construct(typeArguments);
+ // all type arguments belong to containing type
+ containingType = unboundType.ContainingType!.Construct(typeArguments);
}
else
{
- containingType = unboundType.ContainingType.Construct(typeArguments.Take(parentParams));
+ // some type arguments belong to containing type
+ var parentParams = suppliedArgs - thisParams;
+ containingType = unboundType.ContainingType!.Construct(typeArguments.Take(parentParams));
thisTypeArguments = typeArguments.Skip(parentParams).ToArray();
}
}
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
- if(obj is ConstructedType t && Equals(unboundGenericType, t.unboundGenericType) && Equals(containingType, t.containingType))
+ if (obj is ConstructedType t && Equals(unboundGenericType, t.unboundGenericType) && Equals(containingType, t.containingType))
{
if (thisTypeArguments is null) return t.thisTypeArguments is null;
if (!(t.thisTypeArguments is null)) return thisTypeArguments.SequenceEqual(t.thisTypeArguments);
@@ -788,12 +789,12 @@ namespace Semmle.Extraction.CIL.Entities
return h;
}
- readonly Type containingType;
- public override Type ContainingType => containingType;
+ readonly Type? containingType;
+ public override Type? ContainingType => containingType;
public override string Name => unboundGenericType.Name;
- public override Namespace Namespace => unboundGenericType.Namespace;
+ public override Namespace Namespace => unboundGenericType.Namespace!;
public override int ThisTypeParameters => thisTypeArguments == null ? 0 : thisTypeArguments.Length;
@@ -851,7 +852,7 @@ namespace Semmle.Extraction.CIL.Entities
typeCode = tc;
}
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
return obj is PrimitiveType pt && typeCode == pt.typeCode;
}
@@ -871,7 +872,7 @@ namespace Semmle.Extraction.CIL.Entities
public override Namespace Namespace => cx.SystemNamespace;
- public override Type ContainingType => null;
+ public override Type? ContainingType => null;
public override int ThisTypeParameters => 0;
@@ -894,19 +895,17 @@ namespace Semmle.Extraction.CIL.Entities
readonly Type elementType;
readonly int rank;
- public ArrayType(Context cx, Type element, ArrayShape shape) : base(cx)
+ public ArrayType(Context cx, Type elementType, int rank) : base(cx)
{
- rank = shape.Rank;
- elementType = element;
+ this.rank = rank;
+ this.elementType = elementType;
}
- public ArrayType(Context cx, Type element) : base(cx)
+ public ArrayType(Context cx, Type elementType) : this(cx, elementType, 1)
{
- rank = 1;
- elementType = element;
}
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
return obj is ArrayType array && elementType.Equals(array.elementType) && rank == array.rank;
}
@@ -929,7 +928,7 @@ namespace Semmle.Extraction.CIL.Entities
public override Namespace Namespace => cx.SystemNamespace;
- public override Type ContainingType => null;
+ public override Type? ContainingType => null;
public override int ThisTypeParameters => elementType.ThisTypeParameters;
@@ -972,9 +971,9 @@ namespace Semmle.Extraction.CIL.Entities
this.gc = gc;
}
- public override Namespace Namespace => null;
+ public override Namespace? Namespace => null;
- public override Type ContainingType => null;
+ public override Type? ContainingType => null;
public override int ThisTypeParameters => 0;
@@ -1034,7 +1033,7 @@ namespace Semmle.Extraction.CIL.Entities
this.index = index;
}
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
return obj is MethodTypeParameter tp && method.Equals(tp.method) && index == tp.index;
}
@@ -1072,7 +1071,7 @@ namespace Semmle.Extraction.CIL.Entities
type = t;
}
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
return obj is TypeTypeParameter tp && type.Equals(tp.type) && index == tp.index;
}
@@ -1089,7 +1088,7 @@ namespace Semmle.Extraction.CIL.Entities
trapFile.Write(index);
}
- public override TypeContainer Parent => type ?? gc as TypeContainer;
+ public override TypeContainer Parent => type;
public override string Name => "!" + index;
public override IEnumerable TypeParameters => Enumerable.Empty();
@@ -1119,7 +1118,7 @@ namespace Semmle.Extraction.CIL.Entities
this.pointee = pointee;
}
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
return obj is PointerType pt && pointee.Equals(pt.pointee);
}
@@ -1138,9 +1137,9 @@ namespace Semmle.Extraction.CIL.Entities
public override string Name => pointee.Name + "*";
- public override Namespace Namespace => pointee.Namespace;
+ public override Namespace? Namespace => pointee.Namespace;
- public override Type ContainingType => pointee.ContainingType;
+ public override Type? ContainingType => pointee.ContainingType;
public override TypeContainer Parent => pointee.Parent;
@@ -1180,7 +1179,7 @@ namespace Semmle.Extraction.CIL.Entities
public override Namespace Namespace => cx.GlobalNamespace;
- public override Type ContainingType => null;
+ public override Type? ContainingType => null;
public override int ThisTypeParameters => 0;
@@ -1202,13 +1201,20 @@ namespace Semmle.Extraction.CIL.Entities
{
struct Array : ITypeSignature
{
- public ITypeSignature elementType;
- public ArrayShape shape;
+ private readonly ITypeSignature elementType;
+ private readonly ArrayShape shape;
+
+ public Array(ITypeSignature elementType, ArrayShape shape) : this()
+ {
+ this.elementType = elementType;
+ this.shape = shape;
+ }
+
public void WriteId(TextWriter trapFile, GenericContext gc)
{
elementType.WriteId(trapFile, gc);
trapFile.Write('[');
- for (int i=1; i signature;
+ private readonly MethodSignature signature;
+
+ public FnPtr(MethodSignature signature)
+ {
+ this.signature = signature;
+ }
public void WriteId(TextWriter trapFile, GenericContext gc)
{
@@ -1236,25 +1252,32 @@ namespace Semmle.Extraction.CIL.Entities
}
ITypeSignature IConstructedTypeProvider.GetArrayType(ITypeSignature elementType, ArrayShape shape) =>
- new Array { elementType = elementType, shape = shape };
+ new Array(elementType, shape);
ITypeSignature IConstructedTypeProvider.GetByReferenceType(ITypeSignature elementType) =>
- new ByRef { elementType = elementType };
+ new ByRef(elementType);
ITypeSignature ISignatureTypeProvider.GetFunctionPointerType(MethodSignature signature) =>
- new FnPtr { signature = signature };
+ new FnPtr(signature);
class Instantiation : ITypeSignature
{
- public ITypeSignature genericType;
- public ImmutableArray typeArguments;
+ private readonly ITypeSignature genericType;
+ private readonly ImmutableArray typeArguments;
+
+ public Instantiation(ITypeSignature genericType, ImmutableArray typeArguments)
+ {
+ this.genericType = genericType;
+ this.typeArguments = typeArguments;
+ }
+
public void WriteId(TextWriter trapFile, GenericContext gc)
{
genericType.WriteId(trapFile, gc);
trapFile.Write('<');
int index = 0;
- foreach(var arg in typeArguments)
+ foreach (var arg in typeArguments)
{
trapFile.WriteSeparator(",", ref index);
arg.WriteId(trapFile, gc);
@@ -1264,12 +1287,18 @@ namespace Semmle.Extraction.CIL.Entities
}
ITypeSignature IConstructedTypeProvider.GetGenericInstantiation(ITypeSignature genericType, ImmutableArray typeArguments) =>
- new Instantiation { genericType = genericType, typeArguments = typeArguments };
+ new Instantiation(genericType, typeArguments);
class GenericMethodParameter : ITypeSignature
{
- public object innerGc;
- public int index;
+ private readonly object innerGc;
+ private readonly int index;
+
+ public GenericMethodParameter(object innerGc, int index)
+ {
+ this.innerGc = innerGc;
+ this.index = index;
+ }
public void WriteId(TextWriter trapFile, GenericContext outerGc)
{
@@ -1284,7 +1313,12 @@ namespace Semmle.Extraction.CIL.Entities
class GenericTypeParameter : ITypeSignature
{
- public int index;
+ private readonly int index;
+
+ public GenericTypeParameter(int index)
+ {
+ this.index = index;
+ }
public void WriteId(TextWriter trapFile, GenericContext gc)
{
@@ -1294,16 +1328,23 @@ namespace Semmle.Extraction.CIL.Entities
}
ITypeSignature ISignatureTypeProvider.GetGenericMethodParameter(object genericContext, int index) =>
- new GenericMethodParameter { innerGc = genericContext, index = index };
+ new GenericMethodParameter(genericContext, index);
ITypeSignature ISignatureTypeProvider.GetGenericTypeParameter(object genericContext, int index) =>
- new GenericTypeParameter { index = index };
+ new GenericTypeParameter(index);
class Modified : ITypeSignature
{
- public ITypeSignature modifier;
- public ITypeSignature unmodifiedType;
- public bool isRequired;
+ private readonly ITypeSignature modifier;
+ private readonly ITypeSignature unmodifiedType;
+ private readonly bool isRequired;
+
+ public Modified(ITypeSignature modifier, ITypeSignature unmodifiedType, bool isRequired)
+ {
+ this.modifier = modifier;
+ this.unmodifiedType = unmodifiedType;
+ this.isRequired = isRequired;
+ }
public void WriteId(TextWriter trapFile, GenericContext gc)
{
@@ -1313,12 +1354,17 @@ namespace Semmle.Extraction.CIL.Entities
ITypeSignature ISignatureTypeProvider.GetModifiedType(ITypeSignature modifier, ITypeSignature unmodifiedType, bool isRequired)
{
- return new Modified { modifier = modifier, unmodifiedType = unmodifiedType, isRequired = isRequired };
+ return new Modified(modifier, unmodifiedType, isRequired);
}
class Pinned : ITypeSignature
{
- public ITypeSignature elementType;
+ private readonly ITypeSignature elementType;
+
+ public Pinned(ITypeSignature elementType)
+ {
+ this.elementType = elementType;
+ }
public void WriteId(TextWriter trapFile, GenericContext gc)
{
@@ -1329,12 +1375,17 @@ namespace Semmle.Extraction.CIL.Entities
ITypeSignature ISignatureTypeProvider.GetPinnedType(ITypeSignature elementType)
{
- return new Pinned { elementType = elementType };
+ return new Pinned(elementType);
}
class PointerType : ITypeSignature
{
- public ITypeSignature elementType;
+ private readonly ITypeSignature elementType;
+
+ public PointerType(ITypeSignature elementType)
+ {
+ this.elementType = elementType;
+ }
public void WriteId(TextWriter trapFile, GenericContext gc)
{
@@ -1345,12 +1396,17 @@ namespace Semmle.Extraction.CIL.Entities
ITypeSignature IConstructedTypeProvider.GetPointerType(ITypeSignature elementType)
{
- return new PointerType { elementType = elementType };
+ return new PointerType(elementType);
}
class Primitive : ITypeSignature
{
- public PrimitiveTypeCode typeCode;
+ private readonly PrimitiveTypeCode typeCode;
+
+ public Primitive(PrimitiveTypeCode typeCode)
+ {
+ this.typeCode = typeCode;
+ }
public void WriteId(TextWriter trapFile, GenericContext gc)
{
@@ -1360,12 +1416,18 @@ namespace Semmle.Extraction.CIL.Entities
ITypeSignature ISimpleTypeProvider.GetPrimitiveType(PrimitiveTypeCode typeCode)
{
- return new Primitive { typeCode = typeCode };
+ return new Primitive(typeCode);
}
class SzArrayType : ITypeSignature
{
- public ITypeSignature elementType;
+ private readonly ITypeSignature elementType;
+
+ public SzArrayType(ITypeSignature elementType)
+ {
+ this.elementType = elementType;
+ }
+
public void WriteId(TextWriter trapFile, GenericContext gc)
{
elementType.WriteId(trapFile, gc);
@@ -1375,41 +1437,53 @@ namespace Semmle.Extraction.CIL.Entities
ITypeSignature ISZArrayTypeProvider.GetSZArrayType(ITypeSignature elementType)
{
- return new SzArrayType { elementType = elementType };
+ return new SzArrayType(elementType);
}
class TypeDefinition : ITypeSignature
{
- public TypeDefinitionHandle handle;
- public byte rawTypeKind;
- Type type;
+ private readonly TypeDefinitionHandle handle;
+ private readonly byte rawTypeKind;
+
+ public TypeDefinition(TypeDefinitionHandle handle, byte rawTypeKind)
+ {
+ this.handle = handle;
+ this.rawTypeKind = rawTypeKind;
+ }
+
public void WriteId(TextWriter trapFile, GenericContext gc)
{
- type = (Type)gc.cx.Create(handle);
+ var type = (Type)gc.cx.Create(handle);
type.WriteId(trapFile);
}
}
ITypeSignature ISimpleTypeProvider.GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind)
{
- return new TypeDefinition { handle = handle, rawTypeKind = rawTypeKind };
+ return new TypeDefinition(handle, rawTypeKind);
}
class TypeReference : ITypeSignature
{
- public TypeReferenceHandle handle;
- public byte rawTypeKind; // struct/class (not used)
- Type type;
+ private readonly TypeReferenceHandle handle;
+ private readonly byte rawTypeKind; // struct/class (not used)
+
+ public TypeReference(TypeReferenceHandle handle, byte rawTypeKind)
+ {
+ this.handle = handle;
+ this.rawTypeKind = rawTypeKind;
+ }
+
public void WriteId(TextWriter trapFile, GenericContext gc)
{
- type = (Type)gc.cx.Create(handle);
+ var type = (Type)gc.cx.Create(handle);
type.WriteId(trapFile);
}
}
ITypeSignature ISimpleTypeProvider.GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind)
{
- return new TypeReference { handle = handle, rawTypeKind = rawTypeKind };
+ return new TypeReference(handle, rawTypeKind);
}
ITypeSignature ISignatureTypeProvider.GetTypeFromSpecification(MetadataReader reader, object genericContext, TypeSpecificationHandle handle, byte rawTypeKind)
@@ -1433,7 +1507,7 @@ namespace Semmle.Extraction.CIL.Entities
}
Type IConstructedTypeProvider.GetArrayType(Type elementType, ArrayShape shape) =>
- cx.Populate(new ArrayType(cx, elementType, shape));
+ cx.Populate(new ArrayType(cx, elementType, shape.Rank));
Type IConstructedTypeProvider.GetByReferenceType(Type elementType) =>
elementType; // ??
diff --git a/csharp/extractor/Semmle.Extraction.CIL/Factories.cs b/csharp/extractor/Semmle.Extraction.CIL/Factories.cs
index f522521a845..f2f98b64d17 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/Factories.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/Factories.cs
@@ -41,7 +41,7 @@ namespace Semmle.Extraction.CIL
e.WriteId(writer);
var id = writer.ToString();
- if (debugLabels.TryGetValue(id, out IExtractedEntity previousEntity))
+ if (debugLabels.TryGetValue(id, out IExtractedEntity? previousEntity))
{
cx.Extractor.Message(new Message("Duplicate trap ID", id, null, severity: Util.Logging.Severity.Warning));
}
diff --git a/csharp/extractor/Semmle.Extraction.CIL/PDB/MetadataPdbReader.cs b/csharp/extractor/Semmle.Extraction.CIL/PDB/MetadataPdbReader.cs
index 33e8460090d..14bd9fdeb72 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/PDB/MetadataPdbReader.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/PDB/MetadataPdbReader.cs
@@ -25,7 +25,7 @@ namespace Semmle.Extraction.PDB
public string Path { get; private set; }
- public string Contents => File.Exists(Path) ? File.ReadAllText(Path, System.Text.Encoding.Default) : null;
+ public string? Contents => File.Exists(Path) ? File.ReadAllText(Path, System.Text.Encoding.Default) : null;
}
// Turns out to be very important to keep the MetadataReaderProvider live
@@ -41,7 +41,7 @@ namespace Semmle.Extraction.PDB
public IEnumerable SourceFiles => reader.Documents.Select(handle => new SourceFile(reader, handle));
- public IMethod GetMethod(MethodDebugInformationHandle handle)
+ public IMethod? GetMethod(MethodDebugInformationHandle handle)
{
var debugInfo = reader.GetMethodDebugInformation(handle);
@@ -51,10 +51,10 @@ namespace Semmle.Extraction.PDB
Where(p => p.Location.File.Path != null).
ToArray();
- return sequencePoints.Any() ? new Method() { SequencePoints = sequencePoints } : null;
+ return sequencePoints.Any() ? new Method(sequencePoints) : null;
}
- public static MetadataPdbReader CreateFromAssembly(string assemblyPath, PEReader peReader)
+ public static MetadataPdbReader? CreateFromAssembly(string assemblyPath, PEReader peReader)
{
foreach (var provider in peReader.
ReadDebugDirectory().
diff --git a/csharp/extractor/Semmle.Extraction.CIL/PDB/NativePdbReader.cs b/csharp/extractor/Semmle.Extraction.CIL/PDB/NativePdbReader.cs
index 0f25b281aab..283fd4bf35b 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/PDB/NativePdbReader.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/PDB/NativePdbReader.cs
@@ -23,7 +23,7 @@ namespace Semmle.Extraction.PDB
public Document(ISymUnmanagedDocument doc)
{
document = doc;
- contents = new Lazy(() =>
+ contents = new Lazy(() =>
{
bool isEmbedded;
if (document.HasEmbeddedSource(out isEmbedded) == 0 && isEmbedded)
@@ -38,7 +38,7 @@ namespace Semmle.Extraction.PDB
});
}
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
var otherDoc = obj as Document;
return otherDoc != null && Path.Equals(otherDoc.Path);
@@ -50,14 +50,14 @@ namespace Semmle.Extraction.PDB
public override string ToString() => Path;
- readonly Lazy contents;
+ readonly Lazy contents;
- public string Contents => contents.Value;
+ public string? Contents => contents.Value;
}
public IEnumerable SourceFiles => reader.GetDocuments().Select(d => new Document(d));
- public IMethod GetMethod(MethodDebugInformationHandle h)
+ public IMethod? GetMethod(MethodDebugInformationHandle h)
{
int methodToken = MetadataTokens.GetToken(h.ToDefinitionHandle());
var method = reader.GetMethod(methodToken);
@@ -72,7 +72,7 @@ namespace Semmle.Extraction.PDB
Select(sp => new SequencePoint(sp.Offset, new Location(new Document(sp.Document), sp.StartLine, sp.StartColumn, sp.EndLine, sp.EndColumn))).
ToArray();
- return s.Any() ? new Method { SequencePoints = s } : null;
+ return s.Any() ? new Method(s) : null;
}
return null;
}
@@ -87,7 +87,7 @@ namespace Semmle.Extraction.PDB
readonly ISymUnmanagedReader5 reader;
readonly FileStream pdbStream;
- public static NativePdbReader CreateFromAssembly(string assemblyPath, PEReader peReader)
+ public static NativePdbReader? CreateFromAssembly(string assemblyPath, PEReader peReader)
{
// The Native PDB reader uses an unmanaged Windows DLL
// so only works on Windows.
@@ -123,7 +123,7 @@ namespace Semmle.Extraction.PDB
{
}
- public object GetMetadataImport() => null;
+ public object? GetMetadataImport() => null;
public unsafe bool TryGetStandaloneSignature(int standaloneSignatureToken, out byte* signature, out int length) =>
throw new NotImplementedException();
diff --git a/csharp/extractor/Semmle.Extraction.CIL/PDB/PdbReader.cs b/csharp/extractor/Semmle.Extraction.CIL/PDB/PdbReader.cs
index c2f4f94f59f..8fb83554649 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/PDB/PdbReader.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/PDB/PdbReader.cs
@@ -55,7 +55,7 @@ namespace Semmle.Extraction.PDB
return string.Format("({0},{1})-({2},{3})", StartLine, StartColumn, EndLine, EndColumn);
}
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
var otherLocation = obj as Location;
@@ -91,7 +91,12 @@ namespace Semmle.Extraction.PDB
class Method : IMethod
{
- public IEnumerable SequencePoints { get; set; }
+ public IEnumerable SequencePoints { get; }
+
+ public Method(IEnumerable sequencePoints)
+ {
+ SequencePoints = sequencePoints;
+ }
public Location Location => SequencePoints.First().Location;
}
@@ -111,7 +116,7 @@ namespace Semmle.Extraction.PDB
/// null if the contents are unavailable.
/// E.g. if the PDB file exists but the corresponding source files are missing.
///
- string Contents { get; }
+ string? Contents { get; }
}
///
@@ -131,7 +136,7 @@ namespace Semmle.Extraction.PDB
///
/// The handle to query.
/// The method information, or null if the method does not have debug information.
- IMethod GetMethod(MethodDebugInformationHandle methodHandle);
+ IMethod? GetMethod(MethodDebugInformationHandle methodHandle);
}
class PdbReader
@@ -140,11 +145,11 @@ namespace Semmle.Extraction.PDB
/// Returns the PDB information associated with an assembly.
///
/// The path to the assembly.
- /// The PE reader for the assembky.
+ /// The PE reader for the assembly.
/// A PdbReader, or null if no PDB information is available.
- public static IPdb Create(string assemblyPath, PEReader peReader)
+ public static IPdb? Create(string assemblyPath, PEReader peReader)
{
- return (IPdb)MetadataPdbReader.CreateFromAssembly(assemblyPath, peReader) ??
+ return (IPdb?)MetadataPdbReader.CreateFromAssembly(assemblyPath, peReader) ??
NativePdbReader.CreateFromAssembly(assemblyPath, peReader);
}
}
diff --git a/csharp/extractor/Semmle.Extraction.CIL/Semmle.Extraction.CIL.csproj b/csharp/extractor/Semmle.Extraction.CIL/Semmle.Extraction.CIL.csproj
index 9db880787b0..6626eda5474 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/Semmle.Extraction.CIL.csproj
+++ b/csharp/extractor/Semmle.Extraction.CIL/Semmle.Extraction.CIL.csproj
@@ -7,6 +7,7 @@
false
true
win-x64;linux-x64;osx-x64
+ enable
diff --git a/csharp/extractor/Semmle.Util/IEnumerableExtensions.cs b/csharp/extractor/Semmle.Util/IEnumerableExtensions.cs
index 7665dedfa70..d8d3f8cb581 100644
--- a/csharp/extractor/Semmle.Util/IEnumerableExtensions.cs
+++ b/csharp/extractor/Semmle.Util/IEnumerableExtensions.cs
@@ -64,7 +64,7 @@ namespace Semmle.Util
/// Enumerates a possibly null enumerable.
/// If the enumerable is null, the list is empty.
///
- public static IEnumerable EnumerateNull(this IEnumerable items)
+ public static IEnumerable EnumerateNull(this IEnumerable? items)
{
if (items == null) yield break;
foreach (var item in items) yield return item;
@@ -93,7 +93,7 @@ namespace Semmle.Util
/// The type of the item.
/// The list of items to hash.
/// The hash code.
- public static int SequenceHash(this IEnumerable items) where T: notnull
+ public static int SequenceHash(this IEnumerable items) where T : notnull
{
int h = 0;
foreach (var i in items)
From 8e2b2540facb9f0d03c51b8f7a00391336ec5081 Mon Sep 17 00:00:00 2001
From: yoff
Date: Fri, 21 Aug 2020 09:39:00 +0200
Subject: [PATCH 082/146] Apply suggestions from code review
Co-authored-by: Rasmus Wriedt Larsen
---
.../src/experimental/dataflow/internal/DataFlowPublic.qll | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll
index 55fe94350cc..cfb1d81a829 100644
--- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll
+++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll
@@ -56,7 +56,7 @@ class Node extends TNode {
EssaVariable asVar() { none() }
/** Convenience method for casting to CfgNode and calling getNode. */
- DataFlowCfgNode asCfgNode() { none() }
+ ControlFlowNode asCfgNode() { none() }
/** Convenience method for casting to ExprNode and calling getNode and getNode again. */
Expr asExpr() { none() }
@@ -84,9 +84,9 @@ class CfgNode extends Node, TCfgNode {
CfgNode() { this = TCfgNode(node) }
- DataFlowCfgNode getNode() { result = node }
+ ControlFlowNode getNode() { result = node }
- override DataFlowCfgNode asCfgNode() { result = node }
+ override ControlFlowNode asCfgNode() { result = node }
/** Gets a textual representation of this element. */
override string toString() { result = node.toString() }
From e00951edf078d8d99752f14722e2d9ce8994867e Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Fri, 21 Aug 2020 09:50:27 +0200
Subject: [PATCH 083/146] update TypeScript to 4.0.2
---
javascript/extractor/lib/typescript/package.json | 2 +-
javascript/extractor/lib/typescript/yarn.lock | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/javascript/extractor/lib/typescript/package.json b/javascript/extractor/lib/typescript/package.json
index e66383a5ef3..0fa2d78d7d5 100644
--- a/javascript/extractor/lib/typescript/package.json
+++ b/javascript/extractor/lib/typescript/package.json
@@ -2,7 +2,7 @@
"name": "typescript-parser-wrapper",
"private": true,
"dependencies": {
- "typescript": "4.0.1-rc"
+ "typescript": "4.0.2"
},
"scripts": {
"build": "tsc --project tsconfig.json",
diff --git a/javascript/extractor/lib/typescript/yarn.lock b/javascript/extractor/lib/typescript/yarn.lock
index 0fcb30d02f2..42c9f33d41c 100644
--- a/javascript/extractor/lib/typescript/yarn.lock
+++ b/javascript/extractor/lib/typescript/yarn.lock
@@ -225,9 +225,9 @@ tsutils@^2.12.1:
dependencies:
tslib "^1.8.1"
-typescript@4.0.1-rc:
- version "4.0.1-rc"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.1-rc.tgz#8adc78223eae56fe71d906a5fa90c3543b07a677"
+typescript@4.0.2:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2"
wrappy@1:
version "1.0.2"
From 07610e0899bcd5902658073b3b39f381a0e66541 Mon Sep 17 00:00:00 2001
From: Philippe Antoine
Date: Mon, 24 Aug 2020 13:12:54 +0200
Subject: [PATCH 084/146] Format document
---
.../Likely Bugs/RedundantNullCheckParam.ql | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.ql b/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.ql
index d09191a3891..c67e15aceba 100644
--- a/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.ql
+++ b/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.ql
@@ -19,10 +19,7 @@ predicate blockDominates(Block check, Block access) {
}
predicate isCheckedInstruction(VariableAccess unchecked, VariableAccess checked) {
- checked =
- any(VariableAccess va |
- va.getTarget() = unchecked.getTarget()
- ) and
+ checked = any(VariableAccess va | va.getTarget() = unchecked.getTarget()) and
//Simple test if the first access in this code path is dereferenced
not dereferenced(checked) and
blockDominates(checked.getEnclosingBlock(), unchecked.getEnclosingBlock())
@@ -38,7 +35,8 @@ predicate candidateResultChecked(VariableAccess check, EqualityOperation eqop) {
//assert macros are not taken into account
not check.isInMacroExpansion() and
// is part of a comparison against some constant NULL
- eqop.getAnOperand() = check and eqop.getAnOperand() instanceof NullValue
+ eqop.getAnOperand() = check and
+ eqop.getAnOperand() instanceof NullValue
}
from VariableAccess unchecked, VariableAccess check, EqualityOperation eqop, Parameter param
@@ -54,4 +52,5 @@ where
candidateResultChecked(check, eqop) and
// and which has not been checked before in this code path
candidateResultUnchecked(unchecked)
-select check, "This null check is redundant or there is a missing null check before $@ ", unchecked, "where dereferencing happens"
+select check, "This null check is redundant or there is a missing null check before $@ ", unchecked,
+ "where dereferencing happens"
From 699cafa890d5d269649e1f559d1f9a67840ed6ec Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Mon, 24 Aug 2020 15:27:35 +0200
Subject: [PATCH 085/146] C#: Add implicitly sized array creations to tests
---
.../expressions/AnonymousMethod1.expected | 2 +-
.../expressions/AnonymousMethod2.expected | 2 +-
.../expressions/AnonymousMethod3.expected | 2 +-
.../expressions/AnonymousMethod4.expected | 2 +-
.../expressions/AnonymousMethod5.expected | 2 +-
.../expressions/ArrayCreation11.expected | 28 +
.../expressions/Lambda1.expected | 2 +-
.../expressions/Lambda2.expected | 2 +-
.../expressions/Lambda3.expected | 2 +-
.../expressions/Lambda4.expected | 2 +-
.../expressions/Lambda5.expected | 2 +-
.../expressions/Lambda6.expected | 2 +-
.../expressions/OperatorCall6.expected | 8 +-
.../expressions/OperatorCall7.expected | 4 +-
.../expressions/PrintAst.expected | 755 ++++++++++--------
.../expressions/QualifiableExpr.expected | 8 +-
.../expressions/StripCasts.expected | 8 +-
.../expressions/Tuples1.expected | 8 +-
.../library-tests/expressions/expressions.cs | 12 +
19 files changed, 507 insertions(+), 346 deletions(-)
diff --git a/csharp/ql/test/library-tests/expressions/AnonymousMethod1.expected b/csharp/ql/test/library-tests/expressions/AnonymousMethod1.expected
index 99cbc7b7280..a443b2e5f58 100644
--- a/csharp/ql/test/library-tests/expressions/AnonymousMethod1.expected
+++ b/csharp/ql/test/library-tests/expressions/AnonymousMethod1.expected
@@ -1 +1 @@
-| expressions.cs:443:33:443:66 | delegate(...) { ... } |
+| expressions.cs:455:33:455:66 | delegate(...) { ... } |
diff --git a/csharp/ql/test/library-tests/expressions/AnonymousMethod2.expected b/csharp/ql/test/library-tests/expressions/AnonymousMethod2.expected
index 71d45f3859b..a4eb1c44ada 100644
--- a/csharp/ql/test/library-tests/expressions/AnonymousMethod2.expected
+++ b/csharp/ql/test/library-tests/expressions/AnonymousMethod2.expected
@@ -1 +1 @@
-| expressions.cs:443:33:443:66 | delegate(...) { ... } | expressions.cs:443:47:443:47 | x |
+| expressions.cs:455:33:455:66 | delegate(...) { ... } | expressions.cs:455:47:455:47 | x |
diff --git a/csharp/ql/test/library-tests/expressions/AnonymousMethod3.expected b/csharp/ql/test/library-tests/expressions/AnonymousMethod3.expected
index 99cbc7b7280..a443b2e5f58 100644
--- a/csharp/ql/test/library-tests/expressions/AnonymousMethod3.expected
+++ b/csharp/ql/test/library-tests/expressions/AnonymousMethod3.expected
@@ -1 +1 @@
-| expressions.cs:443:33:443:66 | delegate(...) { ... } |
+| expressions.cs:455:33:455:66 | delegate(...) { ... } |
diff --git a/csharp/ql/test/library-tests/expressions/AnonymousMethod4.expected b/csharp/ql/test/library-tests/expressions/AnonymousMethod4.expected
index f1ea5fb3021..650c309ba90 100644
--- a/csharp/ql/test/library-tests/expressions/AnonymousMethod4.expected
+++ b/csharp/ql/test/library-tests/expressions/AnonymousMethod4.expected
@@ -1 +1 @@
-| expressions.cs:445:28:445:53 | delegate(...) { ... } | expressions.cs:445:28:445:53 | delegate(...) { ... } |
+| expressions.cs:457:28:457:53 | delegate(...) { ... } | expressions.cs:457:28:457:53 | delegate(...) { ... } |
diff --git a/csharp/ql/test/library-tests/expressions/AnonymousMethod5.expected b/csharp/ql/test/library-tests/expressions/AnonymousMethod5.expected
index aa2ee29ffda..2f79004d8ef 100644
--- a/csharp/ql/test/library-tests/expressions/AnonymousMethod5.expected
+++ b/csharp/ql/test/library-tests/expressions/AnonymousMethod5.expected
@@ -1 +1 @@
-| expressions.cs:445:28:445:53 | delegate(...) { ... } | expressions.cs:445:46:445:46 | access to local variable j |
+| expressions.cs:457:28:457:53 | delegate(...) { ... } | expressions.cs:457:46:457:46 | access to local variable j |
diff --git a/csharp/ql/test/library-tests/expressions/ArrayCreation11.expected b/csharp/ql/test/library-tests/expressions/ArrayCreation11.expected
index f0e5752ac75..80714fe6906 100644
--- a/csharp/ql/test/library-tests/expressions/ArrayCreation11.expected
+++ b/csharp/ql/test/library-tests/expressions/ArrayCreation11.expected
@@ -1,3 +1,31 @@
| expressions.cs:168:27:168:44 | array creation of type Object[] | expressions.cs:168:27:168:44 | 1 | true |
| expressions.cs:409:23:409:65 | array creation of type Int32[,] | expressions.cs:409:23:409:65 | 3 | true |
| expressions.cs:409:23:409:65 | array creation of type Int32[,] | expressions.cs:409:23:409:65 | 3 | true |
+| expressions.cs:437:24:437:66 | array creation of type Int32[,] | expressions.cs:437:24:437:66 | 3 | true |
+| expressions.cs:437:24:437:66 | array creation of type Int32[,] | expressions.cs:437:24:437:66 | 3 | true |
+| expressions.cs:438:17:438:93 | array creation of type Int32[,,] | expressions.cs:438:17:438:93 | 2 | true |
+| expressions.cs:438:17:438:93 | array creation of type Int32[,,] | expressions.cs:438:17:438:93 | 2 | true |
+| expressions.cs:438:17:438:93 | array creation of type Int32[,,] | expressions.cs:438:17:438:93 | 2 | true |
+| expressions.cs:439:17:443:13 | array creation of type Int32[,][,] | expressions.cs:439:17:443:13 | 2 | true |
+| expressions.cs:439:17:443:13 | array creation of type Int32[,][,] | expressions.cs:439:17:443:13 | 2 | true |
+| expressions.cs:439:17:443:13 | array creation of type Int32[,][,] | expressions.cs:439:17:443:13 | 2 | true |
+| expressions.cs:439:17:443:13 | array creation of type Int32[,][,] | expressions.cs:439:17:443:13 | 2 | true |
+| expressions.cs:441:19:441:45 | array creation of type Int32[,] | expressions.cs:441:19:441:45 | 2 | true |
+| expressions.cs:441:19:441:45 | array creation of type Int32[,] | expressions.cs:441:19:441:45 | 2 | true |
+| expressions.cs:441:48:441:82 | array creation of type Int32[,] | expressions.cs:441:48:441:82 | 3 | true |
+| expressions.cs:441:48:441:82 | array creation of type Int32[,] | expressions.cs:441:48:441:82 | 3 | true |
+| expressions.cs:441:85:441:122 | array creation of type Int32[,] | expressions.cs:441:85:441:122 | 3 | true |
+| expressions.cs:441:85:441:122 | array creation of type Int32[,] | expressions.cs:441:85:441:122 | 3 | true |
+| expressions.cs:442:19:442:45 | array creation of type Int32[,] | expressions.cs:442:19:442:45 | 2 | true |
+| expressions.cs:442:19:442:45 | array creation of type Int32[,] | expressions.cs:442:19:442:45 | 2 | true |
+| expressions.cs:442:48:442:82 | array creation of type Int32[,] | expressions.cs:442:48:442:82 | 3 | true |
+| expressions.cs:442:48:442:82 | array creation of type Int32[,] | expressions.cs:442:48:442:82 | 3 | true |
+| expressions.cs:442:85:442:122 | array creation of type Int32[,] | expressions.cs:442:85:442:122 | 3 | true |
+| expressions.cs:442:85:442:122 | array creation of type Int32[,] | expressions.cs:442:85:442:122 | 3 | true |
+| expressions.cs:444:17:444:81 | array creation of type Int32[,][] | expressions.cs:444:17:444:81 | 2 | true |
+| expressions.cs:444:17:444:81 | array creation of type Int32[,][] | expressions.cs:444:17:444:81 | 2 | true |
+| expressions.cs:444:17:444:81 | array creation of type Int32[,][] | expressions.cs:444:17:444:81 | 2 | true |
+| expressions.cs:444:32:444:54 | array creation of type Int32[,] | expressions.cs:444:32:444:54 | 1 | true |
+| expressions.cs:444:32:444:54 | array creation of type Int32[,] | expressions.cs:444:32:444:54 | 1 | true |
+| expressions.cs:444:57:444:79 | array creation of type Int32[,] | expressions.cs:444:57:444:79 | 1 | true |
+| expressions.cs:444:57:444:79 | array creation of type Int32[,] | expressions.cs:444:57:444:79 | 1 | true |
diff --git a/csharp/ql/test/library-tests/expressions/Lambda1.expected b/csharp/ql/test/library-tests/expressions/Lambda1.expected
index 030ff70fac4..34e99d531ad 100644
--- a/csharp/ql/test/library-tests/expressions/Lambda1.expected
+++ b/csharp/ql/test/library-tests/expressions/Lambda1.expected
@@ -1 +1 @@
-| expressions.cs:437:36:437:53 | (...) => ... |
+| expressions.cs:449:36:449:53 | (...) => ... |
diff --git a/csharp/ql/test/library-tests/expressions/Lambda2.expected b/csharp/ql/test/library-tests/expressions/Lambda2.expected
index 6b9ab4d13af..a59313cf13e 100644
--- a/csharp/ql/test/library-tests/expressions/Lambda2.expected
+++ b/csharp/ql/test/library-tests/expressions/Lambda2.expected
@@ -1 +1 @@
-| expressions.cs:438:38:438:59 | (...) => ... |
+| expressions.cs:450:38:450:59 | (...) => ... |
diff --git a/csharp/ql/test/library-tests/expressions/Lambda3.expected b/csharp/ql/test/library-tests/expressions/Lambda3.expected
index 0a17c1914d6..f00475e2298 100644
--- a/csharp/ql/test/library-tests/expressions/Lambda3.expected
+++ b/csharp/ql/test/library-tests/expressions/Lambda3.expected
@@ -1 +1 @@
-| expressions.cs:439:33:439:48 | (...) => ... |
+| expressions.cs:451:33:451:48 | (...) => ... |
diff --git a/csharp/ql/test/library-tests/expressions/Lambda4.expected b/csharp/ql/test/library-tests/expressions/Lambda4.expected
index da7c72353ae..92b0dea6fa5 100644
--- a/csharp/ql/test/library-tests/expressions/Lambda4.expected
+++ b/csharp/ql/test/library-tests/expressions/Lambda4.expected
@@ -1 +1 @@
-| expressions.cs:440:36:440:64 | (...) => ... |
+| expressions.cs:452:36:452:64 | (...) => ... |
diff --git a/csharp/ql/test/library-tests/expressions/Lambda5.expected b/csharp/ql/test/library-tests/expressions/Lambda5.expected
index cc846823df4..08d4b5546ae 100644
--- a/csharp/ql/test/library-tests/expressions/Lambda5.expected
+++ b/csharp/ql/test/library-tests/expressions/Lambda5.expected
@@ -1 +1 @@
-| expressions.cs:441:20:441:34 | (...) => ... |
+| expressions.cs:453:20:453:34 | (...) => ... |
diff --git a/csharp/ql/test/library-tests/expressions/Lambda6.expected b/csharp/ql/test/library-tests/expressions/Lambda6.expected
index 83d1caa259d..55427d92103 100644
--- a/csharp/ql/test/library-tests/expressions/Lambda6.expected
+++ b/csharp/ql/test/library-tests/expressions/Lambda6.expected
@@ -1 +1 @@
-| expressions.cs:442:23:442:47 | (...) => ... |
+| expressions.cs:454:23:454:47 | (...) => ... |
diff --git a/csharp/ql/test/library-tests/expressions/OperatorCall6.expected b/csharp/ql/test/library-tests/expressions/OperatorCall6.expected
index 208e70975f3..4f483ef92e2 100644
--- a/csharp/ql/test/library-tests/expressions/OperatorCall6.expected
+++ b/csharp/ql/test/library-tests/expressions/OperatorCall6.expected
@@ -1,4 +1,4 @@
-| expressions.cs:458:20:458:27 | addition | expressions.cs:460:26:460:26 | access to parameter a | expressions.cs:473:40:473:40 | + |
-| expressions.cs:458:20:458:27 | addition | expressions.cs:460:30:460:30 | access to parameter b | expressions.cs:473:40:473:40 | + |
-| expressions.cs:458:20:458:27 | addition | expressions.cs:461:13:461:18 | access to local variable result | expressions.cs:473:40:473:40 | + |
-| expressions.cs:458:20:458:27 | addition | expressions.cs:461:23:461:23 | access to parameter c | expressions.cs:473:40:473:40 | + |
+| expressions.cs:470:20:470:27 | addition | expressions.cs:472:26:472:26 | access to parameter a | expressions.cs:485:40:485:40 | + |
+| expressions.cs:470:20:470:27 | addition | expressions.cs:472:30:472:30 | access to parameter b | expressions.cs:485:40:485:40 | + |
+| expressions.cs:470:20:470:27 | addition | expressions.cs:473:13:473:18 | access to local variable result | expressions.cs:485:40:485:40 | + |
+| expressions.cs:470:20:470:27 | addition | expressions.cs:473:23:473:23 | access to parameter c | expressions.cs:485:40:485:40 | + |
diff --git a/csharp/ql/test/library-tests/expressions/OperatorCall7.expected b/csharp/ql/test/library-tests/expressions/OperatorCall7.expected
index b59bcd180fa..46f56cef5ac 100644
--- a/csharp/ql/test/library-tests/expressions/OperatorCall7.expected
+++ b/csharp/ql/test/library-tests/expressions/OperatorCall7.expected
@@ -1,2 +1,2 @@
-| expressions.cs:452:21:452:35 | delegateCombine | expressions.cs:450:11:450:23 | OperatorCalls | expressions.cs:455:13:455:27 | access to local variable PropertyChanged | expressions.cs:479:30:479:39 | MyDelegate |
-| expressions.cs:452:21:452:35 | delegateCombine | expressions.cs:450:11:450:23 | OperatorCalls | expressions.cs:455:32:455:34 | access to parameter fun | expressions.cs:479:30:479:39 | MyDelegate |
+| expressions.cs:464:21:464:35 | delegateCombine | expressions.cs:462:11:462:23 | OperatorCalls | expressions.cs:467:13:467:27 | access to local variable PropertyChanged | expressions.cs:491:30:491:39 | MyDelegate |
+| expressions.cs:464:21:464:35 | delegateCombine | expressions.cs:462:11:462:23 | OperatorCalls | expressions.cs:467:32:467:34 | access to parameter fun | expressions.cs:491:30:491:39 | MyDelegate |
diff --git a/csharp/ql/test/library-tests/expressions/PrintAst.expected b/csharp/ql/test/library-tests/expressions/PrintAst.expected
index 0e10aa2711e..fff855518da 100644
--- a/csharp/ql/test/library-tests/expressions/PrintAst.expected
+++ b/csharp/ql/test/library-tests/expressions/PrintAst.expected
@@ -1491,333 +1491,454 @@ expressions.cs:
# 432| 0: [Parameter] x
# 432| 1: [Parameter] y
# 433| 7: [DelegateType] Unit
-# 435| 8: [Method] MainAnonymousFunctions
+# 435| 8: [Method] MultiDimensionalArrayCreations
# 436| 4: [BlockStmt] {...}
# 437| 0: [LocalVariableDeclStmt] ... ...;
-# 437| 0: [LocalVariableDeclAndInitExpr] Func f1 = ...
-# 437| 0: [LambdaExpr] (...) => ...
-# 437| 0: [CastExpr] (...) ...
-# 437| 0: [AddExpr] ... + ...
-# 437| 0: [CastExpr] (...) ...
-# 437| 0: [ParameterAccess] access to parameter x
-# 437| 1: [IntLiteral] 1
-# 437| 1: [TypeAccess] access to type Byte
+# 437| 0: [LocalVariableDeclAndInitExpr] Object o = ...
+# 437| 0: [ArrayCreation] array creation of type Int32[,]
+# 437| -1: [ArrayInitializer] { ..., ... }
+# 437| 0: [ArrayInitializer] { ..., ... }
+# 437| 0: [IntLiteral] 1
+# 437| 1: [IntLiteral] 2
+# 437| 1: [ArrayInitializer] { ..., ... }
+# 437| 0: [IntLiteral] 3
+# 437| 1: [IntLiteral] 4
+# 437| 2: [ArrayInitializer] { ..., ... }
+# 437| 0: [IntLiteral] 5
+# 437| 1: [IntLiteral] 6
+# 437| 1: [LocalVariableAccess] access to local variable o
+# 438| 1: [ExprStmt] ...;
+# 438| 0: [AssignExpr] ... = ...
+# 438| 0: [ArrayCreation] array creation of type Int32[,,]
+# 438| -1: [ArrayInitializer] { ..., ... }
+# 438| 0: [ArrayInitializer] { ..., ... }
+# 438| 0: [ArrayInitializer] { ..., ... }
+# 438| 0: [IntLiteral] 1
+# 438| 1: [IntLiteral] 2
+# 438| 2: [IntLiteral] 3
+# 438| 1: [ArrayInitializer] { ..., ... }
+# 438| 0: [IntLiteral] 4
+# 438| 1: [IntLiteral] 5
+# 438| 2: [IntLiteral] 6
+# 438| 1: [ArrayInitializer] { ..., ... }
+# 438| 0: [ArrayInitializer] { ..., ... }
+# 438| 0: [IntLiteral] 7
+# 438| 1: [IntLiteral] 8
+# 438| 2: [IntLiteral] 9
+# 438| 1: [ArrayInitializer] { ..., ... }
+# 438| 0: [IntLiteral] 10
+# 438| 1: [IntLiteral] 11
+# 438| 2: [IntLiteral] 12
+# 438| 1: [LocalVariableAccess] access to local variable o
+# 439| 2: [ExprStmt] ...;
+# 439| 0: [AssignExpr] ... = ...
+# 439| 0: [ArrayCreation] array creation of type Int32[,][,]
+# 440| -1: [ArrayInitializer] { ..., ... }
+# 441| 0: [ArrayInitializer] { ..., ... }
+# 441| 0: [ArrayCreation] array creation of type Int32[,]
+# 441| -1: [ArrayInitializer] { ..., ... }
+# 441| 0: [ArrayInitializer] { ..., ... }
+# 441| 0: [IntLiteral] 1
+# 441| 1: [IntLiteral] 3
+# 441| 1: [ArrayInitializer] { ..., ... }
+# 441| 0: [IntLiteral] 5
+# 441| 1: [IntLiteral] 7
+# 441| 1: [ArrayCreation] array creation of type Int32[,]
+# 441| -1: [ArrayInitializer] { ..., ... }
+# 441| 0: [ArrayInitializer] { ..., ... }
+# 441| 0: [IntLiteral] 0
+# 441| 1: [IntLiteral] 2
+# 441| 1: [ArrayInitializer] { ..., ... }
+# 441| 0: [IntLiteral] 4
+# 441| 1: [IntLiteral] 6
+# 441| 2: [ArrayInitializer] { ..., ... }
+# 441| 0: [IntLiteral] 8
+# 441| 1: [IntLiteral] 10
+# 441| 2: [ArrayCreation] array creation of type Int32[,]
+# 441| -1: [ArrayInitializer] { ..., ... }
+# 441| 0: [ArrayInitializer] { ..., ... }
+# 441| 0: [IntLiteral] 11
+# 441| 1: [IntLiteral] 22
+# 441| 1: [ArrayInitializer] { ..., ... }
+# 441| 0: [IntLiteral] 99
+# 441| 1: [IntLiteral] 88
+# 441| 2: [ArrayInitializer] { ..., ... }
+# 441| 0: [IntLiteral] 0
+# 441| 1: [IntLiteral] 9
+# 442| 1: [ArrayInitializer] { ..., ... }
+# 442| 0: [ArrayCreation] array creation of type Int32[,]
+# 442| -1: [ArrayInitializer] { ..., ... }
+# 442| 0: [ArrayInitializer] { ..., ... }
+# 442| 0: [IntLiteral] 1
+# 442| 1: [IntLiteral] 3
+# 442| 1: [ArrayInitializer] { ..., ... }
+# 442| 0: [IntLiteral] 5
+# 442| 1: [IntLiteral] 7
+# 442| 1: [ArrayCreation] array creation of type Int32[,]
+# 442| -1: [ArrayInitializer] { ..., ... }
+# 442| 0: [ArrayInitializer] { ..., ... }
+# 442| 0: [IntLiteral] 0
+# 442| 1: [IntLiteral] 2
+# 442| 1: [ArrayInitializer] { ..., ... }
+# 442| 0: [IntLiteral] 4
+# 442| 1: [IntLiteral] 6
+# 442| 2: [ArrayInitializer] { ..., ... }
+# 442| 0: [IntLiteral] 8
+# 442| 1: [IntLiteral] 10
+# 442| 2: [ArrayCreation] array creation of type Int32[,]
+# 442| -1: [ArrayInitializer] { ..., ... }
+# 442| 0: [ArrayInitializer] { ..., ... }
+# 442| 0: [IntLiteral] 11
+# 442| 1: [IntLiteral] 22
+# 442| 1: [ArrayInitializer] { ..., ... }
+# 442| 0: [IntLiteral] 99
+# 442| 1: [IntLiteral] 88
+# 442| 2: [ArrayInitializer] { ..., ... }
+# 442| 0: [IntLiteral] 0
+# 442| 1: [IntLiteral] 9
+# 439| 1: [LocalVariableAccess] access to local variable o
+# 444| 3: [ExprStmt] ...;
+# 444| 0: [AssignExpr] ... = ...
+# 444| 0: [ArrayCreation] array creation of type Int32[,][]
+# 444| -1: [ArrayInitializer] { ..., ... }
+# 444| 0: [ArrayCreation] array creation of type Int32[,]
+# 444| -1: [ArrayInitializer] { ..., ... }
+# 444| 0: [ArrayInitializer] { ..., ... }
+# 444| 0: [IntLiteral] 1
+# 444| 1: [IntLiteral] 2
+# 444| 1: [ArrayCreation] array creation of type Int32[,]
+# 444| -1: [ArrayInitializer] { ..., ... }
+# 444| 0: [ArrayInitializer] { ..., ... }
+# 444| 0: [IntLiteral] 1
+# 444| 1: [IntLiteral] 2
+# 444| 1: [LocalVariableAccess] access to local variable o
+# 447| 9: [Method] MainAnonymousFunctions
+# 448| 4: [BlockStmt] {...}
+# 449| 0: [LocalVariableDeclStmt] ... ...;
+# 449| 0: [LocalVariableDeclAndInitExpr] Func f1 = ...
+# 449| 0: [LambdaExpr] (...) => ...
+# 449| 0: [CastExpr] (...) ...
+# 449| 0: [AddExpr] ... + ...
+# 449| 0: [CastExpr] (...) ...
+# 449| 0: [ParameterAccess] access to parameter x
+# 449| 1: [IntLiteral] 1
+# 449| 1: [TypeAccess] access to type Byte
#-----| 2: (Parameters)
-# 437| 0: [Parameter] x
-# 437| 1: [LocalVariableAccess] access to local variable f1
-# 438| 1: [LocalVariableDeclStmt] ... ...;
-# 438| 0: [LocalVariableDeclAndInitExpr] Func f2 = ...
-# 438| 0: [LambdaExpr] (...) => ...
-# 438| 0: [BlockStmt] {...}
-# 438| 0: [ReturnStmt] return ...;
-# 438| 0: [CastExpr] (...) ...
-# 438| 0: [AddExpr] ... + ...
-# 438| 0: [ParameterAccess] access to parameter x
-# 438| 1: [IntLiteral] 1
+# 449| 0: [Parameter] x
+# 449| 1: [LocalVariableAccess] access to local variable f1
+# 450| 1: [LocalVariableDeclStmt] ... ...;
+# 450| 0: [LocalVariableDeclAndInitExpr] Func f2 = ...
+# 450| 0: [LambdaExpr] (...) => ...
+# 450| 0: [BlockStmt] {...}
+# 450| 0: [ReturnStmt] return ...;
+# 450| 0: [CastExpr] (...) ...
+# 450| 0: [AddExpr] ... + ...
+# 450| 0: [ParameterAccess] access to parameter x
+# 450| 1: [IntLiteral] 1
#-----| 2: (Parameters)
-# 438| 0: [Parameter] x
-# 438| 1: [LocalVariableAccess] access to local variable f2
-# 439| 2: [LocalVariableDeclStmt] ... ...;
-# 439| 0: [LocalVariableDeclAndInitExpr] Func f3 = ...
-# 439| 0: [LambdaExpr] (...) => ...
-# 439| 0: [AddExpr] ... + ...
-# 439| 0: [ParameterAccess] access to parameter x
-# 439| 1: [IntLiteral] 1
+# 450| 0: [Parameter] x
+# 450| 1: [LocalVariableAccess] access to local variable f2
+# 451| 2: [LocalVariableDeclStmt] ... ...;
+# 451| 0: [LocalVariableDeclAndInitExpr] Func f3 = ...
+# 451| 0: [LambdaExpr] (...) => ...
+# 451| 0: [AddExpr] ... + ...
+# 451| 0: [ParameterAccess] access to parameter x
+# 451| 1: [IntLiteral] 1
#-----| 2: (Parameters)
-# 439| 0: [Parameter] x
-# 439| 1: [LocalVariableAccess] access to local variable f3
-# 440| 3: [LocalVariableDeclStmt] ... ...;
-# 440| 0: [LocalVariableDeclAndInitExpr] Func f4 = ...
-# 440| 0: [LambdaExpr] (...) => ...
-# 440| 0: [BlockStmt] {...}
-# 440| 0: [ReturnStmt] return ...;
-# 440| 0: [AddExpr] ... + ...
-# 440| 0: [CastExpr] (...) ...
-# 440| 0: [ParameterAccess] access to parameter x
-# 440| 1: [StringLiteral] ""
+# 451| 0: [Parameter] x
+# 451| 1: [LocalVariableAccess] access to local variable f3
+# 452| 3: [LocalVariableDeclStmt] ... ...;
+# 452| 0: [LocalVariableDeclAndInitExpr] Func f4 = ...
+# 452| 0: [LambdaExpr] (...) => ...
+# 452| 0: [BlockStmt] {...}
+# 452| 0: [ReturnStmt] return ...;
+# 452| 0: [AddExpr] ... + ...
+# 452| 0: [CastExpr] (...) ...
+# 452| 0: [ParameterAccess] access to parameter x
+# 452| 1: [StringLiteral] ""
#-----| 2: (Parameters)
-# 440| 0: [Parameter] x
-# 440| 1: [LocalVariableAccess] access to local variable f4
-# 441| 4: [LocalVariableDeclStmt] ... ...;
-# 441| 0: [LocalVariableDeclAndInitExpr] S f5 = ...
-# 441| 0: [LambdaExpr] (...) => ...
-# 441| 0: [MulExpr] ... * ...
-# 441| 0: [ParameterAccess] access to parameter x
-# 441| 1: [ParameterAccess] access to parameter y
+# 452| 0: [Parameter] x
+# 452| 1: [LocalVariableAccess] access to local variable f4
+# 453| 4: [LocalVariableDeclStmt] ... ...;
+# 453| 0: [LocalVariableDeclAndInitExpr] S f5 = ...
+# 453| 0: [LambdaExpr] (...) => ...
+# 453| 0: [MulExpr] ... * ...
+# 453| 0: [ParameterAccess] access to parameter x
+# 453| 1: [ParameterAccess] access to parameter y
#-----| 2: (Parameters)
-# 441| 0: [Parameter] x
-# 441| 1: [Parameter] y
-# 441| 1: [LocalVariableAccess] access to local variable f5
-# 442| 5: [LocalVariableDeclStmt] ... ...;
-# 442| 0: [LocalVariableDeclAndInitExpr] Unit f6 = ...
-# 442| 0: [LambdaExpr] (...) => ...
-# 442| 0: [MethodCall] call to method WriteLine
-# 442| -1: [TypeAccess] access to type Console
-# 442| 1: [LocalVariableAccess] access to local variable f6
-# 443| 6: [LocalVariableDeclStmt] ... ...;
-# 443| 0: [LocalVariableDeclAndInitExpr] Func f7 = ...
-# 443| 0: [AnonymousMethodExpr] delegate(...) { ... }
-# 443| 0: [BlockStmt] {...}
-# 443| 0: [ReturnStmt] return ...;
-# 443| 0: [AddExpr] ... + ...
-# 443| 0: [ParameterAccess] access to parameter x
-# 443| 1: [IntLiteral] 1
+# 453| 0: [Parameter] x
+# 453| 1: [Parameter] y
+# 453| 1: [LocalVariableAccess] access to local variable f5
+# 454| 5: [LocalVariableDeclStmt] ... ...;
+# 454| 0: [LocalVariableDeclAndInitExpr] Unit f6 = ...
+# 454| 0: [LambdaExpr] (...) => ...
+# 454| 0: [MethodCall] call to method WriteLine
+# 454| -1: [TypeAccess] access to type Console
+# 454| 1: [LocalVariableAccess] access to local variable f6
+# 455| 6: [LocalVariableDeclStmt] ... ...;
+# 455| 0: [LocalVariableDeclAndInitExpr] Func f7 = ...
+# 455| 0: [AnonymousMethodExpr] delegate(...) { ... }
+# 455| 0: [BlockStmt] {...}
+# 455| 0: [ReturnStmt] return ...;
+# 455| 0: [AddExpr] ... + ...
+# 455| 0: [ParameterAccess] access to parameter x
+# 455| 1: [IntLiteral] 1
#-----| 2: (Parameters)
-# 443| 0: [Parameter] x
-# 443| 1: [LocalVariableAccess] access to local variable f7
-# 444| 7: [LocalVariableDeclStmt] ... ...;
-# 444| 0: [LocalVariableDeclAndInitExpr] Int32 j = ...
-# 444| 0: [IntLiteral] 0
-# 444| 1: [LocalVariableAccess] access to local variable j
-# 445| 8: [LocalVariableDeclStmt] ... ...;
-# 445| 0: [LocalVariableDeclAndInitExpr] Func f8 = ...
-# 445| 0: [AnonymousMethodExpr] delegate(...) { ... }
-# 445| 0: [BlockStmt] {...}
-# 445| 0: [ReturnStmt] return ...;
-# 445| 0: [AddExpr] ... + ...
-# 445| 0: [LocalVariableAccess] access to local variable j
-# 445| 1: [IntLiteral] 1
-# 445| 1: [LocalVariableAccess] access to local variable f8
-# 450| 18: [Class] OperatorCalls
-# 452| 5: [Method] delegateCombine
+# 455| 0: [Parameter] x
+# 455| 1: [LocalVariableAccess] access to local variable f7
+# 456| 7: [LocalVariableDeclStmt] ... ...;
+# 456| 0: [LocalVariableDeclAndInitExpr] Int32 j = ...
+# 456| 0: [IntLiteral] 0
+# 456| 1: [LocalVariableAccess] access to local variable j
+# 457| 8: [LocalVariableDeclStmt] ... ...;
+# 457| 0: [LocalVariableDeclAndInitExpr] Func f8 = ...
+# 457| 0: [AnonymousMethodExpr] delegate(...) { ... }
+# 457| 0: [BlockStmt] {...}
+# 457| 0: [ReturnStmt] return ...;
+# 457| 0: [AddExpr] ... + ...
+# 457| 0: [LocalVariableAccess] access to local variable j
+# 457| 1: [IntLiteral] 1
+# 457| 1: [LocalVariableAccess] access to local variable f8
+# 462| 18: [Class] OperatorCalls
+# 464| 5: [Method] delegateCombine
#-----| 2: (Parameters)
-# 452| 0: [Parameter] fun
-# 453| 4: [BlockStmt] {...}
-# 454| 0: [LocalVariableDeclStmt] ... ...;
-# 454| 0: [LocalVariableDeclAndInitExpr] MyDelegate PropertyChanged = ...
-# 454| 0: [NullLiteral] null
-# 454| 1: [LocalVariableAccess] access to local variable PropertyChanged
-# 455| 1: [ExprStmt] ...;
-# 455| 0: [AssignAddExpr] ... += ...
-# 455| 0: [ParameterAccess] access to parameter fun
-# 455| 1: [LocalVariableAccess] access to local variable PropertyChanged
-# 458| 6: [Method] addition
+# 464| 0: [Parameter] fun
+# 465| 4: [BlockStmt] {...}
+# 466| 0: [LocalVariableDeclStmt] ... ...;
+# 466| 0: [LocalVariableDeclAndInitExpr] MyDelegate PropertyChanged = ...
+# 466| 0: [NullLiteral] null
+# 466| 1: [LocalVariableAccess] access to local variable PropertyChanged
+# 467| 1: [ExprStmt] ...;
+# 467| 0: [AssignAddExpr] ... += ...
+# 467| 0: [ParameterAccess] access to parameter fun
+# 467| 1: [LocalVariableAccess] access to local variable PropertyChanged
+# 470| 6: [Method] addition
#-----| 2: (Parameters)
-# 458| 0: [Parameter] a
-# 458| 1: [Parameter] b
-# 458| 2: [Parameter] c
-# 459| 4: [BlockStmt] {...}
-# 460| 0: [LocalVariableDeclStmt] ... ...;
-# 460| 0: [LocalVariableDeclAndInitExpr] Num result = ...
-# 460| 0: [OperatorCall] call to operator +
-# 460| 0: [ParameterAccess] access to parameter a
-# 460| 1: [ParameterAccess] access to parameter b
-# 460| 1: [LocalVariableAccess] access to local variable result
-# 461| 1: [ExprStmt] ...;
-# 461| 0: [AssignAddExpr] ... += ...
-# 461| 0: [ParameterAccess] access to parameter c
-# 461| 1: [LocalVariableAccess] access to local variable result
-# 462| 2: [ReturnStmt] return ...;
-# 462| 0: [LocalVariableAccess] access to local variable result
-# 464| 7: [Class] Num
-# 466| 4: [Field] value
-# 468| 5: [InstanceConstructor] Num
+# 470| 0: [Parameter] a
+# 470| 1: [Parameter] b
+# 470| 2: [Parameter] c
+# 471| 4: [BlockStmt] {...}
+# 472| 0: [LocalVariableDeclStmt] ... ...;
+# 472| 0: [LocalVariableDeclAndInitExpr] Num result = ...
+# 472| 0: [OperatorCall] call to operator +
+# 472| 0: [ParameterAccess] access to parameter a
+# 472| 1: [ParameterAccess] access to parameter b
+# 472| 1: [LocalVariableAccess] access to local variable result
+# 473| 1: [ExprStmt] ...;
+# 473| 0: [AssignAddExpr] ... += ...
+# 473| 0: [ParameterAccess] access to parameter c
+# 473| 1: [LocalVariableAccess] access to local variable result
+# 474| 2: [ReturnStmt] return ...;
+# 474| 0: [LocalVariableAccess] access to local variable result
+# 476| 7: [Class] Num
+# 478| 4: [Field] value
+# 480| 5: [InstanceConstructor] Num
#-----| 2: (Parameters)
-# 468| 0: [Parameter] value
-# 469| 4: [BlockStmt] {...}
-# 470| 0: [ExprStmt] ...;
-# 470| 0: [AssignExpr] ... = ...
-# 470| 0: [ParameterAccess] access to parameter value
-# 470| 1: [FieldAccess] access to field value
-# 470| -1: [ThisAccess] this access
-# 473| 6: [AddOperator] +
+# 480| 0: [Parameter] value
+# 481| 4: [BlockStmt] {...}
+# 482| 0: [ExprStmt] ...;
+# 482| 0: [AssignExpr] ... = ...
+# 482| 0: [ParameterAccess] access to parameter value
+# 482| 1: [FieldAccess] access to field value
+# 482| -1: [ThisAccess] this access
+# 485| 6: [AddOperator] +
#-----| 2: (Parameters)
-# 473| 0: [Parameter] c1
-# 473| 1: [Parameter] c2
-# 474| 4: [BlockStmt] {...}
-# 475| 0: [ReturnStmt] return ...;
-# 475| 0: [ObjectCreation] object creation of type Num
-# 475| 0: [AddExpr] ... + ...
-# 475| 0: [FieldAccess] access to field value
-# 475| -1: [ParameterAccess] access to parameter c1
-# 475| 1: [FieldAccess] access to field value
-# 475| -1: [ParameterAccess] access to parameter c2
-# 479| 8: [DelegateType] MyDelegate
+# 485| 0: [Parameter] c1
+# 485| 1: [Parameter] c2
+# 486| 4: [BlockStmt] {...}
+# 487| 0: [ReturnStmt] return ...;
+# 487| 0: [ObjectCreation] object creation of type Num
+# 487| 0: [AddExpr] ... + ...
+# 487| 0: [FieldAccess] access to field value
+# 487| -1: [ParameterAccess] access to parameter c1
+# 487| 1: [FieldAccess] access to field value
+# 487| -1: [ParameterAccess] access to parameter c2
+# 491| 8: [DelegateType] MyDelegate
#-----| 2: (Parameters)
-# 479| 0: [Parameter] e
-# 482| 19: [Class] ExpressionDepth
-# 484| 5: [Field] d
-# 484| 1: [AssignExpr] ... = ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [AddExpr] ... + ...
-# 484| 0: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 484| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 485| 1: [IntLiteral] 1
-# 484| 1: [MemberConstantAccess] access to constant d
-# 488| 20: [Class] TupleExprs
-# 490| 5: [Method] Test
-# 491| 4: [BlockStmt] {...}
-# 492| 0: [LocalVariableDeclStmt] ... ...;
-# 492| 0: [LocalVariableDeclAndInitExpr] (Int32,String) a = ...
-# 492| 0: [DefaultValueExpr] default(...)
-# 492| 0: [TypeAccess] access to type (Int32,String)
-# 492| 1: [LocalVariableAccess] access to local variable a
-# 493| 1: [LocalVariableDeclStmt] ... ...;
-# 493| 0: [LocalVariableDeclAndInitExpr] (Boolean,Int32[],Object) b = ...
-# 493| 0: [DefaultValueExpr] default(...)
-# 493| 0: [TypeAccess] access to type (Boolean,Int32[],Object)
-# 493| 1: [LocalVariableAccess] access to local variable b
-# 494| 2: [LocalVariableDeclStmt] ... ...;
-# 494| 0: [LocalVariableDeclAndInitExpr] Type x = ...
-# 494| 0: [TypeofExpr] typeof(...)
-# 494| 0: [TypeAccess] access to type (Int32,String)
-# 494| 1: [LocalVariableAccess] access to local variable x
-# 495| 3: [LocalVariableDeclStmt] ... ...;
-# 495| 0: [LocalVariableDeclAndInitExpr] Type y = ...
-# 495| 0: [TypeofExpr] typeof(...)
-# 495| 0: [TypeAccess] access to type (Boolean,Int32[],dynamic)
-# 495| 1: [LocalVariableAccess] access to local variable y
+# 491| 0: [Parameter] e
+# 494| 19: [Class] ExpressionDepth
+# 496| 5: [Field] d
+# 496| 1: [AssignExpr] ... = ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [AddExpr] ... + ...
+# 496| 0: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 496| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 497| 1: [IntLiteral] 1
+# 496| 1: [MemberConstantAccess] access to constant d
+# 500| 20: [Class] TupleExprs
+# 502| 5: [Method] Test
+# 503| 4: [BlockStmt] {...}
+# 504| 0: [LocalVariableDeclStmt] ... ...;
+# 504| 0: [LocalVariableDeclAndInitExpr] (Int32,String) a = ...
+# 504| 0: [DefaultValueExpr] default(...)
+# 504| 0: [TypeAccess] access to type (Int32,String)
+# 504| 1: [LocalVariableAccess] access to local variable a
+# 505| 1: [LocalVariableDeclStmt] ... ...;
+# 505| 0: [LocalVariableDeclAndInitExpr] (Boolean,Int32[],Object) b = ...
+# 505| 0: [DefaultValueExpr] default(...)
+# 505| 0: [TypeAccess] access to type (Boolean,Int32[],Object)
+# 505| 1: [LocalVariableAccess] access to local variable b
+# 506| 2: [LocalVariableDeclStmt] ... ...;
+# 506| 0: [LocalVariableDeclAndInitExpr] Type x = ...
+# 506| 0: [TypeofExpr] typeof(...)
+# 506| 0: [TypeAccess] access to type (Int32,String)
+# 506| 1: [LocalVariableAccess] access to local variable x
+# 507| 3: [LocalVariableDeclStmt] ... ...;
+# 507| 0: [LocalVariableDeclAndInitExpr] Type y = ...
+# 507| 0: [TypeofExpr] typeof(...)
+# 507| 0: [TypeAccess] access to type (Boolean,Int32[],dynamic)
+# 507| 1: [LocalVariableAccess] access to local variable y
diff --git a/csharp/ql/test/library-tests/expressions/QualifiableExpr.expected b/csharp/ql/test/library-tests/expressions/QualifiableExpr.expected
index 0013cee52a9..f5b5c0aa892 100644
--- a/csharp/ql/test/library-tests/expressions/QualifiableExpr.expected
+++ b/csharp/ql/test/library-tests/expressions/QualifiableExpr.expected
@@ -65,7 +65,7 @@
| expressions.cs:377:43:377:46 | access to field name | expressions.cs:377:43:377:46 | this access |
| expressions.cs:377:57:377:60 | access to field name | expressions.cs:377:57:377:60 | this access |
| expressions.cs:378:57:378:68 | access to field phoneNumbers | expressions.cs:378:57:378:68 | this access |
-| expressions.cs:442:29:442:47 | call to method WriteLine | expressions.cs:442:29:442:35 | access to type Console |
-| expressions.cs:470:17:470:26 | access to field value | expressions.cs:470:17:470:20 | this access |
-| expressions.cs:475:32:475:39 | access to field value | expressions.cs:475:32:475:33 | access to parameter c1 |
-| expressions.cs:475:43:475:50 | access to field value | expressions.cs:475:43:475:44 | access to parameter c2 |
+| expressions.cs:454:29:454:47 | call to method WriteLine | expressions.cs:454:29:454:35 | access to type Console |
+| expressions.cs:482:17:482:26 | access to field value | expressions.cs:482:17:482:20 | this access |
+| expressions.cs:487:32:487:39 | access to field value | expressions.cs:487:32:487:33 | access to parameter c1 |
+| expressions.cs:487:43:487:50 | access to field value | expressions.cs:487:43:487:44 | access to parameter c2 |
diff --git a/csharp/ql/test/library-tests/expressions/StripCasts.expected b/csharp/ql/test/library-tests/expressions/StripCasts.expected
index bf16559809a..6fdfa4be3d5 100644
--- a/csharp/ql/test/library-tests/expressions/StripCasts.expected
+++ b/csharp/ql/test/library-tests/expressions/StripCasts.expected
@@ -50,7 +50,7 @@
| expressions.cs:334:30:334:30 | (...) ... | expressions.cs:334:30:334:30 | 8 |
| expressions.cs:414:31:414:31 | (...) ... | expressions.cs:414:31:414:31 | 1 |
| expressions.cs:414:39:414:39 | (...) ... | expressions.cs:414:39:414:39 | 2 |
-| expressions.cs:437:41:437:53 | (...) ... | expressions.cs:437:48:437:52 | ... + ... |
-| expressions.cs:437:48:437:48 | (...) ... | expressions.cs:437:48:437:48 | access to parameter x |
-| expressions.cs:438:52:438:56 | (...) ... | expressions.cs:438:52:438:56 | ... + ... |
-| expressions.cs:440:56:440:56 | (...) ... | expressions.cs:440:56:440:56 | access to parameter x |
+| expressions.cs:449:41:449:53 | (...) ... | expressions.cs:449:48:449:52 | ... + ... |
+| expressions.cs:449:48:449:48 | (...) ... | expressions.cs:449:48:449:48 | access to parameter x |
+| expressions.cs:450:52:450:56 | (...) ... | expressions.cs:450:52:450:56 | ... + ... |
+| expressions.cs:452:56:452:56 | (...) ... | expressions.cs:452:56:452:56 | access to parameter x |
diff --git a/csharp/ql/test/library-tests/expressions/Tuples1.expected b/csharp/ql/test/library-tests/expressions/Tuples1.expected
index 70210471b03..ea05f07d8eb 100644
--- a/csharp/ql/test/library-tests/expressions/Tuples1.expected
+++ b/csharp/ql/test/library-tests/expressions/Tuples1.expected
@@ -1,4 +1,4 @@
-| expressions.cs:492:29:492:41 | access to type (Int32,String) | expressions.cs:492:29:492:41 | (Int32,String) |
-| expressions.cs:493:29:493:49 | access to type (Boolean,Int32[],Object) | expressions.cs:493:29:493:49 | (Boolean,Int32[],Object) |
-| expressions.cs:494:28:494:40 | access to type (Int32,String) | expressions.cs:492:29:492:41 | (Int32,String) |
-| expressions.cs:495:28:495:49 | access to type (Boolean,Int32[],dynamic) | expressions.cs:495:28:495:49 | (Boolean,Int32[],dynamic) |
+| expressions.cs:504:29:504:41 | access to type (Int32,String) | expressions.cs:504:29:504:41 | (Int32,String) |
+| expressions.cs:505:29:505:49 | access to type (Boolean,Int32[],Object) | expressions.cs:505:29:505:49 | (Boolean,Int32[],Object) |
+| expressions.cs:506:28:506:40 | access to type (Int32,String) | expressions.cs:504:29:504:41 | (Int32,String) |
+| expressions.cs:507:28:507:49 | access to type (Boolean,Int32[],dynamic) | expressions.cs:507:28:507:49 | (Boolean,Int32[],dynamic) |
diff --git a/csharp/ql/test/library-tests/expressions/expressions.cs b/csharp/ql/test/library-tests/expressions/expressions.cs
index aea2d80729c..c9e7419af9b 100644
--- a/csharp/ql/test/library-tests/expressions/expressions.cs
+++ b/csharp/ql/test/library-tests/expressions/expressions.cs
@@ -432,6 +432,18 @@ namespace Expressions
delegate int S(int x, int y);
delegate void Unit();
+ void MultiDimensionalArrayCreations()
+ {
+ object o = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 } };
+ o = new int[,,] { { { 1, 2, 3 }, { 4, 5, 6 } }, { { 7, 8, 9 }, { 10, 11, 12 } } };
+ o = new int[,][,]
+ {
+ { new int[,] { {1,3}, {5,7} }, new int[,] { {0,2}, {4,6}, {8,10} }, new int[,] { {11,22}, {99,88}, {0,9} } },
+ { new int[,] { {1,3}, {5,7} }, new int[,] { {0,2}, {4,6}, {8,10} }, new int[,] { {11,22}, {99,88}, {0,9} } }
+ };
+ o = new int[][,] { new int[,] { { 1, 2 } }, new int[,] { { 1, 2 } } };
+ }
+
void MainAnonymousFunctions()
{
Func f1 = x => (byte)(x + 1); // Implicitly typed, expression body
From 7516825b5faa3cc24df18f2b559aec583bd42510 Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Mon, 24 Aug 2020 16:08:13 +0200
Subject: [PATCH 086/146] C#: Fix computed sizes for implicitly sized array
creation
---
.../Entities/Expressions/ArrayCreation.cs | 71 +++++++++++--------
.../expressions/ArrayCreation11.expected | 24 +++----
2 files changed, 50 insertions(+), 45 deletions(-)
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ArrayCreation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ArrayCreation.cs
index 068cf2368fd..6c31ba9ac35 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ArrayCreation.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ArrayCreation.cs
@@ -1,4 +1,5 @@
using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Semmle.Extraction.Kinds;
using System.IO;
@@ -21,7 +22,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
protected override void PopulateExpression(TextWriter trapFile)
{
- var child = 0;
+
var explicitlySized = false;
if (TypeSyntax is null)
@@ -29,38 +30,21 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
cx.ModelError(Syntax, "Array has unexpected type syntax");
}
- foreach (var rank in TypeSyntax.RankSpecifiers.SelectMany(rs => rs.Sizes))
+ var firstLevelSizes = TypeSyntax.RankSpecifiers.First()?.Sizes ?? SyntaxFactory.SeparatedList();
+
+ if (firstLevelSizes.OfType().Any(s => s is OmittedArraySizeExpressionSyntax))
{
- if (rank is OmittedArraySizeExpressionSyntax)
- {
- // Create an expression which simulates the explicit size of the array
-
- if (!(Initializer is null))
- {
- // An implicitly-sized array must have an initializer.
- // Guard it just in case.
- var size = Initializer.Expressions.Count;
-
- var info = new ExpressionInfo(
- cx,
- new AnnotatedType(Entities.Type.Create(cx, cx.Compilation.GetSpecialType(Microsoft.CodeAnalysis.SpecialType.System_Int32)), NullableAnnotation.None),
- Location,
- ExprKind.INT_LITERAL,
- this,
- child,
- true,
- size.ToString());
-
- new Expression(info);
- }
- }
- else
- {
- Create(cx, rank, this, child);
- explicitlySized = true;
- }
- child++;
+ SetArraySizes(Initializer, firstLevelSizes.Count);
}
+ else
+ {
+ for (var sizeIndex = 0; sizeIndex < firstLevelSizes.Count; sizeIndex++)
+ {
+ Create(cx, firstLevelSizes[sizeIndex], this, sizeIndex);
+ }
+ explicitlySized = true;
+ }
+
if (!(Initializer is null))
{
ArrayInitializer.Create(new ExpressionNodeInfo(cx, Initializer, this, -1));
@@ -69,6 +53,31 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
if (explicitlySized)
trapFile.explicitly_sized_array_creation(this);
}
+
+ private void SetArraySizes(InitializerExpressionSyntax initializer, int rank)
+ {
+ for (var level = 0; level < rank; level++)
+ {
+ if (initializer is null)
+ {
+ return;
+ }
+
+ var info = new ExpressionInfo(
+ cx,
+ new AnnotatedType(Entities.Type.Create(cx, cx.Compilation.GetSpecialType(Microsoft.CodeAnalysis.SpecialType.System_Int32)), NullableAnnotation.None),
+ Location,
+ ExprKind.INT_LITERAL,
+ this,
+ level,
+ true,
+ initializer.Expressions.Count.ToString());
+
+ new Expression(info);
+
+ initializer = initializer.Expressions.FirstOrDefault() as InitializerExpressionSyntax;
+ }
+ }
}
class NormalArrayCreation : ExplicitArrayCreation
diff --git a/csharp/ql/test/library-tests/expressions/ArrayCreation11.expected b/csharp/ql/test/library-tests/expressions/ArrayCreation11.expected
index 80714fe6906..9542bb21b21 100644
--- a/csharp/ql/test/library-tests/expressions/ArrayCreation11.expected
+++ b/csharp/ql/test/library-tests/expressions/ArrayCreation11.expected
@@ -1,31 +1,27 @@
| expressions.cs:168:27:168:44 | array creation of type Object[] | expressions.cs:168:27:168:44 | 1 | true |
+| expressions.cs:409:23:409:65 | array creation of type Int32[,] | expressions.cs:409:23:409:65 | 2 | true |
| expressions.cs:409:23:409:65 | array creation of type Int32[,] | expressions.cs:409:23:409:65 | 3 | true |
-| expressions.cs:409:23:409:65 | array creation of type Int32[,] | expressions.cs:409:23:409:65 | 3 | true |
-| expressions.cs:437:24:437:66 | array creation of type Int32[,] | expressions.cs:437:24:437:66 | 3 | true |
+| expressions.cs:437:24:437:66 | array creation of type Int32[,] | expressions.cs:437:24:437:66 | 2 | true |
| expressions.cs:437:24:437:66 | array creation of type Int32[,] | expressions.cs:437:24:437:66 | 3 | true |
| expressions.cs:438:17:438:93 | array creation of type Int32[,,] | expressions.cs:438:17:438:93 | 2 | true |
| expressions.cs:438:17:438:93 | array creation of type Int32[,,] | expressions.cs:438:17:438:93 | 2 | true |
-| expressions.cs:438:17:438:93 | array creation of type Int32[,,] | expressions.cs:438:17:438:93 | 2 | true |
-| expressions.cs:439:17:443:13 | array creation of type Int32[,][,] | expressions.cs:439:17:443:13 | 2 | true |
-| expressions.cs:439:17:443:13 | array creation of type Int32[,][,] | expressions.cs:439:17:443:13 | 2 | true |
-| expressions.cs:439:17:443:13 | array creation of type Int32[,][,] | expressions.cs:439:17:443:13 | 2 | true |
+| expressions.cs:438:17:438:93 | array creation of type Int32[,,] | expressions.cs:438:17:438:93 | 3 | true |
| expressions.cs:439:17:443:13 | array creation of type Int32[,][,] | expressions.cs:439:17:443:13 | 2 | true |
+| expressions.cs:439:17:443:13 | array creation of type Int32[,][,] | expressions.cs:439:17:443:13 | 3 | true |
| expressions.cs:441:19:441:45 | array creation of type Int32[,] | expressions.cs:441:19:441:45 | 2 | true |
| expressions.cs:441:19:441:45 | array creation of type Int32[,] | expressions.cs:441:19:441:45 | 2 | true |
+| expressions.cs:441:48:441:82 | array creation of type Int32[,] | expressions.cs:441:48:441:82 | 2 | true |
| expressions.cs:441:48:441:82 | array creation of type Int32[,] | expressions.cs:441:48:441:82 | 3 | true |
-| expressions.cs:441:48:441:82 | array creation of type Int32[,] | expressions.cs:441:48:441:82 | 3 | true |
-| expressions.cs:441:85:441:122 | array creation of type Int32[,] | expressions.cs:441:85:441:122 | 3 | true |
+| expressions.cs:441:85:441:122 | array creation of type Int32[,] | expressions.cs:441:85:441:122 | 2 | true |
| expressions.cs:441:85:441:122 | array creation of type Int32[,] | expressions.cs:441:85:441:122 | 3 | true |
| expressions.cs:442:19:442:45 | array creation of type Int32[,] | expressions.cs:442:19:442:45 | 2 | true |
| expressions.cs:442:19:442:45 | array creation of type Int32[,] | expressions.cs:442:19:442:45 | 2 | true |
+| expressions.cs:442:48:442:82 | array creation of type Int32[,] | expressions.cs:442:48:442:82 | 2 | true |
| expressions.cs:442:48:442:82 | array creation of type Int32[,] | expressions.cs:442:48:442:82 | 3 | true |
-| expressions.cs:442:48:442:82 | array creation of type Int32[,] | expressions.cs:442:48:442:82 | 3 | true |
-| expressions.cs:442:85:442:122 | array creation of type Int32[,] | expressions.cs:442:85:442:122 | 3 | true |
+| expressions.cs:442:85:442:122 | array creation of type Int32[,] | expressions.cs:442:85:442:122 | 2 | true |
| expressions.cs:442:85:442:122 | array creation of type Int32[,] | expressions.cs:442:85:442:122 | 3 | true |
| expressions.cs:444:17:444:81 | array creation of type Int32[,][] | expressions.cs:444:17:444:81 | 2 | true |
-| expressions.cs:444:17:444:81 | array creation of type Int32[,][] | expressions.cs:444:17:444:81 | 2 | true |
-| expressions.cs:444:17:444:81 | array creation of type Int32[,][] | expressions.cs:444:17:444:81 | 2 | true |
-| expressions.cs:444:32:444:54 | array creation of type Int32[,] | expressions.cs:444:32:444:54 | 1 | true |
| expressions.cs:444:32:444:54 | array creation of type Int32[,] | expressions.cs:444:32:444:54 | 1 | true |
+| expressions.cs:444:32:444:54 | array creation of type Int32[,] | expressions.cs:444:32:444:54 | 2 | true |
| expressions.cs:444:57:444:79 | array creation of type Int32[,] | expressions.cs:444:57:444:79 | 1 | true |
-| expressions.cs:444:57:444:79 | array creation of type Int32[,] | expressions.cs:444:57:444:79 | 1 | true |
+| expressions.cs:444:57:444:79 | array creation of type Int32[,] | expressions.cs:444:57:444:79 | 2 | true |
From 3dea6b32182c0dce8a7c91d0df7be3ff3b3e40e9 Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Mon, 24 Aug 2020 16:10:42 +0200
Subject: [PATCH 087/146] C#: Change implicitly sized array test input
---
.../test/library-tests/expressions/ArrayCreation11.expected | 6 +++---
csharp/ql/test/library-tests/expressions/expressions.cs | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/csharp/ql/test/library-tests/expressions/ArrayCreation11.expected b/csharp/ql/test/library-tests/expressions/ArrayCreation11.expected
index 9542bb21b21..2fa6120d695 100644
--- a/csharp/ql/test/library-tests/expressions/ArrayCreation11.expected
+++ b/csharp/ql/test/library-tests/expressions/ArrayCreation11.expected
@@ -20,8 +20,8 @@
| expressions.cs:442:48:442:82 | array creation of type Int32[,] | expressions.cs:442:48:442:82 | 3 | true |
| expressions.cs:442:85:442:122 | array creation of type Int32[,] | expressions.cs:442:85:442:122 | 2 | true |
| expressions.cs:442:85:442:122 | array creation of type Int32[,] | expressions.cs:442:85:442:122 | 3 | true |
-| expressions.cs:444:17:444:81 | array creation of type Int32[,][] | expressions.cs:444:17:444:81 | 2 | true |
+| expressions.cs:444:17:444:123 | array creation of type Int32[,][] | expressions.cs:444:17:444:123 | 2 | true |
| expressions.cs:444:32:444:54 | array creation of type Int32[,] | expressions.cs:444:32:444:54 | 1 | true |
| expressions.cs:444:32:444:54 | array creation of type Int32[,] | expressions.cs:444:32:444:54 | 2 | true |
-| expressions.cs:444:57:444:79 | array creation of type Int32[,] | expressions.cs:444:57:444:79 | 1 | true |
-| expressions.cs:444:57:444:79 | array creation of type Int32[,] | expressions.cs:444:57:444:79 | 2 | true |
+| expressions.cs:444:57:444:121 | array creation of type Int32[,] | expressions.cs:444:57:444:121 | 3 | true |
+| expressions.cs:444:57:444:121 | array creation of type Int32[,] | expressions.cs:444:57:444:121 | 4 | true |
diff --git a/csharp/ql/test/library-tests/expressions/expressions.cs b/csharp/ql/test/library-tests/expressions/expressions.cs
index c9e7419af9b..5d995e10985 100644
--- a/csharp/ql/test/library-tests/expressions/expressions.cs
+++ b/csharp/ql/test/library-tests/expressions/expressions.cs
@@ -441,7 +441,7 @@ namespace Expressions
{ new int[,] { {1,3}, {5,7} }, new int[,] { {0,2}, {4,6}, {8,10} }, new int[,] { {11,22}, {99,88}, {0,9} } },
{ new int[,] { {1,3}, {5,7} }, new int[,] { {0,2}, {4,6}, {8,10} }, new int[,] { {11,22}, {99,88}, {0,9} } }
};
- o = new int[][,] { new int[,] { { 1, 2 } }, new int[,] { { 1, 2 } } };
+ o = new int[][,] { new int[,] { { 1, 2 } }, new int[,] { { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 } } };
}
void MainAnonymousFunctions()
From 22f5ae4ad432f7a4ad60181ccdeddf145e667d92 Mon Sep 17 00:00:00 2001
From: ubuntu <43420907+dellalibera@users.noreply.github.com>
Date: Mon, 24 Aug 2020 18:53:37 +0200
Subject: [PATCH 088/146] Format code
---
.../ql/src/experimental/Security/CWE-614/InsecureCookie.ql | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.ql b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.ql
index d08548df993..d735399bfb0 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.ql
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.ql
@@ -16,4 +16,4 @@ import InsecureCookie::Cookie
from Cookie cookie
where not cookie.isSecure()
select "Cookie is added to response without the 'secure' flag being set to true (using " +
-cookie.getKind() + ").", cookie
+ cookie.getKind() + ").", cookie
From 66e3739e726033c02a034c3c99bde1d7fee87b88 Mon Sep 17 00:00:00 2001
From: Tamas Vajk