From 851317e34ffbf27c939d4575fa399c7227c85e6e Mon Sep 17 00:00:00 2001
From: Chris Smowton
Date: Thu, 11 Mar 2021 11:42:32 +0000
Subject: [PATCH 001/272] Add models for StrBuilder's fluent methods
---
.../semmle/code/java/frameworks/apache/Lang.qll | 9 +++++++++
.../apache-commons-lang3/StrBuilderTest.java | 14 ++++++++++++++
2 files changed, 23 insertions(+)
diff --git a/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll b/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll
index deab8f4a692..1ddf8876c3d 100644
--- a/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll
+++ b/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll
@@ -427,6 +427,15 @@ private class ApacheStrBuilderModel extends SummaryModelCsv {
}
}
+/**
+ * An Apache Commons-Lang StrBuilder method that returns `this`.
+ */
+private class ApacheStrBuilderFluentMethod extends FluentMethod {
+ ApacheStrBuilderFluentMethod() {
+ this.getReturnType().(RefType).hasQualifiedName("org.apache.commons.lang3.text", "StrBuilder")
+ }
+}
+
/**
* Taint-propagating models for `WordUtils`.
*/
diff --git a/java/ql/test/library-tests/frameworks/apache-commons-lang3/StrBuilderTest.java b/java/ql/test/library-tests/frameworks/apache-commons-lang3/StrBuilderTest.java
index bc8887d1150..d460184e176 100644
--- a/java/ql/test/library-tests/frameworks/apache-commons-lang3/StrBuilderTest.java
+++ b/java/ql/test/library-tests/frameworks/apache-commons-lang3/StrBuilderTest.java
@@ -128,6 +128,20 @@ class StrBuilderTest {
StrBuilder sb72 = new StrBuilder(); sb72.append(taint()); sink(sb72.toCharArray(0, 0)); // $hasTaintFlow
StrBuilder sb73 = new StrBuilder(); sb73.append(taint()); sink(sb73.toStringBuffer()); // $hasTaintFlow
StrBuilder sb74 = new StrBuilder(); sb74.append(taint()); sink(sb74.toStringBuilder()); // $hasTaintFlow
+
+ // Tests for fluent methods (those returning `this`):
+
+ StrBuilder fluentTest = new StrBuilder();
+ sink(fluentTest.append("Harmless").append(taint()).append("Also harmless").toString()); // $hasTaintFlow
+
+ StrBuilder fluentBackflowTest = new StrBuilder();
+ fluentBackflowTest.append("Harmless").append(taint()).append("Also harmless");
+ sink(fluentBackflowTest.toString()); // $hasTaintFlow
+
+ // Test the case where the fluent method contributing taint is at the end of a statement:
+ StrBuilder fluentBackflowTest2 = new StrBuilder();
+ fluentBackflowTest2.append("Harmless").append(taint());
+ sink(fluentBackflowTest2.toString()); // $hasTaintFlow
}
}
\ No newline at end of file
From 3a274424abc0bdc7b6d84678aa3a9697c9f2ace0 Mon Sep 17 00:00:00 2001
From: Chris Smowton
Date: Thu, 11 Mar 2021 13:45:55 +0000
Subject: [PATCH 002/272] Convert fluent method models to csv and generalise to
the three different variants of StrBuilder.
---
.../code/java/frameworks/apache/Lang.qll | 86 +++++++++++++++++++
.../apache-commons-lang3/StrBuilderTest.java | 62 +++++++++++++
.../StrBuilderTextTest.java | 76 ++++++++++++++++
.../TextStringBuilderTest.java | 76 ++++++++++++++++
4 files changed, 300 insertions(+)
diff --git a/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll b/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll
index 1ddf8876c3d..2422efc83d9 100644
--- a/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll
+++ b/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll
@@ -427,6 +427,92 @@ private class ApacheStrBuilderModel extends SummaryModelCsv {
}
}
+private class ApacheStrBuilderFluentMethodsModel extends SummaryModelCsv {
+ override predicate row(string row) {
+ row =
+ [
+ "org.apache.commons.lang3.text;StrBuilder;false;append;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.text;StrBuilder;false;appendAll;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.text;StrBuilder;false;appendFixedWidthPadLeft;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.text;StrBuilder;false;appendFixedWidthPadRight;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.text;StrBuilder;false;appendln;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.text;StrBuilder;false;appendNewLine;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.text;StrBuilder;false;appendNull;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.text;StrBuilder;false;appendPadding;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.text;StrBuilder;false;appendSeparator;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.text;StrBuilder;false;appendWithSeparators;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.text;StrBuilder;false;delete;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.text;StrBuilder;false;deleteAll;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.text;StrBuilder;false;deleteCharAt;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.text;StrBuilder;false;deleteFirst;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.text;StrBuilder;false;ensureCapacity;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.text;StrBuilder;false;insert;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.text;StrBuilder;false;minimizeCapacity;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.text;StrBuilder;false;replace;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.text;StrBuilder;false;replaceAll;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.text;StrBuilder;false;replaceFirst;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.text;StrBuilder;false;reverse;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.text;StrBuilder;false;setCharAt;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.text;StrBuilder;false;setLength;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.text;StrBuilder;false;setNewLineText;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.text;StrBuilder;false;setNullText;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.text;StrBuilder;false;trim;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;StrBuilder;false;append;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;StrBuilder;false;appendAll;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;StrBuilder;false;appendFixedWidthPadLeft;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;StrBuilder;false;appendFixedWidthPadRight;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;StrBuilder;false;appendln;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;StrBuilder;false;appendNewLine;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;StrBuilder;false;appendNull;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;StrBuilder;false;appendPadding;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;StrBuilder;false;appendSeparator;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;StrBuilder;false;appendWithSeparators;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;StrBuilder;false;delete;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;StrBuilder;false;deleteAll;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;StrBuilder;false;deleteCharAt;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;StrBuilder;false;deleteFirst;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;StrBuilder;false;ensureCapacity;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;StrBuilder;false;insert;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;StrBuilder;false;minimizeCapacity;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;StrBuilder;false;replace;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;StrBuilder;false;replaceAll;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;StrBuilder;false;replaceFirst;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;StrBuilder;false;reverse;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;StrBuilder;false;setCharAt;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;StrBuilder;false;setLength;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;StrBuilder;false;setNewLineText;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;StrBuilder;false;setNullText;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;StrBuilder;false;trim;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;TextStringBuilder;false;append;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;TextStringBuilder;false;appendAll;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;TextStringBuilder;false;appendFixedWidthPadLeft;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;TextStringBuilder;false;appendFixedWidthPadRight;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;TextStringBuilder;false;appendln;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;TextStringBuilder;false;appendNewLine;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;TextStringBuilder;false;appendNull;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;TextStringBuilder;false;appendPadding;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;TextStringBuilder;false;appendSeparator;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;TextStringBuilder;false;appendWithSeparators;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;TextStringBuilder;false;delete;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;TextStringBuilder;false;deleteAll;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;TextStringBuilder;false;deleteCharAt;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;TextStringBuilder;false;deleteFirst;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;TextStringBuilder;false;ensureCapacity;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;TextStringBuilder;false;insert;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;TextStringBuilder;false;minimizeCapacity;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;TextStringBuilder;false;replace;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;TextStringBuilder;false;replaceAll;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;TextStringBuilder;false;replaceFirst;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;TextStringBuilder;false;reverse;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;TextStringBuilder;false;setCharAt;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;TextStringBuilder;false;setLength;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;TextStringBuilder;false;setNewLineText;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;TextStringBuilder;false;setNullText;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.text;TextStringBuilder;false;trim;;;Argument[-1];ReturnValue;value"
+ ]
+ }
+}
+
/**
* An Apache Commons-Lang StrBuilder method that returns `this`.
*/
diff --git a/java/ql/test/library-tests/frameworks/apache-commons-lang3/StrBuilderTest.java b/java/ql/test/library-tests/frameworks/apache-commons-lang3/StrBuilderTest.java
index d460184e176..0c0e386e9c2 100644
--- a/java/ql/test/library-tests/frameworks/apache-commons-lang3/StrBuilderTest.java
+++ b/java/ql/test/library-tests/frameworks/apache-commons-lang3/StrBuilderTest.java
@@ -142,6 +142,68 @@ class StrBuilderTest {
StrBuilder fluentBackflowTest2 = new StrBuilder();
fluentBackflowTest2.append("Harmless").append(taint());
sink(fluentBackflowTest2.toString()); // $hasTaintFlow
+
+ // Test all fluent methods are passing taint through to their result:
+ StrBuilder fluentAllMethodsTest = new StrBuilder(taint());
+ sink(fluentAllMethodsTest // $hasTaintFlow
+ .append("text")
+ .appendAll("text")
+ .appendFixedWidthPadLeft("text", 4, ' ')
+ .appendFixedWidthPadRight("text", 4, ' ')
+ .appendln("text")
+ .appendNewLine()
+ .appendNull()
+ .appendPadding(0, ' ')
+ .appendSeparator(',')
+ .appendWithSeparators(new String[] { }, ",")
+ .delete(0, 0)
+ .deleteAll(' ')
+ .deleteCharAt(0)
+ .deleteFirst("delme")
+ .ensureCapacity(100)
+ .insert(1, "insertme")
+ .minimizeCapacity()
+ .replace(0, 0, "replacement")
+ .replaceAll("find", "replace")
+ .replaceFirst("find", "replace")
+ .reverse()
+ .setCharAt(0, 'a')
+ .setLength(500)
+ .setNewLineText("newline")
+ .setNullText("NULL")
+ .trim());
+
+ // Test all fluent methods are passing taint back to their qualifier:
+ StrBuilder fluentAllMethodsTest2 = new StrBuilder();
+ fluentAllMethodsTest2
+ .append("text")
+ .appendAll("text")
+ .appendFixedWidthPadLeft("text", 4, ' ')
+ .appendFixedWidthPadRight("text", 4, ' ')
+ .appendln("text")
+ .appendNewLine()
+ .appendNull()
+ .appendPadding(0, ' ')
+ .appendSeparator(',')
+ .appendWithSeparators(new String[] { }, ",")
+ .delete(0, 0)
+ .deleteAll(' ')
+ .deleteCharAt(0)
+ .deleteFirst("delme")
+ .ensureCapacity(100)
+ .insert(1, "insertme")
+ .minimizeCapacity()
+ .replace(0, 0, "replacement")
+ .replaceAll("find", "replace")
+ .replaceFirst("find", "replace")
+ .reverse()
+ .setCharAt(0, 'a')
+ .setLength(500)
+ .setNewLineText("newline")
+ .setNullText("NULL")
+ .trim()
+ .append(taint());
+ sink(fluentAllMethodsTest2); // $hasTaintFlow
}
}
\ No newline at end of file
diff --git a/java/ql/test/library-tests/frameworks/apache-commons-lang3/StrBuilderTextTest.java b/java/ql/test/library-tests/frameworks/apache-commons-lang3/StrBuilderTextTest.java
index 796900e8a3b..74f0f1d17c9 100644
--- a/java/ql/test/library-tests/frameworks/apache-commons-lang3/StrBuilderTextTest.java
+++ b/java/ql/test/library-tests/frameworks/apache-commons-lang3/StrBuilderTextTest.java
@@ -128,6 +128,82 @@ class StrBuilderTextTest {
StrBuilder sb72 = new StrBuilder(); sb72.append(taint()); sink(sb72.toCharArray(0, 0)); // $hasTaintFlow
StrBuilder sb73 = new StrBuilder(); sb73.append(taint()); sink(sb73.toStringBuffer()); // $hasTaintFlow
StrBuilder sb74 = new StrBuilder(); sb74.append(taint()); sink(sb74.toStringBuilder()); // $hasTaintFlow
+
+ // Tests for fluent methods (those returning `this`):
+
+ StrBuilder fluentTest = new StrBuilder();
+ sink(fluentTest.append("Harmless").append(taint()).append("Also harmless").toString()); // $hasTaintFlow
+
+ StrBuilder fluentBackflowTest = new StrBuilder();
+ fluentBackflowTest.append("Harmless").append(taint()).append("Also harmless");
+ sink(fluentBackflowTest.toString()); // $hasTaintFlow
+
+ // Test the case where the fluent method contributing taint is at the end of a statement:
+ StrBuilder fluentBackflowTest2 = new StrBuilder();
+ fluentBackflowTest2.append("Harmless").append(taint());
+ sink(fluentBackflowTest2.toString()); // $hasTaintFlow
+
+ // Test all fluent methods are passing taint through to their result:
+ StrBuilder fluentAllMethodsTest = new StrBuilder(taint());
+ sink(fluentAllMethodsTest // $hasTaintFlow
+ .append("text")
+ .appendAll("text")
+ .appendFixedWidthPadLeft("text", 4, ' ')
+ .appendFixedWidthPadRight("text", 4, ' ')
+ .appendln("text")
+ .appendNewLine()
+ .appendNull()
+ .appendPadding(0, ' ')
+ .appendSeparator(',')
+ .appendWithSeparators(new String[] { }, ",")
+ .delete(0, 0)
+ .deleteAll(' ')
+ .deleteCharAt(0)
+ .deleteFirst("delme")
+ .ensureCapacity(100)
+ .insert(1, "insertme")
+ .minimizeCapacity()
+ .replace(0, 0, "replacement")
+ .replaceAll("find", "replace")
+ .replaceFirst("find", "replace")
+ .reverse()
+ .setCharAt(0, 'a')
+ .setLength(500)
+ .setNewLineText("newline")
+ .setNullText("NULL")
+ .trim());
+
+ // Test all fluent methods are passing taint back to their qualifier:
+ StrBuilder fluentAllMethodsTest2 = new StrBuilder();
+ fluentAllMethodsTest2
+ .append("text")
+ .appendAll("text")
+ .appendFixedWidthPadLeft("text", 4, ' ')
+ .appendFixedWidthPadRight("text", 4, ' ')
+ .appendln("text")
+ .appendNewLine()
+ .appendNull()
+ .appendPadding(0, ' ')
+ .appendSeparator(',')
+ .appendWithSeparators(new String[] { }, ",")
+ .delete(0, 0)
+ .deleteAll(' ')
+ .deleteCharAt(0)
+ .deleteFirst("delme")
+ .ensureCapacity(100)
+ .insert(1, "insertme")
+ .minimizeCapacity()
+ .replace(0, 0, "replacement")
+ .replaceAll("find", "replace")
+ .replaceFirst("find", "replace")
+ .reverse()
+ .setCharAt(0, 'a')
+ .setLength(500)
+ .setNewLineText("newline")
+ .setNullText("NULL")
+ .trim()
+ .append(taint());
+ sink(fluentAllMethodsTest2); // $hasTaintFlow
}
}
\ No newline at end of file
diff --git a/java/ql/test/library-tests/frameworks/apache-commons-lang3/TextStringBuilderTest.java b/java/ql/test/library-tests/frameworks/apache-commons-lang3/TextStringBuilderTest.java
index 69db28cb3e9..e490c11c7cb 100644
--- a/java/ql/test/library-tests/frameworks/apache-commons-lang3/TextStringBuilderTest.java
+++ b/java/ql/test/library-tests/frameworks/apache-commons-lang3/TextStringBuilderTest.java
@@ -129,6 +129,82 @@ class TextStringBuilderTest {
TextStringBuilder sb72 = new TextStringBuilder(); sb72.append(taint()); sink(sb72.toCharArray(0, 0)); // $hasTaintFlow
TextStringBuilder sb73 = new TextStringBuilder(); sb73.append(taint()); sink(sb73.toStringBuffer()); // $hasTaintFlow
TextStringBuilder sb74 = new TextStringBuilder(); sb74.append(taint()); sink(sb74.toStringBuilder()); // $hasTaintFlow
+
+ // Tests for fluent methods (those returning `this`):
+
+ TextStringBuilder fluentTest = new TextStringBuilder();
+ sink(fluentTest.append("Harmless").append(taint()).append("Also harmless").toString()); // $hasTaintFlow
+
+ TextStringBuilder fluentBackflowTest = new TextStringBuilder();
+ fluentBackflowTest.append("Harmless").append(taint()).append("Also harmless");
+ sink(fluentBackflowTest.toString()); // $hasTaintFlow
+
+ // Test the case where the fluent method contributing taint is at the end of a statement:
+ TextStringBuilder fluentBackflowTest2 = new TextStringBuilder();
+ fluentBackflowTest2.append("Harmless").append(taint());
+ sink(fluentBackflowTest2.toString()); // $hasTaintFlow
+
+ // Test all fluent methods are passing taint through to their result:
+ TextStringBuilder fluentAllMethodsTest = new TextStringBuilder(taint());
+ sink(fluentAllMethodsTest // $hasTaintFlow
+ .append("text")
+ .appendAll("text")
+ .appendFixedWidthPadLeft("text", 4, ' ')
+ .appendFixedWidthPadRight("text", 4, ' ')
+ .appendln("text")
+ .appendNewLine()
+ .appendNull()
+ .appendPadding(0, ' ')
+ .appendSeparator(',')
+ .appendWithSeparators(new String[] { }, ",")
+ .delete(0, 0)
+ .deleteAll(' ')
+ .deleteCharAt(0)
+ .deleteFirst("delme")
+ .ensureCapacity(100)
+ .insert(1, "insertme")
+ .minimizeCapacity()
+ .replace(0, 0, "replacement")
+ .replaceAll("find", "replace")
+ .replaceFirst("find", "replace")
+ .reverse()
+ .setCharAt(0, 'a')
+ .setLength(500)
+ .setNewLineText("newline")
+ .setNullText("NULL")
+ .trim());
+
+ // Test all fluent methods are passing taint back to their qualifier:
+ TextStringBuilder fluentAllMethodsTest2 = new TextStringBuilder();
+ fluentAllMethodsTest2
+ .append("text")
+ .appendAll("text")
+ .appendFixedWidthPadLeft("text", 4, ' ')
+ .appendFixedWidthPadRight("text", 4, ' ')
+ .appendln("text")
+ .appendNewLine()
+ .appendNull()
+ .appendPadding(0, ' ')
+ .appendSeparator(',')
+ .appendWithSeparators(new String[] { }, ",")
+ .delete(0, 0)
+ .deleteAll(' ')
+ .deleteCharAt(0)
+ .deleteFirst("delme")
+ .ensureCapacity(100)
+ .insert(1, "insertme")
+ .minimizeCapacity()
+ .replace(0, 0, "replacement")
+ .replaceAll("find", "replace")
+ .replaceFirst("find", "replace")
+ .reverse()
+ .setCharAt(0, 'a')
+ .setLength(500)
+ .setNewLineText("newline")
+ .setNullText("NULL")
+ .trim()
+ .append(taint());
+ sink(fluentAllMethodsTest2); // $hasTaintFlow
}
}
\ No newline at end of file
From 42b63a61ae82b19a7e61c86bebfe27365d35bf79 Mon Sep 17 00:00:00 2001
From: Chris Smowton
Date: Thu, 11 Mar 2021 18:34:11 +0000
Subject: [PATCH 003/272] Add change note
---
java/change-notes/2021-03-11-commons-strbuilder.md | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 java/change-notes/2021-03-11-commons-strbuilder.md
diff --git a/java/change-notes/2021-03-11-commons-strbuilder.md b/java/change-notes/2021-03-11-commons-strbuilder.md
new file mode 100644
index 00000000000..ce8f647ab0f
--- /dev/null
+++ b/java/change-notes/2021-03-11-commons-strbuilder.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* Added support for the Apache Commons Lang and Commons Text StrBuilder class, and its successor TextStringBuilder.
From 6589460357229d5340539ae8e9b4442460a70902 Mon Sep 17 00:00:00 2001
From: Chris Smowton
Date: Tue, 16 Feb 2021 12:21:57 +0000
Subject: [PATCH 004/272] Add models for Commons ToStringBuilder
These don't include support for reflectionToString yet, which is coming up in a subsequent PR.
---
.../code/java/frameworks/apache/Lang.qll | 34 +++
.../ToStringBuilderTest.java | 37 +++
.../lang3/builder/ToStringBuilder.java | 275 ++++++++++++++++++
.../commons/lang3/builder/ToStringStyle.java | 102 +++++++
4 files changed, 448 insertions(+)
create mode 100644 java/ql/test/library-tests/frameworks/apache-commons-lang3/ToStringBuilderTest.java
create mode 100644 java/ql/test/stubs/apache-commons-lang3-3.7/org/apache/commons/lang3/builder/ToStringBuilder.java
create mode 100644 java/ql/test/stubs/apache-commons-lang3-3.7/org/apache/commons/lang3/builder/ToStringStyle.java
diff --git a/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll b/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll
index 670014e0f8f..935b6347ec5 100644
--- a/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll
+++ b/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll
@@ -637,3 +637,37 @@ private class ApacheObjectUtilsModel extends SummaryModelCsv {
]
}
}
+
+private class ApacheToStringBuilderModel extends SummaryModelCsv {
+ override predicate row(string row) {
+ row =
+ [
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;toString;;;Argument[-1];ReturnValue;taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.Object);;Argument;ReturnValue;taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.Object);;Argument;Argument[-1];taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.Object[]);;Argument;ReturnValue;taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.Object[]);;Argument;Argument[-1];taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[]);;Argument;ReturnValue;taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[]);;Argument;Argument[-1];taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,boolean);;Argument[0];ReturnValue;taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,boolean);;Argument[0];Argument[-1];taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object);;Argument;ReturnValue;taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object);;Argument;Argument[-1];taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[],boolean);;Argument[0];ReturnValue;taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[],boolean);;Argument[0];Argument[-1];taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[],boolean);;Argument[1];ReturnValue;taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[],boolean);;Argument[1];Argument[-1];taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;build;;;Argument[-1];ReturnValue;taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;getStringBuffer;;;Argument[-1];ReturnValue;taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;appendToString;;;Argument;ReturnValue;taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;appendToString;;;Argument;Argument[-1];taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;appendSuper;;;Argument;ReturnValue;taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;appendSuper;;;Argument;Argument[-1];taint",
+ // The following are value-preserving steps for fluent methods:
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;appendAsObjectToString;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;appendSuper;;;Argument[-1];ReturnValue;value",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;appendToString;;;Argument[-1];ReturnValue;value"
+ ]
+ }
+}
diff --git a/java/ql/test/library-tests/frameworks/apache-commons-lang3/ToStringBuilderTest.java b/java/ql/test/library-tests/frameworks/apache-commons-lang3/ToStringBuilderTest.java
new file mode 100644
index 00000000000..ed2e4400dd7
--- /dev/null
+++ b/java/ql/test/library-tests/frameworks/apache-commons-lang3/ToStringBuilderTest.java
@@ -0,0 +1,37 @@
+import org.apache.commons.lang3.builder.ToStringBuilder;
+
+class ToStringBuilderTest {
+ String taint() { return "tainted"; }
+
+ void sink(Object o) {}
+
+ void test() throws Exception {
+
+ ToStringBuilder sb1 = new ToStringBuilder(null); sb1.append((Object)taint()); sink(sb1.toString()); // $hasTaintFlow
+ ToStringBuilder sb2 = new ToStringBuilder(null); sb2.append(new Object[] { taint() }); sink(sb2.toString()); // $hasTaintFlow
+ ToStringBuilder sb3 = new ToStringBuilder(null); sb3.append(taint(), true); sink(sb3.toString()); // $hasTaintFlow
+ ToStringBuilder sb4 = new ToStringBuilder(null); sb4.append("fieldname", taint()); sink(sb4.toString()); // $hasTaintFlow
+ ToStringBuilder sb5 = new ToStringBuilder(null); sb5.append("fieldname", new Object[] { taint() }); sink(sb5.toString()); // $hasTaintFlow
+ ToStringBuilder sb6 = new ToStringBuilder(null); sb6.append("fieldname", new Object[] { taint() }, true); sink(sb6.toString()); // $hasTaintFlow
+ // GOOD: this appends an Object using the Object.toString style, which does not expose fields or String content.
+ ToStringBuilder sb7 = new ToStringBuilder(null); sb7.appendAsObjectToString(taint()); sink(sb7.toString());
+ ToStringBuilder sb8 = new ToStringBuilder(null); sb8.appendSuper(taint()); sink(sb8.toString()); // $hasTaintFlow
+ ToStringBuilder sb9 = new ToStringBuilder(null); sb9.appendToString(taint()); sink(sb9.toString()); // $hasTaintFlow
+ ToStringBuilder sb10 = new ToStringBuilder(null); sb10.append((Object)taint()); sink(sb10.build()); // $hasTaintFlow
+ ToStringBuilder sb11 = new ToStringBuilder(null); sb11.append((Object)taint()); sink(sb11.getStringBuffer().toString()); // $hasTaintFlow
+
+ // Test fluent methods:
+ ToStringBuilder fluentTest = new ToStringBuilder(null);
+ sink(fluentTest.append("Harmless").append(taint()).append("Also harmless").toString()); // $hasTaintFlow
+
+ ToStringBuilder fluentBackflowTest = new ToStringBuilder(null);
+ fluentBackflowTest.append("Harmless").append(taint()).append("Also harmless");
+ sink(fluentBackflowTest.toString()); // $hasTaintFlow
+
+ // Test the case where the fluent method contributing taint is at the end of a statement:
+ ToStringBuilder fluentBackflowTest2 = new ToStringBuilder(null);
+ fluentBackflowTest2.append("Harmless").append(taint());
+ sink(fluentBackflowTest2.toString()); // $hasTaintFlow
+
+ }
+}
\ No newline at end of file
diff --git a/java/ql/test/stubs/apache-commons-lang3-3.7/org/apache/commons/lang3/builder/ToStringBuilder.java b/java/ql/test/stubs/apache-commons-lang3-3.7/org/apache/commons/lang3/builder/ToStringBuilder.java
new file mode 100644
index 00000000000..886d59637f0
--- /dev/null
+++ b/java/ql/test/stubs/apache-commons-lang3-3.7/org/apache/commons/lang3/builder/ToStringBuilder.java
@@ -0,0 +1,275 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.builder;
+
+
+public class ToStringBuilder implements Builder {
+ public static ToStringStyle getDefaultStyle() {
+ return null;
+ }
+
+ public static void setDefaultStyle(final ToStringStyle style) {
+ }
+
+ public static String reflectionToString(final Object object) {
+ return null;
+ }
+
+ public static String reflectionToString(final Object object, final ToStringStyle style) {
+ return null;
+ }
+
+ public static String reflectionToString(final Object object, final ToStringStyle style, final boolean outputTransients) {
+ return null;
+ }
+
+ public static String reflectionToString(
+ final T object,
+ final ToStringStyle style,
+ final boolean outputTransients,
+ final Class super T> reflectUpToClass) {
+ return null;
+ }
+
+ public ToStringBuilder(final Object object) {
+ }
+
+ public ToStringBuilder(final Object object, final ToStringStyle style) {
+ }
+
+ public ToStringBuilder(final Object object, ToStringStyle style, StringBuffer buffer) {
+ }
+
+ public ToStringBuilder append(final boolean value) {
+ return null;
+ }
+
+ public ToStringBuilder append(final boolean[] array) {
+ return null;
+ }
+
+ public ToStringBuilder append(final byte value) {
+ return null;
+ }
+
+ public ToStringBuilder append(final byte[] array) {
+ return null;
+ }
+
+ public ToStringBuilder append(final char value) {
+ return null;
+ }
+
+ public ToStringBuilder append(final char[] array) {
+ return null;
+ }
+
+ public ToStringBuilder append(final double value) {
+ return null;
+ }
+
+ public ToStringBuilder append(final double[] array) {
+ return null;
+ }
+
+ public ToStringBuilder append(final float value) {
+ return null;
+ }
+
+ public ToStringBuilder append(final float[] array) {
+ return null;
+ }
+
+ public ToStringBuilder append(final int value) {
+ return null;
+ }
+
+ public ToStringBuilder append(final int[] array) {
+ return null;
+ }
+
+ public ToStringBuilder append(final long value) {
+ return null;
+ }
+
+ public ToStringBuilder append(final long[] array) {
+ return null;
+ }
+
+ public ToStringBuilder append(final Object obj) {
+ return null;
+ }
+
+ public ToStringBuilder append(final Object[] array) {
+ return null;
+ }
+
+ public ToStringBuilder append(final short value) {
+ return null;
+ }
+
+ public ToStringBuilder append(final short[] array) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final boolean value) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final boolean[] array) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final boolean[] array, final boolean fullDetail) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final byte value) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final byte[] array) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final byte[] array, final boolean fullDetail) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final char value) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final char[] array) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final char[] array, final boolean fullDetail) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final double value) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final double[] array) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final double[] array, final boolean fullDetail) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final float value) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final float[] array) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final float[] array, final boolean fullDetail) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final int value) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final int[] array) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final int[] array, final boolean fullDetail) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final long value) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final long[] array) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final long[] array, final boolean fullDetail) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final Object obj) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final Object obj, final boolean fullDetail) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final Object[] array) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final Object[] array, final boolean fullDetail) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final short value) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final short[] array) {
+ return null;
+ }
+
+ public ToStringBuilder append(final String fieldName, final short[] array, final boolean fullDetail) {
+ return null;
+ }
+
+ public ToStringBuilder appendAsObjectToString(final Object srcObject) {
+ return null;
+ }
+
+ public ToStringBuilder appendSuper(final String superToString) {
+ return null;
+ }
+
+ public ToStringBuilder appendToString(final String toString) {
+ return null;
+ }
+
+ public Object getObject() {
+ return null;
+ }
+
+ public StringBuffer getStringBuffer() {
+ return null;
+ }
+
+ public ToStringStyle getStyle() {
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return null;
+ }
+
+ @Override
+ public String build() {
+ return null;
+ }
+
+}
diff --git a/java/ql/test/stubs/apache-commons-lang3-3.7/org/apache/commons/lang3/builder/ToStringStyle.java b/java/ql/test/stubs/apache-commons-lang3-3.7/org/apache/commons/lang3/builder/ToStringStyle.java
new file mode 100644
index 00000000000..eef4db08804
--- /dev/null
+++ b/java/ql/test/stubs/apache-commons-lang3-3.7/org/apache/commons/lang3/builder/ToStringStyle.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.builder;
+
+import java.io.Serializable;
+
+public abstract class ToStringStyle implements Serializable {
+ public static final ToStringStyle DEFAULT_STYLE = null;
+
+ public static final ToStringStyle MULTI_LINE_STYLE = null;
+
+ public static final ToStringStyle NO_FIELD_NAMES_STYLE = null;
+
+ public static final ToStringStyle SHORT_PREFIX_STYLE = null;
+
+ public static final ToStringStyle SIMPLE_STYLE = null;
+
+ public static final ToStringStyle NO_CLASS_NAME_STYLE = null;
+
+ public static final ToStringStyle JSON_STYLE = null;
+
+ public void appendSuper(final StringBuffer buffer, final String superToString) {
+ }
+
+ public void appendToString(final StringBuffer buffer, final String toString) {
+ }
+
+ public void appendStart(final StringBuffer buffer, final Object object) {
+ }
+
+ public void appendEnd(final StringBuffer buffer, final Object object) {
+ }
+
+ public void append(final StringBuffer buffer, final String fieldName, final Object value, final Boolean fullDetail) {
+ }
+
+ public void append(final StringBuffer buffer, final String fieldName, final long value) {
+ }
+
+ public void append(final StringBuffer buffer, final String fieldName, final int value) {
+ }
+
+ public void append(final StringBuffer buffer, final String fieldName, final short value) {
+ }
+
+ public void append(final StringBuffer buffer, final String fieldName, final byte value) {
+ }
+
+ public void append(final StringBuffer buffer, final String fieldName, final char value) {
+ }
+
+ public void append(final StringBuffer buffer, final String fieldName, final double value) {
+ }
+
+ public void append(final StringBuffer buffer, final String fieldName, final float value) {
+ }
+
+ public void append(final StringBuffer buffer, final String fieldName, final boolean value) {
+ }
+
+ public void append(final StringBuffer buffer, final String fieldName, final Object[] array, final Boolean fullDetail) {
+ }
+
+ public void append(final StringBuffer buffer, final String fieldName, final long[] array, final Boolean fullDetail) {
+ }
+
+ public void append(final StringBuffer buffer, final String fieldName, final int[] array, final Boolean fullDetail) {
+ }
+
+ public void append(final StringBuffer buffer, final String fieldName, final short[] array, final Boolean fullDetail) {
+ }
+
+ public void append(final StringBuffer buffer, final String fieldName, final byte[] array, final Boolean fullDetail) {
+ }
+
+ public void append(final StringBuffer buffer, final String fieldName, final char[] array, final Boolean fullDetail) {
+ }
+
+ public void append(final StringBuffer buffer, final String fieldName, final double[] array, final Boolean fullDetail) {
+ }
+
+ public void append(final StringBuffer buffer, final String fieldName, final float[] array, final Boolean fullDetail) {
+ }
+
+ public void append(final StringBuffer buffer, final String fieldName, final boolean[] array, final Boolean fullDetail) {
+ }
+
+}
From fce1d6122fa1ba2372d53595f76b026751e05643 Mon Sep 17 00:00:00 2001
From: Chris Smowton
Date: Thu, 18 Mar 2021 09:09:16 +0000
Subject: [PATCH 005/272] Add change note
---
java/change-notes/2021-03-18-commons-tostring-builder.md | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 java/change-notes/2021-03-18-commons-tostring-builder.md
diff --git a/java/change-notes/2021-03-18-commons-tostring-builder.md b/java/change-notes/2021-03-18-commons-tostring-builder.md
new file mode 100644
index 00000000000..41ccfb95237
--- /dev/null
+++ b/java/change-notes/2021-03-18-commons-tostring-builder.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* Added models for Apache Commons Lang's `ToStringBuilder` class. This may lead to more results from any data-flow query where ToStringBuilder operations fall between the relevant untrusted source and vulnerable sink.
From 874733a61b25d78af4e474aeb045f51ad7a0aa5f Mon Sep 17 00:00:00 2001
From: Chris Smowton
Date: Wed, 21 Apr 2021 15:53:55 +0100
Subject: [PATCH 006/272] Argument -> specific Argument indices
---
.../code/java/frameworks/apache/Lang.qll | 24 +++++++++----------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll b/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll
index 935b6347ec5..b87c7044216 100644
--- a/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll
+++ b/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll
@@ -643,26 +643,26 @@ private class ApacheToStringBuilderModel extends SummaryModelCsv {
row =
[
"org.apache.commons.lang3.builder;ToStringBuilder;false;toString;;;Argument[-1];ReturnValue;taint",
- "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.Object);;Argument;ReturnValue;taint",
- "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.Object);;Argument;Argument[-1];taint",
- "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.Object[]);;Argument;ReturnValue;taint",
- "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.Object[]);;Argument;Argument[-1];taint",
- "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[]);;Argument;ReturnValue;taint",
- "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[]);;Argument;Argument[-1];taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.Object);;Argument[0];ReturnValue;taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.Object);;Argument[0];Argument[-1];taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.Object[]);;Argument[0];ReturnValue;taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.Object[]);;Argument[0];Argument[-1];taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[]);;Argument[0..1];ReturnValue;taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[]);;Argument[0..1];Argument[-1];taint",
"org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,boolean);;Argument[0];ReturnValue;taint",
"org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,boolean);;Argument[0];Argument[-1];taint",
- "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object);;Argument;ReturnValue;taint",
- "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object);;Argument;Argument[-1];taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object);;Argument[0..1];ReturnValue;taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object);;Argument[0..1];Argument[-1];taint",
"org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[],boolean);;Argument[0];ReturnValue;taint",
"org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[],boolean);;Argument[0];Argument[-1];taint",
"org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[],boolean);;Argument[1];ReturnValue;taint",
"org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[],boolean);;Argument[1];Argument[-1];taint",
"org.apache.commons.lang3.builder;ToStringBuilder;false;build;;;Argument[-1];ReturnValue;taint",
"org.apache.commons.lang3.builder;ToStringBuilder;false;getStringBuffer;;;Argument[-1];ReturnValue;taint",
- "org.apache.commons.lang3.builder;ToStringBuilder;false;appendToString;;;Argument;ReturnValue;taint",
- "org.apache.commons.lang3.builder;ToStringBuilder;false;appendToString;;;Argument;Argument[-1];taint",
- "org.apache.commons.lang3.builder;ToStringBuilder;false;appendSuper;;;Argument;ReturnValue;taint",
- "org.apache.commons.lang3.builder;ToStringBuilder;false;appendSuper;;;Argument;Argument[-1];taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;appendToString;;;Argument[0];ReturnValue;taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;appendToString;;;Argument[0];Argument[-1];taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;appendSuper;;;Argument[0];ReturnValue;taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;appendSuper;;;Argument[0];Argument[-1];taint",
// The following are value-preserving steps for fluent methods:
"org.apache.commons.lang3.builder;ToStringBuilder;false;append;;;Argument[-1];ReturnValue;value",
"org.apache.commons.lang3.builder;ToStringBuilder;false;appendAsObjectToString;;;Argument[-1];ReturnValue;value",
From 2c95b7539f8eeaffcee400a2ab8d730a1abbe312 Mon Sep 17 00:00:00 2001
From: Chris Smowton
Date: Wed, 21 Apr 2021 15:57:09 +0100
Subject: [PATCH 007/272] Remove now-redundant steps
---
.../src/semmle/code/java/frameworks/apache/Lang.qll | 12 +-----------
1 file changed, 1 insertion(+), 11 deletions(-)
diff --git a/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll b/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll
index b87c7044216..7ff539ec041 100644
--- a/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll
+++ b/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll
@@ -643,25 +643,15 @@ private class ApacheToStringBuilderModel extends SummaryModelCsv {
row =
[
"org.apache.commons.lang3.builder;ToStringBuilder;false;toString;;;Argument[-1];ReturnValue;taint",
- "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.Object);;Argument[0];ReturnValue;taint",
"org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.Object);;Argument[0];Argument[-1];taint",
- "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.Object[]);;Argument[0];ReturnValue;taint",
"org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.Object[]);;Argument[0];Argument[-1];taint",
- "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[]);;Argument[0..1];ReturnValue;taint",
"org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[]);;Argument[0..1];Argument[-1];taint",
- "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,boolean);;Argument[0];ReturnValue;taint",
"org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,boolean);;Argument[0];Argument[-1];taint",
- "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object);;Argument[0..1];ReturnValue;taint",
"org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object);;Argument[0..1];Argument[-1];taint",
- "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[],boolean);;Argument[0];ReturnValue;taint",
- "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[],boolean);;Argument[0];Argument[-1];taint",
- "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[],boolean);;Argument[1];ReturnValue;taint",
- "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[],boolean);;Argument[1];Argument[-1];taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[],boolean);;Argument[0..1];Argument[-1];taint",
"org.apache.commons.lang3.builder;ToStringBuilder;false;build;;;Argument[-1];ReturnValue;taint",
"org.apache.commons.lang3.builder;ToStringBuilder;false;getStringBuffer;;;Argument[-1];ReturnValue;taint",
- "org.apache.commons.lang3.builder;ToStringBuilder;false;appendToString;;;Argument[0];ReturnValue;taint",
"org.apache.commons.lang3.builder;ToStringBuilder;false;appendToString;;;Argument[0];Argument[-1];taint",
- "org.apache.commons.lang3.builder;ToStringBuilder;false;appendSuper;;;Argument[0];ReturnValue;taint",
"org.apache.commons.lang3.builder;ToStringBuilder;false;appendSuper;;;Argument[0];Argument[-1];taint",
// The following are value-preserving steps for fluent methods:
"org.apache.commons.lang3.builder;ToStringBuilder;false;append;;;Argument[-1];ReturnValue;value",
From 76091f0f8db56886b9dd204b5a032132f0810605 Mon Sep 17 00:00:00 2001
From: Chris Smowton
Date: Wed, 21 Apr 2021 15:58:41 +0100
Subject: [PATCH 008/272] Use ArrayElement accessor where needed
---
java/ql/src/semmle/code/java/frameworks/apache/Lang.qll | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll b/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll
index 7ff539ec041..516b3c5588e 100644
--- a/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll
+++ b/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll
@@ -644,11 +644,13 @@ private class ApacheToStringBuilderModel extends SummaryModelCsv {
[
"org.apache.commons.lang3.builder;ToStringBuilder;false;toString;;;Argument[-1];ReturnValue;taint",
"org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.Object);;Argument[0];Argument[-1];taint",
- "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.Object[]);;Argument[0];Argument[-1];taint",
- "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[]);;Argument[0..1];Argument[-1];taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.Object[]);;ArrayElement of Argument[0];Argument[-1];taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[]);;Argument[0];Argument[-1];taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[]);;ArrayElement of Argument[1];Argument[-1];taint",
"org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,boolean);;Argument[0];Argument[-1];taint",
"org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object);;Argument[0..1];Argument[-1];taint",
- "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[],boolean);;Argument[0..1];Argument[-1];taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[],boolean);;Argument[0];Argument[-1];taint",
+ "org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[],boolean);;ArrayElement of Argument[1];Argument[-1];taint",
"org.apache.commons.lang3.builder;ToStringBuilder;false;build;;;Argument[-1];ReturnValue;taint",
"org.apache.commons.lang3.builder;ToStringBuilder;false;getStringBuffer;;;Argument[-1];ReturnValue;taint",
"org.apache.commons.lang3.builder;ToStringBuilder;false;appendToString;;;Argument[0];Argument[-1];taint",
From 53e04d0d96dd0038a8d2f47d2a098bd79c2095c0 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Fri, 30 Apr 2021 17:53:43 +0200
Subject: [PATCH 009/272] Refactor to CSV sink model
---
.../Security/CWE/CWE-094/JexlInjection.ql | 19 ++
.../Security/CWE/CWE-094/JexlInjectionLib.qll | 178 +++++++++++-------
2 files changed, 127 insertions(+), 70 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjection.ql
index 2a23dd7368d..8b5e0d8eea1 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjection.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjection.ql
@@ -13,6 +13,25 @@
import java
import JexlInjectionLib
import DataFlow::PathGraph
+import FlowUtils
+
+/**
+ * A taint-tracking configuration for unsafe user input
+ * that is used to construct and evaluate a JEXL expression.
+ * It supports both JEXL 2 and 3.
+ */
+class JexlInjectionConfig extends TaintTracking::Configuration {
+ JexlInjectionConfig() { this = "JexlInjectionConfig" }
+
+ override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
+
+ override predicate isSink(DataFlow::Node sink) { sink instanceof JexlEvaluationSink }
+
+ override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
+ any(JexlInjectionAdditionalTaintStep c).step(node1, node2) or
+ hasGetterFlow(node1, node2)
+ }
+}
from DataFlow::PathNode source, DataFlow::PathNode sink, JexlInjectionConfig conf
where conf.hasFlowPath(source, sink)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjectionLib.qll b/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjectionLib.qll
index 89d7cb496a4..dfe3a793ef3 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjectionLib.qll
+++ b/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjectionLib.qll
@@ -1,89 +1,127 @@
import java
-import FlowUtils
-import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking
-
-/**
- * A taint-tracking configuration for unsafe user input
- * that is used to construct and evaluate a JEXL expression.
- * It supports both JEXL 2 and 3.
- */
-class JexlInjectionConfig extends TaintTracking::Configuration {
- JexlInjectionConfig() { this = "JexlInjectionConfig" }
-
- override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
-
- override predicate isSink(DataFlow::Node sink) { sink instanceof JexlEvaluationSink }
-
- override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
- any(TaintPropagatingJexlMethodCall c).taintFlow(fromNode, toNode) or
- hasGetterFlow(fromNode, toNode)
- }
-}
+private import semmle.code.java.dataflow.ExternalFlow
/**
* A sink for Expresssion Language injection vulnerabilities via Jexl,
* i.e. method calls that run evaluation of a JEXL expression.
- *
- * Creating a `Callable` from a tainted JEXL expression or script is considered as a sink
- * although the tainted expression is not executed at this point.
- * Here we assume that it will get executed at some point,
- * maybe stored in an object field and then reached by a different flow.
*/
-private class JexlEvaluationSink extends DataFlow::ExprNode {
- JexlEvaluationSink() {
- exists(MethodAccess ma, Method m, Expr taintFrom |
- ma.getMethod() = m and taintFrom = this.asExpr()
- |
- m instanceof DirectJexlEvaluationMethod and ma.getQualifier() = taintFrom
- or
- m instanceof CreateJexlCallableMethod and ma.getQualifier() = taintFrom
- or
- m instanceof JexlEngineGetSetPropertyMethod and
- taintFrom.getType() instanceof TypeString and
- ma.getAnArgument() = taintFrom
- )
+abstract class JexlEvaluationSink extends DataFlow::ExprNode { }
+
+/** Default sink for JXEL injection vulnerabilities. */
+private class DefaultJexlEvaluationSink extends JexlEvaluationSink {
+ DefaultJexlEvaluationSink() { sinkNode(this, "jexl") }
+}
+
+private class DefaultJexlInjectionSinkModel extends SinkModelCsv {
+ override predicate row(string row) {
+ row =
+ [
+ // JEXL2
+ "org.apache.commons.jexl2;JexlEngine;false;getProperty;(JexlContext,Object,String);;Argument[2];jexl",
+ "org.apache.commons.jexl2;JexlEngine;false;getProperty;(Object,String);;Argument[1];jexl",
+ "org.apache.commons.jexl2;JexlEngine;false;setProperty;(JexlContext,Object,String,Object);;Argument[2];jexl",
+ "org.apache.commons.jexl2;JexlEngine;false;setProperty;(Object,String,Object);;Argument[1];jexl",
+ "org.apache.commons.jexl2;Expression;false;evaluate;;;Argument[-1];jexl",
+ "org.apache.commons.jexl2;Expression;false;callable;;;Argument[-1];jexl",
+ "org.apache.commons.jexl2;JexlExpression;false;evaluate;;;Argument[-1];jexl",
+ "org.apache.commons.jexl2;JexlExpression;false;callable;;;Argument[-1];jexl",
+ "org.apache.commons.jexl2;Script;false;execute;;;Argument[-1];jexl",
+ "org.apache.commons.jexl2;Script;false;callable;;;Argument[-1];jexl",
+ "org.apache.commons.jexl2;JexlScript;false;execute;;;Argument[-1];jexl",
+ "org.apache.commons.jexl2;JexlScript;false;callable;;;Argument[-1];jexl",
+ "org.apache.commons.jexl2;JxltEngine$Expression;false;evaluate;;;Argument[-1];jexl",
+ "org.apache.commons.jexl2;JxltEngine$Expression;false;prepare;;;Argument[-1];jexl",
+ "org.apache.commons.jexl2;JxltEngine$Template;false;evaluate;;;Argument[-1];jexl",
+ "org.apache.commons.jexl2;UnifiedJEXL$Expression;false;evaluate;;;Argument[-1];jexl",
+ "org.apache.commons.jexl2;UnifiedJEXL$Expression;false;prepare;;;Argument[-1];jexl",
+ "org.apache.commons.jexl2;UnifiedJEXL$Template;false;evaluate;;;Argument[-1];jexl",
+ // JEXL3
+ "org.apache.commons.jexl3;JexlEngine;false;getProperty;(JexlContext,Object,String);;Argument[2];jexl",
+ "org.apache.commons.jexl3;JexlEngine;false;getProperty;(Object,String);;Argument[1];jexl",
+ "org.apache.commons.jexl3;JexlEngine;false;setProperty;(JexlContext,Object,String);;Argument[2];jexl",
+ "org.apache.commons.jexl3;JexlEngine;false;setProperty;(Object,String,Object);;Argument[1];jexl",
+ "org.apache.commons.jexl3;Expression;false;evaluate;;;Argument[-1];jexl",
+ "org.apache.commons.jexl3;Expression;false;callable;;;Argument[-1];jexl",
+ "org.apache.commons.jexl3;JexlExpression;false;evaluate;;;Argument[-1];jexl",
+ "org.apache.commons.jexl3;JexlExpression;false;callable;;;Argument[-1];jexl",
+ "org.apache.commons.jexl3;Script;false;execute;;;Argument[-1];jexl",
+ "org.apache.commons.jexl3;Script;false;callable;;;Argument[-1];jexl",
+ "org.apache.commons.jexl3;JexlScript;false;execute;;;Argument[-1];jexl",
+ "org.apache.commons.jexl3;JexlScript;false;callable;;;Argument[-1];jexl",
+ "org.apache.commons.jexl3;JxltEngine$Expression;false;evaluate;;;Argument[-1];jexl",
+ "org.apache.commons.jexl3;JxltEngine$Expression;false;prepare;;;Argument[-1];jexl",
+ "org.apache.commons.jexl3;JxltEngine$Template;false;evaluate;;;Argument[-1];jexl",
+ "org.apache.commons.jexl3;UnifiedJEXL$Expression;false;evaluate;;;Argument[-1];jexl",
+ "org.apache.commons.jexl3;UnifiedJEXL$Expression;false;prepare;;;Argument[-1];jexl",
+ "org.apache.commons.jexl3;UnifiedJEXL$Template;false;evaluate;;;Argument[-1];jexl"
+ ]
}
}
/**
- * Defines method calls that propagate tainted data via one of the methods
- * from JEXL library.
+ * A unit class for adding additional taint steps.
+ *
+ * Extend this class to add additional taint steps that should apply to the `JexlInjectionFlowConfig`.
*/
-private class TaintPropagatingJexlMethodCall extends MethodAccess {
- Expr taintFromExpr;
-
- TaintPropagatingJexlMethodCall() {
- exists(Method m, RefType taintType |
- this.getMethod() = m and
- taintType = taintFromExpr.getType()
- |
- isUnsafeEngine(this.getQualifier()) and
- (
- m instanceof CreateJexlScriptMethod and
- taintFromExpr = this.getArgument(0) and
- taintType instanceof TypeString
- or
- m instanceof CreateJexlExpressionMethod and
- taintFromExpr = this.getAnArgument() and
- taintType instanceof TypeString
- or
- m instanceof CreateJexlTemplateMethod and
- (taintType instanceof TypeString or taintType instanceof Reader) and
- taintFromExpr = this.getArgument([0, 1])
- )
- )
- }
-
+abstract class JexlInjectionAdditionalTaintStep extends Unit {
/**
- * Holds if `fromNode` to `toNode` is a dataflow step that propagates
- * tainted data.
+ * Holds if the step from `node1` to `node2` should be considered a taint
+ * step for the `JexlInjectionConfig` configuration.
*/
- predicate taintFlow(DataFlow::Node fromNode, DataFlow::Node toNode) {
- fromNode.asExpr() = taintFromExpr and toNode.asExpr() = this
+ abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
+}
+
+/** A set of additional taint steps to consider when taint tracking JXEL related data flows. */
+private class DefaultJexlInjectionAdditionalTaintStep extends JexlInjectionAdditionalTaintStep {
+ override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
+ createJexlScriptStep(node1, node2) or
+ createJexlExpressionStep(node1, node2) or
+ createJexlTemplateStep(node1, node2)
}
}
+/**
+ * Holds if `n1` to `n2` is a dataflow step that creates a JEXL script using an unsafe engine
+ * i.e. `tainted.createScript(jexlExpr)`.
+ */
+private predicate createJexlScriptStep(DataFlow::Node n1, DataFlow::Node n2) {
+ exists(MethodAccess ma, Method m | m = ma.getMethod() and n2.asExpr() = ma |
+ isUnsafeEngine(ma.getQualifier()) and
+ m instanceof CreateJexlScriptMethod and
+ n1.asExpr() = ma.getArgument(0) and
+ n1.asExpr().getType() instanceof TypeString
+ )
+}
+
+/**
+ * Holds if `n1` to `n2` is a dataflow step that creates a JEXL expression using an unsafe engine
+ * i.e. `tainted.createExpression(jexlExpr)`.
+ */
+private predicate createJexlExpressionStep(DataFlow::Node n1, DataFlow::Node n2) {
+ exists(MethodAccess ma, Method m | m = ma.getMethod() and n2.asExpr() = ma |
+ isUnsafeEngine(ma.getQualifier()) and
+ m instanceof CreateJexlExpressionMethod and
+ n1.asExpr() = ma.getAnArgument() and
+ n1.asExpr().getType() instanceof TypeString
+ )
+}
+
+/**
+ * Holds if `n1` to `n2` is a dataflow step that creates a JEXL template using an unsafe engine
+ * i.e. `tainted.createTemplate(jexlExpr)`.
+ */
+private predicate createJexlTemplateStep(DataFlow::Node n1, DataFlow::Node n2) {
+ exists(MethodAccess ma, Method m, RefType taintType |
+ m = ma.getMethod() and n2.asExpr() = ma and taintType = n1.asExpr().getType()
+ |
+ isUnsafeEngine(ma.getQualifier()) and
+ m instanceof CreateJexlTemplateMethod and
+ n1.asExpr() = ma.getArgument([0, 1]) and
+ (taintType instanceof TypeString or taintType instanceof Reader)
+ )
+}
+
/**
* Holds if `expr` is a JEXL engine that is not configured with a sandbox.
*/
@@ -92,7 +130,7 @@ private predicate isUnsafeEngine(Expr expr) {
}
/**
- * A configuration for a tracking sandboxed JEXL engines.
+ * A configuration for tracking sandboxed JEXL engines.
*/
private class SandboxedJexlFlowConfig extends DataFlow2::Configuration {
SandboxedJexlFlowConfig() { this = "JexlInjection::SandboxedJexlFlowConfig" }
From 38e052482c4b1017396962f29705d38f1c8854ae Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Mon, 3 May 2021 12:44:53 +0200
Subject: [PATCH 010/272] More csv sinks and sources
---
.../Security/CWE/CWE-094/JexlInjectionLib.qll | 140 +++++-------------
.../query-tests/security/CWE-094/options | 2 +-
.../apache/commons/jexl2/JexlArithmetic.java | 5 +
.../org/apache/commons/jexl2/JexlEngine.java | 16 +-
.../org/apache/commons/logging/Log.java | 5 +
5 files changed, 54 insertions(+), 114 deletions(-)
create mode 100644 java/ql/test/stubs/apache-commons-jexl-2.1.1/org/apache/commons/jexl2/JexlArithmetic.java
create mode 100644 java/ql/test/stubs/apache-commons-logging-1.2/org/apache/commons/logging/Log.java
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjectionLib.qll b/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjectionLib.qll
index dfe3a793ef3..811d9877112 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjectionLib.qll
+++ b/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjectionLib.qll
@@ -30,9 +30,6 @@ private class DefaultJexlInjectionSinkModel extends SinkModelCsv {
"org.apache.commons.jexl2;Script;false;callable;;;Argument[-1];jexl",
"org.apache.commons.jexl2;JexlScript;false;execute;;;Argument[-1];jexl",
"org.apache.commons.jexl2;JexlScript;false;callable;;;Argument[-1];jexl",
- "org.apache.commons.jexl2;JxltEngine$Expression;false;evaluate;;;Argument[-1];jexl",
- "org.apache.commons.jexl2;JxltEngine$Expression;false;prepare;;;Argument[-1];jexl",
- "org.apache.commons.jexl2;JxltEngine$Template;false;evaluate;;;Argument[-1];jexl",
"org.apache.commons.jexl2;UnifiedJEXL$Expression;false;evaluate;;;Argument[-1];jexl",
"org.apache.commons.jexl2;UnifiedJEXL$Expression;false;prepare;;;Argument[-1];jexl",
"org.apache.commons.jexl2;UnifiedJEXL$Template;false;evaluate;;;Argument[-1];jexl",
@@ -51,10 +48,7 @@ private class DefaultJexlInjectionSinkModel extends SinkModelCsv {
"org.apache.commons.jexl3;JexlScript;false;callable;;;Argument[-1];jexl",
"org.apache.commons.jexl3;JxltEngine$Expression;false;evaluate;;;Argument[-1];jexl",
"org.apache.commons.jexl3;JxltEngine$Expression;false;prepare;;;Argument[-1];jexl",
- "org.apache.commons.jexl3;JxltEngine$Template;false;evaluate;;;Argument[-1];jexl",
- "org.apache.commons.jexl3;UnifiedJEXL$Expression;false;evaluate;;;Argument[-1];jexl",
- "org.apache.commons.jexl3;UnifiedJEXL$Expression;false;prepare;;;Argument[-1];jexl",
- "org.apache.commons.jexl3;UnifiedJEXL$Template;false;evaluate;;;Argument[-1];jexl"
+ "org.apache.commons.jexl3;JxltEngine$Template;false;evaluate;;;Argument[-1];jexl"
]
}
}
@@ -135,48 +129,50 @@ private predicate isUnsafeEngine(Expr expr) {
private class SandboxedJexlFlowConfig extends DataFlow2::Configuration {
SandboxedJexlFlowConfig() { this = "JexlInjection::SandboxedJexlFlowConfig" }
- override predicate isSource(DataFlow::Node node) { node instanceof SandboxedJexlSource }
+ override predicate isSource(DataFlow::Node node) { sourceNode(node, "sandboxed-jexl") }
- override predicate isSink(DataFlow::Node node) {
- exists(MethodAccess ma, Method m | ma.getMethod() = m |
- (
- m instanceof CreateJexlScriptMethod or
- m instanceof CreateJexlExpressionMethod or
- m instanceof CreateJexlTemplateMethod
- ) and
- ma.getQualifier() = node.asExpr()
- )
- }
+ override predicate isSink(DataFlow::Node node) { sinkNode(node, "sandboxed-jexl") }
override predicate isAdditionalFlowStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
- createsJexlEngine(fromNode, toNode)
+ createJexlEngineStep(fromNode, toNode)
}
}
-/**
- * Defines a data flow source for JEXL engines configured with a sandbox.
- */
-private class SandboxedJexlSource extends DataFlow::ExprNode {
- SandboxedJexlSource() {
- exists(MethodAccess ma, Method m | m = ma.getMethod() |
- m.getDeclaringType() instanceof JexlBuilder and
- m.hasName(["uberspect", "sandbox"]) and
- m.getReturnType() instanceof JexlBuilder and
- this.asExpr() = [ma, ma.getQualifier()]
- )
- or
- exists(ConstructorCall cc |
- cc.getConstructedType() instanceof JexlEngine and
- cc.getArgument(0).getType() instanceof JexlUberspect and
- cc = this.asExpr()
- )
+private class SandoboxedJexlSourceModel extends SourceModelCsv {
+ override predicate row(string row) {
+ row =
+ [
+ // JEXL2
+ "org.apache.commons.jexl2;JexlEngine;false;JexlEngine;(Uberspect,JexlArithmetic,Map,Log);;ReturnValue;sandboxed-jexl",
+ // JEXL3
+ "org.apache.commons.jexl3;JexlBuilder;false;uberspect;(JexlUberspect);;ReturnValue;sandboxed-jexl",
+ "org.apache.commons.jexl3;JexlBuilder;false;sandbox;(JexlSandbox);;ReturnValue;sandboxed-jexl"
+ ]
+ }
+}
+
+private class SandoboxedJexlSinkModel extends SinkModelCsv {
+ override predicate row(string row) {
+ row =
+ [
+ // JEXL2
+ "org.apache.commons.jexl2;JexlEngine;false;createScript;;;Argument[-1];sandboxed-jexl",
+ "org.apache.commons.jexl2;JexlEngine;false;createExpression;;;Argument[-1];sandboxed-jexl",
+ "org.apache.commons.jexl2;UnifiedJEXL;false;parse;;;Argument[-1];sandboxed-jexl",
+ "org.apache.commons.jexl2;UnifiedJEXL;false;createTemplate;;;Argument[-1];sandboxed-jexl",
+ // JEXL3
+ "org.apache.commons.jexl3;JexlEngine;false;createScript;;;Argument[-1];sandboxed-jexl",
+ "org.apache.commons.jexl3;JexlEngine;false;createExpression;;;Argument[-1];sandboxed-jexl",
+ "org.apache.commons.jexl3;JxltEngine;false;createExpression;;;Argument[-1];sandboxed-jexl",
+ "org.apache.commons.jexl3;JxltEngine;false;createTemplate;;;Argument[-1];sandboxed-jexl"
+ ]
}
}
/**
* Holds if `fromNode` to `toNode` is a dataflow step that creates one of the JEXL engines.
*/
-private predicate createsJexlEngine(DataFlow::Node fromNode, DataFlow::Node toNode) {
+private predicate createJexlEngineStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
exists(MethodAccess ma, Method m | m = ma.getMethod() |
(m.getDeclaringType() instanceof JexlBuilder or m.getDeclaringType() instanceof JexlEngine) and
m.hasName(["create", "createJxltEngine"]) and
@@ -191,35 +187,6 @@ private predicate createsJexlEngine(DataFlow::Node fromNode, DataFlow::Node toNo
)
}
-/**
- * A methods in the `JexlEngine` class that gets or sets a property with a JEXL expression.
- */
-private class JexlEngineGetSetPropertyMethod extends Method {
- JexlEngineGetSetPropertyMethod() {
- getDeclaringType() instanceof JexlEngine and
- hasName(["getProperty", "setProperty"])
- }
-}
-
-/**
- * A method that triggers direct evaluation of JEXL expressions.
- */
-private class DirectJexlEvaluationMethod extends Method {
- DirectJexlEvaluationMethod() {
- getDeclaringType() instanceof JexlExpression and hasName("evaluate")
- or
- getDeclaringType() instanceof JexlScript and hasName("execute")
- or
- getDeclaringType() instanceof JxltEngineExpression and hasName(["evaluate", "prepare"])
- or
- getDeclaringType() instanceof JxltEngineTemplate and hasName("evaluate")
- or
- getDeclaringType() instanceof UnifiedJexlExpression and hasName(["evaluate", "prepare"])
- or
- getDeclaringType() instanceof UnifiedJexlTemplate and hasName("evaluate")
- }
-}
-
/**
* A method that creates a JEXL script.
*/
@@ -227,16 +194,6 @@ private class CreateJexlScriptMethod extends Method {
CreateJexlScriptMethod() { getDeclaringType() instanceof JexlEngine and hasName("createScript") }
}
-/**
- * A method that creates a `Callable` for a JEXL expression or script.
- */
-private class CreateJexlCallableMethod extends Method {
- CreateJexlCallableMethod() {
- (getDeclaringType() instanceof JexlExpression or getDeclaringType() instanceof JexlScript) and
- hasName("callable")
- }
-}
-
/**
* A method that creates a JEXL template.
*/
@@ -263,14 +220,6 @@ private class JexlRefType extends RefType {
JexlRefType() { getPackage().hasName(["org.apache.commons.jexl2", "org.apache.commons.jexl3"]) }
}
-private class JexlExpression extends JexlRefType {
- JexlExpression() { hasName(["Expression", "JexlExpression"]) }
-}
-
-private class JexlScript extends JexlRefType {
- JexlScript() { hasName(["Script", "JexlScript"]) }
-}
-
private class JexlBuilder extends JexlRefType {
JexlBuilder() { hasName("JexlBuilder") }
}
@@ -287,29 +236,6 @@ private class UnifiedJexl extends JexlRefType {
UnifiedJexl() { hasName("UnifiedJEXL") }
}
-private class JexlUberspect extends Interface {
- JexlUberspect() {
- hasQualifiedName("org.apache.commons.jexl2.introspection", "Uberspect") or
- hasQualifiedName("org.apache.commons.jexl3.introspection", "JexlUberspect")
- }
-}
-
-private class JxltEngineExpression extends NestedType {
- JxltEngineExpression() { getEnclosingType() instanceof JxltEngine and hasName("Expression") }
-}
-
-private class JxltEngineTemplate extends NestedType {
- JxltEngineTemplate() { getEnclosingType() instanceof JxltEngine and hasName("Template") }
-}
-
-private class UnifiedJexlExpression extends NestedType {
- UnifiedJexlExpression() { getEnclosingType() instanceof UnifiedJexl and hasName("Expression") }
-}
-
-private class UnifiedJexlTemplate extends NestedType {
- UnifiedJexlTemplate() { getEnclosingType() instanceof UnifiedJexl and hasName("Template") }
-}
-
private class Reader extends RefType {
Reader() { hasQualifiedName("java.io", "Reader") }
}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/options b/java/ql/test/experimental/query-tests/security/CWE-094/options
index 18e3518fc97..4841ceb7488 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-094/options
+++ b/java/ql/test/experimental/query-tests/security/CWE-094/options
@@ -1,2 +1,2 @@
-//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../../stubs/mvel2-2.4.7:${testdir}/../../../../stubs/jsr223-api:${testdir}/../../../../stubs/apache-commons-jexl-2.1.1:${testdir}/../../../../stubs/apache-commons-jexl-3.1:${testdir}/../../../../stubs/scriptengine:${testdir}/../../../../stubs/java-ee-el:${testdir}/../../../../stubs/juel-2.2:${testdir}/../../../stubs/groovy-all-3.0.7:${testdir}/../../../../stubs/servlet-api-2.4
+//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../../stubs/mvel2-2.4.7:${testdir}/../../../../stubs/jsr223-api:${testdir}/../../../../stubs/apache-commons-jexl-2.1.1:${testdir}/../../../../stubs/apache-commons-jexl-3.1:${testdir}/../../../../stubs/scriptengine:${testdir}/../../../../stubs/java-ee-el:${testdir}/../../../../stubs/juel-2.2:${testdir}/../../../stubs/groovy-all-3.0.7:${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/apache-commons-logging-1.2
diff --git a/java/ql/test/stubs/apache-commons-jexl-2.1.1/org/apache/commons/jexl2/JexlArithmetic.java b/java/ql/test/stubs/apache-commons-jexl-2.1.1/org/apache/commons/jexl2/JexlArithmetic.java
new file mode 100644
index 00000000000..cff0dbeb54c
--- /dev/null
+++ b/java/ql/test/stubs/apache-commons-jexl-2.1.1/org/apache/commons/jexl2/JexlArithmetic.java
@@ -0,0 +1,5 @@
+package org.apache.commons.jexl2;
+
+public class JexlArithmetic {
+
+}
diff --git a/java/ql/test/stubs/apache-commons-jexl-2.1.1/org/apache/commons/jexl2/JexlEngine.java b/java/ql/test/stubs/apache-commons-jexl-2.1.1/org/apache/commons/jexl2/JexlEngine.java
index 38568b08ac0..b6cf38487f9 100644
--- a/java/ql/test/stubs/apache-commons-jexl-2.1.1/org/apache/commons/jexl2/JexlEngine.java
+++ b/java/ql/test/stubs/apache-commons-jexl-2.1.1/org/apache/commons/jexl2/JexlEngine.java
@@ -2,12 +2,15 @@ package org.apache.commons.jexl2;
import java.util.Map;
import org.apache.commons.jexl2.introspection.*;
+import org.apache.commons.logging.Log;
public class JexlEngine {
- public JexlEngine() {}
+ public JexlEngine() {
+ }
- public JexlEngine(Uberspect uberspect, Object arithmetic, Map functions, Object log) {}
+ public JexlEngine(Uberspect uberspect, JexlArithmetic arithmetic, Map functions, Log log) {
+ }
public Expression createExpression(String expression) {
return null;
@@ -41,9 +44,10 @@ public class JexlEngine {
return null;
}
- public void setProperty(Object bean, String expr, Object value) {}
+ public void setProperty(Object bean, String expr, Object value) {
+ }
+
+ public void setProperty(JexlContext context, Object bean, String expr, Object value) {
+ }
- public void setProperty(JexlContext context, Object bean, String expr, Object value) {}
-
-
}
\ No newline at end of file
diff --git a/java/ql/test/stubs/apache-commons-logging-1.2/org/apache/commons/logging/Log.java b/java/ql/test/stubs/apache-commons-logging-1.2/org/apache/commons/logging/Log.java
new file mode 100644
index 00000000000..4c8ee14acc8
--- /dev/null
+++ b/java/ql/test/stubs/apache-commons-logging-1.2/org/apache/commons/logging/Log.java
@@ -0,0 +1,5 @@
+package org.apache.commons.logging;
+
+public interface Log {
+
+}
From 4bfd34b1fea100f4f08cd9c68065a44eeec0499f Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Mon, 3 May 2021 13:15:24 +0200
Subject: [PATCH 011/272] Moved from experimental
---
.../Security/CWE/CWE-094/JexlInjection.qhelp | 0
.../Security/CWE/CWE-094/JexlInjection.ql | 7 ++++---
.../Security/CWE/CWE-094/JexlInjectionLib.qll | 0
.../query-tests/security/CWE-094/JexlInjection.qlref | 1 -
.../test/experimental/query-tests/security/CWE-094/options | 2 +-
.../query-tests/security/CWE-094/Jexl2Injection.java | 0
.../query-tests/security/CWE-094/Jexl3Injection.java | 0
.../query-tests/security/CWE-094/JexlInjection.expected | 0
.../test/query-tests/security/CWE-094/JexlInjection.qlref | 1 +
.../query-tests/security/CWE-094/SandboxedJexl2.java | 0
.../query-tests/security/CWE-094/SandboxedJexl3.java | 0
java/ql/test/query-tests/security/CWE-094/options | 2 +-
12 files changed, 7 insertions(+), 6 deletions(-)
rename java/ql/src/{experimental => }/Security/CWE/CWE-094/JexlInjection.qhelp (100%)
rename java/ql/src/{experimental => }/Security/CWE/CWE-094/JexlInjection.ql (87%)
rename java/ql/src/{experimental => }/Security/CWE/CWE-094/JexlInjectionLib.qll (100%)
delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/JexlInjection.qlref
rename java/ql/test/{experimental => }/query-tests/security/CWE-094/Jexl2Injection.java (100%)
rename java/ql/test/{experimental => }/query-tests/security/CWE-094/Jexl3Injection.java (100%)
rename java/ql/test/{experimental => }/query-tests/security/CWE-094/JexlInjection.expected (100%)
create mode 100644 java/ql/test/query-tests/security/CWE-094/JexlInjection.qlref
rename java/ql/test/{experimental => }/query-tests/security/CWE-094/SandboxedJexl2.java (100%)
rename java/ql/test/{experimental => }/query-tests/security/CWE-094/SandboxedJexl3.java (100%)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjection.qhelp b/java/ql/src/Security/CWE/CWE-094/JexlInjection.qhelp
similarity index 100%
rename from java/ql/src/experimental/Security/CWE/CWE-094/JexlInjection.qhelp
rename to java/ql/src/Security/CWE/CWE-094/JexlInjection.qhelp
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjection.ql b/java/ql/src/Security/CWE/CWE-094/JexlInjection.ql
similarity index 87%
rename from java/ql/src/experimental/Security/CWE/CWE-094/JexlInjection.ql
rename to java/ql/src/Security/CWE/CWE-094/JexlInjection.ql
index 8b5e0d8eea1..460fa4540ef 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjection.ql
+++ b/java/ql/src/Security/CWE/CWE-094/JexlInjection.ql
@@ -13,7 +13,8 @@
import java
import JexlInjectionLib
import DataFlow::PathGraph
-import FlowUtils
+import semmle.code.java.dataflow.FlowSources
+//import FlowUtils
/**
* A taint-tracking configuration for unsafe user input
@@ -28,8 +29,8 @@ class JexlInjectionConfig extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) { sink instanceof JexlEvaluationSink }
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
- any(JexlInjectionAdditionalTaintStep c).step(node1, node2) or
- hasGetterFlow(node1, node2)
+ any(JexlInjectionAdditionalTaintStep c).step(node1, node2) /*or
+ hasGetterFlow(node1, node2)*/
}
}
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjectionLib.qll b/java/ql/src/Security/CWE/CWE-094/JexlInjectionLib.qll
similarity index 100%
rename from java/ql/src/experimental/Security/CWE/CWE-094/JexlInjectionLib.qll
rename to java/ql/src/Security/CWE/CWE-094/JexlInjectionLib.qll
diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JexlInjection.qlref b/java/ql/test/experimental/query-tests/security/CWE-094/JexlInjection.qlref
deleted file mode 100644
index 82ad87b1751..00000000000
--- a/java/ql/test/experimental/query-tests/security/CWE-094/JexlInjection.qlref
+++ /dev/null
@@ -1 +0,0 @@
-experimental/Security/CWE/CWE-094/JexlInjection.ql
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/options b/java/ql/test/experimental/query-tests/security/CWE-094/options
index 4841ceb7488..0104bad4f22 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-094/options
+++ b/java/ql/test/experimental/query-tests/security/CWE-094/options
@@ -1,2 +1,2 @@
-//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../../stubs/mvel2-2.4.7:${testdir}/../../../../stubs/jsr223-api:${testdir}/../../../../stubs/apache-commons-jexl-2.1.1:${testdir}/../../../../stubs/apache-commons-jexl-3.1:${testdir}/../../../../stubs/scriptengine:${testdir}/../../../../stubs/java-ee-el:${testdir}/../../../../stubs/juel-2.2:${testdir}/../../../stubs/groovy-all-3.0.7:${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/apache-commons-logging-1.2
+//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../../stubs/mvel2-2.4.7:${testdir}/../../../../stubs/jsr223-api:${testdir}/../../../../stubs/scriptengine:${testdir}/../../../../stubs/java-ee-el:${testdir}/../../../../stubs/juel-2.2:${testdir}/../../../stubs/groovy-all-3.0.7:${testdir}/../../../../stubs/servlet-api-2.4
diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/Jexl2Injection.java b/java/ql/test/query-tests/security/CWE-094/Jexl2Injection.java
similarity index 100%
rename from java/ql/test/experimental/query-tests/security/CWE-094/Jexl2Injection.java
rename to java/ql/test/query-tests/security/CWE-094/Jexl2Injection.java
diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/Jexl3Injection.java b/java/ql/test/query-tests/security/CWE-094/Jexl3Injection.java
similarity index 100%
rename from java/ql/test/experimental/query-tests/security/CWE-094/Jexl3Injection.java
rename to java/ql/test/query-tests/security/CWE-094/Jexl3Injection.java
diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JexlInjection.expected b/java/ql/test/query-tests/security/CWE-094/JexlInjection.expected
similarity index 100%
rename from java/ql/test/experimental/query-tests/security/CWE-094/JexlInjection.expected
rename to java/ql/test/query-tests/security/CWE-094/JexlInjection.expected
diff --git a/java/ql/test/query-tests/security/CWE-094/JexlInjection.qlref b/java/ql/test/query-tests/security/CWE-094/JexlInjection.qlref
new file mode 100644
index 00000000000..63f170df617
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-094/JexlInjection.qlref
@@ -0,0 +1 @@
+Security/CWE/CWE-094/JexlInjection.ql
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/SandboxedJexl2.java b/java/ql/test/query-tests/security/CWE-094/SandboxedJexl2.java
similarity index 100%
rename from java/ql/test/experimental/query-tests/security/CWE-094/SandboxedJexl2.java
rename to java/ql/test/query-tests/security/CWE-094/SandboxedJexl2.java
diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/SandboxedJexl3.java b/java/ql/test/query-tests/security/CWE-094/SandboxedJexl3.java
similarity index 100%
rename from java/ql/test/experimental/query-tests/security/CWE-094/SandboxedJexl3.java
rename to java/ql/test/query-tests/security/CWE-094/SandboxedJexl3.java
diff --git a/java/ql/test/query-tests/security/CWE-094/options b/java/ql/test/query-tests/security/CWE-094/options
index 468d90aeabc..27fdb569cd8 100644
--- a/java/ql/test/query-tests/security/CWE-094/options
+++ b/java/ql/test/query-tests/security/CWE-094/options
@@ -1 +1 @@
-//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/validation-api-2.0.1.Final
+//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/validation-api-2.0.1.Final:${testdir}/../../../stubs/springframework-5.2.3:${testdir}/../../../stubs/apache-commons-jexl-2.1.1:${testdir}/../../../stubs/apache-commons-jexl-3.1:${testdir}/../../../stubs/apache-commons-logging-1.2
From 4d5ec87de9b368fc57a21fea948316a10b21786b Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Mon, 3 May 2021 13:27:24 +0200
Subject: [PATCH 012/272] Use InlineTest
---
.../src/Security/CWE/CWE-094/JexlInjection.ql | 6 +-
.../code/java/security/JexlInjection.qll} | 0
.../security/CWE-094/Jexl2Injection.java | 21 +-
.../security/CWE-094/Jexl3Injection.java | 26 ++-
.../security/CWE-094/JexlInjection.expected | 199 ------------------
.../CWE-094/JexlInjectionTest.expected | 0
.../security/CWE-094/JexlInjectionTest.ql | 33 +++
.../security/CWE-094/SandboxedJexl2.java | 6 +-
8 files changed, 60 insertions(+), 231 deletions(-)
rename java/ql/src/{Security/CWE/CWE-094/JexlInjectionLib.qll => semmle/code/java/security/JexlInjection.qll} (100%)
delete mode 100644 java/ql/test/query-tests/security/CWE-094/JexlInjection.expected
create mode 100644 java/ql/test/query-tests/security/CWE-094/JexlInjectionTest.expected
create mode 100644 java/ql/test/query-tests/security/CWE-094/JexlInjectionTest.ql
diff --git a/java/ql/src/Security/CWE/CWE-094/JexlInjection.ql b/java/ql/src/Security/CWE/CWE-094/JexlInjection.ql
index 460fa4540ef..80810378dce 100644
--- a/java/ql/src/Security/CWE/CWE-094/JexlInjection.ql
+++ b/java/ql/src/Security/CWE/CWE-094/JexlInjection.ql
@@ -11,10 +11,9 @@
*/
import java
-import JexlInjectionLib
import DataFlow::PathGraph
import semmle.code.java.dataflow.FlowSources
-//import FlowUtils
+import semmle.code.java.security.JexlInjection
/**
* A taint-tracking configuration for unsafe user input
@@ -29,8 +28,7 @@ class JexlInjectionConfig extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) { sink instanceof JexlEvaluationSink }
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
- any(JexlInjectionAdditionalTaintStep c).step(node1, node2) /*or
- hasGetterFlow(node1, node2)*/
+ any(JexlInjectionAdditionalTaintStep c).step(node1, node2)
}
}
diff --git a/java/ql/src/Security/CWE/CWE-094/JexlInjectionLib.qll b/java/ql/src/semmle/code/java/security/JexlInjection.qll
similarity index 100%
rename from java/ql/src/Security/CWE/CWE-094/JexlInjectionLib.qll
rename to java/ql/src/semmle/code/java/security/JexlInjection.qll
diff --git a/java/ql/test/query-tests/security/CWE-094/Jexl2Injection.java b/java/ql/test/query-tests/security/CWE-094/Jexl2Injection.java
index 438e9b036b6..d7ee659a4c8 100644
--- a/java/ql/test/query-tests/security/CWE-094/Jexl2Injection.java
+++ b/java/ql/test/query-tests/security/CWE-094/Jexl2Injection.java
@@ -11,22 +11,21 @@ public class Jexl2Injection {
JexlEngine jexl = new JexlEngine();
Expression e = jexl.createExpression(jexlExpr);
JexlContext jc = new MapContext();
- e.evaluate(jc);
+ e.evaluate(jc); // $hasJexlInjection
}
private static void runJexlExpressionWithJexlInfo(String jexlExpr) {
JexlEngine jexl = new JexlEngine();
- Expression e = jexl.createExpression(
- jexlExpr, new DebugInfo("unknown", 0, 0));
+ Expression e = jexl.createExpression(jexlExpr, new DebugInfo("unknown", 0, 0));
JexlContext jc = new MapContext();
- e.evaluate(jc);
+ e.evaluate(jc); // $hasJexlInjection
}
private static void runJexlScript(String jexlExpr) {
JexlEngine jexl = new JexlEngine();
Script script = jexl.createScript(jexlExpr);
JexlContext jc = new MapContext();
- script.execute(jc);
+ script.execute(jc); // $hasJexlInjection
}
private static void runJexlScriptViaCallable(String jexlExpr) {
@@ -35,7 +34,7 @@ public class Jexl2Injection {
JexlContext jc = new MapContext();
try {
- script.callable(jc).call();
+ script.callable(jc).call(); // $hasJexlInjection
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -43,30 +42,30 @@ public class Jexl2Injection {
private static void runJexlExpressionViaGetProperty(String jexlExpr) {
JexlEngine jexl = new JexlEngine();
- jexl.getProperty(new Object(), jexlExpr);
+ jexl.getProperty(new Object(), jexlExpr); // $hasJexlInjection
}
private static void runJexlExpressionViaSetProperty(String jexlExpr) {
JexlEngine jexl = new JexlEngine();
- jexl.setProperty(new Object(), jexlExpr, new Object());
+ jexl.setProperty(new Object(), jexlExpr, new Object()); // $hasJexlInjection
}
private static void runJexlExpressionViaUnifiedJEXLParseAndEvaluate(String jexlExpr) {
JexlEngine jexl = new JexlEngine();
UnifiedJEXL unifiedJEXL = new UnifiedJEXL(jexl);
- unifiedJEXL.parse(jexlExpr).evaluate(new MapContext());
+ unifiedJEXL.parse(jexlExpr).evaluate(new MapContext()); // $hasJexlInjection
}
private static void runJexlExpressionViaUnifiedJEXLParseAndPrepare(String jexlExpr) {
JexlEngine jexl = new JexlEngine();
UnifiedJEXL unifiedJEXL = new UnifiedJEXL(jexl);
- unifiedJEXL.parse(jexlExpr).prepare(new MapContext());
+ unifiedJEXL.parse(jexlExpr).prepare(new MapContext()); // $hasJexlInjection
}
private static void runJexlExpressionViaUnifiedJEXLTemplateEvaluate(String jexlExpr) {
JexlEngine jexl = new JexlEngine();
UnifiedJEXL unifiedJEXL = new UnifiedJEXL(jexl);
- unifiedJEXL.createTemplate(jexlExpr).evaluate(new MapContext(), new StringWriter());
+ unifiedJEXL.createTemplate(jexlExpr).evaluate(new MapContext(), new StringWriter()); // $hasJexlInjection
}
private static void testWithSocket(Consumer action) throws Exception {
diff --git a/java/ql/test/query-tests/security/CWE-094/Jexl3Injection.java b/java/ql/test/query-tests/security/CWE-094/Jexl3Injection.java
index a23a8b35841..0300b8ffe3f 100644
--- a/java/ql/test/query-tests/security/CWE-094/Jexl3Injection.java
+++ b/java/ql/test/query-tests/security/CWE-094/Jexl3Injection.java
@@ -18,21 +18,21 @@ public class Jexl3Injection {
JexlEngine jexl = new JexlBuilder().create();
JexlExpression e = jexl.createExpression(jexlExpr);
JexlContext jc = new MapContext();
- e.evaluate(jc);
+ e.evaluate(jc); // $hasJexlInjection
}
private static void runJexlExpressionWithJexlInfo(String jexlExpr) {
JexlEngine jexl = new JexlBuilder().create();
JexlExpression e = jexl.createExpression(new JexlInfo("unknown", 0, 0), jexlExpr);
JexlContext jc = new MapContext();
- e.evaluate(jc);
+ e.evaluate(jc); // $hasJexlInjection
}
private static void runJexlScript(String jexlExpr) {
JexlEngine jexl = new JexlBuilder().create();
JexlScript script = jexl.createScript(jexlExpr);
JexlContext jc = new MapContext();
- script.execute(jc);
+ script.execute(jc); // $hasJexlInjection
}
private static void runJexlScriptViaCallable(String jexlExpr) {
@@ -41,7 +41,7 @@ public class Jexl3Injection {
JexlContext jc = new MapContext();
try {
- script.callable(jc).call();
+ script.callable(jc).call(); // $hasJexlInjection
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -49,30 +49,30 @@ public class Jexl3Injection {
private static void runJexlExpressionViaGetProperty(String jexlExpr) {
JexlEngine jexl = new JexlBuilder().create();
- jexl.getProperty(new Object(), jexlExpr);
+ jexl.getProperty(new Object(), jexlExpr); // $hasJexlInjection
}
private static void runJexlExpressionViaSetProperty(String jexlExpr) {
JexlEngine jexl = new JexlBuilder().create();
- jexl.setProperty(new Object(), jexlExpr, new Object());
+ jexl.setProperty(new Object(), jexlExpr, new Object()); // $hasJexlInjection
}
private static void runJexlExpressionViaJxltEngineExpressionEvaluate(String jexlExpr) {
JexlEngine jexl = new JexlBuilder().create();
JxltEngine jxlt = jexl.createJxltEngine();
- jxlt.createExpression(jexlExpr).evaluate(new MapContext());
+ jxlt.createExpression(jexlExpr).evaluate(new MapContext()); // $hasJexlInjection
}
private static void runJexlExpressionViaJxltEngineExpressionPrepare(String jexlExpr) {
JexlEngine jexl = new JexlBuilder().create();
JxltEngine jxlt = jexl.createJxltEngine();
- jxlt.createExpression(jexlExpr).prepare(new MapContext());
+ jxlt.createExpression(jexlExpr).prepare(new MapContext()); // $hasJexlInjection
}
private static void runJexlExpressionViaJxltEngineTemplateEvaluate(String jexlExpr) {
JexlEngine jexl = new JexlBuilder().create();
JxltEngine jxlt = jexl.createJxltEngine();
- jxlt.createTemplate(jexlExpr).evaluate(new MapContext(), new StringWriter());
+ jxlt.createTemplate(jexlExpr).evaluate(new MapContext(), new StringWriter()); // $hasJexlInjection
}
private static void runJexlExpressionViaCallable(String jexlExpr) {
@@ -81,7 +81,7 @@ public class Jexl3Injection {
JexlContext jc = new MapContext();
try {
- e.callable(jc).call();
+ e.callable(jc).call(); // $hasJexlInjection
} catch (Exception ex) {
throw new RuntimeException(ex);
}
@@ -141,16 +141,14 @@ public class Jexl3Injection {
}
@PostMapping("/request")
- public ResponseEntity testWithSpringControllerThatEvaluatesJexlFromPathVariable(
- @PathVariable String expr) {
+ public ResponseEntity testWithSpringControllerThatEvaluatesJexlFromPathVariable(@PathVariable String expr) {
runJexlExpression(expr);
return ResponseEntity.ok(HttpStatus.OK);
}
@PostMapping("/request")
- public ResponseEntity testWithSpringControllerThatEvaluatesJexlFromRequestBody(
- @RequestBody Data data) {
+ public ResponseEntity testWithSpringControllerThatEvaluatesJexlFromRequestBody(@RequestBody Data data) {
String expr = data.getExpr();
runJexlExpression(expr);
diff --git a/java/ql/test/query-tests/security/CWE-094/JexlInjection.expected b/java/ql/test/query-tests/security/CWE-094/JexlInjection.expected
deleted file mode 100644
index f42f083578e..00000000000
--- a/java/ql/test/query-tests/security/CWE-094/JexlInjection.expected
+++ /dev/null
@@ -1,199 +0,0 @@
-edges
-| Jexl2Injection.java:10:43:10:57 | jexlExpr : String | Jexl2Injection.java:14:9:14:9 | e |
-| Jexl2Injection.java:17:55:17:69 | jexlExpr : String | Jexl2Injection.java:22:9:22:9 | e |
-| Jexl2Injection.java:25:39:25:53 | jexlExpr : String | Jexl2Injection.java:29:9:29:14 | script |
-| Jexl2Injection.java:32:50:32:64 | jexlExpr : String | Jexl2Injection.java:38:13:38:18 | script |
-| Jexl2Injection.java:44:57:44:71 | jexlExpr : String | Jexl2Injection.java:46:40:46:47 | jexlExpr |
-| Jexl2Injection.java:49:57:49:71 | jexlExpr : String | Jexl2Injection.java:51:40:51:47 | jexlExpr |
-| Jexl2Injection.java:54:73:54:87 | jexlExpr : String | Jexl2Injection.java:57:9:57:35 | parse(...) |
-| Jexl2Injection.java:60:72:60:86 | jexlExpr : String | Jexl2Injection.java:63:9:63:35 | parse(...) |
-| Jexl2Injection.java:66:73:66:87 | jexlExpr : String | Jexl2Injection.java:69:9:69:44 | createTemplate(...) |
-| Jexl2Injection.java:76:25:76:47 | getInputStream(...) : InputStream | Jexl2Injection.java:76:54:76:58 | bytes [post update] : byte[] |
-| Jexl2Injection.java:76:54:76:58 | bytes [post update] : byte[] | Jexl2Injection.java:78:31:78:38 | jexlExpr : String |
-| Jexl2Injection.java:78:31:78:38 | jexlExpr : String | Jexl2Injection.java:86:24:86:56 | jexlExpr : String |
-| Jexl2Injection.java:78:31:78:38 | jexlExpr : String | Jexl2Injection.java:90:24:90:68 | jexlExpr : String |
-| Jexl2Injection.java:78:31:78:38 | jexlExpr : String | Jexl2Injection.java:94:24:94:52 | jexlExpr : String |
-| Jexl2Injection.java:78:31:78:38 | jexlExpr : String | Jexl2Injection.java:98:24:98:63 | jexlExpr : String |
-| Jexl2Injection.java:78:31:78:38 | jexlExpr : String | Jexl2Injection.java:102:24:102:70 | jexlExpr : String |
-| Jexl2Injection.java:78:31:78:38 | jexlExpr : String | Jexl2Injection.java:106:24:106:70 | jexlExpr : String |
-| Jexl2Injection.java:78:31:78:38 | jexlExpr : String | Jexl2Injection.java:110:24:110:86 | jexlExpr : String |
-| Jexl2Injection.java:78:31:78:38 | jexlExpr : String | Jexl2Injection.java:114:24:114:85 | jexlExpr : String |
-| Jexl2Injection.java:78:31:78:38 | jexlExpr : String | Jexl2Injection.java:118:24:118:86 | jexlExpr : String |
-| Jexl2Injection.java:86:24:86:56 | jexlExpr : String | Jexl2Injection.java:10:43:10:57 | jexlExpr : String |
-| Jexl2Injection.java:86:24:86:56 | jexlExpr : String | Jexl2Injection.java:86:24:86:56 | jexlExpr : String |
-| Jexl2Injection.java:90:24:90:68 | jexlExpr : String | Jexl2Injection.java:17:55:17:69 | jexlExpr : String |
-| Jexl2Injection.java:90:24:90:68 | jexlExpr : String | Jexl2Injection.java:90:24:90:68 | jexlExpr : String |
-| Jexl2Injection.java:94:24:94:52 | jexlExpr : String | Jexl2Injection.java:25:39:25:53 | jexlExpr : String |
-| Jexl2Injection.java:94:24:94:52 | jexlExpr : String | Jexl2Injection.java:94:24:94:52 | jexlExpr : String |
-| Jexl2Injection.java:98:24:98:63 | jexlExpr : String | Jexl2Injection.java:32:50:32:64 | jexlExpr : String |
-| Jexl2Injection.java:98:24:98:63 | jexlExpr : String | Jexl2Injection.java:98:24:98:63 | jexlExpr : String |
-| Jexl2Injection.java:102:24:102:70 | jexlExpr : String | Jexl2Injection.java:44:57:44:71 | jexlExpr : String |
-| Jexl2Injection.java:102:24:102:70 | jexlExpr : String | Jexl2Injection.java:102:24:102:70 | jexlExpr : String |
-| Jexl2Injection.java:106:24:106:70 | jexlExpr : String | Jexl2Injection.java:49:57:49:71 | jexlExpr : String |
-| Jexl2Injection.java:106:24:106:70 | jexlExpr : String | Jexl2Injection.java:106:24:106:70 | jexlExpr : String |
-| Jexl2Injection.java:110:24:110:86 | jexlExpr : String | Jexl2Injection.java:54:73:54:87 | jexlExpr : String |
-| Jexl2Injection.java:110:24:110:86 | jexlExpr : String | Jexl2Injection.java:110:24:110:86 | jexlExpr : String |
-| Jexl2Injection.java:114:24:114:85 | jexlExpr : String | Jexl2Injection.java:60:72:60:86 | jexlExpr : String |
-| Jexl2Injection.java:114:24:114:85 | jexlExpr : String | Jexl2Injection.java:114:24:114:85 | jexlExpr : String |
-| Jexl2Injection.java:118:24:118:86 | jexlExpr : String | Jexl2Injection.java:66:73:66:87 | jexlExpr : String |
-| Jexl2Injection.java:118:24:118:86 | jexlExpr : String | Jexl2Injection.java:118:24:118:86 | jexlExpr : String |
-| Jexl3Injection.java:17:43:17:57 | jexlExpr : String | Jexl3Injection.java:21:9:21:9 | e |
-| Jexl3Injection.java:24:55:24:69 | jexlExpr : String | Jexl3Injection.java:28:9:28:9 | e |
-| Jexl3Injection.java:31:39:31:53 | jexlExpr : String | Jexl3Injection.java:35:9:35:14 | script |
-| Jexl3Injection.java:38:50:38:64 | jexlExpr : String | Jexl3Injection.java:44:13:44:18 | script |
-| Jexl3Injection.java:50:57:50:71 | jexlExpr : String | Jexl3Injection.java:52:40:52:47 | jexlExpr |
-| Jexl3Injection.java:55:57:55:71 | jexlExpr : String | Jexl3Injection.java:57:40:57:47 | jexlExpr |
-| Jexl3Injection.java:60:74:60:88 | jexlExpr : String | Jexl3Injection.java:63:9:63:39 | createExpression(...) |
-| Jexl3Injection.java:66:73:66:87 | jexlExpr : String | Jexl3Injection.java:69:9:69:39 | createExpression(...) |
-| Jexl3Injection.java:72:72:72:86 | jexlExpr : String | Jexl3Injection.java:75:9:75:37 | createTemplate(...) |
-| Jexl3Injection.java:78:54:78:68 | jexlExpr : String | Jexl3Injection.java:84:13:84:13 | e |
-| Jexl3Injection.java:94:25:94:47 | getInputStream(...) : InputStream | Jexl3Injection.java:94:54:94:58 | bytes [post update] : byte[] |
-| Jexl3Injection.java:94:54:94:58 | bytes [post update] : byte[] | Jexl3Injection.java:96:31:96:38 | jexlExpr : String |
-| Jexl3Injection.java:96:31:96:38 | jexlExpr : String | Jexl3Injection.java:104:24:104:56 | jexlExpr : String |
-| Jexl3Injection.java:96:31:96:38 | jexlExpr : String | Jexl3Injection.java:108:24:108:68 | jexlExpr : String |
-| Jexl3Injection.java:96:31:96:38 | jexlExpr : String | Jexl3Injection.java:112:24:112:52 | jexlExpr : String |
-| Jexl3Injection.java:96:31:96:38 | jexlExpr : String | Jexl3Injection.java:116:24:116:63 | jexlExpr : String |
-| Jexl3Injection.java:96:31:96:38 | jexlExpr : String | Jexl3Injection.java:120:24:120:70 | jexlExpr : String |
-| Jexl3Injection.java:96:31:96:38 | jexlExpr : String | Jexl3Injection.java:124:24:124:70 | jexlExpr : String |
-| Jexl3Injection.java:96:31:96:38 | jexlExpr : String | Jexl3Injection.java:128:24:128:87 | jexlExpr : String |
-| Jexl3Injection.java:96:31:96:38 | jexlExpr : String | Jexl3Injection.java:132:24:132:86 | jexlExpr : String |
-| Jexl3Injection.java:96:31:96:38 | jexlExpr : String | Jexl3Injection.java:136:24:136:85 | jexlExpr : String |
-| Jexl3Injection.java:96:31:96:38 | jexlExpr : String | Jexl3Injection.java:140:24:140:67 | jexlExpr : String |
-| Jexl3Injection.java:104:24:104:56 | jexlExpr : String | Jexl3Injection.java:17:43:17:57 | jexlExpr : String |
-| Jexl3Injection.java:104:24:104:56 | jexlExpr : String | Jexl3Injection.java:104:24:104:56 | jexlExpr : String |
-| Jexl3Injection.java:108:24:108:68 | jexlExpr : String | Jexl3Injection.java:24:55:24:69 | jexlExpr : String |
-| Jexl3Injection.java:108:24:108:68 | jexlExpr : String | Jexl3Injection.java:108:24:108:68 | jexlExpr : String |
-| Jexl3Injection.java:112:24:112:52 | jexlExpr : String | Jexl3Injection.java:31:39:31:53 | jexlExpr : String |
-| Jexl3Injection.java:112:24:112:52 | jexlExpr : String | Jexl3Injection.java:112:24:112:52 | jexlExpr : String |
-| Jexl3Injection.java:116:24:116:63 | jexlExpr : String | Jexl3Injection.java:38:50:38:64 | jexlExpr : String |
-| Jexl3Injection.java:116:24:116:63 | jexlExpr : String | Jexl3Injection.java:116:24:116:63 | jexlExpr : String |
-| Jexl3Injection.java:120:24:120:70 | jexlExpr : String | Jexl3Injection.java:50:57:50:71 | jexlExpr : String |
-| Jexl3Injection.java:120:24:120:70 | jexlExpr : String | Jexl3Injection.java:120:24:120:70 | jexlExpr : String |
-| Jexl3Injection.java:124:24:124:70 | jexlExpr : String | Jexl3Injection.java:55:57:55:71 | jexlExpr : String |
-| Jexl3Injection.java:124:24:124:70 | jexlExpr : String | Jexl3Injection.java:124:24:124:70 | jexlExpr : String |
-| Jexl3Injection.java:128:24:128:87 | jexlExpr : String | Jexl3Injection.java:60:74:60:88 | jexlExpr : String |
-| Jexl3Injection.java:128:24:128:87 | jexlExpr : String | Jexl3Injection.java:128:24:128:87 | jexlExpr : String |
-| Jexl3Injection.java:132:24:132:86 | jexlExpr : String | Jexl3Injection.java:66:73:66:87 | jexlExpr : String |
-| Jexl3Injection.java:132:24:132:86 | jexlExpr : String | Jexl3Injection.java:132:24:132:86 | jexlExpr : String |
-| Jexl3Injection.java:136:24:136:85 | jexlExpr : String | Jexl3Injection.java:72:72:72:86 | jexlExpr : String |
-| Jexl3Injection.java:136:24:136:85 | jexlExpr : String | Jexl3Injection.java:136:24:136:85 | jexlExpr : String |
-| Jexl3Injection.java:140:24:140:67 | jexlExpr : String | Jexl3Injection.java:78:54:78:68 | jexlExpr : String |
-| Jexl3Injection.java:140:24:140:67 | jexlExpr : String | Jexl3Injection.java:140:24:140:67 | jexlExpr : String |
-| Jexl3Injection.java:145:13:145:37 | expr : String | Jexl3Injection.java:147:27:147:30 | expr : String |
-| Jexl3Injection.java:147:27:147:30 | expr : String | Jexl3Injection.java:17:43:17:57 | jexlExpr : String |
-| Jexl3Injection.java:153:13:153:34 | data : Data | Jexl3Injection.java:156:27:156:30 | expr : String |
-| Jexl3Injection.java:156:27:156:30 | expr : String | Jexl3Injection.java:17:43:17:57 | jexlExpr : String |
-| Jexl3Injection.java:163:13:163:52 | customRequest : CustomRequest | Jexl3Injection.java:166:27:166:30 | expr : String |
-| Jexl3Injection.java:166:27:166:30 | expr : String | Jexl3Injection.java:17:43:17:57 | jexlExpr : String |
-nodes
-| Jexl2Injection.java:10:43:10:57 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl2Injection.java:14:9:14:9 | e | semmle.label | e |
-| Jexl2Injection.java:17:55:17:69 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl2Injection.java:22:9:22:9 | e | semmle.label | e |
-| Jexl2Injection.java:25:39:25:53 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl2Injection.java:29:9:29:14 | script | semmle.label | script |
-| Jexl2Injection.java:32:50:32:64 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl2Injection.java:38:13:38:18 | script | semmle.label | script |
-| Jexl2Injection.java:44:57:44:71 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl2Injection.java:46:40:46:47 | jexlExpr | semmle.label | jexlExpr |
-| Jexl2Injection.java:49:57:49:71 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl2Injection.java:51:40:51:47 | jexlExpr | semmle.label | jexlExpr |
-| Jexl2Injection.java:54:73:54:87 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl2Injection.java:57:9:57:35 | parse(...) | semmle.label | parse(...) |
-| Jexl2Injection.java:60:72:60:86 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl2Injection.java:63:9:63:35 | parse(...) | semmle.label | parse(...) |
-| Jexl2Injection.java:66:73:66:87 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl2Injection.java:69:9:69:44 | createTemplate(...) | semmle.label | createTemplate(...) |
-| Jexl2Injection.java:76:25:76:47 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| Jexl2Injection.java:76:54:76:58 | bytes [post update] : byte[] | semmle.label | bytes [post update] : byte[] |
-| Jexl2Injection.java:78:31:78:38 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl2Injection.java:86:24:86:56 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl2Injection.java:86:24:86:56 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl2Injection.java:90:24:90:68 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl2Injection.java:90:24:90:68 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl2Injection.java:94:24:94:52 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl2Injection.java:94:24:94:52 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl2Injection.java:98:24:98:63 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl2Injection.java:98:24:98:63 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl2Injection.java:102:24:102:70 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl2Injection.java:102:24:102:70 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl2Injection.java:106:24:106:70 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl2Injection.java:106:24:106:70 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl2Injection.java:110:24:110:86 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl2Injection.java:110:24:110:86 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl2Injection.java:114:24:114:85 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl2Injection.java:114:24:114:85 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl2Injection.java:118:24:118:86 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl2Injection.java:118:24:118:86 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:17:43:17:57 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:21:9:21:9 | e | semmle.label | e |
-| Jexl3Injection.java:24:55:24:69 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:28:9:28:9 | e | semmle.label | e |
-| Jexl3Injection.java:31:39:31:53 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:35:9:35:14 | script | semmle.label | script |
-| Jexl3Injection.java:38:50:38:64 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:44:13:44:18 | script | semmle.label | script |
-| Jexl3Injection.java:50:57:50:71 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:52:40:52:47 | jexlExpr | semmle.label | jexlExpr |
-| Jexl3Injection.java:55:57:55:71 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:57:40:57:47 | jexlExpr | semmle.label | jexlExpr |
-| Jexl3Injection.java:60:74:60:88 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:63:9:63:39 | createExpression(...) | semmle.label | createExpression(...) |
-| Jexl3Injection.java:66:73:66:87 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:69:9:69:39 | createExpression(...) | semmle.label | createExpression(...) |
-| Jexl3Injection.java:72:72:72:86 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:75:9:75:37 | createTemplate(...) | semmle.label | createTemplate(...) |
-| Jexl3Injection.java:78:54:78:68 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:84:13:84:13 | e | semmle.label | e |
-| Jexl3Injection.java:94:25:94:47 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| Jexl3Injection.java:94:54:94:58 | bytes [post update] : byte[] | semmle.label | bytes [post update] : byte[] |
-| Jexl3Injection.java:96:31:96:38 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:104:24:104:56 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:104:24:104:56 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:108:24:108:68 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:108:24:108:68 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:112:24:112:52 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:112:24:112:52 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:116:24:116:63 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:116:24:116:63 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:120:24:120:70 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:120:24:120:70 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:124:24:124:70 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:124:24:124:70 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:128:24:128:87 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:128:24:128:87 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:132:24:132:86 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:132:24:132:86 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:136:24:136:85 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:136:24:136:85 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:140:24:140:67 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:140:24:140:67 | jexlExpr : String | semmle.label | jexlExpr : String |
-| Jexl3Injection.java:145:13:145:37 | expr : String | semmle.label | expr : String |
-| Jexl3Injection.java:147:27:147:30 | expr : String | semmle.label | expr : String |
-| Jexl3Injection.java:153:13:153:34 | data : Data | semmle.label | data : Data |
-| Jexl3Injection.java:156:27:156:30 | expr : String | semmle.label | expr : String |
-| Jexl3Injection.java:163:13:163:52 | customRequest : CustomRequest | semmle.label | customRequest : CustomRequest |
-| Jexl3Injection.java:166:27:166:30 | expr : String | semmle.label | expr : String |
-#select
-| Jexl2Injection.java:14:9:14:9 | e | Jexl2Injection.java:76:25:76:47 | getInputStream(...) : InputStream | Jexl2Injection.java:14:9:14:9 | e | JEXL injection from $@. | Jexl2Injection.java:76:25:76:47 | getInputStream(...) | this user input |
-| Jexl2Injection.java:22:9:22:9 | e | Jexl2Injection.java:76:25:76:47 | getInputStream(...) : InputStream | Jexl2Injection.java:22:9:22:9 | e | JEXL injection from $@. | Jexl2Injection.java:76:25:76:47 | getInputStream(...) | this user input |
-| Jexl2Injection.java:29:9:29:14 | script | Jexl2Injection.java:76:25:76:47 | getInputStream(...) : InputStream | Jexl2Injection.java:29:9:29:14 | script | JEXL injection from $@. | Jexl2Injection.java:76:25:76:47 | getInputStream(...) | this user input |
-| Jexl2Injection.java:38:13:38:18 | script | Jexl2Injection.java:76:25:76:47 | getInputStream(...) : InputStream | Jexl2Injection.java:38:13:38:18 | script | JEXL injection from $@. | Jexl2Injection.java:76:25:76:47 | getInputStream(...) | this user input |
-| Jexl2Injection.java:46:40:46:47 | jexlExpr | Jexl2Injection.java:76:25:76:47 | getInputStream(...) : InputStream | Jexl2Injection.java:46:40:46:47 | jexlExpr | JEXL injection from $@. | Jexl2Injection.java:76:25:76:47 | getInputStream(...) | this user input |
-| Jexl2Injection.java:51:40:51:47 | jexlExpr | Jexl2Injection.java:76:25:76:47 | getInputStream(...) : InputStream | Jexl2Injection.java:51:40:51:47 | jexlExpr | JEXL injection from $@. | Jexl2Injection.java:76:25:76:47 | getInputStream(...) | this user input |
-| Jexl2Injection.java:57:9:57:35 | parse(...) | Jexl2Injection.java:76:25:76:47 | getInputStream(...) : InputStream | Jexl2Injection.java:57:9:57:35 | parse(...) | JEXL injection from $@. | Jexl2Injection.java:76:25:76:47 | getInputStream(...) | this user input |
-| Jexl2Injection.java:63:9:63:35 | parse(...) | Jexl2Injection.java:76:25:76:47 | getInputStream(...) : InputStream | Jexl2Injection.java:63:9:63:35 | parse(...) | JEXL injection from $@. | Jexl2Injection.java:76:25:76:47 | getInputStream(...) | this user input |
-| Jexl2Injection.java:69:9:69:44 | createTemplate(...) | Jexl2Injection.java:76:25:76:47 | getInputStream(...) : InputStream | Jexl2Injection.java:69:9:69:44 | createTemplate(...) | JEXL injection from $@. | Jexl2Injection.java:76:25:76:47 | getInputStream(...) | this user input |
-| Jexl3Injection.java:21:9:21:9 | e | Jexl3Injection.java:94:25:94:47 | getInputStream(...) : InputStream | Jexl3Injection.java:21:9:21:9 | e | JEXL injection from $@. | Jexl3Injection.java:94:25:94:47 | getInputStream(...) | this user input |
-| Jexl3Injection.java:21:9:21:9 | e | Jexl3Injection.java:145:13:145:37 | expr : String | Jexl3Injection.java:21:9:21:9 | e | JEXL injection from $@. | Jexl3Injection.java:145:13:145:37 | expr | this user input |
-| Jexl3Injection.java:21:9:21:9 | e | Jexl3Injection.java:153:13:153:34 | data : Data | Jexl3Injection.java:21:9:21:9 | e | JEXL injection from $@. | Jexl3Injection.java:153:13:153:34 | data | this user input |
-| Jexl3Injection.java:21:9:21:9 | e | Jexl3Injection.java:163:13:163:52 | customRequest : CustomRequest | Jexl3Injection.java:21:9:21:9 | e | JEXL injection from $@. | Jexl3Injection.java:163:13:163:52 | customRequest | this user input |
-| Jexl3Injection.java:28:9:28:9 | e | Jexl3Injection.java:94:25:94:47 | getInputStream(...) : InputStream | Jexl3Injection.java:28:9:28:9 | e | JEXL injection from $@. | Jexl3Injection.java:94:25:94:47 | getInputStream(...) | this user input |
-| Jexl3Injection.java:35:9:35:14 | script | Jexl3Injection.java:94:25:94:47 | getInputStream(...) : InputStream | Jexl3Injection.java:35:9:35:14 | script | JEXL injection from $@. | Jexl3Injection.java:94:25:94:47 | getInputStream(...) | this user input |
-| Jexl3Injection.java:44:13:44:18 | script | Jexl3Injection.java:94:25:94:47 | getInputStream(...) : InputStream | Jexl3Injection.java:44:13:44:18 | script | JEXL injection from $@. | Jexl3Injection.java:94:25:94:47 | getInputStream(...) | this user input |
-| Jexl3Injection.java:52:40:52:47 | jexlExpr | Jexl3Injection.java:94:25:94:47 | getInputStream(...) : InputStream | Jexl3Injection.java:52:40:52:47 | jexlExpr | JEXL injection from $@. | Jexl3Injection.java:94:25:94:47 | getInputStream(...) | this user input |
-| Jexl3Injection.java:57:40:57:47 | jexlExpr | Jexl3Injection.java:94:25:94:47 | getInputStream(...) : InputStream | Jexl3Injection.java:57:40:57:47 | jexlExpr | JEXL injection from $@. | Jexl3Injection.java:94:25:94:47 | getInputStream(...) | this user input |
-| Jexl3Injection.java:63:9:63:39 | createExpression(...) | Jexl3Injection.java:94:25:94:47 | getInputStream(...) : InputStream | Jexl3Injection.java:63:9:63:39 | createExpression(...) | JEXL injection from $@. | Jexl3Injection.java:94:25:94:47 | getInputStream(...) | this user input |
-| Jexl3Injection.java:69:9:69:39 | createExpression(...) | Jexl3Injection.java:94:25:94:47 | getInputStream(...) : InputStream | Jexl3Injection.java:69:9:69:39 | createExpression(...) | JEXL injection from $@. | Jexl3Injection.java:94:25:94:47 | getInputStream(...) | this user input |
-| Jexl3Injection.java:75:9:75:37 | createTemplate(...) | Jexl3Injection.java:94:25:94:47 | getInputStream(...) : InputStream | Jexl3Injection.java:75:9:75:37 | createTemplate(...) | JEXL injection from $@. | Jexl3Injection.java:94:25:94:47 | getInputStream(...) | this user input |
-| Jexl3Injection.java:84:13:84:13 | e | Jexl3Injection.java:94:25:94:47 | getInputStream(...) : InputStream | Jexl3Injection.java:84:13:84:13 | e | JEXL injection from $@. | Jexl3Injection.java:94:25:94:47 | getInputStream(...) | this user input |
diff --git a/java/ql/test/query-tests/security/CWE-094/JexlInjectionTest.expected b/java/ql/test/query-tests/security/CWE-094/JexlInjectionTest.expected
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/java/ql/test/query-tests/security/CWE-094/JexlInjectionTest.ql b/java/ql/test/query-tests/security/CWE-094/JexlInjectionTest.ql
new file mode 100644
index 00000000000..e7c713213b0
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-094/JexlInjectionTest.ql
@@ -0,0 +1,33 @@
+import java
+import semmle.code.java.dataflow.DataFlow
+import semmle.code.java.dataflow.FlowSteps
+import semmle.code.java.dataflow.FlowSources
+import semmle.code.java.security.JexlInjection
+import TestUtilities.InlineExpectationsTest
+
+class Conf extends TaintTracking::Configuration {
+ Conf() { this = "qltest:cwe:jexl-injection" }
+
+ override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
+
+ override predicate isSink(DataFlow::Node sink) { sink instanceof JexlEvaluationSink }
+
+ override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
+ any(JexlInjectionAdditionalTaintStep c).step(node1, node2)
+ }
+}
+
+class JexlInjectionTest extends InlineExpectationsTest {
+ JexlInjectionTest() { this = "HasJexlInjectionTest" }
+
+ override string getARelevantTag() { result = "hasJexlInjection" }
+
+ override predicate hasActualResult(Location location, string element, string tag, string value) {
+ tag = "hasJexlInjection" and
+ exists(DataFlow::Node src, DataFlow::Node sink, Conf conf | conf.hasFlow(src, sink) |
+ sink.getLocation() = location and
+ element = sink.toString() and
+ value = ""
+ )
+ }
+}
diff --git a/java/ql/test/query-tests/security/CWE-094/SandboxedJexl2.java b/java/ql/test/query-tests/security/CWE-094/SandboxedJexl2.java
index dd9b113ca0b..cbc955389b3 100644
--- a/java/ql/test/query-tests/security/CWE-094/SandboxedJexl2.java
+++ b/java/ql/test/query-tests/security/CWE-094/SandboxedJexl2.java
@@ -6,7 +6,7 @@ import java.net.Socket;
import java.util.function.Consumer;
public class SandboxedJexl2 {
-
+
private static void runJexlExpressionWithSandbox(String jexlExpr) {
Sandbox sandbox = new Sandbox();
sandbox.white(SandboxedJexl2.class.getCanonicalName());
@@ -14,7 +14,7 @@ public class SandboxedJexl2 {
JexlEngine jexl = new JexlEngine(uberspect, null, null, null);
Expression e = jexl.createExpression(jexlExpr);
JexlContext jc = new MapContext();
- e.evaluate(jc);
+ e.evaluate(jc); // Safe
}
private static void runJexlExpressionViaSandboxedUnifiedJexl(String jexlExpr) {
@@ -23,7 +23,7 @@ public class SandboxedJexl2 {
Uberspect uberspect = new SandboxUberspectImpl(null, sandbox);
JexlEngine jexl = new JexlEngine(uberspect, null, null, null);
UnifiedJEXL unifiedJEXL = new UnifiedJEXL(jexl);
- unifiedJEXL.parse(jexlExpr).evaluate(new MapContext());
+ unifiedJEXL.parse(jexlExpr).evaluate(new MapContext()); // Safe
}
private static void simpleServer(Consumer action) throws Exception {
From 745a6f6fb4a8c66daf615354f1f6de6c08f7a902 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Mon, 3 May 2021 17:43:33 +0200
Subject: [PATCH 013/272] Getters called on parameters propagate taint
---
java/ql/src/Security/CWE/CWE-094/JexlInjection.ql | 2 +-
.../semmle/code/java/dataflow/internal/TaintTrackingUtil.qll | 3 +++
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/java/ql/src/Security/CWE/CWE-094/JexlInjection.ql b/java/ql/src/Security/CWE/CWE-094/JexlInjection.ql
index 80810378dce..c97de906aec 100644
--- a/java/ql/src/Security/CWE/CWE-094/JexlInjection.ql
+++ b/java/ql/src/Security/CWE/CWE-094/JexlInjection.ql
@@ -28,7 +28,7 @@ class JexlInjectionConfig extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) { sink instanceof JexlEvaluationSink }
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
- any(JexlInjectionAdditionalTaintStep c).step(node1, node2)
+ any(JexlInjectionAdditionalTaintStep c).step(node1, node2)
}
}
diff --git a/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll b/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll
index f9278ab815e..00d5b2bb093 100644
--- a/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll
+++ b/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll
@@ -14,6 +14,7 @@ private import semmle.code.java.dataflow.ExternalFlow
private import semmle.code.java.dataflow.internal.DataFlowPrivate
import semmle.code.java.dataflow.FlowSteps
private import FlowSummaryImpl as FlowSummaryImpl
+private import semmle.code.java.frameworks.JaxWS
/**
* Holds if taint can flow from `src` to `sink` in zero or more
@@ -263,6 +264,8 @@ private predicate taintPreservingQualifierToMethod(Method m) {
)
or
m.(TaintPreservingCallable).returnsTaintFrom(-1)
+ or
+ exists(JaxRsResourceMethod resourceMethod | m.(GetterMethod).getDeclaringType() = resourceMethod.getAParameter().getType())
}
private class StringReplaceMethod extends TaintPreservingCallable {
From e68c6e66a5cb7fe70e61badb82206abeb3840007 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Mon, 3 May 2021 17:53:37 +0200
Subject: [PATCH 014/272] Remove qlref file
---
java/ql/test/query-tests/security/CWE-094/JexlInjection.qlref | 1 -
1 file changed, 1 deletion(-)
delete mode 100644 java/ql/test/query-tests/security/CWE-094/JexlInjection.qlref
diff --git a/java/ql/test/query-tests/security/CWE-094/JexlInjection.qlref b/java/ql/test/query-tests/security/CWE-094/JexlInjection.qlref
deleted file mode 100644
index 63f170df617..00000000000
--- a/java/ql/test/query-tests/security/CWE-094/JexlInjection.qlref
+++ /dev/null
@@ -1 +0,0 @@
-Security/CWE/CWE-094/JexlInjection.ql
\ No newline at end of file
From 6b79ca6403b7b562b9a81389580f2394bfc706bd Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Tue, 4 May 2021 09:32:03 +0200
Subject: [PATCH 015/272] Fix warning
---
java/ql/src/semmle/code/java/security/JexlInjection.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/ql/src/semmle/code/java/security/JexlInjection.qll b/java/ql/src/semmle/code/java/security/JexlInjection.qll
index 811d9877112..448c1f3cdb5 100644
--- a/java/ql/src/semmle/code/java/security/JexlInjection.qll
+++ b/java/ql/src/semmle/code/java/security/JexlInjection.qll
@@ -58,7 +58,7 @@ private class DefaultJexlInjectionSinkModel extends SinkModelCsv {
*
* Extend this class to add additional taint steps that should apply to the `JexlInjectionFlowConfig`.
*/
-abstract class JexlInjectionAdditionalTaintStep extends Unit {
+class JexlInjectionAdditionalTaintStep extends Unit {
/**
* Holds if the step from `node1` to `node2` should be considered a taint
* step for the `JexlInjectionConfig` configuration.
From f79d2e06f90cadb82da04424f9456f5e6fb20a9a Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Tue, 4 May 2021 11:29:09 +0200
Subject: [PATCH 016/272] Fix failing checks
---
java/change-notes/2021-05-04-jexl-injection-query.md | 2 ++
.../CWE/CWE-094/SaferJexlExpressionEvaluationWithSandbox.java | 0
.../SaferJexlExpressionEvaluationWithUberspectSandbox.java | 0
.../Security/CWE/CWE-094/UnsafeJexlExpressionEvaluation.java | 0
java/ql/src/semmle/code/java/security/JexlInjection.qll | 2 ++
5 files changed, 4 insertions(+)
create mode 100644 java/change-notes/2021-05-04-jexl-injection-query.md
rename java/ql/src/{experimental => }/Security/CWE/CWE-094/SaferJexlExpressionEvaluationWithSandbox.java (100%)
rename java/ql/src/{experimental => }/Security/CWE/CWE-094/SaferJexlExpressionEvaluationWithUberspectSandbox.java (100%)
rename java/ql/src/{experimental => }/Security/CWE/CWE-094/UnsafeJexlExpressionEvaluation.java (100%)
diff --git a/java/change-notes/2021-05-04-jexl-injection-query.md b/java/change-notes/2021-05-04-jexl-injection-query.md
new file mode 100644
index 00000000000..4dad3c4a8f9
--- /dev/null
+++ b/java/change-notes/2021-05-04-jexl-injection-query.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* The query "Expression language injection (JEXL)" (`java/jexl-expression-injection`) has been promoted from experimental to the main query pack. Its results will now appear by default. This query was originally [submitted as an experimental query by @artem-smotrakov](https://github.com/github/codeql/pull/4965)
\ No newline at end of file
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/SaferJexlExpressionEvaluationWithSandbox.java b/java/ql/src/Security/CWE/CWE-094/SaferJexlExpressionEvaluationWithSandbox.java
similarity index 100%
rename from java/ql/src/experimental/Security/CWE/CWE-094/SaferJexlExpressionEvaluationWithSandbox.java
rename to java/ql/src/Security/CWE/CWE-094/SaferJexlExpressionEvaluationWithSandbox.java
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/SaferJexlExpressionEvaluationWithUberspectSandbox.java b/java/ql/src/Security/CWE/CWE-094/SaferJexlExpressionEvaluationWithUberspectSandbox.java
similarity index 100%
rename from java/ql/src/experimental/Security/CWE/CWE-094/SaferJexlExpressionEvaluationWithUberspectSandbox.java
rename to java/ql/src/Security/CWE/CWE-094/SaferJexlExpressionEvaluationWithUberspectSandbox.java
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/UnsafeJexlExpressionEvaluation.java b/java/ql/src/Security/CWE/CWE-094/UnsafeJexlExpressionEvaluation.java
similarity index 100%
rename from java/ql/src/experimental/Security/CWE/CWE-094/UnsafeJexlExpressionEvaluation.java
rename to java/ql/src/Security/CWE/CWE-094/UnsafeJexlExpressionEvaluation.java
diff --git a/java/ql/src/semmle/code/java/security/JexlInjection.qll b/java/ql/src/semmle/code/java/security/JexlInjection.qll
index 448c1f3cdb5..d36fa0aad9b 100644
--- a/java/ql/src/semmle/code/java/security/JexlInjection.qll
+++ b/java/ql/src/semmle/code/java/security/JexlInjection.qll
@@ -1,3 +1,5 @@
+/** Provides classes to reason about Expression Langauge (JEXL) injection vulnerabilities. */
+
import java
import semmle.code.java.dataflow.TaintTracking
private import semmle.code.java.dataflow.ExternalFlow
From 6e94dc5b85e6a0f245d06d956339a7842a6ba829 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Tue, 4 May 2021 13:15:20 +0200
Subject: [PATCH 017/272] Autoformatting
---
.../semmle/code/java/dataflow/internal/TaintTrackingUtil.qll | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll b/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll
index 00d5b2bb093..262a3babc0b 100644
--- a/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll
+++ b/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll
@@ -265,7 +265,9 @@ private predicate taintPreservingQualifierToMethod(Method m) {
or
m.(TaintPreservingCallable).returnsTaintFrom(-1)
or
- exists(JaxRsResourceMethod resourceMethod | m.(GetterMethod).getDeclaringType() = resourceMethod.getAParameter().getType())
+ exists(JaxRsResourceMethod resourceMethod |
+ m.(GetterMethod).getDeclaringType() = resourceMethod.getAParameter().getType()
+ )
}
private class StringReplaceMethod extends TaintPreservingCallable {
From 3ceb8bbcc6b5b43deae31a1c64331e86555eb601 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 5 May 2021 10:52:57 +0200
Subject: [PATCH 018/272] Python: Add cryptography test for EC
Apparently, passing in the class (without instantiating it) is allowed
---
python/ql/test/library-tests/frameworks/cryptography/test_ec.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/python/ql/test/library-tests/frameworks/cryptography/test_ec.py b/python/ql/test/library-tests/frameworks/cryptography/test_ec.py
index 0372d7e9dbf..96d60adb2fa 100644
--- a/python/ql/test/library-tests/frameworks/cryptography/test_ec.py
+++ b/python/ql/test/library-tests/frameworks/cryptography/test_ec.py
@@ -6,6 +6,7 @@ from cryptography.exceptions import InvalidSignature
private_key = ec.generate_private_key(curve=ec.SECP384R1()) # $ PublicKeyGeneration keySize=384
+private_key = ec.generate_private_key(curve=ec.SECP384R1) # $ MISSING: PublicKeyGeneration keySize=384
public_key = private_key.public_key()
HASH_ALGORITHM = hashes.SHA256()
From 668bfd3a4163118fbffef379e613274e1d057f05 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 5 May 2021 12:29:55 +0200
Subject: [PATCH 019/272] Python: Support EC keygen without class-instance for
cryptography
I also added a new test to show off how what the origin ends up looking
like... I think it looks ok
---
.../semmle/python/frameworks/Cryptography.qll | 26 ++++++++++++++++---
.../cryptography/EcKeygenOrigin.expected | 5 ++++
.../frameworks/cryptography/EcKeygenOrigin.ql | 8 ++++++
.../cryptography/ec_keygen_origin.py | 20 ++++++++++++++
.../frameworks/cryptography/test_ec.py | 2 +-
5 files changed, 56 insertions(+), 5 deletions(-)
create mode 100644 python/ql/test/library-tests/frameworks/cryptography/EcKeygenOrigin.expected
create mode 100644 python/ql/test/library-tests/frameworks/cryptography/EcKeygenOrigin.ql
create mode 100644 python/ql/test/library-tests/frameworks/cryptography/ec_keygen_origin.py
diff --git a/python/ql/src/semmle/python/frameworks/Cryptography.qll b/python/ql/src/semmle/python/frameworks/Cryptography.qll
index 266d41897d3..fe9177227bd 100644
--- a/python/ql/src/semmle/python/frameworks/Cryptography.qll
+++ b/python/ql/src/semmle/python/frameworks/Cryptography.qll
@@ -22,7 +22,7 @@ private module CryptographyModel {
* Gets a predefined curve class from
* `cryptography.hazmat.primitives.asymmetric.ec` with a specific key size (in bits).
*/
- private DataFlow::Node curveClassWithKeySize(int keySize) {
+ private API::Node predefinedCurveClass(int keySize) {
exists(string curveName |
result =
API::moduleImport("cryptography")
@@ -31,7 +31,6 @@ private module CryptographyModel {
.getMember("asymmetric")
.getMember("ec")
.getMember(curveName)
- .getAUse()
|
// obtained by manually looking at source code in
// https://github.com/pyca/cryptography/blob/cba69f1922803f4f29a3fde01741890d88b8e217/src/cryptography/hazmat/primitives/asymmetric/ec.py#L208-L300
@@ -75,13 +74,30 @@ private module CryptographyModel {
)
}
+ /** Gets a reference to a predefined curve class with a specific key size (in bits), as well as the origin of the class. */
+ private DataFlow::LocalSourceNode curveClassWithKeySize(
+ DataFlow::TypeTracker t, int keySize, DataFlow::Node origin
+ ) {
+ t.start() and
+ result = predefinedCurveClass(keySize).getAnImmediateUse() and
+ origin = result
+ or
+ exists(DataFlow::TypeTracker t2 |
+ result = curveClassWithKeySize(t2, keySize, origin).track(t2, t)
+ )
+ }
+
+ /** Gets a reference to a predefined curve class with a specific key size (in bits), as well as the origin of the class. */
+ DataFlow::Node curveClassWithKeySize(int keySize, DataFlow::Node origin) {
+ curveClassWithKeySize(DataFlow::TypeTracker::end(), keySize, origin).flowsTo(result)
+ }
+
/** Gets a reference to a predefined curve class instance with a specific key size (in bits), as well as the origin of the class. */
private DataFlow::LocalSourceNode curveClassInstanceWithKeySize(
DataFlow::TypeTracker t, int keySize, DataFlow::Node origin
) {
t.start() and
- result.(DataFlow::CallCfgNode).getFunction() = curveClassWithKeySize(keySize) and
- origin = result
+ result.(DataFlow::CallCfgNode).getFunction() = curveClassWithKeySize(keySize, origin)
or
exists(DataFlow::TypeTracker t2 |
result = curveClassInstanceWithKeySize(t2, keySize, origin).track(t2, t)
@@ -164,6 +180,8 @@ private module CryptographyModel {
override int getKeySizeWithOrigin(DataFlow::Node origin) {
this.getCurveArg() = Ecc::curveClassInstanceWithKeySize(result, origin)
+ or
+ this.getCurveArg() = Ecc::curveClassWithKeySize(result, origin)
}
// Note: There is not really a key-size argument, since it's always specified by the curve.
diff --git a/python/ql/test/library-tests/frameworks/cryptography/EcKeygenOrigin.expected b/python/ql/test/library-tests/frameworks/cryptography/EcKeygenOrigin.expected
new file mode 100644
index 00000000000..a94092533f0
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/cryptography/EcKeygenOrigin.expected
@@ -0,0 +1,5 @@
+| ec_keygen_origin.py:8:1:8:45 | ControlFlowNode for Attribute() | 384 | ec_keygen_origin.py:8:31:8:42 | ControlFlowNode for Attribute |
+| ec_keygen_origin.py:9:1:9:43 | ControlFlowNode for Attribute() | 384 | ec_keygen_origin.py:9:31:9:42 | ControlFlowNode for Attribute |
+| ec_keygen_origin.py:12:1:12:36 | ControlFlowNode for Attribute() | 384 | ec_keygen_origin.py:11:9:11:20 | ControlFlowNode for Attribute |
+| ec_keygen_origin.py:15:1:15:39 | ControlFlowNode for Attribute() | 384 | ec_keygen_origin.py:11:9:11:20 | ControlFlowNode for Attribute |
+| ec_keygen_origin.py:20:1:20:32 | ControlFlowNode for Attribute() | 384 | ec_keygen_origin.py:6:58:6:66 | ControlFlowNode for ImportMember |
diff --git a/python/ql/test/library-tests/frameworks/cryptography/EcKeygenOrigin.ql b/python/ql/test/library-tests/frameworks/cryptography/EcKeygenOrigin.ql
new file mode 100644
index 00000000000..4234177d50d
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/cryptography/EcKeygenOrigin.ql
@@ -0,0 +1,8 @@
+import semmle.python.dataflow.new.DataFlow
+import semmle.python.Concepts
+
+from Cryptography::PublicKey::KeyGeneration keyGen, int keySize, DataFlow::Node origin
+where
+ keyGen.getLocation().getFile().getShortName() = "ec_keygen_origin.py" and
+ keySize = keyGen.getKeySizeWithOrigin(origin)
+select keyGen, keySize, origin
diff --git a/python/ql/test/library-tests/frameworks/cryptography/ec_keygen_origin.py b/python/ql/test/library-tests/frameworks/cryptography/ec_keygen_origin.py
new file mode 100644
index 00000000000..c1469adb2ac
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/cryptography/ec_keygen_origin.py
@@ -0,0 +1,20 @@
+# Since key-size is not specified explicitly as an integer for the predefined
+# classes in the `cryptography.hazmat.primitives.asymmetric.ec` module, we need
+# special handling of the origin... this test is simply to show off how we handle this.
+
+from cryptography.hazmat.primitives.asymmetric import ec
+from cryptography.hazmat.primitives.asymmetric.ec import SECP384R1
+
+ec.generate_private_key(curve=ec.SECP384R1()) # $ PublicKeyGeneration keySize=384
+ec.generate_private_key(curve=ec.SECP384R1) # $ PublicKeyGeneration keySize=384
+
+alias = ec.SECP384R1
+ec.generate_private_key(curve=alias) # $ PublicKeyGeneration keySize=384
+
+instance = alias()
+ec.generate_private_key(curve=instance) # $ PublicKeyGeneration keySize=384
+
+
+x = SECP384R1
+y = x
+ec.generate_private_key(curve=y) # $ PublicKeyGeneration keySize=384
diff --git a/python/ql/test/library-tests/frameworks/cryptography/test_ec.py b/python/ql/test/library-tests/frameworks/cryptography/test_ec.py
index 96d60adb2fa..1e1e284dc33 100644
--- a/python/ql/test/library-tests/frameworks/cryptography/test_ec.py
+++ b/python/ql/test/library-tests/frameworks/cryptography/test_ec.py
@@ -6,7 +6,7 @@ from cryptography.exceptions import InvalidSignature
private_key = ec.generate_private_key(curve=ec.SECP384R1()) # $ PublicKeyGeneration keySize=384
-private_key = ec.generate_private_key(curve=ec.SECP384R1) # $ MISSING: PublicKeyGeneration keySize=384
+private_key = ec.generate_private_key(curve=ec.SECP384R1) # $ PublicKeyGeneration keySize=384
public_key = private_key.public_key()
HASH_ALGORITHM = hashes.SHA256()
From d50f22504e9517ee1ac959ab756ee739476479d9 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 5 May 2021 14:07:15 +0200
Subject: [PATCH 020/272] Python: Fix .expected
---
.../test/query-tests/Security/CWE-326/WeakCryptoKey.expected | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/python/ql/test/query-tests/Security/CWE-326/WeakCryptoKey.expected b/python/ql/test/query-tests/Security/CWE-326/WeakCryptoKey.expected
index 05d759d6f70..dc50ddd7873 100644
--- a/python/ql/test/query-tests/Security/CWE-326/WeakCryptoKey.expected
+++ b/python/ql/test/query-tests/Security/CWE-326/WeakCryptoKey.expected
@@ -1,8 +1,8 @@
| weak_crypto.py:68:1:68:21 | ControlFlowNode for dsa_gen_key() | Creation of an DSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:16:12:16:15 | ControlFlowNode for IntegerLiteral | 1024 |
-| weak_crypto.py:69:1:69:19 | ControlFlowNode for ec_gen_key() | Creation of an ECC key uses $@ bits, which is below 224 and considered breakable. | weak_crypto.py:22:11:22:24 | ControlFlowNode for Attribute() | 163 |
+| weak_crypto.py:69:1:69:19 | ControlFlowNode for ec_gen_key() | Creation of an ECC key uses $@ bits, which is below 224 and considered breakable. | weak_crypto.py:22:11:22:22 | ControlFlowNode for Attribute | 163 |
| weak_crypto.py:70:1:70:28 | ControlFlowNode for rsa_gen_key() | Creation of an RSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:12:12:12:15 | ControlFlowNode for IntegerLiteral | 1024 |
| weak_crypto.py:72:1:72:30 | ControlFlowNode for dsa_gen_key() | Creation of an DSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:16:12:16:15 | ControlFlowNode for IntegerLiteral | 1024 |
-| weak_crypto.py:73:1:73:25 | ControlFlowNode for ec_gen_key() | Creation of an ECC key uses $@ bits, which is below 224 and considered breakable. | weak_crypto.py:22:11:22:24 | ControlFlowNode for Attribute() | 163 |
+| weak_crypto.py:73:1:73:25 | ControlFlowNode for ec_gen_key() | Creation of an ECC key uses $@ bits, which is below 224 and considered breakable. | weak_crypto.py:22:11:22:22 | ControlFlowNode for Attribute | 163 |
| weak_crypto.py:74:1:74:37 | ControlFlowNode for rsa_gen_key() | Creation of an RSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:12:12:12:15 | ControlFlowNode for IntegerLiteral | 1024 |
| weak_crypto.py:76:1:76:22 | ControlFlowNode for Attribute() | Creation of an DSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:16:12:16:15 | ControlFlowNode for IntegerLiteral | 1024 |
| weak_crypto.py:77:1:77:22 | ControlFlowNode for Attribute() | Creation of an RSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:12:12:12:15 | ControlFlowNode for IntegerLiteral | 1024 |
From b37b15cea40514fcf811621a094ecbec1f922c57 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Fri, 7 May 2021 12:33:51 +0200
Subject: [PATCH 021/272] Re-structure imports, add some new comments to tests
---
java/ql/src/Security/CWE/CWE-094/JexlInjection.ql | 2 +-
java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll | 1 +
.../query-tests/security/CWE-094/SandboxedJexl3.java | 10 +++++-----
3 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-094/JexlInjection.ql b/java/ql/src/Security/CWE/CWE-094/JexlInjection.ql
index c97de906aec..c9827d3a123 100644
--- a/java/ql/src/Security/CWE/CWE-094/JexlInjection.ql
+++ b/java/ql/src/Security/CWE/CWE-094/JexlInjection.ql
@@ -11,9 +11,9 @@
*/
import java
-import DataFlow::PathGraph
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.security.JexlInjection
+import DataFlow::PathGraph
/**
* A taint-tracking configuration for unsafe user input
diff --git a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll
index 7073c57ff9c..2463777f620 100644
--- a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll
+++ b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll
@@ -80,6 +80,7 @@ private module Frameworks {
private import semmle.code.java.security.ResponseSplitting
private import semmle.code.java.security.XSS
private import semmle.code.java.security.LdapInjection
+ private import semmle.code.java.security.JexlInjection
}
private predicate sourceModelCsv(string row) {
diff --git a/java/ql/test/query-tests/security/CWE-094/SandboxedJexl3.java b/java/ql/test/query-tests/security/CWE-094/SandboxedJexl3.java
index 4c20ac0c901..1950564ecb1 100644
--- a/java/ql/test/query-tests/security/CWE-094/SandboxedJexl3.java
+++ b/java/ql/test/query-tests/security/CWE-094/SandboxedJexl3.java
@@ -15,17 +15,17 @@ public class SandboxedJexl3 {
JexlSandbox sandbox = new JexlSandbox(false);
sandbox.white(SandboxedJexl3.class.getCanonicalName());
JexlEngine jexl = new JexlBuilder().sandbox(sandbox).create();
- JexlExpression e = jexl.createExpression(jexlExpr);
+ JexlExpression e = jexl.createExpression(jexlExpr); // Safe
JexlContext jc = new MapContext();
- e.evaluate(jc);
+ e.evaluate(jc); // Safe
}
private static void runJexlExpressionWithUberspectSandbox(String jexlExpr) {
JexlUberspect sandbox = new JexlUberspectSandbox();
JexlEngine jexl = new JexlBuilder().uberspect(sandbox).create();
- JexlExpression e = jexl.createExpression(jexlExpr);
+ JexlExpression e = jexl.createExpression(jexlExpr); // Safe
JexlContext jc = new MapContext();
- e.evaluate(jc);
+ e.evaluate(jc); // Safe
}
private static JexlBuilder STATIC_JEXL_BUILDER;
@@ -39,7 +39,7 @@ public class SandboxedJexl3 {
private static void runJexlExpressionViaJxltEngineWithSandbox(String jexlExpr) {
JexlEngine jexl = STATIC_JEXL_BUILDER.create();
JxltEngine jxlt = jexl.createJxltEngine();
- jxlt.createExpression(jexlExpr).evaluate(new MapContext());
+ jxlt.createExpression(jexlExpr).evaluate(new MapContext()); // Safe
}
private static class JexlUberspectSandbox implements JexlUberspect {
From 2e098f050e07a5647001e98694b83a13470b74e7 Mon Sep 17 00:00:00 2001
From: Marcono1234
Date: Mon, 10 May 2021 18:33:07 +0200
Subject: [PATCH 022/272] Java: Ignore char array based closeables for
CloseReader.ql and CloseWriter.ql
---
java/ql/src/Likely Bugs/Resource Leaks/CloseReader.qhelp | 2 +-
java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql | 7 ++-----
java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.qhelp | 2 +-
java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql | 8 ++------
4 files changed, 6 insertions(+), 13 deletions(-)
diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.qhelp b/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.qhelp
index b0ded8e53a1..e5574bfc179 100644
--- a/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.qhelp
+++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.qhelp
@@ -14,7 +14,7 @@ but not closed may cause a resource leak.
Ensure that the resource is always closed to avoid a resource leak. Note that, because of exceptions,
it is safest to close a resource in a finally block. (However, this is unnecessary for
-subclasses of StringReader and ByteArrayInputStream.)
+subclasses of CharArrayReader, StringReader and ByteArrayInputStream.)
For Java 7 or later, the recommended way to close resources that implement java.lang.AutoCloseable
diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql b/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql
index 9d62237bd71..5993bd1de2b 100644
--- a/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql
+++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql
@@ -17,17 +17,14 @@ import CloseType
predicate readerType(RefType t) {
exists(RefType sup | sup = t.getASupertype*() |
- sup.hasName("Reader") or
- sup.hasName("InputStream") or
+ sup.hasName(["Reader", "InputStream"]) or
sup.hasQualifiedName("java.util.zip", "ZipFile")
)
}
predicate safeReaderType(RefType t) {
exists(RefType sup | sup = t.getASupertype*() |
- sup.hasName("StringReader") or
- sup.hasName("ByteArrayInputStream") or
- sup.hasName("StringInputStream")
+ sup.hasName(["CharArrayReader", "StringReader", "ByteArrayInputStream"])
)
}
diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.qhelp b/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.qhelp
index 0b348a3f9b8..84a50f914f7 100644
--- a/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.qhelp
+++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.qhelp
@@ -14,7 +14,7 @@ but not properly closed later may cause a resource leak.
Ensure that the resource is always closed to avoid a resource leak. Note that, because of exceptions,
it is safest to close a resource properly in a finally block. (However, this is unnecessary for
-subclasses of StringWriter and ByteArrayOutputStream.)
+subclasses of CharArrayWriter, StringWriter and ByteArrayOutputStream.)
For Java 7 or later, the recommended way to close resources that implement java.lang.AutoCloseable
is to declare them within a try-with-resources statement, so that they are closed implicitly.
diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql b/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql
index 113f3bd3267..824951f86f5 100644
--- a/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql
+++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql
@@ -16,16 +16,12 @@
import CloseType
predicate writerType(RefType t) {
- exists(RefType sup | sup = t.getASupertype*() |
- sup.hasName("Writer") or
- sup.hasName("OutputStream")
- )
+ exists(RefType sup | sup = t.getASupertype*() | sup.hasName(["Writer", "OutputStream"]))
}
predicate safeWriterType(RefType t) {
exists(RefType sup | sup = t.getASupertype*() |
- sup.hasName("StringWriter") or
- sup.hasName("ByteArrayOutputStream")
+ sup.hasName(["CharArrayWriter", "StringWriter", "ByteArrayOutputStream"])
)
}
From 8969da7775e88116959fee9ff79c5d3396233d79 Mon Sep 17 00:00:00 2001
From: Marcono1234
Date: Tue, 11 May 2021 19:21:40 +0200
Subject: [PATCH 023/272] Java: Improve not closing resource query; add tests
---
.../Likely Bugs/Resource Leaks/CloseReader.ql | 4 +-
.../Likely Bugs/Resource Leaks/CloseWriter.ql | 6 +-
.../CloseReader/CloseReader.expected | 6 +-
.../CloseReader/CloseReader.java | 89 ++++++++----
.../CloseReader/CloseReader.qlref | 2 +-
.../CloseWriter/CloseWriter.expected | 3 +
.../CloseWriter/CloseWriter.java | 131 ++++++++++++++++++
.../CloseWriter/CloseWriter.qlref | 1 +
8 files changed, 205 insertions(+), 37 deletions(-)
create mode 100644 java/ql/test/query-tests/CloseResource/CloseWriter/CloseWriter.expected
create mode 100644 java/ql/test/query-tests/CloseResource/CloseWriter/CloseWriter.java
create mode 100644 java/ql/test/query-tests/CloseResource/CloseWriter/CloseWriter.qlref
diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql b/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql
index 5993bd1de2b..73a9c81f343 100644
--- a/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql
+++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql
@@ -17,14 +17,14 @@ import CloseType
predicate readerType(RefType t) {
exists(RefType sup | sup = t.getASupertype*() |
- sup.hasName(["Reader", "InputStream"]) or
+ sup.hasQualifiedName("java.io", ["Reader", "InputStream"]) or
sup.hasQualifiedName("java.util.zip", "ZipFile")
)
}
predicate safeReaderType(RefType t) {
exists(RefType sup | sup = t.getASupertype*() |
- sup.hasName(["CharArrayReader", "StringReader", "ByteArrayInputStream"])
+ sup.hasQualifiedName("java.io", ["CharArrayReader", "StringReader", "ByteArrayInputStream"])
)
}
diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql b/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql
index 824951f86f5..a0714fe3a2f 100644
--- a/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql
+++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql
@@ -16,12 +16,14 @@
import CloseType
predicate writerType(RefType t) {
- exists(RefType sup | sup = t.getASupertype*() | sup.hasName(["Writer", "OutputStream"]))
+ exists(RefType sup | sup = t.getASupertype*() |
+ sup.hasQualifiedName("java.io", ["Writer", "OutputStream"])
+ )
}
predicate safeWriterType(RefType t) {
exists(RefType sup | sup = t.getASupertype*() |
- sup.hasName(["CharArrayWriter", "StringWriter", "ByteArrayOutputStream"])
+ sup.hasQualifiedName("java.io", ["CharArrayWriter", "StringWriter", "ByteArrayOutputStream"])
)
}
diff --git a/java/ql/test/query-tests/CloseResource/CloseReader/CloseReader.expected b/java/ql/test/query-tests/CloseResource/CloseReader/CloseReader.expected
index 12d09e0d65a..e76a4c6d1e4 100644
--- a/java/ql/test/query-tests/CloseResource/CloseReader/CloseReader.expected
+++ b/java/ql/test/query-tests/CloseResource/CloseReader/CloseReader.expected
@@ -1,2 +1,4 @@
-| CloseReader.java:11:42:11:71 | new FileReader(...) | This FileReader is not always closed on method exit. |
-| CloseReader.java:44:6:44:40 | new FileInputStream(...) | This FileInputStream is not always closed on method exit. |
+| CloseReader.java:18:42:18:71 | new FileReader(...) | This FileReader is not always closed on method exit. |
+| CloseReader.java:23:20:23:50 | new FileInputStream(...) | This FileInputStream is not always closed on method exit. |
+| CloseReader.java:33:6:33:40 | new FileInputStream(...) | This FileInputStream is not always closed on method exit. |
+| CloseReader.java:43:21:43:43 | new ZipFile(...) | This ZipFile is not always closed on method exit. |
diff --git a/java/ql/test/query-tests/CloseResource/CloseReader/CloseReader.java b/java/ql/test/query-tests/CloseResource/CloseReader/CloseReader.java
index 7d78f8dbfef..b77afc49105 100644
--- a/java/ql/test/query-tests/CloseResource/CloseReader/CloseReader.java
+++ b/java/ql/test/query-tests/CloseResource/CloseReader/CloseReader.java
@@ -1,41 +1,30 @@
import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.CharArrayReader;
+import java.io.Closeable;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
-import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.zip.ZipFile;
class CloseReader {
- public static void test1() throws IOException {
+ void test1() throws IOException {
BufferedReader br = new BufferedReader(new FileReader("C:\\test.txt"));
System.out.println(br.readLine());
}
- public static void test2() throws FileNotFoundException, IOException {
- BufferedReader br = null;
- try {
- br = new BufferedReader(new FileReader("C:\\test.txt"));
- System.out.println(br.readLine());
- }
- finally {
- if(br != null)
- br.close(); // 'br' is closed
- }
+ void test2() throws IOException {
+ InputStream in = new FileInputStream("file.bin");
+ in.read();
}
- public static void test3() throws IOException {
- BufferedReader br = null;
- try {
- br = new BufferedReader(new FileReader("C:\\test.txt"));
- System.out.println(br.readLine());
- }
- finally {
- cleanup(br); // 'br' is closed within a helper method
- }
- }
-
- public static void test4() throws IOException {
+ void test3() throws IOException {
InputStreamReader reader = null;
try {
// InputStreamReader may throw an exception, in which case the ...
@@ -50,7 +39,35 @@ class CloseReader {
}
}
- public static void test5() throws IOException {
+ void test4() throws IOException {
+ ZipFile zipFile = new ZipFile("file.zip");
+ System.out.println(zipFile.getComment());
+ }
+
+ void testCorrect1() throws IOException {
+ BufferedReader br = null;
+ try {
+ br = new BufferedReader(new FileReader("C:\\test.txt"));
+ System.out.println(br.readLine());
+ }
+ finally {
+ if(br != null)
+ br.close(); // 'br' is closed
+ }
+ }
+
+ void testCorrect2() throws IOException {
+ BufferedReader br = null;
+ try {
+ br = new BufferedReader(new FileReader("C:\\test.txt"));
+ System.out.println(br.readLine());
+ }
+ finally {
+ cleanup(br); // 'br' is closed within a helper method
+ }
+ }
+
+ void testCorrect3() throws IOException {
FileInputStream fis = null;
InputStreamReader reader = null;
try {
@@ -66,7 +83,7 @@ class CloseReader {
}
}
- public static void test6() throws IOException {
+ void testCorrect4() throws IOException {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("C:\\test.txt"));
@@ -77,15 +94,15 @@ class CloseReader {
}
}
- public static void cleanup(java.io.Closeable... closeables) throws IOException {
- for (java.io.Closeable c : closeables) {
+ void cleanup(Closeable... closeables) throws IOException {
+ for (Closeable c : closeables) {
if (c != null) {
c.close();
}
}
}
- public static class LogFile {
+ static class LogFile {
private BufferedReader fileRd;
LogFile(String path) {
FileReader fr = null;
@@ -100,9 +117,21 @@ class CloseReader {
private void init(InputStreamReader reader) {
fileRd = new BufferedReader(reader);
}
- public void readStuff() throws java.io.IOException {
+ public void readStuff() throws IOException {
System.out.println(fileRd.readLine());
fileRd.close();
}
}
+
+ // Classes which should be ignored
+ void testIgnore() throws IOException {
+ Reader r1 = new CharArrayReader(new char[] {'a'});
+ r1.read();
+
+ Reader r2 = new StringReader("a");
+ r2.read();
+
+ InputStream i1 = new ByteArrayInputStream(new byte[] {1});
+ i1.read();
+ }
}
diff --git a/java/ql/test/query-tests/CloseResource/CloseReader/CloseReader.qlref b/java/ql/test/query-tests/CloseResource/CloseReader/CloseReader.qlref
index 3c76645e809..1c808bb9f46 100644
--- a/java/ql/test/query-tests/CloseResource/CloseReader/CloseReader.qlref
+++ b/java/ql/test/query-tests/CloseResource/CloseReader/CloseReader.qlref
@@ -1 +1 @@
-Likely Bugs/Resource Leaks/CloseReader.ql
\ No newline at end of file
+Likely Bugs/Resource Leaks/CloseReader.ql
diff --git a/java/ql/test/query-tests/CloseResource/CloseWriter/CloseWriter.expected b/java/ql/test/query-tests/CloseResource/CloseWriter/CloseWriter.expected
new file mode 100644
index 00000000000..efbfe15f2c9
--- /dev/null
+++ b/java/ql/test/query-tests/CloseResource/CloseWriter/CloseWriter.expected
@@ -0,0 +1,3 @@
+| CloseWriter.java:17:42:17:71 | new FileWriter(...) | This FileWriter is not always closed on method exit. |
+| CloseWriter.java:22:22:22:53 | new FileOutputStream(...) | This FileOutputStream is not always closed on method exit. |
+| CloseWriter.java:32:6:32:41 | new FileOutputStream(...) | This FileOutputStream is not always closed on method exit. |
diff --git a/java/ql/test/query-tests/CloseResource/CloseWriter/CloseWriter.java b/java/ql/test/query-tests/CloseResource/CloseWriter/CloseWriter.java
new file mode 100644
index 00000000000..3733237b8de
--- /dev/null
+++ b/java/ql/test/query-tests/CloseResource/CloseWriter/CloseWriter.java
@@ -0,0 +1,131 @@
+import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
+import java.io.CharArrayWriter;
+import java.io.Closeable;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.zip.ZipFile;
+
+class CloseWriter {
+
+ void test1() throws IOException {
+ BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\test.txt"));
+ bw.write("test");
+ }
+
+ void test2() throws IOException {
+ OutputStream out = new FileOutputStream("test.bin");
+ out.write(1);
+ }
+
+ void test3() throws IOException {
+ OutputStreamWriter writer = null;
+ try {
+ // OutputStreamWriter may throw an exception, in which case the ...
+ writer = new OutputStreamWriter(
+ // ... FileOutputStream is not closed by the finally block
+ new FileOutputStream("C:\\test.txt"), "UTF-8");
+ writer.write("test");
+ }
+ finally {
+ if (writer != null)
+ writer.close();
+ }
+ }
+
+ void testCorrect1() throws IOException {
+ BufferedWriter bw = null;
+ try {
+ bw = new BufferedWriter(new FileWriter("C:\\test.txt"));
+ bw.write("test");
+ }
+ finally {
+ if(bw != null)
+ bw.close(); // 'bw' is closed
+ }
+ }
+
+ void testCorrect2() throws IOException {
+ BufferedWriter bw = null;
+ try {
+ bw = new BufferedWriter(new FileWriter("C:\\test.txt"));
+ bw.write("test");
+ }
+ finally {
+ cleanup(bw); // 'bw' is closed within a helper method
+ }
+ }
+
+ void testCorrect3() throws IOException {
+ FileOutputStream fos = null;
+ OutputStreamWriter writer = null;
+ try {
+ fos = new FileOutputStream("C:\\test.txt");
+ writer = new OutputStreamWriter(fos);
+ writer.write("test");
+ }
+ finally {
+ if (fos != null)
+ fos.close(); // 'fos' is closed
+ if (writer != null)
+ writer.close(); // 'writer' is closed
+ }
+ }
+
+ void testCorrect4() throws IOException {
+ BufferedWriter bw = null;
+ try {
+ bw = new BufferedWriter(new FileWriter("C:\\test.txt"));
+ bw.write("test");
+ }
+ finally {
+ cleanup(null, bw); // 'bw' is closed within a varargs helper method, invoked with multiple args
+ }
+ }
+
+ void cleanup(Closeable... closeables) throws IOException {
+ for (Closeable c : closeables) {
+ if (c != null) {
+ c.close();
+ }
+ }
+ }
+
+ static class LogFile {
+ private BufferedWriter fileWr;
+ LogFile(String path) {
+ FileWriter fw = null;
+ try {
+ fw = new FileWriter(path);
+ } catch (IOException e) {
+ System.out.println("Error: File not readable: " + path);
+ System.exit(1);
+ }
+ init(fw);
+ }
+ private void init(OutputStreamWriter writer) {
+ fileWr = new BufferedWriter(writer);
+ }
+ public void writeStuff() throws IOException {
+ fileWr.write("test");
+ fileWr.close();
+ }
+ }
+
+ // Classes which should be ignored
+ void testIgnore() throws IOException {
+ Writer w1 = new CharArrayWriter();
+ w1.write("test");
+
+ Writer w2 = new StringWriter();
+ w2.write("test");
+
+ OutputStream o1 = new ByteArrayOutputStream();
+ o1.write(1);
+ }
+}
diff --git a/java/ql/test/query-tests/CloseResource/CloseWriter/CloseWriter.qlref b/java/ql/test/query-tests/CloseResource/CloseWriter/CloseWriter.qlref
new file mode 100644
index 00000000000..88008367363
--- /dev/null
+++ b/java/ql/test/query-tests/CloseResource/CloseWriter/CloseWriter.qlref
@@ -0,0 +1 @@
+Likely Bugs/Resource Leaks/CloseWriter.ql
From 33641c84f6e83964b59509c44ef3078ee6d4acb3 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Fri, 14 May 2021 11:58:27 +0200
Subject: [PATCH 024/272] recognize sanitizing string replace call for
regexp-injection
---
.../RegExpInjectionCustomizations.qll | 23 +++++++++++++++++
.../Security/CWE-730/RegExpInjection.expected | 24 ++++++++++++++++++
.../Security/CWE-730/RegExpInjection.js | 25 +++++++++++++++++++
3 files changed, 72 insertions(+)
diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/RegExpInjectionCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/RegExpInjectionCustomizations.qll
index df791146b21..af0488a7641 100644
--- a/javascript/ql/src/semmle/javascript/security/dataflow/RegExpInjectionCustomizations.qll
+++ b/javascript/ql/src/semmle/javascript/security/dataflow/RegExpInjectionCustomizations.qll
@@ -55,4 +55,27 @@ module RegExpInjection {
)
}
}
+
+ /**
+ * A global regexp replacement involving the `{`, `[`, or `+` meta-character, viewed as a sanitizer for
+ * regexp-injection vulnerabilities.
+ */
+ class MetacharEscapeSanitizer extends Sanitizer, StringReplaceCall {
+ MetacharEscapeSanitizer() {
+ isGlobal() and
+ (
+ RegExp::alwaysMatchesMetaCharacter(getRegExp().getRoot(), ["{", "[", "+"])
+ or
+ // or it's like a wild-card.
+ RegExp::isWildcardLike(getRegExp().getRoot())
+ )
+ }
+ }
+
+ /**
+ * Meta characters used in the above sanitizer.
+ */
+ private class RegexpMetaChars extends RegExp::MetaCharacter {
+ RegexpMetaChars() { this = ["{", "[", "+"] }
+ }
}
diff --git a/javascript/ql/test/query-tests/Security/CWE-730/RegExpInjection.expected b/javascript/ql/test/query-tests/Security/CWE-730/RegExpInjection.expected
index 29bad1be0fb..1e0d857fbfb 100644
--- a/javascript/ql/test/query-tests/Security/CWE-730/RegExpInjection.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-730/RegExpInjection.expected
@@ -44,6 +44,18 @@ nodes
| RegExpInjection.js:54:14:54:42 | key.spl ... x => x) |
| RegExpInjection.js:54:14:54:52 | key.spl ... in("-") |
| RegExpInjection.js:54:14:54:52 | key.spl ... in("-") |
+| RegExpInjection.js:60:31:60:56 | input |
+| RegExpInjection.js:60:39:60:56 | req.param("input") |
+| RegExpInjection.js:60:39:60:56 | req.param("input") |
+| RegExpInjection.js:64:14:64:18 | input |
+| RegExpInjection.js:64:14:64:18 | input |
+| RegExpInjection.js:82:7:82:32 | input |
+| RegExpInjection.js:82:15:82:32 | req.param("input") |
+| RegExpInjection.js:82:15:82:32 | req.param("input") |
+| RegExpInjection.js:87:14:87:55 | "^.*\\.( ... + ")$" |
+| RegExpInjection.js:87:14:87:55 | "^.*\\.( ... + ")$" |
+| RegExpInjection.js:87:25:87:29 | input |
+| RegExpInjection.js:87:25:87:48 | input.r ... g, "\|") |
| tst.js:1:46:1:46 | e |
| tst.js:1:46:1:46 | e |
| tst.js:2:9:2:21 | data |
@@ -99,6 +111,16 @@ edges
| RegExpInjection.js:54:14:54:27 | key.split(".") | RegExpInjection.js:54:14:54:42 | key.spl ... x => x) |
| RegExpInjection.js:54:14:54:42 | key.spl ... x => x) | RegExpInjection.js:54:14:54:52 | key.spl ... in("-") |
| RegExpInjection.js:54:14:54:42 | key.spl ... x => x) | RegExpInjection.js:54:14:54:52 | key.spl ... in("-") |
+| RegExpInjection.js:60:31:60:56 | input | RegExpInjection.js:64:14:64:18 | input |
+| RegExpInjection.js:60:31:60:56 | input | RegExpInjection.js:64:14:64:18 | input |
+| RegExpInjection.js:60:39:60:56 | req.param("input") | RegExpInjection.js:60:31:60:56 | input |
+| RegExpInjection.js:60:39:60:56 | req.param("input") | RegExpInjection.js:60:31:60:56 | input |
+| RegExpInjection.js:82:7:82:32 | input | RegExpInjection.js:87:25:87:29 | input |
+| RegExpInjection.js:82:15:82:32 | req.param("input") | RegExpInjection.js:82:7:82:32 | input |
+| RegExpInjection.js:82:15:82:32 | req.param("input") | RegExpInjection.js:82:7:82:32 | input |
+| RegExpInjection.js:87:25:87:29 | input | RegExpInjection.js:87:25:87:48 | input.r ... g, "\|") |
+| RegExpInjection.js:87:25:87:48 | input.r ... g, "\|") | RegExpInjection.js:87:14:87:55 | "^.*\\.( ... + ")$" |
+| RegExpInjection.js:87:25:87:48 | input.r ... g, "\|") | RegExpInjection.js:87:14:87:55 | "^.*\\.( ... + ")$" |
| tst.js:1:46:1:46 | e | tst.js:2:16:2:16 | e |
| tst.js:1:46:1:46 | e | tst.js:2:16:2:16 | e |
| tst.js:2:9:2:21 | data | tst.js:3:21:3:24 | data |
@@ -122,4 +144,6 @@ edges
| RegExpInjection.js:47:22:47:26 | input | RegExpInjection.js:5:39:5:56 | req.param("input") | RegExpInjection.js:47:22:47:26 | input | This regular expression is constructed from a $@. | RegExpInjection.js:5:39:5:56 | req.param("input") | user-provided value |
| RegExpInjection.js:50:46:50:50 | input | RegExpInjection.js:5:39:5:56 | req.param("input") | RegExpInjection.js:50:46:50:50 | input | This regular expression is constructed from a $@. | RegExpInjection.js:5:39:5:56 | req.param("input") | user-provided value |
| RegExpInjection.js:54:14:54:52 | key.spl ... in("-") | RegExpInjection.js:5:13:5:28 | req.param("key") | RegExpInjection.js:54:14:54:52 | key.spl ... in("-") | This regular expression is constructed from a $@. | RegExpInjection.js:5:13:5:28 | req.param("key") | user-provided value |
+| RegExpInjection.js:64:14:64:18 | input | RegExpInjection.js:60:39:60:56 | req.param("input") | RegExpInjection.js:64:14:64:18 | input | This regular expression is constructed from a $@. | RegExpInjection.js:60:39:60:56 | req.param("input") | user-provided value |
+| RegExpInjection.js:87:14:87:55 | "^.*\\.( ... + ")$" | RegExpInjection.js:82:15:82:32 | req.param("input") | RegExpInjection.js:87:14:87:55 | "^.*\\.( ... + ")$" | This regular expression is constructed from a $@. | RegExpInjection.js:82:15:82:32 | req.param("input") | user-provided value |
| tst.js:3:16:3:35 | "^"+ data.name + "$" | tst.js:1:46:1:46 | e | tst.js:3:16:3:35 | "^"+ data.name + "$" | This regular expression is constructed from a $@. | tst.js:1:46:1:46 | e | user-provided value |
diff --git a/javascript/ql/test/query-tests/Security/CWE-730/RegExpInjection.js b/javascript/ql/test/query-tests/Security/CWE-730/RegExpInjection.js
index dc1910b1ae5..e22ec9c6a17 100644
--- a/javascript/ql/test/query-tests/Security/CWE-730/RegExpInjection.js
+++ b/javascript/ql/test/query-tests/Security/CWE-730/RegExpInjection.js
@@ -60,4 +60,29 @@ app.get('/findKey', function(req, res) {
var key = req.param("key"), input = req.param("input");
Search.search(input); // OK!
+
+ new RegExp(input); // NOT OK
+
+ var sanitized = input.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
+ new RegExp(sanitized); // OK
+});
+
+function escape1(pattern) {
+ return pattern.replace(/[\x00-\x7f]/g,
+ function(s) { return '\\x' + ('00' + s.charCodeAt().toString(16)).substr(-2); });
+}
+
+function escape2(str){
+ return str.replace(/([\.$?*|{}\(\)\[\]\\\/\+\-^])/g, function(ch){
+ return "\\" + ch;
+});
+};
+
+app.get('/has-sanitizer', function(req, res) {
+ var input = req.param("input");
+
+ new RegExp(escape1(input)); // OK
+ new RegExp(escape2(input)); // OK
+
+ new RegExp("^.*\.(" + input.replace(/,/g, "|") + ")$"); // NOT OK
});
From 3766678d60b7b47d5fb9a699155988f6c9ddf0e5 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Fri, 14 May 2021 13:23:36 +0200
Subject: [PATCH 025/272] move `RegexpMetaChars` into Regexp.qll
---
javascript/ql/src/semmle/javascript/Regexp.qll | 14 ++++++++++++--
.../dataflow/RegExpInjectionCustomizations.qll | 7 -------
2 files changed, 12 insertions(+), 9 deletions(-)
diff --git a/javascript/ql/src/semmle/javascript/Regexp.qll b/javascript/ql/src/semmle/javascript/Regexp.qll
index d3b7e9cac7e..c0f01fa130a 100644
--- a/javascript/ql/src/semmle/javascript/Regexp.qll
+++ b/javascript/ql/src/semmle/javascript/Regexp.qll
@@ -1237,8 +1237,18 @@ module RegExp {
}
}
- private class DefaultMetaCharacter extends MetaCharacter {
- DefaultMetaCharacter() { this = ["<", "'", "\""] }
+ /**
+ * A meta character used by HTML.
+ */
+ private class HTMLMetaCharacter extends MetaCharacter {
+ HTMLMetaCharacter() { this = ["<", "'", "\""] }
+ }
+
+ /**
+ * A meta character used by regular expressions.
+ */
+ private class RegexpMetaChars extends RegExp::MetaCharacter {
+ RegexpMetaChars() { this = ["{", "[", "+"] }
}
/**
diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/RegExpInjectionCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/RegExpInjectionCustomizations.qll
index af0488a7641..e4555bb5ca8 100644
--- a/javascript/ql/src/semmle/javascript/security/dataflow/RegExpInjectionCustomizations.qll
+++ b/javascript/ql/src/semmle/javascript/security/dataflow/RegExpInjectionCustomizations.qll
@@ -71,11 +71,4 @@ module RegExpInjection {
)
}
}
-
- /**
- * Meta characters used in the above sanitizer.
- */
- private class RegexpMetaChars extends RegExp::MetaCharacter {
- RegexpMetaChars() { this = ["{", "[", "+"] }
- }
}
From 73c7e15580bc9591842dcfc4fa872def88a74ecc Mon Sep 17 00:00:00 2001
From: Marcono1234
Date: Fri, 14 May 2021 22:25:00 +0200
Subject: [PATCH 026/272] Java: Add back StringInputStream to CloseReader.ql
---
java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql | 3 +++
1 file changed, 3 insertions(+)
diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql b/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql
index 73a9c81f343..e93c59b0879 100644
--- a/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql
+++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql
@@ -25,6 +25,9 @@ predicate readerType(RefType t) {
predicate safeReaderType(RefType t) {
exists(RefType sup | sup = t.getASupertype*() |
sup.hasQualifiedName("java.io", ["CharArrayReader", "StringReader", "ByteArrayInputStream"])
+ or
+ // Note: It is unclear which specific class this is supposed to match
+ sup.hasName("StringInputStream")
)
}
From e205e4bbcee12e121669e661f288d4fc6806e89c Mon Sep 17 00:00:00 2001
From: Marcono1234
Date: Fri, 14 May 2021 22:31:14 +0200
Subject: [PATCH 027/272] Java: Add change note for close resource query
changes
---
.../2021-05-14-close-resource-leaks-improvements.md | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 java/change-notes/2021-05-14-close-resource-leaks-improvements.md
diff --git a/java/change-notes/2021-05-14-close-resource-leaks-improvements.md b/java/change-notes/2021-05-14-close-resource-leaks-improvements.md
new file mode 100644
index 00000000000..bae8640d85a
--- /dev/null
+++ b/java/change-notes/2021-05-14-close-resource-leaks-improvements.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* The "Potential input resource leak" (`java/input-resource-leak`) and "Potential output resource leak" (`java/output-resource-leak`) queries have been made more specific to only consider JDK classes and subtypes. Additionally the number of false positives has been reduced.
From ef410b998431c1467d10341e9581bf639f6f6b80 Mon Sep 17 00:00:00 2001
From: Chris Smowton
Date: Mon, 17 May 2021 19:27:10 +0100
Subject: [PATCH 028/272] Update
java/change-notes/2021-05-14-close-resource-leaks-improvements.md
---
.../2021-05-14-close-resource-leaks-improvements.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/change-notes/2021-05-14-close-resource-leaks-improvements.md b/java/change-notes/2021-05-14-close-resource-leaks-improvements.md
index bae8640d85a..08a77840a1f 100644
--- a/java/change-notes/2021-05-14-close-resource-leaks-improvements.md
+++ b/java/change-notes/2021-05-14-close-resource-leaks-improvements.md
@@ -1,2 +1,2 @@
lgtm,codescanning
-* The "Potential input resource leak" (`java/input-resource-leak`) and "Potential output resource leak" (`java/output-resource-leak`) queries have been made more specific to only consider JDK classes and subtypes. Additionally the number of false positives has been reduced.
+* The "Potential input resource leak" (`java/input-resource-leak`) and "Potential output resource leak" (`java/output-resource-leak`) queries no longer confuse `java.io` classes such as `Reader` with others that happen to share the same base name. Additionally the number of false positives has been reduced by recognizing `CharArrayReader` and `CharArrayWriter` as types that don't need to be closed.
From 904eacf9a21ef22be6cb7dfe3f208d2162648fbd Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 19 May 2021 11:10:06 +0200
Subject: [PATCH 029/272] Python: Use absolute import for PEP249
---
python/ql/src/semmle/python/frameworks/MySQLdb.qll | 2 +-
python/ql/src/semmle/python/frameworks/MysqlConnectorPython.qll | 2 +-
python/ql/src/semmle/python/frameworks/Psycopg2.qll | 2 +-
python/ql/src/semmle/python/frameworks/PyMySQL.qll | 2 +-
python/ql/src/semmle/python/frameworks/Stdlib.qll | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/python/ql/src/semmle/python/frameworks/MySQLdb.qll b/python/ql/src/semmle/python/frameworks/MySQLdb.qll
index b36bf87c7d9..4f9c799d640 100644
--- a/python/ql/src/semmle/python/frameworks/MySQLdb.qll
+++ b/python/ql/src/semmle/python/frameworks/MySQLdb.qll
@@ -10,7 +10,7 @@ private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
-private import PEP249
+private import semmle.python.frameworks.PEP249
/**
* Provides models for the `MySQLdb` PyPI package.
diff --git a/python/ql/src/semmle/python/frameworks/MysqlConnectorPython.qll b/python/ql/src/semmle/python/frameworks/MysqlConnectorPython.qll
index 97cd4fd162c..989fb668746 100644
--- a/python/ql/src/semmle/python/frameworks/MysqlConnectorPython.qll
+++ b/python/ql/src/semmle/python/frameworks/MysqlConnectorPython.qll
@@ -10,7 +10,7 @@ private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
-private import PEP249
+private import semmle.python.frameworks.PEP249
/**
* Provides models for the `mysql-connector-python` package.
diff --git a/python/ql/src/semmle/python/frameworks/Psycopg2.qll b/python/ql/src/semmle/python/frameworks/Psycopg2.qll
index 1cc20026801..d966776b082 100644
--- a/python/ql/src/semmle/python/frameworks/Psycopg2.qll
+++ b/python/ql/src/semmle/python/frameworks/Psycopg2.qll
@@ -10,7 +10,7 @@ private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
-private import PEP249
+private import semmle.python.frameworks.PEP249
/**
* Provides models for the `psycopg2` PyPI package.
diff --git a/python/ql/src/semmle/python/frameworks/PyMySQL.qll b/python/ql/src/semmle/python/frameworks/PyMySQL.qll
index c078302b73f..452b87ed030 100644
--- a/python/ql/src/semmle/python/frameworks/PyMySQL.qll
+++ b/python/ql/src/semmle/python/frameworks/PyMySQL.qll
@@ -8,7 +8,7 @@ private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
-private import PEP249
+private import semmle.python.frameworks.PEP249
/**
* Provides models for the `PyMySQL` PyPI package.
diff --git a/python/ql/src/semmle/python/frameworks/Stdlib.qll b/python/ql/src/semmle/python/frameworks/Stdlib.qll
index 07753e9fde5..7acbbcae31b 100644
--- a/python/ql/src/semmle/python/frameworks/Stdlib.qll
+++ b/python/ql/src/semmle/python/frameworks/Stdlib.qll
@@ -9,7 +9,7 @@ private import semmle.python.dataflow.new.TaintTracking
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
-private import PEP249
+private import semmle.python.frameworks.PEP249
/** Provides models for the Python standard library. */
private module Stdlib {
From f66dccafda939f94740e9aaf1a5d44ca625bb647 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Tue, 11 May 2021 13:49:56 +0200
Subject: [PATCH 030/272] Python: Rename prettyExp => prettyExpr
So we're consistenly using `expr` and not leaving our the `r`.
---
.../experimental/dataflow/TestUtil/PrintNode.qll | 12 ++++++------
.../dataflow/tainttracking/TestTaintLib.qll | 2 +-
python/ql/test/experimental/meta/InlineTaintTest.qll | 4 ++--
3 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/python/ql/test/experimental/dataflow/TestUtil/PrintNode.qll b/python/ql/test/experimental/dataflow/TestUtil/PrintNode.qll
index 84f97d3f6ff..46b1bfd9814 100644
--- a/python/ql/test/experimental/dataflow/TestUtil/PrintNode.qll
+++ b/python/ql/test/experimental/dataflow/TestUtil/PrintNode.qll
@@ -1,7 +1,7 @@
import python
import semmle.python.dataflow.new.DataFlow
-string prettyExp(Expr e) {
+string prettyExpr(Expr e) {
not e instanceof Num and
not e instanceof StrConst and
not e instanceof Subscript and
@@ -15,17 +15,17 @@ string prettyExp(Expr e) {
e.(StrConst).getPrefix() + e.(StrConst).getText() +
e.(StrConst).getPrefix().regexpReplaceAll("[a-zA-Z]+", "")
or
- result = prettyExp(e.(Subscript).getObject()) + "[" + prettyExp(e.(Subscript).getIndex()) + "]"
+ result = prettyExpr(e.(Subscript).getObject()) + "[" + prettyExpr(e.(Subscript).getIndex()) + "]"
or
(
if exists(e.(Call).getAnArg()) or exists(e.(Call).getANamedArg())
- then result = prettyExp(e.(Call).getFunc()) + "(..)"
- else result = prettyExp(e.(Call).getFunc()) + "()"
+ then result = prettyExpr(e.(Call).getFunc()) + "(..)"
+ else result = prettyExpr(e.(Call).getFunc()) + "()"
)
or
- result = prettyExp(e.(Attribute).getObject()) + "." + e.(Attribute).getName()
+ result = prettyExpr(e.(Attribute).getObject()) + "." + e.(Attribute).getName()
}
string prettyNode(DataFlow::Node node) {
- if exists(node.asExpr()) then result = prettyExp(node.asExpr()) else result = node.toString()
+ if exists(node.asExpr()) then result = prettyExpr(node.asExpr()) else result = node.toString()
}
diff --git a/python/ql/test/experimental/dataflow/tainttracking/TestTaintLib.qll b/python/ql/test/experimental/dataflow/tainttracking/TestTaintLib.qll
index 32c04f3f3ab..f0e57d66787 100644
--- a/python/ql/test/experimental/dataflow/tainttracking/TestTaintLib.qll
+++ b/python/ql/test/experimental/dataflow/tainttracking/TestTaintLib.qll
@@ -46,6 +46,6 @@ query predicate test_taint(string arg_location, string test_res, string scope_na
arg_location = arg.getLocation().toString() and
test_res = test_res and
scope_name = call.getScope().getName() and
- repr = prettyExp(arg)
+ repr = prettyExpr(arg)
)
}
diff --git a/python/ql/test/experimental/meta/InlineTaintTest.qll b/python/ql/test/experimental/meta/InlineTaintTest.qll
index dd0865adc4b..da1f084f97d 100644
--- a/python/ql/test/experimental/meta/InlineTaintTest.qll
+++ b/python/ql/test/experimental/meta/InlineTaintTest.qll
@@ -58,7 +58,7 @@ class InlineTaintTest extends InlineExpectationsTest {
exists(DataFlow::Node sink |
any(TestTaintTrackingConfiguration config).hasFlow(_, sink) and
location = sink.getLocation() and
- element = prettyExp(sink.asExpr()) and
+ element = prettyExpr(sink.asExpr()) and
value = "" and
tag = "tainted"
)
@@ -84,7 +84,7 @@ query predicate untaintedArgumentToEnsureTaintedNotMarkedAsMissing(
error = "ERROR, you should add `# $ MISSING: tainted` annotation" and
exists(DataFlow::Node sink |
sink = shouldBeTainted() and
- element = prettyExp(sink.asExpr()) and
+ element = prettyExpr(sink.asExpr()) and
not any(TestTaintTrackingConfiguration config).hasFlow(_, sink) and
location = sink.getLocation() and
not exists(FalseNegativeExpectation missingResult |
From 1af6d97c5118e15811cdf347bf6155a9d5a57a84 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Tue, 11 May 2021 14:21:55 +0200
Subject: [PATCH 031/272] Python: Remove straggling f-: annotations
---
python/ql/test/library-tests/frameworks/stdlib/Encoding.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/python/ql/test/library-tests/frameworks/stdlib/Encoding.py b/python/ql/test/library-tests/frameworks/stdlib/Encoding.py
index 9a6923ce11f..5ab230ca20f 100644
--- a/python/ql/test/library-tests/frameworks/stdlib/Encoding.py
+++ b/python/ql/test/library-tests/frameworks/stdlib/Encoding.py
@@ -2,8 +2,8 @@ import pickle
import marshal
import base64
-pickle.dumps(obj) # $ MISSING: f-:encodeInput=obj f-:encodeOutput=Attribute() f-:encodeFormat=pickle f-:encodeMayExecuteInput
-marshal.dumps(obj) # $ MISSING: f-:encodeInput=obj f-:encodeOutput=Attribute() f-:encodeFormat=marshal f-:encodeMayExecuteInput
+pickle.dumps(obj) # $ MISSING: encodeInput=obj encodeOutput=Attribute() encodeFormat=pickle encodeMayExecuteInput
+marshal.dumps(obj) # $ MISSING: encodeInput=obj encodeOutput=Attribute() encodeFormat=marshal encodeMayExecuteInput
# TODO: These tests should be merged with python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_string.py
base64.b64encode(bs) # $ encodeInput=bs encodeOutput=Attribute() encodeFormat=Base64
From 51a25e45fe55484f39e3ea3d42f0b2d16f045890 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Tue, 11 May 2021 14:45:34 +0200
Subject: [PATCH 032/272] Python: Use shared prettyExpr in ConceptsTest.qll
This required quite some changes in the expected output. I think it's much more
clear what the selected nodes are now :+1: (but it was a bit boring work to fix
this up)
---
.../test/experimental/meta/ConceptsTest.qll | 32 ++++++-------------
.../library-tests/frameworks/dill/Decoding.py | 2 +-
.../frameworks/django-v2-v3/response_test.py | 10 +++---
.../django-v2-v3/testproj/settings.py | 2 +-
.../frameworks/idna/taint_test.py | 8 ++---
.../frameworks/simplejson/taint_test.py | 14 ++++----
.../frameworks/stdlib-py3/Decoding.py | 6 ++--
.../frameworks/stdlib-py3/Encoding.py | 6 ++--
.../frameworks/stdlib/Decoding.py | 16 +++++-----
.../frameworks/stdlib/Encoding.py | 16 +++++-----
.../frameworks/ujson/taint_test.py | 20 ++++++------
.../library-tests/frameworks/yaml/Decoding.py | 32 +++++++++----------
12 files changed, 76 insertions(+), 88 deletions(-)
diff --git a/python/ql/test/experimental/meta/ConceptsTest.qll b/python/ql/test/experimental/meta/ConceptsTest.qll
index 3800a4cd273..0a75937600f 100644
--- a/python/ql/test/experimental/meta/ConceptsTest.qll
+++ b/python/ql/test/experimental/meta/ConceptsTest.qll
@@ -2,19 +2,7 @@ import python
import semmle.python.dataflow.new.DataFlow
import semmle.python.Concepts
import TestUtilities.InlineExpectationsTest
-
-string value_from_expr(Expr e) {
- // TODO: This one is starting to look like `repr` predicate from TestTaintLib
- result =
- e.(StrConst).getPrefix() + e.(StrConst).getText() +
- e.(StrConst).getPrefix().regexpReplaceAll("[a-zA-Z]+", "")
- or
- result = e.(Name).getId()
- or
- not e instanceof StrConst and
- not e instanceof Name and
- result = e.toString()
-}
+import experimental.dataflow.TestUtil.PrintNode
class SystemCommandExecutionTest extends InlineExpectationsTest {
SystemCommandExecutionTest() { this = "SystemCommandExecutionTest" }
@@ -27,7 +15,7 @@ class SystemCommandExecutionTest extends InlineExpectationsTest {
command = sce.getCommand() and
location = command.getLocation() and
element = command.toString() and
- value = value_from_expr(command.asExpr()) and
+ value = prettyExpr(command.asExpr()) and
tag = "getCommand"
)
}
@@ -46,7 +34,7 @@ class DecodingTest extends InlineExpectationsTest {
exists(DataFlow::Node data |
location = data.getLocation() and
element = data.toString() and
- value = value_from_expr(data.asExpr()) and
+ value = prettyExpr(data.asExpr()) and
(
data = d.getAnInput() and
tag = "decodeInput"
@@ -84,7 +72,7 @@ class EncodingTest extends InlineExpectationsTest {
exists(DataFlow::Node data |
location = data.getLocation() and
element = data.toString() and
- value = value_from_expr(data.asExpr()) and
+ value = prettyExpr(data.asExpr()) and
(
data = e.getAnInput() and
tag = "encodeInput"
@@ -117,7 +105,7 @@ class CodeExecutionTest extends InlineExpectationsTest {
code = ce.getCode() and
location = code.getLocation() and
element = code.toString() and
- value = value_from_expr(code.asExpr()) and
+ value = prettyExpr(code.asExpr()) and
tag = "getCode"
)
}
@@ -135,7 +123,7 @@ class SqlExecutionTest extends InlineExpectationsTest {
sql = e.getSql() and
location = e.getLocation() and
element = sql.toString() and
- value = value_from_expr(sql.asExpr()) and
+ value = prettyExpr(sql.asExpr()) and
tag = "getSql"
)
}
@@ -218,7 +206,7 @@ class HttpServerHttpResponseTest extends InlineExpectationsTest {
exists(HTTP::Server::HttpResponse response |
location = response.getLocation() and
element = response.toString() and
- value = value_from_expr(response.getBody().asExpr()) and
+ value = prettyExpr(response.getBody().asExpr()) and
tag = "responseBody"
)
or
@@ -257,7 +245,7 @@ class HttpServerHttpRedirectResponseTest extends InlineExpectationsTest {
exists(HTTP::Server::HttpRedirectResponse redirect |
location = redirect.getLocation() and
element = redirect.toString() and
- value = value_from_expr(redirect.getRedirectLocation().asExpr()) and
+ value = prettyExpr(redirect.getRedirectLocation().asExpr()) and
tag = "redirectLocation"
)
)
@@ -275,7 +263,7 @@ class FileSystemAccessTest extends InlineExpectationsTest {
path = a.getAPathArgument() and
location = a.getLocation() and
element = path.toString() and
- value = value_from_expr(path.asExpr()) and
+ value = prettyExpr(path.asExpr()) and
tag = "getAPathArgument"
)
}
@@ -309,7 +297,7 @@ class SafeAccessCheckTest extends InlineExpectationsTest {
location = c.getLocation() and
(
element = checks.toString() and
- value = value_from_expr(checks.asExpr()) and
+ value = prettyExpr(checks.asExpr()) and
tag = "checks"
or
element = branch.toString() and
diff --git a/python/ql/test/library-tests/frameworks/dill/Decoding.py b/python/ql/test/library-tests/frameworks/dill/Decoding.py
index a02b013104b..49eb551af04 100644
--- a/python/ql/test/library-tests/frameworks/dill/Decoding.py
+++ b/python/ql/test/library-tests/frameworks/dill/Decoding.py
@@ -1,3 +1,3 @@
import dill
-dill.loads(payload) # $decodeInput=payload decodeOutput=Attribute() decodeFormat=dill decodeMayExecuteInput
+dill.loads(payload) # $decodeInput=payload decodeOutput=dill.loads(..) decodeFormat=dill decodeMayExecuteInput
diff --git a/python/ql/test/library-tests/frameworks/django-v2-v3/response_test.py b/python/ql/test/library-tests/frameworks/django-v2-v3/response_test.py
index feeb4d48b88..c7da2f35b6a 100644
--- a/python/ql/test/library-tests/frameworks/django-v2-v3/response_test.py
+++ b/python/ql/test/library-tests/frameworks/django-v2-v3/response_test.py
@@ -74,20 +74,20 @@ class CustomRedirectView(RedirectView):
# Ensure that simple subclasses are still vuln to XSS
def xss__not_found(request):
- return HttpResponseNotFound(request.GET.get("name")) # $HttpResponse mimetype=text/html responseBody=Attribute()
+ return HttpResponseNotFound(request.GET.get("name")) # $HttpResponse mimetype=text/html responseBody=request.GET.get(..)
# Ensure we still have an XSS sink when manually setting the content_type to HTML
def xss__manual_response_type(request):
- return HttpResponse(request.GET.get("name"), content_type="text/html; charset=utf-8") # $HttpResponse mimetype=text/html responseBody=Attribute()
+ return HttpResponse(request.GET.get("name"), content_type="text/html; charset=utf-8") # $HttpResponse mimetype=text/html responseBody=request.GET.get(..)
def xss__write(request):
response = HttpResponse() # $HttpResponse mimetype=text/html
- response.write(request.GET.get("name")) # $HttpResponse mimetype=text/html responseBody=Attribute()
+ response.write(request.GET.get("name")) # $HttpResponse mimetype=text/html responseBody=request.GET.get(..)
# This is safe but probably a bug if the argument to `write` is not a result of `json.dumps` or similar.
def safe__write_json(request):
response = JsonResponse() # $HttpResponse mimetype=application/json
- response.write(request.GET.get("name")) # $HttpResponse mimetype=application/json responseBody=Attribute()
+ response.write(request.GET.get("name")) # $HttpResponse mimetype=application/json responseBody=request.GET.get(..)
# Ensure manual subclasses are vulnerable
class CustomResponse(HttpResponse):
@@ -95,7 +95,7 @@ class CustomResponse(HttpResponse):
super().__init__(content, *args, content_type="text/html", **kwargs)
def xss__custom_response(request):
- return CustomResponse("ACME Responses", request.GET("name")) # $HttpResponse MISSING: mimetype=text/html responseBody=Attribute() SPURIOUS: responseBody="ACME Responses"
+ return CustomResponse("ACME Responses", request.GET("name")) # $HttpResponse MISSING: mimetype=text/html responseBody=request.GET.get(..) SPURIOUS: responseBody="ACME Responses"
class CustomJsonResponse(JsonResponse):
def __init__(self, banner, content, *args, **kwargs):
diff --git a/python/ql/test/library-tests/frameworks/django-v2-v3/testproj/settings.py b/python/ql/test/library-tests/frameworks/django-v2-v3/testproj/settings.py
index 1339a14312c..5343182c1c9 100644
--- a/python/ql/test/library-tests/frameworks/django-v2-v3/testproj/settings.py
+++ b/python/ql/test/library-tests/frameworks/django-v2-v3/testproj/settings.py
@@ -13,7 +13,7 @@ https://docs.djangoproject.com/en/3.1/ref/settings/
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
-BASE_DIR = Path(__file__).resolve().parent.parent #$ getAPathArgument=Path()
+BASE_DIR = Path(__file__).resolve().parent.parent #$ getAPathArgument=Path(..)
# Quick-start development settings - unsuitable for production
diff --git a/python/ql/test/library-tests/frameworks/idna/taint_test.py b/python/ql/test/library-tests/frameworks/idna/taint_test.py
index 61b9dad166c..3b20b9e678e 100644
--- a/python/ql/test/library-tests/frameworks/idna/taint_test.py
+++ b/python/ql/test/library-tests/frameworks/idna/taint_test.py
@@ -5,9 +5,9 @@ def test_idna():
tb = TAINTED_BYTES
ensure_tainted(
- idna.encode(ts), # $ tainted encodeInput=ts encodeOutput=Attribute() encodeFormat=IDNA
- idna.encode(s=ts), # $ tainted encodeInput=ts encodeOutput=Attribute() encodeFormat=IDNA
+ idna.encode(ts), # $ tainted encodeInput=ts encodeOutput=idna.encode(..) encodeFormat=IDNA
+ idna.encode(s=ts), # $ tainted encodeInput=ts encodeOutput=idna.encode(..) encodeFormat=IDNA
- idna.decode(tb), # $ tainted decodeInput=tb decodeOutput=Attribute() decodeFormat=IDNA
- idna.decode(s=tb), # $ tainted decodeInput=tb decodeOutput=Attribute() decodeFormat=IDNA
+ idna.decode(tb), # $ tainted decodeInput=tb decodeOutput=idna.decode(..) decodeFormat=IDNA
+ idna.decode(s=tb), # $ tainted decodeInput=tb decodeOutput=idna.decode(..) decodeFormat=IDNA
)
diff --git a/python/ql/test/library-tests/frameworks/simplejson/taint_test.py b/python/ql/test/library-tests/frameworks/simplejson/taint_test.py
index 6f5d45b0b33..59bec5d6978 100644
--- a/python/ql/test/library-tests/frameworks/simplejson/taint_test.py
+++ b/python/ql/test/library-tests/frameworks/simplejson/taint_test.py
@@ -5,14 +5,14 @@ def test():
ts = TAINTED_STRING
tainted_obj = {"foo": ts}
- encoded = simplejson.dumps(tainted_obj) # $ encodeOutput=Attribute() encodeFormat=JSON encodeInput=tainted_obj
+ encoded = simplejson.dumps(tainted_obj) # $ encodeOutput=simplejson.dumps(..) encodeFormat=JSON encodeInput=tainted_obj
ensure_tainted(
encoded, # $ tainted
- simplejson.dumps(tainted_obj), # $ tainted encodeOutput=Attribute() encodeFormat=JSON encodeInput=tainted_obj
- simplejson.dumps(obj=tainted_obj), # $ tainted encodeOutput=Attribute() encodeFormat=JSON encodeInput=tainted_obj
- simplejson.loads(encoded), # $ tainted decodeOutput=Attribute() decodeFormat=JSON decodeInput=encoded
- simplejson.loads(s=encoded), # $ tainted decodeOutput=Attribute() decodeFormat=JSON decodeInput=encoded
+ simplejson.dumps(tainted_obj), # $ tainted encodeOutput=simplejson.dumps(..) encodeFormat=JSON encodeInput=tainted_obj
+ simplejson.dumps(obj=tainted_obj), # $ tainted encodeOutput=simplejson.dumps(..) encodeFormat=JSON encodeInput=tainted_obj
+ simplejson.loads(encoded), # $ tainted decodeOutput=simplejson.loads(..) decodeFormat=JSON decodeInput=encoded
+ simplejson.loads(s=encoded), # $ tainted decodeOutput=simplejson.loads(..) decodeFormat=JSON decodeInput=encoded
)
# load/dump with file-like
@@ -22,7 +22,7 @@ def test():
tainted_filelike.seek(0)
ensure_tainted(
tainted_filelike, # $ MISSING: tainted
- simplejson.load(tainted_filelike), # $ decodeOutput=Attribute() decodeFormat=JSON decodeInput=tainted_filelike MISSING: tainted
+ simplejson.load(tainted_filelike), # $ decodeOutput=simplejson.load(..) decodeFormat=JSON decodeInput=tainted_filelike MISSING: tainted
)
# load/dump with file-like using keyword-args
@@ -32,7 +32,7 @@ def test():
tainted_filelike.seek(0)
ensure_tainted(
tainted_filelike, # $ MISSING: tainted
- simplejson.load(fp=tainted_filelike), # $ decodeOutput=Attribute() decodeFormat=JSON decodeInput=tainted_filelike MISSING: tainted
+ simplejson.load(fp=tainted_filelike), # $ decodeOutput=simplejson.load(..) decodeFormat=JSON decodeInput=tainted_filelike MISSING: tainted
)
# To make things runable
diff --git a/python/ql/test/library-tests/frameworks/stdlib-py3/Decoding.py b/python/ql/test/library-tests/frameworks/stdlib-py3/Decoding.py
index 886932495f2..11cca46bdf7 100644
--- a/python/ql/test/library-tests/frameworks/stdlib-py3/Decoding.py
+++ b/python/ql/test/library-tests/frameworks/stdlib-py3/Decoding.py
@@ -1,6 +1,6 @@
import base64
# TODO: These tests should be merged with python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/test_string.py
-base64.a85decode(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=Ascii85
-base64.b85decode(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=Base85
-base64.decodebytes(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=Base64
+base64.a85decode(payload) # $ decodeInput=payload decodeOutput=base64.a85decode(..) decodeFormat=Ascii85
+base64.b85decode(payload) # $ decodeInput=payload decodeOutput=base64.b85decode(..) decodeFormat=Base85
+base64.decodebytes(payload) # $ decodeInput=payload decodeOutput=base64.decodebytes(..) decodeFormat=Base64
diff --git a/python/ql/test/library-tests/frameworks/stdlib-py3/Encoding.py b/python/ql/test/library-tests/frameworks/stdlib-py3/Encoding.py
index 79fd3a2cd76..7aea6a5e579 100644
--- a/python/ql/test/library-tests/frameworks/stdlib-py3/Encoding.py
+++ b/python/ql/test/library-tests/frameworks/stdlib-py3/Encoding.py
@@ -1,6 +1,6 @@
import base64
# TODO: These tests should be merged with python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/test_string.py
-base64.a85encode(bs) # $ encodeInput=bs encodeOutput=Attribute() encodeFormat=Ascii85
-base64.b85encode(bs)# $ encodeInput=bs encodeOutput=Attribute() encodeFormat=Base85
-base64.encodebytes(bs)# $ encodeInput=bs encodeOutput=Attribute() encodeFormat=Base64
+base64.a85encode(bs) # $ encodeInput=bs encodeOutput=base64.a85encode(..) encodeFormat=Ascii85
+base64.b85encode(bs)# $ encodeInput=bs encodeOutput=base64.b85encode(..) encodeFormat=Base85
+base64.encodebytes(bs)# $ encodeInput=bs encodeOutput=base64.encodebytes(..) encodeFormat=Base64
diff --git a/python/ql/test/library-tests/frameworks/stdlib/Decoding.py b/python/ql/test/library-tests/frameworks/stdlib/Decoding.py
index baf97c90c4d..5d157a61f6e 100644
--- a/python/ql/test/library-tests/frameworks/stdlib/Decoding.py
+++ b/python/ql/test/library-tests/frameworks/stdlib/Decoding.py
@@ -2,14 +2,14 @@ import pickle
import marshal
import base64
-pickle.loads(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=pickle decodeMayExecuteInput
-marshal.loads(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=marshal decodeMayExecuteInput
+pickle.loads(payload) # $ decodeInput=payload decodeOutput=pickle.loads(..) decodeFormat=pickle decodeMayExecuteInput
+marshal.loads(payload) # $ decodeInput=payload decodeOutput=marshal.loads(..) decodeFormat=marshal decodeMayExecuteInput
# TODO: These tests should be merged with python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_string.py
-base64.b64decode(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=Base64
-base64.standard_b64decode(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=Base64
-base64.urlsafe_b64decode(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=Base64
-base64.b32decode(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=Base32
-base64.b16decode(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=Base16
+base64.b64decode(payload) # $ decodeInput=payload decodeOutput=base64.b64decode(..) decodeFormat=Base64
+base64.standard_b64decode(payload) # $ decodeInput=payload decodeOutput=base64.standard_b64decode(..) decodeFormat=Base64
+base64.urlsafe_b64decode(payload) # $ decodeInput=payload decodeOutput=base64.urlsafe_b64decode(..) decodeFormat=Base64
+base64.b32decode(payload) # $ decodeInput=payload decodeOutput=base64.b32decode(..) decodeFormat=Base32
+base64.b16decode(payload) # $ decodeInput=payload decodeOutput=base64.b16decode(..) decodeFormat=Base16
# deprecated since Python 3.1, but still works
-base64.decodestring(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=Base64
+base64.decodestring(payload) # $ decodeInput=payload decodeOutput=base64.decodestring(..) decodeFormat=Base64
diff --git a/python/ql/test/library-tests/frameworks/stdlib/Encoding.py b/python/ql/test/library-tests/frameworks/stdlib/Encoding.py
index 5ab230ca20f..2ad51e66fc1 100644
--- a/python/ql/test/library-tests/frameworks/stdlib/Encoding.py
+++ b/python/ql/test/library-tests/frameworks/stdlib/Encoding.py
@@ -2,14 +2,14 @@ import pickle
import marshal
import base64
-pickle.dumps(obj) # $ MISSING: encodeInput=obj encodeOutput=Attribute() encodeFormat=pickle encodeMayExecuteInput
-marshal.dumps(obj) # $ MISSING: encodeInput=obj encodeOutput=Attribute() encodeFormat=marshal encodeMayExecuteInput
+pickle.dumps(obj) # $ MISSING: encodeInput=obj encodeOutput=pickle.dumps(..) encodeFormat=pickle encodeMayExecuteInput
+marshal.dumps(obj) # $ MISSING: encodeInput=obj encodeOutput=marshal.dumps(..) encodeFormat=marshal encodeMayExecuteInput
# TODO: These tests should be merged with python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_string.py
-base64.b64encode(bs) # $ encodeInput=bs encodeOutput=Attribute() encodeFormat=Base64
-base64.standard_b64encode(bs) # $ encodeInput=bs encodeOutput=Attribute() encodeFormat=Base64
-base64.urlsafe_b64encode(bs) # $ encodeInput=bs encodeOutput=Attribute() encodeFormat=Base64
-base64.b32encode(bs) # $ encodeInput=bs encodeOutput=Attribute() encodeFormat=Base32
-base64.b16encode(bs) # $ encodeInput=bs encodeOutput=Attribute() encodeFormat=Base16
+base64.b64encode(bs) # $ encodeInput=bs encodeOutput=base64.b64encode(..) encodeFormat=Base64
+base64.standard_b64encode(bs) # $ encodeInput=bs encodeOutput=base64.standard_b64encode(..) encodeFormat=Base64
+base64.urlsafe_b64encode(bs) # $ encodeInput=bs encodeOutput=base64.urlsafe_b64encode(..) encodeFormat=Base64
+base64.b32encode(bs) # $ encodeInput=bs encodeOutput=base64.b32encode(..) encodeFormat=Base32
+base64.b16encode(bs) # $ encodeInput=bs encodeOutput=base64.b16encode(..) encodeFormat=Base16
# deprecated since Python 3.1, but still works
-base64.encodestring(bs) # $ encodeInput=bs encodeOutput=Attribute() encodeFormat=Base64
+base64.encodestring(bs) # $ encodeInput=bs encodeOutput=base64.encodestring(..) encodeFormat=Base64
diff --git a/python/ql/test/library-tests/frameworks/ujson/taint_test.py b/python/ql/test/library-tests/frameworks/ujson/taint_test.py
index 2c965518393..49423625a58 100644
--- a/python/ql/test/library-tests/frameworks/ujson/taint_test.py
+++ b/python/ql/test/library-tests/frameworks/ujson/taint_test.py
@@ -5,19 +5,19 @@ def test():
ts = TAINTED_STRING
tainted_obj = {"foo": ts}
- encoded = ujson.dumps(tainted_obj) # $ encodeOutput=Attribute() encodeFormat=JSON encodeInput=tainted_obj
+ encoded = ujson.dumps(tainted_obj) # $ encodeOutput=ujson.dumps(..) encodeFormat=JSON encodeInput=tainted_obj
ensure_tainted(
encoded, # $ tainted
- ujson.dumps(tainted_obj), # $ tainted encodeOutput=Attribute() encodeFormat=JSON encodeInput=tainted_obj
- ujson.dumps(obj=tainted_obj), # $ tainted encodeOutput=Attribute() encodeFormat=JSON encodeInput=tainted_obj
- ujson.loads(encoded), # $ tainted decodeOutput=Attribute() decodeFormat=JSON decodeInput=encoded
- ujson.loads(obj=encoded), # $ tainted decodeOutput=Attribute() decodeFormat=JSON decodeInput=encoded
+ ujson.dumps(tainted_obj), # $ tainted encodeOutput=ujson.dumps(..) encodeFormat=JSON encodeInput=tainted_obj
+ ujson.dumps(obj=tainted_obj), # $ tainted encodeOutput=ujson.dumps(..) encodeFormat=JSON encodeInput=tainted_obj
+ ujson.loads(encoded), # $ tainted decodeOutput=ujson.loads(..) decodeFormat=JSON decodeInput=encoded
+ ujson.loads(obj=encoded), # $ tainted decodeOutput=ujson.loads(..) decodeFormat=JSON decodeInput=encoded
- ujson.encode(tainted_obj), # $ tainted encodeOutput=Attribute() encodeFormat=JSON encodeInput=tainted_obj
- ujson.encode(obj=tainted_obj), # $ tainted encodeOutput=Attribute() encodeFormat=JSON encodeInput=tainted_obj
- ujson.decode(encoded), # $ tainted decodeOutput=Attribute() decodeFormat=JSON decodeInput=encoded
- ujson.decode(obj=encoded), # $ tainted decodeOutput=Attribute() decodeFormat=JSON decodeInput=encoded
+ ujson.encode(tainted_obj), # $ tainted encodeOutput=ujson.encode(..) encodeFormat=JSON encodeInput=tainted_obj
+ ujson.encode(obj=tainted_obj), # $ tainted encodeOutput=ujson.encode(..) encodeFormat=JSON encodeInput=tainted_obj
+ ujson.decode(encoded), # $ tainted decodeOutput=ujson.decode(..) decodeFormat=JSON decodeInput=encoded
+ ujson.decode(obj=encoded), # $ tainted decodeOutput=ujson.decode(..) decodeFormat=JSON decodeInput=encoded
)
# load/dump with file-like
@@ -27,7 +27,7 @@ def test():
tainted_filelike.seek(0)
ensure_tainted(
tainted_filelike, # $ MISSING: tainted
- ujson.load(tainted_filelike), # $ decodeOutput=Attribute() decodeFormat=JSON decodeInput=tainted_filelike MISSING: tainted
+ ujson.load(tainted_filelike), # $ decodeOutput=ujson.load(..) decodeFormat=JSON decodeInput=tainted_filelike MISSING: tainted
)
# load/dump with file-like using keyword-args does not work in `ujson`
diff --git a/python/ql/test/library-tests/frameworks/yaml/Decoding.py b/python/ql/test/library-tests/frameworks/yaml/Decoding.py
index 987ea76552a..b3cc627883d 100644
--- a/python/ql/test/library-tests/frameworks/yaml/Decoding.py
+++ b/python/ql/test/library-tests/frameworks/yaml/Decoding.py
@@ -1,37 +1,37 @@
import yaml
# Unsafe:
-yaml.load(payload) # $decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput
-yaml.load(payload, yaml.Loader) # $decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput
-yaml.unsafe_load(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput
-yaml.full_load(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput
+yaml.load(payload) # $decodeInput=payload decodeOutput=yaml.load(..) decodeFormat=YAML decodeMayExecuteInput
+yaml.load(payload, yaml.Loader) # $decodeInput=payload decodeOutput=yaml.load(..) decodeFormat=YAML decodeMayExecuteInput
+yaml.unsafe_load(payload) # $ decodeInput=payload decodeOutput=yaml.unsafe_load(..) decodeFormat=YAML decodeMayExecuteInput
+yaml.full_load(payload) # $ decodeInput=payload decodeOutput=yaml.full_load(..) decodeFormat=YAML decodeMayExecuteInput
# Safe:
-yaml.load(payload, yaml.SafeLoader) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML
-yaml.load(payload, Loader=yaml.SafeLoader) # $decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML
-yaml.load(payload, yaml.BaseLoader) # $decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML
-yaml.safe_load(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML
+yaml.load(payload, yaml.SafeLoader) # $ decodeInput=payload decodeOutput=yaml.load(..) decodeFormat=YAML
+yaml.load(payload, Loader=yaml.SafeLoader) # $decodeInput=payload decodeOutput=yaml.load(..) decodeFormat=YAML
+yaml.load(payload, yaml.BaseLoader) # $decodeInput=payload decodeOutput=yaml.load(..) decodeFormat=YAML
+yaml.safe_load(payload) # $ decodeInput=payload decodeOutput=yaml.safe_load(..) decodeFormat=YAML
################################################################################
# load_all variants
################################################################################
# Unsafe:
-yaml.load_all(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput
-yaml.unsafe_load_all(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput
-yaml.full_load_all(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput
+yaml.load_all(payload) # $ decodeInput=payload decodeOutput=yaml.load_all(..) decodeFormat=YAML decodeMayExecuteInput
+yaml.unsafe_load_all(payload) # $ decodeInput=payload decodeOutput=yaml.unsafe_load_all(..) decodeFormat=YAML decodeMayExecuteInput
+yaml.full_load_all(payload) # $ decodeInput=payload decodeOutput=yaml.full_load_all(..) decodeFormat=YAML decodeMayExecuteInput
# Safe:
-yaml.safe_load_all(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML
+yaml.safe_load_all(payload) # $ decodeInput=payload decodeOutput=yaml.safe_load_all(..) decodeFormat=YAML
################################################################################
# C-based loaders with `libyaml`
################################################################################
# Unsafe:
-yaml.load(payload, yaml.CLoader) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput
-yaml.load(payload, yaml.CFullLoader) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput
+yaml.load(payload, yaml.CLoader) # $ decodeInput=payload decodeOutput=yaml.load(..) decodeFormat=YAML decodeMayExecuteInput
+yaml.load(payload, yaml.CFullLoader) # $ decodeInput=payload decodeOutput=yaml.load(..) decodeFormat=YAML decodeMayExecuteInput
# Safe:
-yaml.load(payload, yaml.CSafeLoader) # $decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML
-yaml.load(payload, yaml.CBaseLoader) # $decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML
+yaml.load(payload, yaml.CSafeLoader) # $decodeInput=payload decodeOutput=yaml.load(..) decodeFormat=YAML
+yaml.load(payload, yaml.CBaseLoader) # $decodeInput=payload decodeOutput=yaml.load(..) decodeFormat=YAML
From 9dbb364ccab04f03d81666f2845cb16921700f28 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Tue, 11 May 2021 15:19:06 +0200
Subject: [PATCH 033/272] Python: Move json tests to be part of stdlib
This is better, since the modeling is also part of Stdlib.qll
---
.../defaultAdditionalTaintStep/test_json.py | 53 -------------------
.../frameworks/stdlib/test_json.py | 40 ++++++++++++++
2 files changed, 40 insertions(+), 53 deletions(-)
delete mode 100644 python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_json.py
create mode 100644 python/ql/test/library-tests/frameworks/stdlib/test_json.py
diff --git a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_json.py b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_json.py
deleted file mode 100644
index b1b0536b03b..00000000000
--- a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_json.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# Add taintlib to PATH so it can be imported during runtime without any hassle
-import sys; import os; sys.path.append(os.path.dirname(os.path.dirname((__file__))))
-from taintlib import *
-
-# This has no runtime impact, but allows autocomplete to work
-from typing import TYPE_CHECKING
-if TYPE_CHECKING:
- from ..taintlib import *
-
-
-# Actual tests
-
-from io import StringIO
-import json
-
-def test():
- print("\n# test")
- ts = TAINTED_STRING
-
- encoded = json.dumps(ts)
-
- ensure_tainted(
- encoded, # $ tainted
- json.dumps(ts), # $ tainted
- json.dumps(obj=ts), # $ tainted
- json.loads(encoded), # $ tainted
- json.loads(s=encoded), # $ tainted
- )
-
- # load/dump with file-like
- tainted_filelike = StringIO()
- json.dump(ts, tainted_filelike)
-
- tainted_filelike.seek(0)
- ensure_tainted(
- tainted_filelike, # $ tainted
- json.load(tainted_filelike), # $ tainted
- )
-
- # load/dump with file-like using keyword-args
- tainted_filelike = StringIO()
- json.dump(obj=ts, fp=tainted_filelike)
-
- tainted_filelike.seek(0)
- ensure_tainted(
- tainted_filelike, # $ tainted
- json.load(fp=tainted_filelike), # $ tainted
- )
-
-
-# Make tests runable
-
-test()
diff --git a/python/ql/test/library-tests/frameworks/stdlib/test_json.py b/python/ql/test/library-tests/frameworks/stdlib/test_json.py
new file mode 100644
index 00000000000..34f15391f56
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/stdlib/test_json.py
@@ -0,0 +1,40 @@
+from io import StringIO
+import json
+
+def test():
+ print("\n# test")
+ ts = TAINTED_STRING
+
+ encoded = json.dumps(ts) # $ encodeOutput=json.dumps(..) encodeFormat=JSON encodeInput=ts
+
+ ensure_tainted(
+ encoded, # $ tainted
+ json.dumps(ts), # $ tainted encodeOutput=json.dumps(..) encodeFormat=JSON encodeInput=ts
+ json.dumps(obj=ts), # $ tainted encodeOutput=json.dumps(..) encodeFormat=JSON encodeInput=ts
+ json.loads(encoded), # $ tainted decodeOutput=json.loads(..) decodeFormat=JSON decodeInput=encoded
+ json.loads(s=encoded), # $ tainted decodeOutput=json.loads(..) decodeFormat=JSON decodeInput=encoded
+ )
+
+ # load/dump with file-like
+ tainted_filelike = StringIO()
+ json.dump(ts, tainted_filelike) # $ encodeFormat=JSON encodeInput=ts
+
+ tainted_filelike.seek(0)
+ ensure_tainted(
+ tainted_filelike, # $ tainted
+ json.load(tainted_filelike), # $ tainted decodeOutput=json.load(..) decodeFormat=JSON decodeInput=tainted_filelike
+ )
+
+ # load/dump with file-like using keyword-args
+ tainted_filelike = StringIO()
+ json.dump(obj=ts, fp=tainted_filelike) # $ encodeFormat=JSON encodeInput=ts
+
+ tainted_filelike.seek(0)
+ ensure_tainted(
+ tainted_filelike, # $ tainted
+ json.load(fp=tainted_filelike), # $ tainted decodeOutput=json.load(..) decodeFormat=JSON decodeInput=tainted_filelike
+ )
+
+
+# Make tests runable
+test()
From 61ad5d0673915ded9fc65ffbaf5b4ae73de1b74a Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Tue, 11 May 2021 14:59:35 +0200
Subject: [PATCH 034/272] Python: Allow printing PostUpdateNode in
ConceptsTest.qll
See how this works in `test_json.py`
---
.../dataflow/TestUtil/PrintNode.qll | 24 +++++++++++++++++++
.../test/experimental/meta/ConceptsTest.qll | 18 +++++++-------
.../frameworks/stdlib/test_json.py | 4 ++--
3 files changed, 35 insertions(+), 11 deletions(-)
diff --git a/python/ql/test/experimental/dataflow/TestUtil/PrintNode.qll b/python/ql/test/experimental/dataflow/TestUtil/PrintNode.qll
index 46b1bfd9814..6b55beada17 100644
--- a/python/ql/test/experimental/dataflow/TestUtil/PrintNode.qll
+++ b/python/ql/test/experimental/dataflow/TestUtil/PrintNode.qll
@@ -26,6 +26,30 @@ string prettyExpr(Expr e) {
result = prettyExpr(e.(Attribute).getObject()) + "." + e.(Attribute).getName()
}
+/**
+ * Gets pretty-printed version of the DataFlow::Node `node`
+ */
+bindingset[node]
string prettyNode(DataFlow::Node node) {
if exists(node.asExpr()) then result = prettyExpr(node.asExpr()) else result = node.toString()
}
+
+/**
+ * Gets pretty-printed version of the DataFlow::Node `node`, that is suitable for use
+ * with `TestUtilities.InlineExpectationsTest` (that is, no spaces unless required).
+ */
+bindingset[node]
+string prettyNodeForInlineTest(DataFlow::Node node) {
+ exists(node.asExpr()) and
+ result = prettyExpr(node.asExpr())
+ or
+ exists(Expr e | e = node.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr() |
+ // since PostUpdateNode both has space in the `[post ]` annotation, and does
+ // not pretty print the pre-update node, we do custom handling of this.
+ result = "[post]" + prettyExpr(e)
+ )
+ or
+ not exists(node.asExpr()) and
+ not exists(Expr e | e = node.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr()) and
+ result = node.toString()
+}
diff --git a/python/ql/test/experimental/meta/ConceptsTest.qll b/python/ql/test/experimental/meta/ConceptsTest.qll
index 0a75937600f..6c97662feea 100644
--- a/python/ql/test/experimental/meta/ConceptsTest.qll
+++ b/python/ql/test/experimental/meta/ConceptsTest.qll
@@ -15,7 +15,7 @@ class SystemCommandExecutionTest extends InlineExpectationsTest {
command = sce.getCommand() and
location = command.getLocation() and
element = command.toString() and
- value = prettyExpr(command.asExpr()) and
+ value = prettyNodeForInlineTest(command) and
tag = "getCommand"
)
}
@@ -34,7 +34,7 @@ class DecodingTest extends InlineExpectationsTest {
exists(DataFlow::Node data |
location = data.getLocation() and
element = data.toString() and
- value = prettyExpr(data.asExpr()) and
+ value = prettyNodeForInlineTest(data) and
(
data = d.getAnInput() and
tag = "decodeInput"
@@ -72,7 +72,7 @@ class EncodingTest extends InlineExpectationsTest {
exists(DataFlow::Node data |
location = data.getLocation() and
element = data.toString() and
- value = prettyExpr(data.asExpr()) and
+ value = prettyNodeForInlineTest(data) and
(
data = e.getAnInput() and
tag = "encodeInput"
@@ -105,7 +105,7 @@ class CodeExecutionTest extends InlineExpectationsTest {
code = ce.getCode() and
location = code.getLocation() and
element = code.toString() and
- value = prettyExpr(code.asExpr()) and
+ value = prettyNodeForInlineTest(code) and
tag = "getCode"
)
}
@@ -123,7 +123,7 @@ class SqlExecutionTest extends InlineExpectationsTest {
sql = e.getSql() and
location = e.getLocation() and
element = sql.toString() and
- value = prettyExpr(sql.asExpr()) and
+ value = prettyNodeForInlineTest(sql) and
tag = "getSql"
)
}
@@ -206,7 +206,7 @@ class HttpServerHttpResponseTest extends InlineExpectationsTest {
exists(HTTP::Server::HttpResponse response |
location = response.getLocation() and
element = response.toString() and
- value = prettyExpr(response.getBody().asExpr()) and
+ value = prettyNodeForInlineTest(response.getBody()) and
tag = "responseBody"
)
or
@@ -245,7 +245,7 @@ class HttpServerHttpRedirectResponseTest extends InlineExpectationsTest {
exists(HTTP::Server::HttpRedirectResponse redirect |
location = redirect.getLocation() and
element = redirect.toString() and
- value = prettyExpr(redirect.getRedirectLocation().asExpr()) and
+ value = prettyNodeForInlineTest(redirect.getRedirectLocation()) and
tag = "redirectLocation"
)
)
@@ -263,7 +263,7 @@ class FileSystemAccessTest extends InlineExpectationsTest {
path = a.getAPathArgument() and
location = a.getLocation() and
element = path.toString() and
- value = prettyExpr(path.asExpr()) and
+ value = prettyNodeForInlineTest(path) and
tag = "getAPathArgument"
)
}
@@ -297,7 +297,7 @@ class SafeAccessCheckTest extends InlineExpectationsTest {
location = c.getLocation() and
(
element = checks.toString() and
- value = prettyExpr(checks.asExpr()) and
+ value = prettyNodeForInlineTest(checks) and
tag = "checks"
or
element = branch.toString() and
diff --git a/python/ql/test/library-tests/frameworks/stdlib/test_json.py b/python/ql/test/library-tests/frameworks/stdlib/test_json.py
index 34f15391f56..113065c77fd 100644
--- a/python/ql/test/library-tests/frameworks/stdlib/test_json.py
+++ b/python/ql/test/library-tests/frameworks/stdlib/test_json.py
@@ -17,7 +17,7 @@ def test():
# load/dump with file-like
tainted_filelike = StringIO()
- json.dump(ts, tainted_filelike) # $ encodeFormat=JSON encodeInput=ts
+ json.dump(ts, tainted_filelike) # $ encodeOutput=[post]tainted_filelike encodeFormat=JSON encodeInput=ts
tainted_filelike.seek(0)
ensure_tainted(
@@ -27,7 +27,7 @@ def test():
# load/dump with file-like using keyword-args
tainted_filelike = StringIO()
- json.dump(obj=ts, fp=tainted_filelike) # $ encodeFormat=JSON encodeInput=ts
+ json.dump(obj=ts, fp=tainted_filelike) # $ encodeOutput=[post]tainted_filelike encodeFormat=JSON encodeInput=ts
tainted_filelike.seek(0)
ensure_tainted(
From 254c769089f9187a0ed6a148b3d6d40980c76e22 Mon Sep 17 00:00:00 2001
From: shati-patel <42641846+shati-patel@users.noreply.github.com>
Date: Fri, 21 May 2021 21:41:09 +0100
Subject: [PATCH 035/272] Docs: Describe custom log directory setting in VS
Code extension
---
.../codeql-for-visual-studio-code/customizing-settings.rst | 4 ++++
.../troubleshooting-codeql-for-visual-studio-code.rst | 2 ++
2 files changed, 6 insertions(+)
diff --git a/docs/codeql/codeql-for-visual-studio-code/customizing-settings.rst b/docs/codeql/codeql-for-visual-studio-code/customizing-settings.rst
index 8197858d3ab..10c0fb12744 100644
--- a/docs/codeql/codeql-for-visual-studio-code/customizing-settings.rst
+++ b/docs/codeql/codeql-for-visual-studio-code/customizing-settings.rst
@@ -46,6 +46,8 @@ The query history **Format** setting controls how the extension lists queries in
To override the default label, you can specify a different format for the query history items.
+.. _configuring-settings-for-running-queries:
+
Configuring settings for running queries
-----------------------------------------
@@ -53,6 +55,8 @@ There are a number of settings for **Running Queries**. If your queries run too
.. include:: ../reusables/running-queries-debug.rst
+To save CodeQL Query Server logs in a custom location, edit the **Running Queries: Custom Log Directory** setting. If you use a custom log directory, the extension doesn't automatically delete the CodeQL Query Server logs. This is useful if you want to investigate these logs to improve the performance of your queries.
+
Configuring settings for testing queries
-----------------------------------------
diff --git a/docs/codeql/codeql-for-visual-studio-code/troubleshooting-codeql-for-visual-studio-code.rst b/docs/codeql/codeql-for-visual-studio-code/troubleshooting-codeql-for-visual-studio-code.rst
index 80fc5efea0f..3f50d29604b 100644
--- a/docs/codeql/codeql-for-visual-studio-code/troubleshooting-codeql-for-visual-studio-code.rst
+++ b/docs/codeql/codeql-for-visual-studio-code/troubleshooting-codeql-for-visual-studio-code.rst
@@ -38,6 +38,8 @@ You are most likely to need to restart the query server if you make external cha
To see the logs from running a particular query, right-click the query in the Query History and select **Show Query Log**.
If the log file is too large for the extension to open in the VS Code editor, the file will be displayed in your file explorer so you can open it with an external program.
+By default, the extension deletes logs after each workspace session. To override this behavior, you can specify a custom directory for query server logs. For more information, see ":ref:`Customizing settings `."
+
Exploring problems with running tests
----------------------------------------------
From 8b96ff96013dca8039333bfe83fcc5f978eef91a Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Mon, 12 Apr 2021 21:21:13 +0300
Subject: [PATCH 036/272] First draft of RmiUnsafeDeserialization.ql
---
.../CWE/CWE-502/RmiUnsafeDeserialization.ql | 52 +++++++++++++++++++
1 file changed, 52 insertions(+)
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.ql
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.ql b/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.ql
new file mode 100644
index 00000000000..ddb5cf26f09
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.ql
@@ -0,0 +1,52 @@
+/**
+ * @name Unsafe deserialization with RMI.
+ * @description TBD
+ * @kind problem
+ * @problem.severity error
+ * @precision high
+ * @id java/unsafe-deserialization-rmi
+ * @tags security
+ * external/cwe/cwe-502
+ */
+
+import java
+import semmle.code.java.frameworks.Rmi
+
+private class ObjectInputStream extends RefType {
+ ObjectInputStream() { hasQualifiedName("java.io", "ObjectInputStream") }
+}
+
+private class BindMethod extends Method {
+ BindMethod() {
+ getDeclaringType().hasQualifiedName("java.rmi", "Naming") and
+ hasName(["bind", "rebind"])
+ }
+}
+
+private Method getVulnerableMethod(Type type) {
+ type.(RefType).getASupertype*() instanceof TypeRemote and
+ exists(Method m, Type parameterType |
+ m.getDeclaringType() = type and parameterType = m.getAParamType()
+ |
+ not parameterType instanceof PrimitiveType and
+ not parameterType instanceof TypeString and
+ not parameterType instanceof ObjectInputStream and
+ result = m
+ )
+}
+
+private class UnsafeRmiBinding extends MethodAccess {
+ Method vulnerableMethod;
+
+ UnsafeRmiBinding() {
+ this.getMethod() instanceof BindMethod and
+ vulnerableMethod = getVulnerableMethod(this.getArgument(1).getType())
+ }
+
+ Method getVulnerableMethod() { result = vulnerableMethod }
+}
+
+// TODO: Cover Registry.bind() and rebind() -- test these sinks first
+
+from UnsafeRmiBinding call
+select call, "Unsafe deserialization with RMI in '" + call.getVulnerableMethod() + "' method"
From efa4b4f414b73ec078b7740d178576bd2ce690b8 Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Thu, 15 Apr 2021 15:37:20 +0300
Subject: [PATCH 037/272] Cover Registry in RmiUnsafeDeserialization.ql
---
.../CWE/CWE-502/RmiUnsafeDeserialization.ql | 21 +++++++++++++++----
1 file changed, 17 insertions(+), 4 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.ql b/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.ql
index ddb5cf26f09..e90e6704428 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.ql
@@ -1,6 +1,9 @@
/**
* @name Unsafe deserialization with RMI.
- * @description TBD
+ * @description Java RMI uses native Java serialization mechanism.
+ * If a registered remote object has a method that takes a complex object,
+ * an attacker can take advantage of unsafe Java deserialization mechanism.
+ * In the worst case, it results in remote code execution.
* @kind problem
* @problem.severity error
* @precision high
@@ -16,13 +19,22 @@ private class ObjectInputStream extends RefType {
ObjectInputStream() { hasQualifiedName("java.io", "ObjectInputStream") }
}
+/**
+ * A method that binds a name to a remote object.
+ */
private class BindMethod extends Method {
BindMethod() {
- getDeclaringType().hasQualifiedName("java.rmi", "Naming") and
+ (
+ getDeclaringType().hasQualifiedName("java.rmi", "Naming") or
+ getDeclaringType().hasQualifiedName("java.rmi.registry", "Registry")
+ ) and
hasName(["bind", "rebind"])
}
}
+/**
+ * Looks for a vulnerable method in a `Remote` object.
+ */
private Method getVulnerableMethod(Type type) {
type.(RefType).getASupertype*() instanceof TypeRemote and
exists(Method m, Type parameterType |
@@ -35,6 +47,9 @@ private Method getVulnerableMethod(Type type) {
)
}
+/**
+ * A method call that registers a remote object that has a vulnerable method.
+ */
private class UnsafeRmiBinding extends MethodAccess {
Method vulnerableMethod;
@@ -46,7 +61,5 @@ private class UnsafeRmiBinding extends MethodAccess {
Method getVulnerableMethod() { result = vulnerableMethod }
}
-// TODO: Cover Registry.bind() and rebind() -- test these sinks first
-
from UnsafeRmiBinding call
select call, "Unsafe deserialization with RMI in '" + call.getVulnerableMethod() + "' method"
From ec6186a1c5d91d8f68c92da45bac985fdd897ec1 Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Thu, 15 Apr 2021 16:18:39 +0300
Subject: [PATCH 038/272] Draft of tests for RmiUnsafeDeserialization.ql
---
.../CWE-502/RmiUnsafeDeserialization.expected | 0
.../CWE-502/RmiUnsafeDeserialization.java | 24 +++++++++++++++++++
.../CWE-502/RmiUnsafeDeserialization.qlref | 1 +
3 files changed, 25 insertions(+)
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.expected
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.java
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.qlref
diff --git a/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.expected b/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.expected
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.java b/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.java
new file mode 100644
index 00000000000..f3495404e44
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.java
@@ -0,0 +1,24 @@
+import java.rmi.Naming;
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+
+public class RmiUnsafeDeserialization {
+
+ // BAD (bind a remote object that has a vulnerable method that takes Object)
+ public static void testRegistryBindWithObjectParameter() throws Exception {
+ Registry registry = LocateRegistry.createRegistry(1099);
+ registry.bind("test", new RemoteObjectWithObject());
+ }
+}
+
+interface RemoteObjectWithObjectInterface extends Remote {
+
+ void take(Object obj) throws RemoteException;
+}
+
+class RemoteObjectWithObject implements RemoteObjectWithObjectInterface {
+
+ public void take(Object obj) throws RemoteException {}
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.qlref b/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.qlref
new file mode 100644
index 00000000000..d750f371002
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.ql
\ No newline at end of file
From 3d20330a92144a49b8f564aa8c25a773b38926a1 Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Thu, 15 Apr 2021 22:13:19 +0300
Subject: [PATCH 039/272] More tests for RmiUnsafeDeserialization
---
.../CWE-502/RmiUnsafeDeserialization.java | 37 ++++++++++++++++++-
1 file changed, 35 insertions(+), 2 deletions(-)
diff --git a/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.java b/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.java
index f3495404e44..60c073916b7 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.java
@@ -1,3 +1,4 @@
+import java.io.ObjectInputStream;
import java.rmi.Naming;
import java.rmi.Remote;
import java.rmi.RemoteException;
@@ -10,15 +11,47 @@ public class RmiUnsafeDeserialization {
public static void testRegistryBindWithObjectParameter() throws Exception {
Registry registry = LocateRegistry.createRegistry(1099);
registry.bind("test", new RemoteObjectWithObject());
+ registry.rebind("test", new RemoteObjectWithObject());
+ }
+
+ // GOOD (bind a remote object that has methods that takes safe parameters)
+ public static void testRegistryBindWithIntParameter() throws Exception {
+ Registry registry = LocateRegistry.createRegistry(1099);
+ registry.bind("test", new SafeRemoteObject());
+ registry.rebind("test", new SafeRemoteObject());
+ }
+
+ // BAD (bind a remote object that has a vulnerable method that takes Object)
+ public static void testNamingBindWithObjectParameter() throws Exception {
+ Naming.bind("test", new RemoteObjectWithObject());
+ Naming.rebind("test", new RemoteObjectWithObject());
+ }
+
+ // GOOD (bind a remote object that has methods that takes safe parameters)
+ public static void testNamingBindWithIntParameter() throws Exception {
+ Naming.bind("test", new SafeRemoteObject());
+ Naming.rebind("test", new SafeRemoteObject());
}
}
interface RemoteObjectWithObjectInterface extends Remote {
-
void take(Object obj) throws RemoteException;
}
class RemoteObjectWithObject implements RemoteObjectWithObjectInterface {
-
public void take(Object obj) throws RemoteException {}
}
+
+interface SafeRemoteObjectInterface extends Remote {
+ void take(int n) throws RemoteException;
+ void take(double n) throws RemoteException;
+ void take(String s) throws RemoteException;
+ void take(ObjectInputStream ois) throws RemoteException;
+}
+
+class SafeRemoteObject implements SafeRemoteObjectInterface {
+ public void take(int n) throws RemoteException {}
+ public void take(double n) throws RemoteException {}
+ public void take(String s) throws RemoteException {}
+ public void take(ObjectInputStream ois) throws RemoteException {}
+}
\ No newline at end of file
From 5ffe04d6a507ac264dbde5c9da4626a33e42d929 Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Thu, 15 Apr 2021 22:21:39 +0300
Subject: [PATCH 040/272] Updated expected output for
RmiUnsafeDeserialization.java test
---
.../security/CWE-502/RmiUnsafeDeserialization.expected | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.expected b/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.expected
index e69de29bb2d..fb91691cc47 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.expected
@@ -0,0 +1,4 @@
+| RmiUnsafeDeserialization.java:13:9:13:59 | bind(...) | Unsafe deserialization with RMI in 'take' method |
+| RmiUnsafeDeserialization.java:14:9:14:61 | rebind(...) | Unsafe deserialization with RMI in 'take' method |
+| RmiUnsafeDeserialization.java:26:9:26:57 | bind(...) | Unsafe deserialization with RMI in 'take' method |
+| RmiUnsafeDeserialization.java:27:9:27:59 | rebind(...) | Unsafe deserialization with RMI in 'take' method |
From 0182dfe1c04cd915871655977f4f57b9e2121fdc Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Fri, 16 Apr 2021 16:55:00 +0300
Subject: [PATCH 041/272] Added RmiUnsafeDeserialization.qhelp
---
.../CWE/CWE-502/RmiSafeRemoteObject.java | 15 ++++
.../CWE-502/RmiUnsafeDeserialization.qhelp | 68 +++++++++++++++++++
.../CWE/CWE-502/RmiUnsafeDeserialization.ql | 6 +-
.../CWE/CWE-502/RmiUnsafeRemoteObject.java | 14 ++++
4 files changed, 100 insertions(+), 3 deletions(-)
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-502/RmiSafeRemoteObject.java
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.qhelp
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeRemoteObject.java
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/RmiSafeRemoteObject.java b/java/ql/src/experimental/Security/CWE/CWE-502/RmiSafeRemoteObject.java
new file mode 100644
index 00000000000..60a1b49d2fe
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/RmiSafeRemoteObject.java
@@ -0,0 +1,15 @@
+public class Server {
+ public static void main(String... args) throws Exception {
+ Registry registry = LocateRegistry.createRegistry(1099);
+ registry.bind("unsafe", new RemoteObjectImpl());
+ }
+}
+
+interface RemoteObject extends Remote {
+ void calculate(int a, double b) throws RemoteException;
+ void save(String s) throws RemoteException;
+}
+
+class RemoteObjectImpl implements RemoteObject {
+ // ...
+}
\ No newline at end of file
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.qhelp b/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.qhelp
new file mode 100644
index 00000000000..1921476ffbd
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.qhelp
@@ -0,0 +1,68 @@
+
+
+
+
+
+Java RMI uses the default Java serialization mechanism (in other words, ObjectInputStream)
+to pass parameters in remote method invocations. This mechanism is known to be unsafe when deserializing
+untrusted data. If a registered remote object has a method that accepts a complex object,
+an attacker can take advantage of the unsafe deserialization mechanism.
+In the worst case, it results in remote code execution.
+
+
+
+
+
+Use only strings and primitive types in parameters of remote objects.
+
+
+Java RMI does not offer API for specifying classes which are only allowed for deserialization.
+However, it is possible to set a process-wide deserialization filter that was introduced in JEP 290.
+The filter can be set via system or security property jdk.serialFilter.
+Make sure that you use the latest Java versions that include JEP 290.
+
+
+Consider using other implementations of remote procedure calls. For example, HTTP API with JSON.
+Make sure that the underlying deserialization mechanism is properly configured
+so that deserialization attacks are not possible.
+
+
+
+
+
+The following code registers a vulnerable remote object
+which has a method that accepts a complex object:
+
+
+
+
+The next example registers a safe remote object
+which has methods that use only primitive types and strings:
+
+
+
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.ql b/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.ql
index e90e6704428..1ece9d14fd9 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.ql
@@ -1,8 +1,8 @@
/**
* @name Unsafe deserialization with RMI.
- * @description Java RMI uses native Java serialization mechanism.
- * If a registered remote object has a method that takes a complex object,
- * an attacker can take advantage of unsafe Java deserialization mechanism.
+ * @description If a registered remote object has a method that accepts a complex object,
+ * an attacker can take advantage of the unsafe deserialization mechanism
+ * which is used to pass parameters in RMI.
* In the worst case, it results in remote code execution.
* @kind problem
* @problem.severity error
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeRemoteObject.java b/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeRemoteObject.java
new file mode 100644
index 00000000000..4c04b57cde9
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeRemoteObject.java
@@ -0,0 +1,14 @@
+public class Server {
+ public static void main(String... args) throws Exception {
+ Registry registry = LocateRegistry.createRegistry(1099);
+ registry.bind("unsafe", new RemoteObjectImpl());
+ }
+}
+
+interface RemoteObject extends Remote {
+ void action(Object obj) throws RemoteException;
+}
+
+class RemoteObjectImpl implements RemoteObject {
+ // ...
+}
\ No newline at end of file
From e28f919f3dc88ecf295e01cfa4ac71911735f61e Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Mon, 17 May 2021 13:19:08 +0200
Subject: [PATCH 042/272] Look for remote callable method only in
RmiUnsafeDeserialization.ql
---
.../Security/CWE/CWE-502/RmiSafeRemoteObject.java | 2 +-
.../CWE/CWE-502/RmiUnsafeDeserialization.ql | 13 +++++++------
.../CWE-502/RmiUnsafeDeserialization.expected | 8 ++++----
.../security/CWE-502/RmiUnsafeDeserialization.java | 1 +
4 files changed, 13 insertions(+), 11 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/RmiSafeRemoteObject.java b/java/ql/src/experimental/Security/CWE/CWE-502/RmiSafeRemoteObject.java
index 60a1b49d2fe..2f30bc1f3ba 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-502/RmiSafeRemoteObject.java
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/RmiSafeRemoteObject.java
@@ -1,7 +1,7 @@
public class Server {
public static void main(String... args) throws Exception {
Registry registry = LocateRegistry.createRegistry(1099);
- registry.bind("unsafe", new RemoteObjectImpl());
+ registry.bind("safe", new RemoteObjectImpl());
}
}
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.ql b/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.ql
index 1ece9d14fd9..52c637ebc9f 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.ql
@@ -15,7 +15,7 @@
import java
import semmle.code.java.frameworks.Rmi
-private class ObjectInputStream extends RefType {
+private class ObjectInputStream extends Class {
ObjectInputStream() { hasQualifiedName("java.io", "ObjectInputStream") }
}
@@ -35,9 +35,8 @@ private class BindMethod extends Method {
/**
* Looks for a vulnerable method in a `Remote` object.
*/
-private Method getVulnerableMethod(Type type) {
- type.(RefType).getASupertype*() instanceof TypeRemote and
- exists(Method m, Type parameterType |
+private Method getVulnerableMethod(RefType type) {
+ exists(RemoteCallableMethod m, Type parameterType |
m.getDeclaringType() = type and parameterType = m.getAParamType()
|
not parameterType instanceof PrimitiveType and
@@ -61,5 +60,7 @@ private class UnsafeRmiBinding extends MethodAccess {
Method getVulnerableMethod() { result = vulnerableMethod }
}
-from UnsafeRmiBinding call
-select call, "Unsafe deserialization with RMI in '" + call.getVulnerableMethod() + "' method"
+from UnsafeRmiBinding call, Method vulnerableMethod
+where vulnerableMethod = call.getVulnerableMethod()
+select call, "Unsafe deserialization with RMI in '$@' method", vulnerableMethod,
+ vulnerableMethod.getStringSignature()
diff --git a/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.expected b/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.expected
index fb91691cc47..52bcccb15c5 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.expected
@@ -1,4 +1,4 @@
-| RmiUnsafeDeserialization.java:13:9:13:59 | bind(...) | Unsafe deserialization with RMI in 'take' method |
-| RmiUnsafeDeserialization.java:14:9:14:61 | rebind(...) | Unsafe deserialization with RMI in 'take' method |
-| RmiUnsafeDeserialization.java:26:9:26:57 | bind(...) | Unsafe deserialization with RMI in 'take' method |
-| RmiUnsafeDeserialization.java:27:9:27:59 | rebind(...) | Unsafe deserialization with RMI in 'take' method |
+| RmiUnsafeDeserialization.java:13:9:13:59 | bind(...) | Unsafe deserialization with RMI in '$@' method | RmiUnsafeDeserialization.java:42:17:42:20 | take | take(Object) |
+| RmiUnsafeDeserialization.java:14:9:14:61 | rebind(...) | Unsafe deserialization with RMI in '$@' method | RmiUnsafeDeserialization.java:42:17:42:20 | take | take(Object) |
+| RmiUnsafeDeserialization.java:26:9:26:57 | bind(...) | Unsafe deserialization with RMI in '$@' method | RmiUnsafeDeserialization.java:42:17:42:20 | take | take(Object) |
+| RmiUnsafeDeserialization.java:27:9:27:59 | rebind(...) | Unsafe deserialization with RMI in '$@' method | RmiUnsafeDeserialization.java:42:17:42:20 | take | take(Object) |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.java b/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.java
index 60c073916b7..f8921eda6ce 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.java
@@ -54,4 +54,5 @@ class SafeRemoteObject implements SafeRemoteObjectInterface {
public void take(double n) throws RemoteException {}
public void take(String s) throws RemoteException {}
public void take(ObjectInputStream ois) throws RemoteException {}
+ public void safeMethod(Object object) {} // this method is not declared in SafeRemoteObjectInterface
}
\ No newline at end of file
From 2d93eeae33f44c89e2f3d87e21471641f1b5dd58 Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Sat, 22 May 2021 20:06:17 +0200
Subject: [PATCH 043/272] Covered deserialization filters in
RmiUnsafeDeserialization.ql
---
.../CWE/CWE-502/RmiUnsafeDeserialization.ql | 58 ++++++++++++-------
1 file changed, 37 insertions(+), 21 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.ql b/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.ql
index 52c637ebc9f..55932027996 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.ql
@@ -1,10 +1,10 @@
/**
- * @name Unsafe deserialization with RMI.
+ * @name Unsafe remote object.
* @description If a registered remote object has a method that accepts a complex object,
* an attacker can take advantage of the unsafe deserialization mechanism
* which is used to pass parameters in RMI.
* In the worst case, it results in remote code execution.
- * @kind problem
+ * @kind path-problem
* @problem.severity error
* @precision high
* @id java/unsafe-deserialization-rmi
@@ -13,11 +13,9 @@
*/
import java
+import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.frameworks.Rmi
-
-private class ObjectInputStream extends Class {
- ObjectInputStream() { hasQualifiedName("java.io", "ObjectInputStream") }
-}
+import DataFlow::PathGraph
/**
* A method that binds a name to a remote object.
@@ -33,34 +31,52 @@ private class BindMethod extends Method {
}
/**
- * Looks for a vulnerable method in a `Remote` object.
+ * Holds if `type` has an unsafe remote method.
*/
-private Method getVulnerableMethod(RefType type) {
+private predicate hasVulnerableMethod(RefType type) {
exists(RemoteCallableMethod m, Type parameterType |
m.getDeclaringType() = type and parameterType = m.getAParamType()
|
not parameterType instanceof PrimitiveType and
not parameterType instanceof TypeString and
- not parameterType instanceof ObjectInputStream and
- result = m
+ not parameterType.(RefType).hasQualifiedName("java.io", "ObjectInputStream")
)
}
/**
- * A method call that registers a remote object that has a vulnerable method.
+ * A taint-tracking configuration for unsafe remote objects
+ * that are vulnerable to deserialization attacks.
*/
-private class UnsafeRmiBinding extends MethodAccess {
- Method vulnerableMethod;
+private class BindingUnsafeRemoteObjectConfig extends TaintTracking::Configuration {
+ BindingUnsafeRemoteObjectConfig() { this = "BindingUnsafeRemoteObjectConfig" }
- UnsafeRmiBinding() {
- this.getMethod() instanceof BindMethod and
- vulnerableMethod = getVulnerableMethod(this.getArgument(1).getType())
+ override predicate isSource(DataFlow::Node source) {
+ exists(ConstructorCall cc | cc = source.asExpr() |
+ hasVulnerableMethod(cc.getConstructedType().getASupertype*())
+ )
}
- Method getVulnerableMethod() { result = vulnerableMethod }
+ override predicate isSink(DataFlow::Node sink) {
+ exists(StaticMethodAccess ma | ma.getArgument(1) = sink.asExpr() |
+ ma.getMethod() instanceof BindMethod
+ )
+ }
+
+ override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
+ exists(StaticMethodAccess ma, Method m | m = ma.getMethod() |
+ m.getDeclaringType().hasQualifiedName("java.rmi.server", "UnicastRemoteObject") and
+ m.hasName("exportObject") and
+ not ma.getArgument([2, 4])
+ .getType()
+ .(RefType)
+ .getASupertype*()
+ .hasQualifiedName("java.io", "ObjectInputFilter") and
+ ma.getArgument(0) = fromNode.asExpr() and
+ ma = toNode.asExpr()
+ )
+ }
}
-from UnsafeRmiBinding call, Method vulnerableMethod
-where vulnerableMethod = call.getVulnerableMethod()
-select call, "Unsafe deserialization with RMI in '$@' method", vulnerableMethod,
- vulnerableMethod.getStringSignature()
+from DataFlow::PathNode source, DataFlow::PathNode sink, BindingUnsafeRemoteObjectConfig conf
+where conf.hasFlowPath(source, sink)
+select sink.getNode(), source, sink, "Binding an unsafe remote object."
From d2e29fc72c00c56bdb8002ab92798a67b85b2ef4 Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Sun, 23 May 2021 10:18:40 +0200
Subject: [PATCH 044/272] Renamed RmiUnsafeDeserialization.ql ->
UnsafeDeserializationRmi.ql
---
...Deserialization.qhelp => UnsafeDeserializationRmi.qhelp} | 0
...UnsafeDeserialization.ql => UnsafeDeserializationRmi.ql} | 0
.../security/CWE-502/RmiUnsafeDeserialization.qlref | 1 -
...alization.expected => UnsafeDeserializationRmi.expected} | 0
...feDeserialization.java => UnsafeDeserializationRmi.java} | 6 +++---
.../security/CWE-502/UnsafeDeserializationRmi.qlref | 1 +
6 files changed, 4 insertions(+), 4 deletions(-)
rename java/ql/src/experimental/Security/CWE/CWE-502/{RmiUnsafeDeserialization.qhelp => UnsafeDeserializationRmi.qhelp} (100%)
rename java/ql/src/experimental/Security/CWE/CWE-502/{RmiUnsafeDeserialization.ql => UnsafeDeserializationRmi.ql} (100%)
delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.qlref
rename java/ql/test/experimental/query-tests/security/CWE-502/{RmiUnsafeDeserialization.expected => UnsafeDeserializationRmi.expected} (100%)
rename java/ql/test/experimental/query-tests/security/CWE-502/{RmiUnsafeDeserialization.java => UnsafeDeserializationRmi.java} (98%)
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-502/UnsafeDeserializationRmi.qlref
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.qhelp b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.qhelp
similarity index 100%
rename from java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.qhelp
rename to java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.qhelp
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.ql b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.ql
similarity index 100%
rename from java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.ql
rename to java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.ql
diff --git a/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.qlref b/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.qlref
deleted file mode 100644
index d750f371002..00000000000
--- a/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.qlref
+++ /dev/null
@@ -1 +0,0 @@
-experimental/Security/CWE/CWE-502/RmiUnsafeDeserialization.ql
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.expected b/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeDeserializationRmi.expected
similarity index 100%
rename from java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.expected
rename to java/ql/test/experimental/query-tests/security/CWE-502/UnsafeDeserializationRmi.expected
diff --git a/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.java b/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeDeserializationRmi.java
similarity index 98%
rename from java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.java
rename to java/ql/test/experimental/query-tests/security/CWE-502/UnsafeDeserializationRmi.java
index f8921eda6ce..ee0bc3a1989 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-502/RmiUnsafeDeserialization.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeDeserializationRmi.java
@@ -5,8 +5,8 @@ import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
-public class RmiUnsafeDeserialization {
-
+public class UnsafeDeserializationRmi {
+
// BAD (bind a remote object that has a vulnerable method that takes Object)
public static void testRegistryBindWithObjectParameter() throws Exception {
Registry registry = LocateRegistry.createRegistry(1099);
@@ -55,4 +55,4 @@ class SafeRemoteObject implements SafeRemoteObjectInterface {
public void take(String s) throws RemoteException {}
public void take(ObjectInputStream ois) throws RemoteException {}
public void safeMethod(Object object) {} // this method is not declared in SafeRemoteObjectInterface
-}
\ No newline at end of file
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeDeserializationRmi.qlref b/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeDeserializationRmi.qlref
new file mode 100644
index 00000000000..fce2e6c6a4a
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeDeserializationRmi.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.ql
\ No newline at end of file
From c837605c856868368deff452eef3b041579b23dc Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Sun, 23 May 2021 13:01:22 +0200
Subject: [PATCH 045/272] Added test cases with sanitizers for
UnsafeDeserializationRmi.ql
---
.../CWE/CWE-502/UnsafeDeserializationRmi.ql | 10 ++---
.../CWE-502/UnsafeDeserializationRmi.expected | 19 ++++++--
.../CWE-502/UnsafeDeserializationRmi.java | 45 ++++++++++++-------
3 files changed, 50 insertions(+), 24 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.ql b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.ql
index 55932027996..91e38cc373e 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.ql
@@ -1,5 +1,5 @@
/**
- * @name Unsafe remote object.
+ * @name Unsafe deserialization in a remotely callable method.
* @description If a registered remote object has a method that accepts a complex object,
* an attacker can take advantage of the unsafe deserialization mechanism
* which is used to pass parameters in RMI.
@@ -31,7 +31,7 @@ private class BindMethod extends Method {
}
/**
- * Holds if `type` has an unsafe remote method.
+ * Holds if `type` has an vulnerable remote method.
*/
private predicate hasVulnerableMethod(RefType type) {
exists(RemoteCallableMethod m, Type parameterType |
@@ -57,13 +57,13 @@ private class BindingUnsafeRemoteObjectConfig extends TaintTracking::Configurati
}
override predicate isSink(DataFlow::Node sink) {
- exists(StaticMethodAccess ma | ma.getArgument(1) = sink.asExpr() |
+ exists(MethodAccess ma | ma.getArgument(1) = sink.asExpr() |
ma.getMethod() instanceof BindMethod
)
}
override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
- exists(StaticMethodAccess ma, Method m | m = ma.getMethod() |
+ exists(MethodAccess ma, Method m | m = ma.getMethod() |
m.getDeclaringType().hasQualifiedName("java.rmi.server", "UnicastRemoteObject") and
m.hasName("exportObject") and
not ma.getArgument([2, 4])
@@ -79,4 +79,4 @@ private class BindingUnsafeRemoteObjectConfig extends TaintTracking::Configurati
from DataFlow::PathNode source, DataFlow::PathNode sink, BindingUnsafeRemoteObjectConfig conf
where conf.hasFlowPath(source, sink)
-select sink.getNode(), source, sink, "Binding an unsafe remote object."
+select sink.getNode(), source, sink, "Unsafe deserialization in a remote object."
diff --git a/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeDeserializationRmi.expected b/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeDeserializationRmi.expected
index 52bcccb15c5..188a8649d98 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeDeserializationRmi.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeDeserializationRmi.expected
@@ -1,4 +1,15 @@
-| RmiUnsafeDeserialization.java:13:9:13:59 | bind(...) | Unsafe deserialization with RMI in '$@' method | RmiUnsafeDeserialization.java:42:17:42:20 | take | take(Object) |
-| RmiUnsafeDeserialization.java:14:9:14:61 | rebind(...) | Unsafe deserialization with RMI in '$@' method | RmiUnsafeDeserialization.java:42:17:42:20 | take | take(Object) |
-| RmiUnsafeDeserialization.java:26:9:26:57 | bind(...) | Unsafe deserialization with RMI in '$@' method | RmiUnsafeDeserialization.java:42:17:42:20 | take | take(Object) |
-| RmiUnsafeDeserialization.java:27:9:27:59 | rebind(...) | Unsafe deserialization with RMI in '$@' method | RmiUnsafeDeserialization.java:42:17:42:20 | take | take(Object) |
+edges
+| UnsafeDeserializationRmi.java:17:68:17:95 | new UnsafeRemoteObjectImpl(...) : UnsafeRemoteObjectImpl | UnsafeDeserializationRmi.java:17:35:17:96 | exportObject(...) |
+nodes
+| UnsafeDeserializationRmi.java:15:33:15:60 | new UnsafeRemoteObjectImpl(...) | semmle.label | new UnsafeRemoteObjectImpl(...) |
+| UnsafeDeserializationRmi.java:16:35:16:62 | new UnsafeRemoteObjectImpl(...) | semmle.label | new UnsafeRemoteObjectImpl(...) |
+| UnsafeDeserializationRmi.java:17:35:17:96 | exportObject(...) | semmle.label | exportObject(...) |
+| UnsafeDeserializationRmi.java:17:68:17:95 | new UnsafeRemoteObjectImpl(...) : UnsafeRemoteObjectImpl | semmle.label | new UnsafeRemoteObjectImpl(...) : UnsafeRemoteObjectImpl |
+| UnsafeDeserializationRmi.java:29:31:29:58 | new UnsafeRemoteObjectImpl(...) | semmle.label | new UnsafeRemoteObjectImpl(...) |
+| UnsafeDeserializationRmi.java:30:33:30:60 | new UnsafeRemoteObjectImpl(...) | semmle.label | new UnsafeRemoteObjectImpl(...) |
+#select
+| UnsafeDeserializationRmi.java:15:33:15:60 | new UnsafeRemoteObjectImpl(...) | UnsafeDeserializationRmi.java:15:33:15:60 | new UnsafeRemoteObjectImpl(...) | UnsafeDeserializationRmi.java:15:33:15:60 | new UnsafeRemoteObjectImpl(...) | Unsafe deserialization in a remote object. |
+| UnsafeDeserializationRmi.java:16:35:16:62 | new UnsafeRemoteObjectImpl(...) | UnsafeDeserializationRmi.java:16:35:16:62 | new UnsafeRemoteObjectImpl(...) | UnsafeDeserializationRmi.java:16:35:16:62 | new UnsafeRemoteObjectImpl(...) | Unsafe deserialization in a remote object. |
+| UnsafeDeserializationRmi.java:17:35:17:96 | exportObject(...) | UnsafeDeserializationRmi.java:17:68:17:95 | new UnsafeRemoteObjectImpl(...) : UnsafeRemoteObjectImpl | UnsafeDeserializationRmi.java:17:35:17:96 | exportObject(...) | Unsafe deserialization in a remote object. |
+| UnsafeDeserializationRmi.java:29:31:29:58 | new UnsafeRemoteObjectImpl(...) | UnsafeDeserializationRmi.java:29:31:29:58 | new UnsafeRemoteObjectImpl(...) | UnsafeDeserializationRmi.java:29:31:29:58 | new UnsafeRemoteObjectImpl(...) | Unsafe deserialization in a remote object. |
+| UnsafeDeserializationRmi.java:30:33:30:60 | new UnsafeRemoteObjectImpl(...) | UnsafeDeserializationRmi.java:30:33:30:60 | new UnsafeRemoteObjectImpl(...) | UnsafeDeserializationRmi.java:30:33:30:60 | new UnsafeRemoteObjectImpl(...) | Unsafe deserialization in a remote object. |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeDeserializationRmi.java b/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeDeserializationRmi.java
index ee0bc3a1989..197a1c47843 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeDeserializationRmi.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeDeserializationRmi.java
@@ -1,58 +1,73 @@
+import java.io.ObjectInputFilter;
import java.io.ObjectInputStream;
import java.rmi.Naming;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
+import java.rmi.server.UnicastRemoteObject;
public class UnsafeDeserializationRmi {
- // BAD (bind a remote object that has a vulnerable method that takes Object)
+ // BAD (bind a remote object that has a vulnerable method)
public static void testRegistryBindWithObjectParameter() throws Exception {
Registry registry = LocateRegistry.createRegistry(1099);
- registry.bind("test", new RemoteObjectWithObject());
- registry.rebind("test", new RemoteObjectWithObject());
+ registry.bind("unsafe", new UnsafeRemoteObjectImpl());
+ registry.rebind("unsafe", new UnsafeRemoteObjectImpl());
+ registry.rebind("unsafe", UnicastRemoteObject.exportObject(new UnsafeRemoteObjectImpl()));
}
// GOOD (bind a remote object that has methods that takes safe parameters)
public static void testRegistryBindWithIntParameter() throws Exception {
Registry registry = LocateRegistry.createRegistry(1099);
- registry.bind("test", new SafeRemoteObject());
- registry.rebind("test", new SafeRemoteObject());
+ registry.bind("safe", new SafeRemoteObjectImpl());
+ registry.rebind("safe", new SafeRemoteObjectImpl());
}
- // BAD (bind a remote object that has a vulnerable method that takes Object)
+ // BAD (bind a remote object that has a vulnerable method)
public static void testNamingBindWithObjectParameter() throws Exception {
- Naming.bind("test", new RemoteObjectWithObject());
- Naming.rebind("test", new RemoteObjectWithObject());
+ Naming.bind("unsafe", new UnsafeRemoteObjectImpl());
+ Naming.rebind("unsafe", new UnsafeRemoteObjectImpl());
}
// GOOD (bind a remote object that has methods that takes safe parameters)
public static void testNamingBindWithIntParameter() throws Exception {
- Naming.bind("test", new SafeRemoteObject());
- Naming.rebind("test", new SafeRemoteObject());
+ Naming.bind("safe", new SafeRemoteObjectImpl());
+ Naming.rebind("safe", new SafeRemoteObjectImpl());
+ }
+
+ // GOOD (bind a remote object with a deserialization filter)
+ public static void testRegistryBindWithDeserializationFilter() throws Exception {
+ Registry registry = LocateRegistry.createRegistry(1099);
+ ObjectInputFilter filter = info -> {
+ if (info.serialClass().getCanonicalName().startsWith("com.safe.package.")) {
+ return ObjectInputFilter.Status.ALLOWED;
+ }
+ return ObjectInputFilter.Status.REJECTED;
+ };
+ registry.rebind("safe", UnicastRemoteObject.exportObject(new UnsafeRemoteObjectImpl(), 12345, filter));
}
}
-interface RemoteObjectWithObjectInterface extends Remote {
+interface UnsafeRemoteObject extends Remote {
void take(Object obj) throws RemoteException;
}
-class RemoteObjectWithObject implements RemoteObjectWithObjectInterface {
+class UnsafeRemoteObjectImpl implements UnsafeRemoteObject {
public void take(Object obj) throws RemoteException {}
}
-interface SafeRemoteObjectInterface extends Remote {
+interface SafeRemoteObject extends Remote {
void take(int n) throws RemoteException;
void take(double n) throws RemoteException;
void take(String s) throws RemoteException;
void take(ObjectInputStream ois) throws RemoteException;
}
-class SafeRemoteObject implements SafeRemoteObjectInterface {
+class SafeRemoteObjectImpl implements SafeRemoteObject {
public void take(int n) throws RemoteException {}
public void take(double n) throws RemoteException {}
public void take(String s) throws RemoteException {}
public void take(ObjectInputStream ois) throws RemoteException {}
- public void safeMethod(Object object) {} // this method is not declared in SafeRemoteObjectInterface
+ public void safeMethod(Object object) {} // this method is not declared in SafeRemoteObject
}
From 1b51dd47ec2717f0da468af752134042cbf9abc3 Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Sun, 23 May 2021 13:24:42 +0200
Subject: [PATCH 046/272] Added an example with deserialization filter to
UnsafeDeserializationRmi.qhelp
---
.../CWE-502/RmiRemoteObjectWithFilter.java | 9 +++++++
.../CWE/CWE-502/RmiSafeRemoteObject.java | 3 +--
.../CWE/CWE-502/RmiUnsafeRemoteObject.java | 3 +--
.../CWE-502/UnsafeDeserializationRmi.qhelp | 26 ++++++++++++++-----
4 files changed, 30 insertions(+), 11 deletions(-)
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-502/RmiRemoteObjectWithFilter.java
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/RmiRemoteObjectWithFilter.java b/java/ql/src/experimental/Security/CWE/CWE-502/RmiRemoteObjectWithFilter.java
new file mode 100644
index 00000000000..6d4d2a5357f
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/RmiRemoteObjectWithFilter.java
@@ -0,0 +1,9 @@
+public void bindRemoteObject(Registry registry, int port) throws Exception {
+ ObjectInputFilter filter = info -> {
+ if (info.serialClass().getCanonicalName().startsWith("com.safe.package.")) {
+ return ObjectInputFilter.Status.ALLOWED;
+ }
+ return ObjectInputFilter.Status.REJECTED;
+ };
+ registry.bind("safer", UnicastRemoteObject.exportObject(new RemoteObjectImpl(), port, filter));
+}
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/RmiSafeRemoteObject.java b/java/ql/src/experimental/Security/CWE/CWE-502/RmiSafeRemoteObject.java
index 2f30bc1f3ba..0c8836522ca 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-502/RmiSafeRemoteObject.java
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/RmiSafeRemoteObject.java
@@ -1,6 +1,5 @@
public class Server {
- public static void main(String... args) throws Exception {
- Registry registry = LocateRegistry.createRegistry(1099);
+ public void bindRemoteObject(Registry registry) throws Exception {
registry.bind("safe", new RemoteObjectImpl());
}
}
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeRemoteObject.java b/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeRemoteObject.java
index 4c04b57cde9..96d48e9c9af 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeRemoteObject.java
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeRemoteObject.java
@@ -1,6 +1,5 @@
public class Server {
- public static void main(String... args) throws Exception {
- Registry registry = LocateRegistry.createRegistry(1099);
+ public void bindRemoteObject(Registry registry) throws Exception {
registry.bind("unsafe", new RemoteObjectImpl());
}
}
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.qhelp b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.qhelp
index 1921476ffbd..985e3a4c08d 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.qhelp
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.qhelp
@@ -15,14 +15,21 @@ In the worst case, it results in remote code execution.
Use only strings and primitive types in parameters of remote objects.
+
+Set a filter for incoming serialized data by wrapping remote objects using either UnicastRemoteObject.exportObject(Remote, int, ObjectInputFilter)
+or UnicastRemoteObject.exportObject(Remote, int, RMIClientSocketFactory, RMIServerSocketFactory, ObjectInputFilter) methods.
+Those methods accept an ObjectInputFilter that decides which classes are allowed for deserialization.
+The filter should allow deserializing only safe classes.
+
-Java RMI does not offer API for specifying classes which are only allowed for deserialization.
-However, it is possible to set a process-wide deserialization filter that was introduced in JEP 290.
-The filter can be set via system or security property jdk.serialFilter.
+It is also possible to set a process-wide deserialization filter.
+The filter can be set by with ObjectInputFilter.Config.setSerialFilter(ObjectInputFilter) method,
+or by setting system or security property jdk.serialFilter.
Make sure that you use the latest Java versions that include JEP 290.
-Consider using other implementations of remote procedure calls. For example, HTTP API with JSON.
+If switching to the latest Java versions is not possible,
+consider using other implementations of remote procedure calls. For example, HTTP API with JSON.
Make sure that the underlying deserialization mechanism is properly configured
so that deserialization attacks are not possible.
@@ -30,17 +37,22 @@ so that deserialization attacks are not possible.
-The following code registers a vulnerable remote object
-which has a method that accepts a complex object:
+The following code registers a remote object
+with a vulnerable method that accepts a complex object:
The next example registers a safe remote object
-which has methods that use only primitive types and strings:
+whose methods use only primitive types and strings:
+
+The next example shows how to set a deserilization filter for a remote object:
+
+
+
From c70651b6feaea545e04107f39dffac31be71a799 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Tue, 25 May 2021 11:48:54 +0200
Subject: [PATCH 047/272] always have `arrayLikeElement` as TypeTracking
properties
---
.../ql/src/semmle/javascript/dataflow/internal/StepSummary.qll | 2 ++
1 file changed, 2 insertions(+)
diff --git a/javascript/ql/src/semmle/javascript/dataflow/internal/StepSummary.qll b/javascript/ql/src/semmle/javascript/dataflow/internal/StepSummary.qll
index 2c1362c871d..14a6cdf0d1e 100644
--- a/javascript/ql/src/semmle/javascript/dataflow/internal/StepSummary.qll
+++ b/javascript/ql/src/semmle/javascript/dataflow/internal/StepSummary.qll
@@ -27,6 +27,8 @@ private module Cached {
SharedTypeTrackingStep::loadStoreStep(_, _, this, _)
or
SharedTypeTrackingStep::loadStoreStep(_, _, _, this)
+ or
+ this = DataFlow::PseudoProperties::arrayLikeElement()
}
}
From bfc8845f23daecfaa14f6ac7ba79d7a653577cfd Mon Sep 17 00:00:00 2001
From: shati-patel <42641846+shati-patel@users.noreply.github.com>
Date: Tue, 25 May 2021 11:36:18 +0100
Subject: [PATCH 048/272] Update wording
---
.../codeql-for-visual-studio-code/customizing-settings.rst | 2 +-
docs/codeql/reusables/running-queries-debug.rst | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/codeql/codeql-for-visual-studio-code/customizing-settings.rst b/docs/codeql/codeql-for-visual-studio-code/customizing-settings.rst
index 10c0fb12744..6b1b820b6aa 100644
--- a/docs/codeql/codeql-for-visual-studio-code/customizing-settings.rst
+++ b/docs/codeql/codeql-for-visual-studio-code/customizing-settings.rst
@@ -55,7 +55,7 @@ There are a number of settings for **Running Queries**. If your queries run too
.. include:: ../reusables/running-queries-debug.rst
-To save CodeQL Query Server logs in a custom location, edit the **Running Queries: Custom Log Directory** setting. If you use a custom log directory, the extension doesn't automatically delete the CodeQL Query Server logs. This is useful if you want to investigate these logs to improve the performance of your queries.
+To save query server logs in a custom location, edit the **Running Queries: Custom Log Directory** setting. If you use a custom log directory, the extension saves the logs permanently, instead of deleting them automatically after each workspace session. This is useful if you want to investigate these logs to improve the performance of your queries.
Configuring settings for testing queries
-----------------------------------------
diff --git a/docs/codeql/reusables/running-queries-debug.rst b/docs/codeql/reusables/running-queries-debug.rst
index d7515fc4236..26e7162aa2b 100644
--- a/docs/codeql/reusables/running-queries-debug.rst
+++ b/docs/codeql/reusables/running-queries-debug.rst
@@ -1 +1 @@
-If you want to examine query performance, enable the **Running Queries: Debug** setting to include timing and tuple counts in the CodeQL Query Server logs shown in the Output view. The tuple count is useful because it indicates the size of the :ref:`predicates ` computed by the query.
\ No newline at end of file
+If you want to examine query performance, enable the **Running Queries: Debug** setting to include timing and tuple counts. This is shown in the logs in the CodeQL Query Server tab of the Output view. The tuple count is useful because it indicates the size of the :ref:`predicates ` computed by the query.
\ No newline at end of file
From ee3477c20a41f64c89b3eed6c75f0be9f014818f Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Tue, 25 May 2021 14:27:29 +0200
Subject: [PATCH 049/272] Python: Remove dummy clickhouse SQL injection query
---
.../CWE-089/ClickHouseSQLInjection.py | 28 ---------
.../CWE-089/ClickHouseSQLInjection.qhelp | 59 -------------------
.../CWE-089/ClickHouseSQLInjection.ql | 22 -------
3 files changed, 109 deletions(-)
delete mode 100644 python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.py
delete mode 100644 python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.qhelp
delete mode 100644 python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.ql
diff --git a/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.py b/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.py
deleted file mode 100644
index 6668d8eb15f..00000000000
--- a/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.py
+++ /dev/null
@@ -1,28 +0,0 @@
-from django.conf.urls import url
-from clickhouse_driver import Client
-from clickhouse_driver import connect
-from aioch import Client as aiochClient
-
-class MyClient(Client):
- def dummy(self):
- return None
-
-def show_user(request, username):
-
- # BAD -- Untrusted user input is directly injected into the sql query using async library 'aioch'
- aclient = aiochClient("localhost")
- progress = await aclient.execute_with_progress("SELECT * FROM users WHERE username = '%s'" % username)
-
- # BAD -- Untrusted user input is directly injected into the sql query using native client of library 'clickhouse_driver'
- Client('localhost').execute("SELECT * FROM users WHERE username = '%s'" % username)
-
- # GOOD -- query uses prepared statements
- query = "SELECT * FROM users WHERE username = %(username)s"
- Client('localhost').execute(query, {"username": username})
-
- # BAD -- PEP249 interface
- conn = connect('clickhouse://localhost')
- cursor = conn.cursor()
- cursor.execute("SELECT * FROM users WHERE username = '%s'" % username)
-
-urlpatterns = [url(r'^users/(?P[^/]+)$', show_user)]
diff --git a/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.qhelp b/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.qhelp
deleted file mode 100644
index be007545632..00000000000
--- a/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.qhelp
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
-
-
-If a database query (such as a SQL or NoSQL query) is built from
-user-provided data without sufficient sanitization, a user
-may be able to run malicious database queries.
-
-
-
-
-
-Most database connector libraries offer a way of safely
-embedding untrusted data into a query by means of query parameters
-or prepared statements.
-
-
-
-
-
-In the following snippet, a user is fetched from a ClickHouse database
-using five different queries. In the "BAD" cases the query is built directly from user-controlled data.
-In the "GOOD" case the user-controlled data is safely embedded into the query by using query parameters.
-
-
-
-In the first case, the query executed via aioch Client. aioch - is a module
-for asynchronous queries to database.
-
-
-
-In the second and third cases, the connection is established via `Client` class.
-This class implement different method to execute a query.
-
-
-
-In the forth case, good pattern is presented. Query parameters are send through
-second dict-like argument.
-
-
-
-In the fifth case, there is example of PEP249 interface usage.
-
-
-
-In the sixth case, there is custom Class usge which is a subclass of default Client.
-
-
-
diff --git a/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.ql b/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.ql
deleted file mode 100644
index f0efb523756..00000000000
--- a/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.ql
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * @id py/yandex/clickhouse-sql-injection
- * @name Clickhouse SQL query built from user-controlled sources
- * @description Building a SQL query from user-controlled sources is vulnerable to insertion of
- * malicious SQL code by the user.
- * @kind path-problem
- * @problem.severity error
- * @precision high
- * @tags security
- * external/cwe/cwe-089
- * external/owasp/owasp-a1
- */
-
-import python
-import experimental.semmle.python.frameworks.ClickHouseDriver
-import semmle.python.security.dataflow.SqlInjection
-import DataFlow::PathGraph
-
-from SQLInjectionConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
-where config.hasFlowPath(source, sink)
-select sink.getNode(), source, sink, "This SQL query depends on $@.", source.getNode(),
- "a user-provided value"
From c9a9535dbcc6b3565032cadb97ead4b883756f36 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Tue, 25 May 2021 14:53:09 +0200
Subject: [PATCH 050/272] Python: Use ConceptsTests for ClickHouse SQL libs
This did reveal a few places where we do not detect the incoming SQL
---
.../frameworks/aioch/ConceptsTest.expected | 0
.../ConceptsTest.ql} | 5 +--
.../semmle/python/frameworks/aioch/options | 1 +
.../python/frameworks/aioch/sql_test.py | 30 +++++++++++++
.../ClickHouseDriver.expected | 5 ---
.../clickhouse-driver/ClickHouseDriver.py | 32 --------------
.../clickhouse_driver/ConceptsTest.expected | 0
.../clickhouse_driver/ConceptsTest.ql | 3 ++
.../frameworks/clickhouse_driver/sql_test.py | 42 +++++++++++++++++++
.../semmle/python/frameworks/options | 1 +
10 files changed, 78 insertions(+), 41 deletions(-)
create mode 100644 python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.expected
rename python/ql/test/experimental/semmle/python/frameworks/{clickhouse-driver/ClickHouseDriver.ql => aioch/ConceptsTest.ql} (51%)
create mode 100644 python/ql/test/experimental/semmle/python/frameworks/aioch/options
create mode 100644 python/ql/test/experimental/semmle/python/frameworks/aioch/sql_test.py
delete mode 100644 python/ql/test/experimental/semmle/python/frameworks/clickhouse-driver/ClickHouseDriver.expected
delete mode 100644 python/ql/test/experimental/semmle/python/frameworks/clickhouse-driver/ClickHouseDriver.py
create mode 100644 python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.expected
create mode 100644 python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.ql
create mode 100644 python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/sql_test.py
create mode 100644 python/ql/test/experimental/semmle/python/frameworks/options
diff --git a/python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.expected b/python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.expected
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/python/ql/test/experimental/semmle/python/frameworks/clickhouse-driver/ClickHouseDriver.ql b/python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.ql
similarity index 51%
rename from python/ql/test/experimental/semmle/python/frameworks/clickhouse-driver/ClickHouseDriver.ql
rename to python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.ql
index bedd47c0af6..65b2c78c74a 100644
--- a/python/ql/test/experimental/semmle/python/frameworks/clickhouse-driver/ClickHouseDriver.ql
+++ b/python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.ql
@@ -1,6 +1,3 @@
import python
+import experimental.meta.ConceptsTest
import experimental.semmle.python.frameworks.ClickHouseDriver
-import semmle.python.Concepts
-
-from SqlExecution s
-select s, s.getSql()
diff --git a/python/ql/test/experimental/semmle/python/frameworks/aioch/options b/python/ql/test/experimental/semmle/python/frameworks/aioch/options
new file mode 100644
index 00000000000..cfef58cf2b2
--- /dev/null
+++ b/python/ql/test/experimental/semmle/python/frameworks/aioch/options
@@ -0,0 +1 @@
+semmle-extractor-options: --max-import-depth=1 --lang=3
diff --git a/python/ql/test/experimental/semmle/python/frameworks/aioch/sql_test.py b/python/ql/test/experimental/semmle/python/frameworks/aioch/sql_test.py
new file mode 100644
index 00000000000..468791aa3dc
--- /dev/null
+++ b/python/ql/test/experimental/semmle/python/frameworks/aioch/sql_test.py
@@ -0,0 +1,30 @@
+import aioch
+
+
+SQL = "SOME SQL"
+
+
+async def aioch_test():
+ client = aioch.Client("localhost")
+
+ await client.execute(SQL) # $ getSql=SQL
+ await client.execute(query=SQL) # $ MISSING: getSql=SQL
+
+ await client.execute_with_progress(SQL) # $ getSql=SQL
+ await client.execute_with_progress(query=SQL) # $ MISSING: getSql=SQL
+
+ await client.execute_iter(SQL) # $ getSql=SQL
+ await client.execute_iter(query=SQL) # $ MISSING: getSql=SQL
+
+
+# Using custom client (this has been seen done for the blocking version in
+# `clickhouse_driver` PyPI package)
+
+
+class MyClient(aioch.Client):
+ pass
+
+
+async def test_custom_client():
+ client = MyClient("localhost")
+ await client.execute(SQL) # $ getSql=SQL
diff --git a/python/ql/test/experimental/semmle/python/frameworks/clickhouse-driver/ClickHouseDriver.expected b/python/ql/test/experimental/semmle/python/frameworks/clickhouse-driver/ClickHouseDriver.expected
deleted file mode 100644
index 52aa30cb8c1..00000000000
--- a/python/ql/test/experimental/semmle/python/frameworks/clickhouse-driver/ClickHouseDriver.expected
+++ /dev/null
@@ -1,5 +0,0 @@
-| ClickHouseDriver.py:15:22:15:106 | ControlFlowNode for Attribute() | ClickHouseDriver.py:15:52:15:105 | ControlFlowNode for BinaryExpr |
-| ClickHouseDriver.py:18:5:18:87 | ControlFlowNode for Attribute() | ClickHouseDriver.py:18:33:18:86 | ControlFlowNode for BinaryExpr |
-| ClickHouseDriver.py:22:5:22:62 | ControlFlowNode for Attribute() | ClickHouseDriver.py:22:33:22:37 | ControlFlowNode for query |
-| ClickHouseDriver.py:27:5:27:74 | ControlFlowNode for Attribute() | ClickHouseDriver.py:27:20:27:73 | ControlFlowNode for BinaryExpr |
-| ClickHouseDriver.py:30:5:30:89 | ControlFlowNode for Attribute() | ClickHouseDriver.py:30:35:30:88 | ControlFlowNode for BinaryExpr |
diff --git a/python/ql/test/experimental/semmle/python/frameworks/clickhouse-driver/ClickHouseDriver.py b/python/ql/test/experimental/semmle/python/frameworks/clickhouse-driver/ClickHouseDriver.py
deleted file mode 100644
index 7684f3df795..00000000000
--- a/python/ql/test/experimental/semmle/python/frameworks/clickhouse-driver/ClickHouseDriver.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from django.conf.urls import url
-from clickhouse_driver import Client
-from clickhouse_driver import connect
-from aioch import Client as aiochClient
-
-# Dummy Client subclass
-class MyClient(Client):
- def dummy(self):
- return None
-
-def show_user(request, username):
-
- # BAD -- Untrusted user input is directly injected into the sql query using async library 'aioch'
- aclient = aiochClient("localhost")
- progress = await aclient.execute_with_progress("SELECT * FROM users WHERE username = '%s'" % username)
-
- # BAD -- Untrusted user input is directly injected into the sql query using native client of library 'clickhouse_driver'
- Client('localhost').execute("SELECT * FROM users WHERE username = '%s'" % username)
-
- # GOOD -- query uses prepared statements
- query = "SELECT * FROM users WHERE username = %(username)s"
- Client('localhost').execute(query, {"username": username})
-
- # BAD -- Untrusted user input is directly injected into the sql query using PEP249 interface
- conn = connect('clickhouse://localhost')
- cursor = conn.cursor()
- cursor.execute("SELECT * FROM users WHERE username = '%s'" % username)
-
- # BAD -- Untrusted user input is directly injected into the sql query using MyClient, which is a subclass of Client
- MyClient('localhost').execute("SELECT * FROM users WHERE username = '%s'" % username)
-
-urlpatterns = [url(r'^users/(?P[^/]+)$', show_user)]
diff --git a/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.expected b/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.expected
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.ql b/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.ql
new file mode 100644
index 00000000000..65b2c78c74a
--- /dev/null
+++ b/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.ql
@@ -0,0 +1,3 @@
+import python
+import experimental.meta.ConceptsTest
+import experimental.semmle.python.frameworks.ClickHouseDriver
diff --git a/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/sql_test.py b/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/sql_test.py
new file mode 100644
index 00000000000..d1e2491e86e
--- /dev/null
+++ b/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/sql_test.py
@@ -0,0 +1,42 @@
+import clickhouse_driver
+
+
+SQL = "SOME SQL"
+
+
+# Normal operation
+client = clickhouse_driver.client.Client("localhost")
+
+client.execute(SQL) # $ MISSING: getSql=SQL
+client.execute(query=SQL) # $ MISSING: getSql=SQL
+
+client.execute_with_progress(SQL) # $ MISSING: getSql=SQL
+client.execute_with_progress(query=SQL) # $ MISSING: getSql=SQL
+
+client.execute_iter(SQL) # $ MISSING: getSql=SQL
+client.execute_iter(query=SQL) # $ MISSING: getSql=SQL
+
+
+# commonly used alias
+client = clickhouse_driver.Client("localhost")
+client.execute(SQL) # $ getSql=SQL
+
+
+# Using PEP249 interface
+conn = clickhouse_driver.connect('clickhouse://localhost')
+cursor = conn.cursor()
+cursor.execute(SQL) # $ getSql=SQL
+
+
+# Using custom client
+#
+# examples from real world code
+# https://github.com/Altinity/clickhouse-mysql-data-reader/blob/3b1b7088751b05e5bbf45890c5949b58208c2343/clickhouse_mysql/dbclient/chclient.py#L10
+# https://github.com/Felixoid/clickhouse-plantuml/blob/d8b2ba7d164a836770ec21f5e4035dfb04c41d9c/clickhouse_plantuml/client.py#L9
+
+
+class MyClient(clickhouse_driver.Client):
+ pass
+
+
+MyClient("localhost").execute(SQL) # $ getSql=SQL
diff --git a/python/ql/test/experimental/semmle/python/frameworks/options b/python/ql/test/experimental/semmle/python/frameworks/options
new file mode 100644
index 00000000000..eb214fc2931
--- /dev/null
+++ b/python/ql/test/experimental/semmle/python/frameworks/options
@@ -0,0 +1 @@
+semmle-extractor-options: --max-import-depth=1
From eb1da152a0d54bff63094056f12ba92a309a9a91 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Tue, 25 May 2021 16:13:31 +0200
Subject: [PATCH 051/272] Python: Rewrite ClickHouse SQL lib modeling
This did turn into a few changes, that maybe could have been split into
separate PRs :shrug:
* Rename `ClickHouseDriver` => `ClickhouseDriver`, to better follow
import name in `.qll` name
* Rewrote modeling to use API graphs
* Split modeling of `aioch` into separate `.qll` file, which does re-use
the `getExecuteMethodName` predicate. I feel that sharing code between
the modeling like this was the best approach, and stuck the
`INTERNAL: Do not use.` labels on both modules.
* I also added handling of keyword arguments (see change in .py files)
---
.../semmle/python/frameworks/Aioch.qll | 52 ++++++++++++
.../python/frameworks/ClickHouseDriver.qll | 85 -------------------
.../python/frameworks/ClickhouseDriver.qll | 65 ++++++++++++++
.../python/frameworks/aioch/ConceptsTest.ql | 2 +-
.../python/frameworks/aioch/sql_test.py | 6 +-
.../clickhouse_driver/ConceptsTest.ql | 2 +-
.../frameworks/clickhouse_driver/sql_test.py | 12 +--
7 files changed, 128 insertions(+), 96 deletions(-)
create mode 100644 python/ql/src/experimental/semmle/python/frameworks/Aioch.qll
delete mode 100644 python/ql/src/experimental/semmle/python/frameworks/ClickHouseDriver.qll
create mode 100644 python/ql/src/experimental/semmle/python/frameworks/ClickhouseDriver.qll
diff --git a/python/ql/src/experimental/semmle/python/frameworks/Aioch.qll b/python/ql/src/experimental/semmle/python/frameworks/Aioch.qll
new file mode 100644
index 00000000000..dde97047046
--- /dev/null
+++ b/python/ql/src/experimental/semmle/python/frameworks/Aioch.qll
@@ -0,0 +1,52 @@
+/**
+ * Provides classes modeling security-relevant aspects of the `aioch` PyPI package (an
+ * async-io version of the `clickhouse-driver` PyPI package).
+ *
+ * See https://pypi.org/project/aioch/
+ */
+
+private import python
+private import semmle.python.Concepts
+private import semmle.python.ApiGraphs
+private import semmle.python.frameworks.PEP249
+private import experimental.semmle.python.frameworks.ClickhouseDriver
+
+/**
+ * INTERNAL: Do not use.
+ *
+ * Provides models for `aioch` PyPI package (an async-io version of the
+ * `clickhouse-driver` PyPI package).
+ *
+ * See https://pypi.org/project/aioch/
+ */
+module Aioch {
+ /** Provides models for `aioch.Client` class and subclasses. */
+ module Client {
+ /** Gets a reference to the `aioch.Client` class or any subclass. */
+ API::Node subclassRef() {
+ result = API::moduleImport("aioch").getMember("Client").getASubclass*()
+ }
+
+ /** Gets a reference to an instance of `clickhouse_driver.Client` or any subclass. */
+ API::Node instance() { result = subclassRef().getReturn() }
+ }
+
+ /**
+ * A call to any of the the execute methods on a `aioch.Client`, which are just async
+ * versions of the methods in the `clickhouse-driver` PyPI package.
+ *
+ * See
+ * - https://clickhouse-driver.readthedocs.io/en/latest/api.html#clickhouse_driver.Client.execute
+ * - https://clickhouse-driver.readthedocs.io/en/latest/api.html#clickhouse_driver.Client.execute_iter
+ * - https://clickhouse-driver.readthedocs.io/en/latest/api.html#clickhouse_driver.Client.execute_with_progress
+ */
+ class ClientExecuteCall extends SqlExecution::Range, DataFlow::CallCfgNode {
+ ClientExecuteCall() {
+ exists(string methodName | methodName = ClickhouseDriver::getExecuteMethodName() |
+ this = Client::instance().getMember(methodName).getACall()
+ )
+ }
+
+ override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("query")] }
+ }
+}
diff --git a/python/ql/src/experimental/semmle/python/frameworks/ClickHouseDriver.qll b/python/ql/src/experimental/semmle/python/frameworks/ClickHouseDriver.qll
deleted file mode 100644
index c456b6bdb89..00000000000
--- a/python/ql/src/experimental/semmle/python/frameworks/ClickHouseDriver.qll
+++ /dev/null
@@ -1,85 +0,0 @@
-/**
- * Provides classes modeling security-relevant aspects of `clickhouse-driver` and `aioch` PyPI packages.
- * See
- * - https://pypi.org/project/clickhouse-driver/
- * - https://pypi.org/project/aioch/
- * - https://clickhouse-driver.readthedocs.io/en/latest/
- */
-
-private import python
-private import semmle.python.Concepts
-private import semmle.python.ApiGraphs
-private import semmle.python.frameworks.PEP249
-
-/**
- * Provides models for `clickhouse-driver` and `aioch` PyPI packages.
- * See
- * - https://pypi.org/project/clickhouse-driver/
- * - https://pypi.org/project/aioch/
- * - https://clickhouse-driver.readthedocs.io/en/latest/
- */
-module ClickHouseDriver {
- /** Gets a reference to the `clickhouse_driver` module. */
- API::Node clickhouse_driver() { result = API::moduleImport("clickhouse_driver") }
-
- /** Gets a reference to the `aioch` module. This module allows to make async db queries. */
- API::Node aioch() { result = API::moduleImport("aioch") }
-
- /**
- * `clickhouse_driver` implements PEP249,
- * providing ways to execute SQL statements against a database.
- */
- class ClickHouseDriverPEP249 extends PEP249ModuleApiNode {
- ClickHouseDriverPEP249() { this = clickhouse_driver() }
- }
-
- module Client {
- /** Gets a reference to a Client call. */
- private DataFlow::Node client_ref() {
- result = clickhouse_driver().getMember("Client").getASubclass*().getAUse()
- or
- result = aioch().getMember("Client").getASubclass*().getAUse()
- }
-
- /** A direct instantiation of `clickhouse_driver.Client`. */
- private class ClientInstantiation extends DataFlow::CallCfgNode {
- ClientInstantiation() { this.getFunction() = client_ref() }
- }
-
- /** Gets a reference to an instance of `clickhouse_driver.Client`. */
- private DataFlow::LocalSourceNode instance(DataFlow::TypeTracker t) {
- t.start() and
- result instanceof ClientInstantiation
- or
- exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
- }
-
- /** Gets a reference to an instance of `clickhouse_driver.Client`. */
- DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
- }
-
- /** clickhouse_driver.Client execute methods */
- private string execute_function() {
- result in ["execute_with_progress", "execute", "execute_iter"]
- }
-
- /** Gets a reference to the `clickhouse_driver.Client.execute` method */
- private DataFlow::LocalSourceNode clickhouse_execute(DataFlow::TypeTracker t) {
- t.startInAttr(execute_function()) and
- result = Client::instance()
- or
- exists(DataFlow::TypeTracker t2 | result = clickhouse_execute(t2).track(t2, t))
- }
-
- /** Gets a reference to the `clickhouse_driver.Client.execute` method */
- DataFlow::Node clickhouse_execute() {
- clickhouse_execute(DataFlow::TypeTracker::end()).flowsTo(result)
- }
-
- /** A call to the `clickhouse_driver.Client.execute` method */
- private class ExecuteCall extends SqlExecution::Range, DataFlow::CallCfgNode {
- ExecuteCall() { this.getFunction() = clickhouse_execute() }
-
- override DataFlow::Node getSql() { result.asCfgNode() = node.getArg(0) }
- }
-}
diff --git a/python/ql/src/experimental/semmle/python/frameworks/ClickhouseDriver.qll b/python/ql/src/experimental/semmle/python/frameworks/ClickhouseDriver.qll
new file mode 100644
index 00000000000..8863b1dbe66
--- /dev/null
+++ b/python/ql/src/experimental/semmle/python/frameworks/ClickhouseDriver.qll
@@ -0,0 +1,65 @@
+/**
+ * Provides classes modeling security-relevant aspects of the `clickhouse-driver` PyPI package.
+ * See
+ * - https://pypi.org/project/clickhouse-driver/
+ * - https://clickhouse-driver.readthedocs.io/en/latest/
+ */
+
+private import python
+private import semmle.python.Concepts
+private import semmle.python.ApiGraphs
+private import semmle.python.frameworks.PEP249
+
+/**
+ * INTERNAL: Do not use.
+ *
+ * Provides models for `clickhouse-driver` PyPI package (imported as `clickhouse_driver`).
+ * See
+ * - https://pypi.org/project/clickhouse-driver/
+ * - https://clickhouse-driver.readthedocs.io/en/latest/
+ */
+module ClickhouseDriver {
+ /**
+ * `clickhouse_driver` implements PEP249,
+ * providing ways to execute SQL statements against a database.
+ */
+ class ClickHouseDriverPEP249 extends PEP249ModuleApiNode {
+ ClickHouseDriverPEP249() { this = API::moduleImport("clickhouse_driver") }
+ }
+
+ /** Provides models for `clickhouse_driver.Client` class and subclasses. */
+ module Client {
+ /** Gets a reference to the `clickhouse_driver.Client` class or any subclass. */
+ API::Node subclassRef() {
+ exists(API::Node classRef |
+ // canonical definition
+ classRef = API::moduleImport("clickhouse_driver").getMember("client").getMember("Client")
+ or
+ // commonly used alias
+ classRef = API::moduleImport("clickhouse_driver").getMember("Client")
+ |
+ result = classRef.getASubclass*()
+ )
+ }
+
+ /** Gets a reference to an instance of `clickhouse_driver.Client` or any subclass. */
+ API::Node instance() { result = subclassRef().getReturn() }
+ }
+
+ /** `clickhouse_driver.Client` execute method names */
+ string getExecuteMethodName() { result in ["execute_with_progress", "execute", "execute_iter"] }
+
+ /**
+ * A call to any of the the execute methods on a `clickhouse_driver.Client` method
+ *
+ * See
+ * - https://clickhouse-driver.readthedocs.io/en/latest/api.html#clickhouse_driver.Client.execute
+ * - https://clickhouse-driver.readthedocs.io/en/latest/api.html#clickhouse_driver.Client.execute_iter
+ * - https://clickhouse-driver.readthedocs.io/en/latest/api.html#clickhouse_driver.Client.execute_with_progress
+ */
+ class ClientExecuteCall extends SqlExecution::Range, DataFlow::CallCfgNode {
+ ClientExecuteCall() { this = Client::instance().getMember(getExecuteMethodName()).getACall() }
+
+ override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("query")] }
+ }
+}
diff --git a/python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.ql b/python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.ql
index 65b2c78c74a..c0c26131055 100644
--- a/python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.ql
+++ b/python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.ql
@@ -1,3 +1,3 @@
import python
import experimental.meta.ConceptsTest
-import experimental.semmle.python.frameworks.ClickHouseDriver
+import experimental.semmle.python.frameworks.Aioch
diff --git a/python/ql/test/experimental/semmle/python/frameworks/aioch/sql_test.py b/python/ql/test/experimental/semmle/python/frameworks/aioch/sql_test.py
index 468791aa3dc..de6b018b0d4 100644
--- a/python/ql/test/experimental/semmle/python/frameworks/aioch/sql_test.py
+++ b/python/ql/test/experimental/semmle/python/frameworks/aioch/sql_test.py
@@ -8,13 +8,13 @@ async def aioch_test():
client = aioch.Client("localhost")
await client.execute(SQL) # $ getSql=SQL
- await client.execute(query=SQL) # $ MISSING: getSql=SQL
+ await client.execute(query=SQL) # $ getSql=SQL
await client.execute_with_progress(SQL) # $ getSql=SQL
- await client.execute_with_progress(query=SQL) # $ MISSING: getSql=SQL
+ await client.execute_with_progress(query=SQL) # $ getSql=SQL
await client.execute_iter(SQL) # $ getSql=SQL
- await client.execute_iter(query=SQL) # $ MISSING: getSql=SQL
+ await client.execute_iter(query=SQL) # $ getSql=SQL
# Using custom client (this has been seen done for the blocking version in
diff --git a/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.ql b/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.ql
index 65b2c78c74a..86e878cf8c7 100644
--- a/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.ql
+++ b/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.ql
@@ -1,3 +1,3 @@
import python
import experimental.meta.ConceptsTest
-import experimental.semmle.python.frameworks.ClickHouseDriver
+import experimental.semmle.python.frameworks.ClickhouseDriver
diff --git a/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/sql_test.py b/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/sql_test.py
index d1e2491e86e..36d4966c186 100644
--- a/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/sql_test.py
+++ b/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/sql_test.py
@@ -7,14 +7,14 @@ SQL = "SOME SQL"
# Normal operation
client = clickhouse_driver.client.Client("localhost")
-client.execute(SQL) # $ MISSING: getSql=SQL
-client.execute(query=SQL) # $ MISSING: getSql=SQL
+client.execute(SQL) # $ getSql=SQL
+client.execute(query=SQL) # $ getSql=SQL
-client.execute_with_progress(SQL) # $ MISSING: getSql=SQL
-client.execute_with_progress(query=SQL) # $ MISSING: getSql=SQL
+client.execute_with_progress(SQL) # $ getSql=SQL
+client.execute_with_progress(query=SQL) # $ getSql=SQL
-client.execute_iter(SQL) # $ MISSING: getSql=SQL
-client.execute_iter(query=SQL) # $ MISSING: getSql=SQL
+client.execute_iter(SQL) # $ getSql=SQL
+client.execute_iter(query=SQL) # $ getSql=SQL
# commonly used alias
From 1b3f857a2fdeb03e38fe0964c68d984fc23c5feb Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Tue, 25 May 2021 16:27:23 +0200
Subject: [PATCH 052/272] Python: Promote ClickHouse SQL models
---
docs/codeql/support/reusables/frameworks.rst | 2 ++
python/change-notes/2021-05-25-add-ClickHouse-sql-libs.md | 2 ++
python/ql/src/semmle/python/Frameworks.qll | 2 ++
.../src/{experimental => }/semmle/python/frameworks/Aioch.qll | 2 +-
.../semmle/python/frameworks/ClickhouseDriver.qll | 0
.../semmle/python/frameworks/clickhouse_driver/ConceptsTest.ql | 3 ---
python/ql/test/experimental/semmle/python/frameworks/options | 1 -
.../frameworks/aioch/ConceptsTest.expected | 0
.../python => library-tests}/frameworks/aioch/ConceptsTest.ql | 1 -
.../semmle/python => library-tests}/frameworks/aioch/options | 0
.../python => library-tests}/frameworks/aioch/sql_test.py | 0
.../frameworks/clickhouse_driver/ConceptsTest.expected | 0
.../library-tests/frameworks/clickhouse_driver/ConceptsTest.ql | 2 ++
.../frameworks/clickhouse_driver/sql_test.py | 0
14 files changed, 9 insertions(+), 6 deletions(-)
create mode 100644 python/change-notes/2021-05-25-add-ClickHouse-sql-libs.md
rename python/ql/src/{experimental => }/semmle/python/frameworks/Aioch.qll (96%)
rename python/ql/src/{experimental => }/semmle/python/frameworks/ClickhouseDriver.qll (100%)
delete mode 100644 python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.ql
delete mode 100644 python/ql/test/experimental/semmle/python/frameworks/options
rename python/ql/test/{experimental/semmle/python => library-tests}/frameworks/aioch/ConceptsTest.expected (100%)
rename python/ql/test/{experimental/semmle/python => library-tests}/frameworks/aioch/ConceptsTest.ql (50%)
rename python/ql/test/{experimental/semmle/python => library-tests}/frameworks/aioch/options (100%)
rename python/ql/test/{experimental/semmle/python => library-tests}/frameworks/aioch/sql_test.py (100%)
rename python/ql/test/{experimental/semmle/python => library-tests}/frameworks/clickhouse_driver/ConceptsTest.expected (100%)
create mode 100644 python/ql/test/library-tests/frameworks/clickhouse_driver/ConceptsTest.ql
rename python/ql/test/{experimental/semmle/python => library-tests}/frameworks/clickhouse_driver/sql_test.py (100%)
diff --git a/docs/codeql/support/reusables/frameworks.rst b/docs/codeql/support/reusables/frameworks.rst
index be9949aad77..532456d76b4 100644
--- a/docs/codeql/support/reusables/frameworks.rst
+++ b/docs/codeql/support/reusables/frameworks.rst
@@ -162,6 +162,8 @@ Python built-in support
fabric, Utility library
invoke, Utility library
idna, Utility library
+ aioch, Database
+ clickhouse-driver, Database
mysql-connector-python, Database
MySQLdb, Database
psycopg2, Database
diff --git a/python/change-notes/2021-05-25-add-ClickHouse-sql-libs.md b/python/change-notes/2021-05-25-add-ClickHouse-sql-libs.md
new file mode 100644
index 00000000000..4638180f8c5
--- /dev/null
+++ b/python/change-notes/2021-05-25-add-ClickHouse-sql-libs.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* Added model of SQL execution in `clickhouse-driver` and `aioch` PyPI packages, resulting in additional sinks for the SQL Injection query (`py/sql-injection`). This modeling was originally [submitted as a contribution by @japroc](https://github.com/github/codeql/pull/5889).
diff --git a/python/ql/src/semmle/python/Frameworks.qll b/python/ql/src/semmle/python/Frameworks.qll
index 3115c3ffac6..96ae176465e 100644
--- a/python/ql/src/semmle/python/Frameworks.qll
+++ b/python/ql/src/semmle/python/Frameworks.qll
@@ -4,6 +4,8 @@
// If you add modeling of a new framework/library, remember to add it it to the docs in
// `docs/codeql/support/reusables/frameworks.rst`
+private import semmle.python.frameworks.Aioch
+private import semmle.python.frameworks.ClickhouseDriver
private import semmle.python.frameworks.Cryptodome
private import semmle.python.frameworks.Cryptography
private import semmle.python.frameworks.Dill
diff --git a/python/ql/src/experimental/semmle/python/frameworks/Aioch.qll b/python/ql/src/semmle/python/frameworks/Aioch.qll
similarity index 96%
rename from python/ql/src/experimental/semmle/python/frameworks/Aioch.qll
rename to python/ql/src/semmle/python/frameworks/Aioch.qll
index dde97047046..ede732e35dc 100644
--- a/python/ql/src/experimental/semmle/python/frameworks/Aioch.qll
+++ b/python/ql/src/semmle/python/frameworks/Aioch.qll
@@ -9,7 +9,7 @@ private import python
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
private import semmle.python.frameworks.PEP249
-private import experimental.semmle.python.frameworks.ClickhouseDriver
+private import semmle.python.frameworks.ClickhouseDriver
/**
* INTERNAL: Do not use.
diff --git a/python/ql/src/experimental/semmle/python/frameworks/ClickhouseDriver.qll b/python/ql/src/semmle/python/frameworks/ClickhouseDriver.qll
similarity index 100%
rename from python/ql/src/experimental/semmle/python/frameworks/ClickhouseDriver.qll
rename to python/ql/src/semmle/python/frameworks/ClickhouseDriver.qll
diff --git a/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.ql b/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.ql
deleted file mode 100644
index 86e878cf8c7..00000000000
--- a/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.ql
+++ /dev/null
@@ -1,3 +0,0 @@
-import python
-import experimental.meta.ConceptsTest
-import experimental.semmle.python.frameworks.ClickhouseDriver
diff --git a/python/ql/test/experimental/semmle/python/frameworks/options b/python/ql/test/experimental/semmle/python/frameworks/options
deleted file mode 100644
index eb214fc2931..00000000000
--- a/python/ql/test/experimental/semmle/python/frameworks/options
+++ /dev/null
@@ -1 +0,0 @@
-semmle-extractor-options: --max-import-depth=1
diff --git a/python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/aioch/ConceptsTest.expected
similarity index 100%
rename from python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.expected
rename to python/ql/test/library-tests/frameworks/aioch/ConceptsTest.expected
diff --git a/python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/aioch/ConceptsTest.ql
similarity index 50%
rename from python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.ql
rename to python/ql/test/library-tests/frameworks/aioch/ConceptsTest.ql
index c0c26131055..b557a0bccb6 100644
--- a/python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.ql
+++ b/python/ql/test/library-tests/frameworks/aioch/ConceptsTest.ql
@@ -1,3 +1,2 @@
import python
import experimental.meta.ConceptsTest
-import experimental.semmle.python.frameworks.Aioch
diff --git a/python/ql/test/experimental/semmle/python/frameworks/aioch/options b/python/ql/test/library-tests/frameworks/aioch/options
similarity index 100%
rename from python/ql/test/experimental/semmle/python/frameworks/aioch/options
rename to python/ql/test/library-tests/frameworks/aioch/options
diff --git a/python/ql/test/experimental/semmle/python/frameworks/aioch/sql_test.py b/python/ql/test/library-tests/frameworks/aioch/sql_test.py
similarity index 100%
rename from python/ql/test/experimental/semmle/python/frameworks/aioch/sql_test.py
rename to python/ql/test/library-tests/frameworks/aioch/sql_test.py
diff --git a/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/clickhouse_driver/ConceptsTest.expected
similarity index 100%
rename from python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.expected
rename to python/ql/test/library-tests/frameworks/clickhouse_driver/ConceptsTest.expected
diff --git a/python/ql/test/library-tests/frameworks/clickhouse_driver/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/clickhouse_driver/ConceptsTest.ql
new file mode 100644
index 00000000000..b557a0bccb6
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/clickhouse_driver/ConceptsTest.ql
@@ -0,0 +1,2 @@
+import python
+import experimental.meta.ConceptsTest
diff --git a/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/sql_test.py b/python/ql/test/library-tests/frameworks/clickhouse_driver/sql_test.py
similarity index 100%
rename from python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/sql_test.py
rename to python/ql/test/library-tests/frameworks/clickhouse_driver/sql_test.py
From 706874491bf8bb59e1d4bc8633421dfd63e67150 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alvaro=20Mun=CC=83oz?=
Date: Fri, 21 May 2021 13:44:03 +0200
Subject: [PATCH 053/272] Remove XSS sink for Java
---
java/ql/src/semmle/code/java/security/XSS.qll | 1 -
1 file changed, 1 deletion(-)
diff --git a/java/ql/src/semmle/code/java/security/XSS.qll b/java/ql/src/semmle/code/java/security/XSS.qll
index 486e8053953..14f10cad9c8 100644
--- a/java/ql/src/semmle/code/java/security/XSS.qll
+++ b/java/ql/src/semmle/code/java/security/XSS.qll
@@ -34,7 +34,6 @@ private class DefaultXssSinkModel extends SinkModelCsv {
override predicate row(string row) {
row =
[
- "javax.servlet.http;HttpServletResponse;false;sendError;(int,String);;Argument[1];xss",
"android.webkit;WebView;false;loadData;;;Argument[0];xss",
"android.webkit;WebView;false;loadUrl;;;Argument[0];xss",
"android.webkit;WebView;false;loadDataWithBaseURL;;;Argument[1];xss"
From 735e4e4b7bcf0e55811652e7fd02639c51eb312b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alvaro=20Mun=CC=83oz?=
Date: Fri, 28 May 2021 13:44:16 +0200
Subject: [PATCH 054/272] update failing tests
---
.../query-tests/security/CWE-079/semmle/tests/XSS.expected | 4 ----
.../test/query-tests/security/CWE-079/semmle/tests/XSS.java | 4 ++--
2 files changed, 2 insertions(+), 6 deletions(-)
diff --git a/java/ql/test/query-tests/security/CWE-079/semmle/tests/XSS.expected b/java/ql/test/query-tests/security/CWE-079/semmle/tests/XSS.expected
index ed67a987e84..c800f627c64 100644
--- a/java/ql/test/query-tests/security/CWE-079/semmle/tests/XSS.expected
+++ b/java/ql/test/query-tests/security/CWE-079/semmle/tests/XSS.expected
@@ -1,19 +1,15 @@
edges
| XSS.java:23:21:23:48 | getParameter(...) : String | XSS.java:23:5:23:70 | ... + ... |
-| XSS.java:27:21:27:48 | getParameter(...) : String | XSS.java:27:5:27:70 | ... + ... |
| XSS.java:38:67:38:87 | getPathInfo(...) : String | XSS.java:38:30:38:87 | ... + ... |
| XSS.java:41:36:41:56 | getPathInfo(...) : String | XSS.java:41:36:41:67 | getBytes(...) |
nodes
| XSS.java:23:5:23:70 | ... + ... | semmle.label | ... + ... |
| XSS.java:23:21:23:48 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| XSS.java:27:5:27:70 | ... + ... | semmle.label | ... + ... |
-| XSS.java:27:21:27:48 | getParameter(...) : String | semmle.label | getParameter(...) : String |
| XSS.java:38:30:38:87 | ... + ... | semmle.label | ... + ... |
| XSS.java:38:67:38:87 | getPathInfo(...) : String | semmle.label | getPathInfo(...) : String |
| XSS.java:41:36:41:56 | getPathInfo(...) : String | semmle.label | getPathInfo(...) : String |
| XSS.java:41:36:41:67 | getBytes(...) | semmle.label | getBytes(...) |
#select
| XSS.java:23:5:23:70 | ... + ... | XSS.java:23:21:23:48 | getParameter(...) : String | XSS.java:23:5:23:70 | ... + ... | Cross-site scripting vulnerability due to $@. | XSS.java:23:21:23:48 | getParameter(...) | user-provided value |
-| XSS.java:27:5:27:70 | ... + ... | XSS.java:27:21:27:48 | getParameter(...) : String | XSS.java:27:5:27:70 | ... + ... | Cross-site scripting vulnerability due to $@. | XSS.java:27:21:27:48 | getParameter(...) | user-provided value |
| XSS.java:38:30:38:87 | ... + ... | XSS.java:38:67:38:87 | getPathInfo(...) : String | XSS.java:38:30:38:87 | ... + ... | Cross-site scripting vulnerability due to $@. | XSS.java:38:67:38:87 | getPathInfo(...) | user-provided value |
| XSS.java:41:36:41:67 | getBytes(...) | XSS.java:41:36:41:56 | getPathInfo(...) : String | XSS.java:41:36:41:67 | getBytes(...) | Cross-site scripting vulnerability due to $@. | XSS.java:41:36:41:56 | getPathInfo(...) | user-provided value |
diff --git a/java/ql/test/query-tests/security/CWE-079/semmle/tests/XSS.java b/java/ql/test/query-tests/security/CWE-079/semmle/tests/XSS.java
index 54e60a13086..3ebaac47819 100644
--- a/java/ql/test/query-tests/security/CWE-079/semmle/tests/XSS.java
+++ b/java/ql/test/query-tests/security/CWE-079/semmle/tests/XSS.java
@@ -22,7 +22,7 @@ public class XSS extends HttpServlet {
response.getWriter().print(
"The page \"" + request.getParameter("page") + "\" was not found.");
- // BAD: a request parameter is written directly to an error response page
+ // GOOD: servlet API encodes the error message HTML for the HTML context
response.sendError(HttpServletResponse.SC_NOT_FOUND,
"The page \"" + request.getParameter("page") + "\" was not found.");
@@ -30,7 +30,7 @@ public class XSS extends HttpServlet {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
"The page \"" + encodeForHtml(request.getParameter("page")) + "\" was not found.");
- // FALSE NEGATIVE: passed through function that is not a secure check
+ // GOOD: servlet API encodes the error message HTML for the HTML context
response.sendError(HttpServletResponse.SC_NOT_FOUND,
"The page \"" + capitalizeName(request.getParameter("page")) + "\" was not found.");
From db2f05ac248566ed9612bd615879e97a3381f424 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alvaro=20Mun=CC=83oz?=
Date: Fri, 28 May 2021 13:48:48 +0200
Subject: [PATCH 055/272] Updated Java change notes
---
java/change-notes/2021-05-28-remove-senderror-xss-sink.md | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 java/change-notes/2021-05-28-remove-senderror-xss-sink.md
diff --git a/java/change-notes/2021-05-28-remove-senderror-xss-sink.md b/java/change-notes/2021-05-28-remove-senderror-xss-sink.md
new file mode 100644
index 00000000000..7b4959f7056
--- /dev/null
+++ b/java/change-notes/2021-05-28-remove-senderror-xss-sink.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* The query "Cross-site scripting" (`java/xss`) has been improved to report fewer false positives by removing the `javax.servlet.http.HttpServletResponse.sendError` sink since the Servlet API implementations already encode the error message for the HTML context.
From 5a894ac7f78c4617d242d719581452df7bc2148b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alvaro=20Mun=CC=83oz?=
Date: Fri, 28 May 2021 14:07:12 +0200
Subject: [PATCH 056/272] update java library coverage documentation
---
java/documentation/library-coverage/flow-model-coverage.csv | 2 +-
java/documentation/library-coverage/flow-model-coverage.rst | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/java/documentation/library-coverage/flow-model-coverage.csv b/java/documentation/library-coverage/flow-model-coverage.csv
index cf97071ad92..73f06306193 100644
--- a/java/documentation/library-coverage/flow-model-coverage.csv
+++ b/java/documentation/library-coverage/flow-model-coverage.csv
@@ -15,7 +15,7 @@ java.nio,10,,2,,10,,,,,,,,,2,
java.util,,,13,,,,,,,,,,,13,
javax.naming.directory,1,,,,,,1,,,,,,,,
javax.net.ssl,2,,,,,,,,2,,,,,,
-javax.servlet,4,21,2,,,3,,,,,,1,21,2,
+javax.servlet,3,21,2,,,3,,,,,,,21,2,
javax.validation,1,1,,1,,,,,,,,,1,,
javax.ws.rs.core,1,,,,,1,,,,,,,,,
javax.xml.transform.sax,,,4,,,,,,,,,,,4,
diff --git a/java/documentation/library-coverage/flow-model-coverage.rst b/java/documentation/library-coverage/flow-model-coverage.rst
index 8bb20a9b6c3..bad79a93c06 100644
--- a/java/documentation/library-coverage/flow-model-coverage.rst
+++ b/java/documentation/library-coverage/flow-model-coverage.rst
@@ -12,8 +12,8 @@ Java framework & library support
`Apache Commons IO `_,``org.apache.commons.io``,,22,,,,,,,,
Google,``com.google.common.*``,,97,6,,6,,,,,
Java Standard Library,``java.*``,3,41,15,13,,,,,,2
- Java extensions,``javax.*``,22,8,12,,,1,,1,1,
+ Java extensions,``javax.*``,22,8,11,,,,,1,1,
`Spring `_,``org.springframework.*``,29,,14,,,,,14,,
Others,"``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.databind``, ``com.unboundid.ldap.sdk``, ``org.dom4j``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.mvc``",7,5,37,,,,,17,,
- Totals,,84,821,91,13,6,7,,33,1,2
+ Totals,,84,821,90,13,6,6,,33,1,2
From f60df3b26a315d665df05edf7bdb99cfa59341be Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alvaro=20Mu=C3=B1oz?=
Date: Fri, 28 May 2021 13:52:31 +0200
Subject: [PATCH 057/272] Update
java/change-notes/2021-05-28-remove-senderror-xss-sink.md
Co-authored-by: Chris Smowton
---
java/change-notes/2021-05-28-remove-senderror-xss-sink.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/change-notes/2021-05-28-remove-senderror-xss-sink.md b/java/change-notes/2021-05-28-remove-senderror-xss-sink.md
index 7b4959f7056..5dd245bcb52 100644
--- a/java/change-notes/2021-05-28-remove-senderror-xss-sink.md
+++ b/java/change-notes/2021-05-28-remove-senderror-xss-sink.md
@@ -1,2 +1,2 @@
lgtm,codescanning
-* The query "Cross-site scripting" (`java/xss`) has been improved to report fewer false positives by removing the `javax.servlet.http.HttpServletResponse.sendError` sink since the Servlet API implementations already encode the error message for the HTML context.
+* The query "Cross-site scripting" (`java/xss`) has been improved to report fewer false positives by removing the `javax.servlet.http.HttpServletResponse.sendError` sink since Servlet API implementations generally already escape the error message, preventing script injection.
From 62c6bee5f89cf3a2edf9a82bfaf675c78f9b772f Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Sat, 29 May 2021 09:21:20 +0200
Subject: [PATCH 058/272] Simplified UnsafeDeserializationRmi.ql
---
.../Security/CWE/CWE-502/UnsafeDeserializationRmi.ql | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.ql b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.ql
index 91e38cc373e..f870d6f423e 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.ql
@@ -66,11 +66,7 @@ private class BindingUnsafeRemoteObjectConfig extends TaintTracking::Configurati
exists(MethodAccess ma, Method m | m = ma.getMethod() |
m.getDeclaringType().hasQualifiedName("java.rmi.server", "UnicastRemoteObject") and
m.hasName("exportObject") and
- not ma.getArgument([2, 4])
- .getType()
- .(RefType)
- .getASupertype*()
- .hasQualifiedName("java.io", "ObjectInputFilter") and
+ not m.getParameterType([2, 4]).(RefType).hasQualifiedName("java.io", "ObjectInputFilter") and
ma.getArgument(0) = fromNode.asExpr() and
ma = toNode.asExpr()
)
From b28d6391666f53767b85209138759fbf5922e7f5 Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Sat, 29 May 2021 09:32:08 +0200
Subject: [PATCH 059/272] Fixed errors in UnsafeDeserializationRmi.qhelp
---
.../Security/CWE/CWE-502/UnsafeDeserializationRmi.qhelp | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.qhelp b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.qhelp
index 985e3a4c08d..ea06f315cc4 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.qhelp
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.qhelp
@@ -15,7 +15,7 @@ In the worst case, it results in remote code execution.
Use only strings and primitive types in parameters of remote objects.
-
+
Set a filter for incoming serialized data by wrapping remote objects using either UnicastRemoteObject.exportObject(Remote, int, ObjectInputFilter)
or UnicastRemoteObject.exportObject(Remote, int, RMIClientSocketFactory, RMIServerSocketFactory, ObjectInputFilter) methods.
Those methods accept an ObjectInputFilter that decides which classes are allowed for deserialization.
@@ -26,6 +26,7 @@ It is also possible to set a process-wide deserialization filter.
The filter can be set by with ObjectInputFilter.Config.setSerialFilter(ObjectInputFilter) method,
or by setting system or security property jdk.serialFilter.
Make sure that you use the latest Java versions that include JEP 290.
+Please note that the query is not sensitive to this mitigation.
If switching to the latest Java versions is not possible,
@@ -62,11 +63,11 @@ Oracle:
OWASP:
From 41d034d5a0fda510d4a5461dc3558e3136ca8edd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alvaro=20Mun=CC=83oz?=
Date: Sun, 30 May 2021 00:22:40 +0200
Subject: [PATCH 060/272] Attempt to use information-leak sink category
---
.../CWE/CWE-209/StackTraceExposure.ql | 14 +++++++----
.../code/java/dataflow/ExternalFlow.qll | 1 +
.../code/java/security/InformationLeak.qll | 23 +++++++++++++++++++
3 files changed, 33 insertions(+), 5 deletions(-)
create mode 100644 java/ql/src/semmle/code/java/security/InformationLeak.qll
diff --git a/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql b/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql
index 3426d9f6f62..b82edc2efc6 100644
--- a/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql
+++ b/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql
@@ -16,6 +16,7 @@ import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.security.XSS
+import semmle.code.java.security.InformationLeak
/**
* One of the `printStackTrace()` overloads on `Throwable`.
@@ -83,14 +84,17 @@ predicate stackTraceExpr(Expr exception, MethodAccess stackTraceString) {
)
}
-class StackTraceStringToXssSinkFlowConfig extends TaintTracking::Configuration {
- StackTraceStringToXssSinkFlowConfig() {
- this = "StackTraceExposure::StackTraceStringToXssSinkFlowConfig"
+class StackTraceStringToHTTPResponseSinkFlowConfig extends TaintTracking::Configuration {
+ StackTraceStringToHTTPResponseSinkFlowConfig() {
+ this = "StackTraceExposure::StackTraceStringToHTTPResponseSinkFlowConfig"
}
override predicate isSource(DataFlow::Node src) { stackTraceExpr(_, src.asExpr()) }
- override predicate isSink(DataFlow::Node sink) { sink instanceof XssSink }
+ override predicate isSink(DataFlow::Node sink) {
+ sink instanceof XssSink or
+ sink instanceof InformationLeakSink
+ }
}
/**
@@ -106,7 +110,7 @@ predicate printsStackExternally(MethodAccess call, Expr stackTrace) {
* A stringified stack trace flows to an external sink.
*/
predicate stringifiedStackFlowsExternally(XssSink externalExpr, Expr stackTrace) {
- exists(MethodAccess stackTraceString, StackTraceStringToXssSinkFlowConfig conf |
+ exists(MethodAccess stackTraceString, StackTraceStringToHTTPResponseSinkFlowConfig conf |
stackTraceExpr(stackTrace, stackTraceString) and
conf.hasFlow(DataFlow::exprNode(stackTraceString), externalExpr)
)
diff --git a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll
index 33a146d07fe..92dfb5a733c 100644
--- a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll
+++ b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll
@@ -79,6 +79,7 @@ private module Frameworks {
private import semmle.code.java.frameworks.guava.Guava
private import semmle.code.java.frameworks.jackson.JacksonSerializability
private import semmle.code.java.security.ResponseSplitting
+ private import semmle.code.java.security.InformationLeak
private import semmle.code.java.security.XSS
private import semmle.code.java.security.LdapInjection
private import semmle.code.java.security.XPath
diff --git a/java/ql/src/semmle/code/java/security/InformationLeak.qll b/java/ql/src/semmle/code/java/security/InformationLeak.qll
new file mode 100644
index 00000000000..4cb12c3d3d9
--- /dev/null
+++ b/java/ql/src/semmle/code/java/security/InformationLeak.qll
@@ -0,0 +1,23 @@
+/** Provides classes to reason about System Information Leak vulnerabilities. */
+
+import java
+import semmle.code.java.dataflow.DataFlow
+import semmle.code.java.dataflow.ExternalFlow
+
+/** CSV sink models representing methods not susceptible to XSS but outputing to an HTTP response body. */
+private class DefaultInformationLeakSinkModel extends SinkModelCsv {
+ override predicate row(string row) {
+ row =
+ [
+ "javax.servlet.http;HttpServletResponse;false;sendError;(int,String);;Argument[1];information-leak"
+ ]
+ }
+}
+
+/** A sink that represent a method that outputs data to an HTTP response. */
+abstract class InformationLeakSink extends DataFlow::Node { }
+
+/** A default sink representing methods outputing data to an HTTP response. */
+private class DefaultInformationLeakSink extends InformationLeakSink {
+ DefaultInformationLeakSink() { sinkNode(this, "information-leak") }
+}
From 35d7fda5e2fb4325933ea39b728498f0bea8049f Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Mon, 31 May 2021 13:08:09 +0200
Subject: [PATCH 061/272] update typescript to 4.3 in the extractor
---
javascript/extractor/lib/typescript/package.json | 2 +-
javascript/extractor/lib/typescript/yarn.lock | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/javascript/extractor/lib/typescript/package.json b/javascript/extractor/lib/typescript/package.json
index cb3119c7ab2..953f69edb1e 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.2.2"
+ "typescript": "^4.3.2"
},
"scripts": {
"build": "tsc --project tsconfig.json",
diff --git a/javascript/extractor/lib/typescript/yarn.lock b/javascript/extractor/lib/typescript/yarn.lock
index 84f20b2c51c..7488658d581 100644
--- a/javascript/extractor/lib/typescript/yarn.lock
+++ b/javascript/extractor/lib/typescript/yarn.lock
@@ -6,7 +6,7 @@
version "12.7.11"
resolved node-12.7.11.tgz#be879b52031cfb5d295b047f5462d8ef1a716446
-typescript@4.2.2:
- version "4.2.2"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.2.tgz#1450f020618f872db0ea17317d16d8da8ddb8c4c"
- integrity sha512-tbb+NVrLfnsJy3M59lsDgrzWIflR4d4TIUjz+heUnHZwdF7YsrMTKoRERiIvI2lvBG95dfpLxB21WZhys1bgaQ==
+typescript@4.3.2:
+ version "4.3.2"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.2.tgz#399ab18aac45802d6f2498de5054fcbbe716a805"
+ integrity sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==
From 2cc2d116bc80d6c1b4ceda7fe67311a0170ddbd4 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Mon, 31 May 2021 13:08:24 +0200
Subject: [PATCH 062/272] bump extractor version
---
javascript/extractor/src/com/semmle/js/extractor/Main.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/extractor/src/com/semmle/js/extractor/Main.java b/javascript/extractor/src/com/semmle/js/extractor/Main.java
index 7c5a8a25984..5d417940d75 100644
--- a/javascript/extractor/src/com/semmle/js/extractor/Main.java
+++ b/javascript/extractor/src/com/semmle/js/extractor/Main.java
@@ -43,7 +43,7 @@ public class Main {
* A version identifier that should be updated every time the extractor changes in such a way that
* it may produce different tuples for the same file under the same {@link ExtractorConfig}.
*/
- public static final String EXTRACTOR_VERSION = "2021-03-19";
+ public static final String EXTRACTOR_VERSION = "2021-05-31";
public static final Pattern NEWLINE = Pattern.compile("\n");
From e6b1c61e816c5e6afc47eb1438f2222d75e9b2c7 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Mon, 31 May 2021 13:08:43 +0200
Subject: [PATCH 063/272] add tests for TypeScript 4.3
---
.../TypeScript/IndexTypes/test.expected | 2 +
.../TypeScript/IndexTypes/test.ql | 3 +
.../TypeScript/IndexTypes/tsconfig.json | 1 +
.../TypeScript/IndexTypes/tst.ts | 8 ++
.../TypeScript/Types/tests.expected | 87 +++++++++++++++++++
.../library-tests/TypeScript/Types/tst.ts | 63 +++++++++++++-
6 files changed, 163 insertions(+), 1 deletion(-)
create mode 100644 javascript/ql/test/library-tests/TypeScript/IndexTypes/test.expected
create mode 100644 javascript/ql/test/library-tests/TypeScript/IndexTypes/test.ql
create mode 100644 javascript/ql/test/library-tests/TypeScript/IndexTypes/tsconfig.json
create mode 100644 javascript/ql/test/library-tests/TypeScript/IndexTypes/tst.ts
diff --git a/javascript/ql/test/library-tests/TypeScript/IndexTypes/test.expected b/javascript/ql/test/library-tests/TypeScript/IndexTypes/test.expected
new file mode 100644
index 00000000000..4779b178e4f
--- /dev/null
+++ b/javascript/ql/test/library-tests/TypeScript/IndexTypes/test.expected
@@ -0,0 +1,2 @@
+| Foo | boolean |
+| typeof Foo in global scope | string |
diff --git a/javascript/ql/test/library-tests/TypeScript/IndexTypes/test.ql b/javascript/ql/test/library-tests/TypeScript/IndexTypes/test.ql
new file mode 100644
index 00000000000..ff6a2a4836f
--- /dev/null
+++ b/javascript/ql/test/library-tests/TypeScript/IndexTypes/test.ql
@@ -0,0 +1,3 @@
+import javascript
+
+query Type stringIndexType(Type t) { result = t.getStringIndexType() }
diff --git a/javascript/ql/test/library-tests/TypeScript/IndexTypes/tsconfig.json b/javascript/ql/test/library-tests/TypeScript/IndexTypes/tsconfig.json
new file mode 100644
index 00000000000..9e26dfeeb6e
--- /dev/null
+++ b/javascript/ql/test/library-tests/TypeScript/IndexTypes/tsconfig.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/javascript/ql/test/library-tests/TypeScript/IndexTypes/tst.ts b/javascript/ql/test/library-tests/TypeScript/IndexTypes/tst.ts
new file mode 100644
index 00000000000..9035ff2b2ac
--- /dev/null
+++ b/javascript/ql/test/library-tests/TypeScript/IndexTypes/tst.ts
@@ -0,0 +1,8 @@
+ // static index signature
+ class Foo {
+ static hello = "world";
+ static [n: string]: string;
+ [n: string]: boolean;
+ }
+ Foo["whatever"] = "foo";
+ new Foo()["something"] = true;
\ No newline at end of file
diff --git a/javascript/ql/test/library-tests/TypeScript/Types/tests.expected b/javascript/ql/test/library-tests/TypeScript/Types/tests.expected
index 7c8159a9b10..187c0c7e6ce 100644
--- a/javascript/ql/test/library-tests/TypeScript/Types/tests.expected
+++ b/javascript/ql/test/library-tests/TypeScript/Types/tests.expected
@@ -127,6 +127,54 @@ getExprType
| tst.ts:69:24:69:45 | {yetAno ... : true} | MyUnion2 |
| tst.ts:69:25:69:38 | yetAnotherType | true |
| tst.ts:69:41:69:44 | true | true |
+| tst.ts:71:8:71:11 | TS43 | typeof TS43 in library-tests/TypeScript/Types/tst.ts |
+| tst.ts:74:5:74:22 | get size(): number | number |
+| tst.ts:74:9:74:12 | size | number |
+| tst.ts:75:5:75:47 | set siz ... olean); | number |
+| tst.ts:75:9:75:12 | size | number |
+| tst.ts:75:14:75:18 | value | string \| number \| boolean |
+| tst.ts:78:16:78:20 | Thing | Thing |
+| tst.ts:79:13:79:13 | 0 | 0 |
+| tst.ts:81:5:83:5 | get siz ... ;\\n } | number |
+| tst.ts:81:9:81:12 | size | number |
+| tst.ts:82:14:82:23 | this.#size | number |
+| tst.ts:85:5:87:5 | set siz ... ;\\n } | number |
+| tst.ts:85:9:85:12 | size | number |
+| tst.ts:85:14:85:18 | value | string \| number \| boolean |
+| tst.ts:86:7:86:16 | this.#size | number |
+| tst.ts:86:7:86:32 | this.#s ... (value) | number |
+| tst.ts:86:20:86:25 | Number | NumberConstructor |
+| tst.ts:86:20:86:32 | Number(value) | number |
+| tst.ts:86:27:86:31 | value | string \| number \| boolean |
+| tst.ts:91:9:91:13 | Super | Super |
+| tst.ts:92:5:92:10 | random | () => number |
+| tst.ts:92:5:94:5 | random( ... ;\\n } | () => number |
+| tst.ts:93:14:93:14 | 4 | 4 |
+| tst.ts:97:9:97:11 | Sub | Sub |
+| tst.ts:97:21:97:25 | Super | Super |
+| tst.ts:98:5:100:5 | overrid ... ;\\n } | () => number |
+| tst.ts:98:14:98:19 | random | () => number |
+| tst.ts:99:14:99:25 | super.random | () => number |
+| tst.ts:99:14:99:27 | super.random() | number |
+| tst.ts:99:14:99:32 | super.random() * 10 | number |
+| tst.ts:99:20:99:25 | random | () => number |
+| tst.ts:99:31:99:32 | 10 | 10 |
+| tst.ts:104:16:104:16 | s | string |
+| tst.ts:106:13:106:18 | hello | any |
+| tst.ts:106:21:106:21 | s | string |
+| tst.ts:110:15:110:16 | s2 | "1-2-3" |
+| tst.ts:112:3:112:9 | s1 = s2 | "1-2-3" |
+| tst.ts:112:8:112:9 | s2 | "1-2-3" |
+| tst.ts:116:9:116:11 | Foo | Foo |
+| tst.ts:117:5:119:5 | #someMe ... ;\\n } | () => number |
+| tst.ts:118:14:118:15 | 42 | 42 |
+| tst.ts:121:5:123:5 | get #so ... ;\\n } | number |
+| tst.ts:122:14:122:16 | 100 | 100 |
+| tst.ts:125:5:125:16 | publicMethod | () => number |
+| tst.ts:125:5:128:5 | publicM ... ;\\n } | () => number |
+| tst.ts:126:7:126:22 | this.#someMethod | () => number |
+| tst.ts:126:7:126:24 | this.#someMethod() | number |
+| tst.ts:127:14:127:28 | this.#someValue | number |
| 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 |
@@ -180,6 +228,11 @@ getTypeDefinitionType
| tst.ts:58:1:60:1 | interfa ... mber;\\n} | HasArea |
| tst.ts:65:1:65:54 | type My ... true}; | MyUnion |
| tst.ts:68:1:68:49 | type My ... true}; | MyUnion2 |
+| tst.ts:73:3:76:3 | interfa ... n);\\n } | ThingI |
+| tst.ts:78:10:88:3 | class T ... }\\n } | Thing |
+| tst.ts:91:3:95:3 | class S ... }\\n } | Super |
+| tst.ts:97:3:101:3 | class S ... }\\n } | Sub |
+| tst.ts:116:3:129:3 | class F ... }\\n } | Foo |
| type_alias.ts:1:1:1:17 | type B = boolean; | boolean |
| type_alias.ts:5:1:5:50 | type Va ... ay>; | ValueOrArray |
| type_alias.ts:9:1:15:13 | type Js ... Json[]; | Json |
@@ -288,6 +341,34 @@ getTypeExprType
| tst.ts:68:27:68:48 | {yetAno ... : true} | { yetAnotherType: true; } |
| tst.ts:68:44:68:47 | true | true |
| tst.ts:69:13:69:20 | MyUnion2 | MyUnion2 |
+| tst.ts:73:13:73:18 | ThingI | ThingI |
+| tst.ts:74:17:74:22 | number | number |
+| tst.ts:75:21:75:26 | number | number |
+| tst.ts:75:21:75:45 | number ... boolean | string \| number \| boolean |
+| tst.ts:75:30:75:35 | string | string |
+| tst.ts:75:39:75:45 | boolean | boolean |
+| tst.ts:78:33:78:38 | ThingI | ThingI |
+| tst.ts:81:17:81:22 | number | number |
+| tst.ts:85:21:85:26 | string | string |
+| tst.ts:85:21:85:45 | string ... boolean | string \| number \| boolean |
+| tst.ts:85:30:85:35 | number | number |
+| tst.ts:85:39:85:45 | boolean | boolean |
+| tst.ts:92:15:92:20 | number | number |
+| tst.ts:98:24:98:29 | number | number |
+| tst.ts:104:19:104:24 | string | string |
+| tst.ts:104:29:104:34 | hello | any |
+| tst.ts:104:37:104:42 | string | string |
+| tst.ts:109:22:109:27 | number | number |
+| tst.ts:109:29:109:29 | - | any |
+| tst.ts:109:32:109:37 | number | number |
+| tst.ts:109:39:109:39 | - | any |
+| tst.ts:109:42:109:47 | number | number |
+| tst.ts:110:19:110:25 | `1-2-3` | "1-2-3" |
+| tst.ts:110:19:110:25 | `1-2-3` | any |
+| tst.ts:111:22:111:27 | number | number |
+| tst.ts:111:29:111:32 | -2-3 | any |
+| tst.ts:117:20:117:25 | number | number |
+| tst.ts:121:23:121:28 | number | number |
| 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 |
@@ -360,6 +441,7 @@ referenceDefinition
| Color.red | type_definitions.ts:14:3:14:5 | red |
| E | type_definition_objects.ts:6:8:6:16 | enum E {} |
| EnumWithOneMember | type_definitions.ts:18:26:18:31 | member |
+| Foo | tst.ts:116:3:129:3 | class F ... }\\n } |
| HasArea | tst.ts:58:1:60:1 | interfa ... mber;\\n} |
| I | type_definitions.ts:3:1:5:1 | interfa ... x: S;\\n} |
| I | type_definitions.ts:3:1:5:1 | interfa ... x: S;\\n} |
@@ -367,6 +449,11 @@ referenceDefinition
| MyUnion | tst.ts:65:1:65:54 | type My ... true}; |
| MyUnion2 | tst.ts:68:1:68:49 | type My ... true}; |
| NonAbstractDummy | tst.ts:54:1:56:1 | interfa ... mber;\\n} |
+| Sub | tst.ts:97:3:101:3 | class S ... }\\n } |
+| Super | tst.ts:91:3:95:3 | class S ... }\\n } |
+| Super | tst.ts:91:3:95:3 | class S ... }\\n } |
+| Thing | tst.ts:78:10:88:3 | class T ... }\\n } |
+| ThingI | tst.ts:73:3:76:3 | interfa ... n);\\n } |
| ValueOrArray | type_alias.ts:5:1:5:50 | type Va ... ay>; |
| ValueOrArray | type_alias.ts:5:1:5:50 | type Va ... ay>; |
| VirtualNode | type_alias.ts:19:1:21:57 | type Vi ... ode[]]; |
diff --git a/javascript/ql/test/library-tests/TypeScript/Types/tst.ts b/javascript/ql/test/library-tests/TypeScript/Types/tst.ts
index 68c4ca6ebdd..389770984fd 100644
--- a/javascript/ql/test/library-tests/TypeScript/Types/tst.ts
+++ b/javascript/ql/test/library-tests/TypeScript/Types/tst.ts
@@ -66,4 +66,65 @@ type MyUnion = {myUnion: true} | {stillMyUnion: true};
let union1: MyUnion = {myUnion: true};
type MyUnion2 = MyUnion | {yetAnotherType: true};
-let union2: MyUnion2 = {yetAnotherType: true};
\ No newline at end of file
+let union2: MyUnion2 = {yetAnotherType: true};
+
+module TS43 {
+ // TypeScript 4.3 setter/getter types
+ interface ThingI {
+ get size(): number
+ set size(value: number | string | boolean);
+ }
+
+ export class Thing implements ThingI {
+ #size = 0;
+
+ get size(): number {
+ return this.#size;
+ }
+
+ set size(value: string | number | boolean) {
+ this.#size = Number(value);
+ }
+ }
+
+ // overrides
+ class Super {
+ random(): number {
+ return 4;
+ }
+ }
+
+ class Sub extends Super {
+ override random(): number {
+ return super.random() * 10;
+ }
+ }
+
+ // inference of template strings.
+ function bar(s: string): `hello ${string}` {
+ // Previously an error, now works!
+ return `hello ${s}`;
+ }
+
+ declare let s1: `${number}-${number}-${number}`;
+ declare let s2: `1-2-3`;
+ declare let s3: `${number}-2-3`;
+ s1 = s2;
+ s1 = s3;
+
+ // private methods
+ class Foo {
+ #someMethod(): number {
+ return 42;
+ }
+
+ get #someValue(): number {
+ return 100;
+ }
+
+ publicMethod() {
+ this.#someMethod();
+ return this.#someValue;
+ }
+ }
+}
\ No newline at end of file
From 85bd8f1020768178915853df4b2f76622cd2d845 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Mon, 31 May 2021 13:08:52 +0200
Subject: [PATCH 064/272] add change-note for TypeScript 4.3
---
javascript/change-notes/2021-05-31-typescript-4.3.md | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 javascript/change-notes/2021-05-31-typescript-4.3.md
diff --git a/javascript/change-notes/2021-05-31-typescript-4.3.md b/javascript/change-notes/2021-05-31-typescript-4.3.md
new file mode 100644
index 00000000000..21960a98dce
--- /dev/null
+++ b/javascript/change-notes/2021-05-31-typescript-4.3.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* TypeScript 4.3 is now supported.
From 8dc1451d42f2eefce0e62ee5a7a1b198272abad3 Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Tue, 1 Jun 2021 12:16:09 +0300
Subject: [PATCH 065/272] Better recommendation in
UnsafeDeserializationRmi.qhelp
Co-authored-by: Chris Smowton
---
.../Security/CWE/CWE-502/UnsafeDeserializationRmi.qhelp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.qhelp b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.qhelp
index ea06f315cc4..02ee7d7dab1 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.qhelp
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.qhelp
@@ -13,7 +13,7 @@ In the worst case, it results in remote code execution.
-Use only strings and primitive types in parameters of remote objects.
+Use only strings and primitive types for parameters of remotely invokable methods.
Set a filter for incoming serialized data by wrapping remote objects using either UnicastRemoteObject.exportObject(Remote, int, ObjectInputFilter)
From 1001dd84e65a7a9dfbb0be8a27bdbb3dc3f18e9d Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Wed, 19 May 2021 10:45:02 +0200
Subject: [PATCH 066/272] Java: Switch array steps and one containerstep.
---
.../java/dataflow/internal/ContainerFlow.qll | 43 +++++++++++++++++++
.../dataflow/internal/DataFlowPrivate.qll | 11 ++++-
.../java/dataflow/internal/DataFlowUtil.qll | 2 +
.../internal/FlowSummaryImplSpecific.qll | 6 +++
.../dataflow/internal/TaintTrackingUtil.qll | 22 ----------
5 files changed, 60 insertions(+), 24 deletions(-)
diff --git a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll
index 5425471ca44..52a9b1704dd 100644
--- a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll
+++ b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll
@@ -1,6 +1,8 @@
import java
import semmle.code.java.Collections
import semmle.code.java.Maps
+private import semmle.code.java.dataflow.SSA
+private import DataFlowUtil
private class EntryType extends RefType {
EntryType() {
@@ -426,3 +428,44 @@ predicate containerStep(Expr n1, Expr n2) {
containerReturnValueStep(n1, n2) or
containerUpdateStep(n1, n2)
}
+
+predicate arrayStoreStep(Node node1, Node node2) {
+ exists(Argument arg |
+ node1.asExpr() = arg and
+ arg.isVararg() and
+ node2.(ImplicitVarargsArray).getCall() = arg.getCall()
+ )
+ or
+ node2.asExpr().(ArrayInit).getAnInit() = node1.asExpr()
+ or
+ exists(Assignment assign | assign.getSource() = node1.asExpr() |
+ node2.(PostUpdateNode).getPreUpdateNode().asExpr() = assign.getDest().(ArrayAccess).getArray()
+ )
+}
+
+private predicate enhancedForStmtStep(Node node1, Node node2, Type containerType) {
+ exists(EnhancedForStmt for, Expr e, SsaExplicitUpdate v |
+ for.getExpr() = e and
+ node1.asExpr() = e and
+ containerType = e.getType() and
+ v.getDefiningExpr() = for.getVariable() and
+ v.getAFirstUse() = node2.asExpr()
+ )
+}
+
+predicate arrayReadStep(Node node1, Node node2, Type elemType) {
+ exists(ArrayAccess aa |
+ aa.getArray() = node1.asExpr() and
+ aa.getType() = elemType and
+ node2.asExpr() = aa
+ )
+ or
+ exists(Array arr |
+ enhancedForStmtStep(node1, node2, arr) and
+ arr.getComponentType() = elemType
+ )
+}
+
+predicate collectionReadStep(Node node1, Node node2) {
+ enhancedForStmtStep(node1, node2, any(Type t | not t instanceof Array))
+}
diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll
index e3ab07fce1f..54c4b775272 100644
--- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll
+++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll
@@ -4,6 +4,7 @@ private import DataFlowImplCommon
private import DataFlowDispatch
private import semmle.code.java.controlflow.Guards
private import semmle.code.java.dataflow.SSA
+private import ContainerFlow
private import FlowSummaryImpl as FlowSummaryImpl
import DataFlowNodes::Private
@@ -137,13 +138,15 @@ class MapValueContent extends Content, TMapValueContent {
* Thus, `node2` references an object with a field `f` that contains the
* value of `node1`.
*/
-predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
+predicate storeStep(Node node1, Content f, Node node2) {
exists(FieldAccess fa |
instanceFieldAssign(node1.asExpr(), fa) and
- node2.getPreUpdateNode() = getFieldQualifier(fa) and
+ node2.(PostUpdateNode).getPreUpdateNode() = getFieldQualifier(fa) and
f.(FieldContent).getField() = fa.getField()
)
or
+ f instanceof ArrayContent and arrayStoreStep(node1, node2)
+ or
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1, f, node2)
}
@@ -171,6 +174,10 @@ predicate readStep(Node node1, Content f, Node node2) {
node2.asExpr() = get
)
or
+ f instanceof ArrayContent and arrayReadStep(node1, node2, _)
+ or
+ f instanceof CollectionContent and collectionReadStep(node1, node2)
+ or
FlowSummaryImpl::Private::Steps::summaryReadStep(node1, f, node2)
}
diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowUtil.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowUtil.qll
index e25ab24cfbb..e3ee2ddd595 100644
--- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowUtil.qll
+++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowUtil.qll
@@ -144,6 +144,8 @@ predicate simpleLocalFlowStep(Node node1, Node node2) {
or
node2.asExpr().(AssignExpr).getSource() = node1.asExpr()
or
+ node2.asExpr().(ArrayCreationExpr).getInit() = node1.asExpr()
+ or
exists(MethodAccess ma, ValuePreservingMethod m, int argNo |
ma.getCallee().getSourceDeclaration() = m and m.returnsValue(argNo)
|
diff --git a/java/ql/src/semmle/code/java/dataflow/internal/FlowSummaryImplSpecific.qll b/java/ql/src/semmle/code/java/dataflow/internal/FlowSummaryImplSpecific.qll
index dae0571f0fa..6f127c4b92d 100644
--- a/java/ql/src/semmle/code/java/dataflow/internal/FlowSummaryImplSpecific.qll
+++ b/java/ql/src/semmle/code/java/dataflow/internal/FlowSummaryImplSpecific.qll
@@ -30,6 +30,12 @@ DataFlowType getContentType(Content c) {
or
c instanceof ArrayContent and
result instanceof TypeObject
+ or
+ c instanceof MapKeyContent and
+ result instanceof TypeObject
+ or
+ c instanceof MapValueContent and
+ result instanceof TypeObject
}
/** Gets the return type of kind `rk` for callable `c`. */
diff --git a/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll b/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll
index 8c514e357d2..339e0f05e1e 100644
--- a/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll
+++ b/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll
@@ -60,12 +60,6 @@ private module Cached {
localAdditionalTaintUpdateStep(src.asExpr(),
sink.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr())
or
- exists(Argument arg |
- src.asExpr() = arg and
- arg.isVararg() and
- sink.(DataFlow::ImplicitVarargsArray).getCall() = arg.getCall()
- )
- or
FlowSummaryImpl::Private::Steps::summaryLocalStep(src, sink, false)
}
@@ -103,20 +97,8 @@ private predicate localAdditionalTaintExprStep(Expr src, Expr sink) {
or
sink.(AssignAddExpr).getSource() = src and sink.getType() instanceof TypeString
or
- sink.(ArrayCreationExpr).getInit() = src
- or
- sink.(ArrayInit).getAnInit() = src
- or
- sink.(ArrayAccess).getArray() = src
- or
sink.(LogicExpr).getAnOperand() = src
or
- exists(EnhancedForStmt for, SsaExplicitUpdate v |
- for.getExpr() = src and
- v.getDefiningExpr() = for.getVariable() and
- v.getAFirstUse() = sink
- )
- or
containerReturnValueStep(src, sink)
or
constructorStep(src, sink)
@@ -141,10 +123,6 @@ private predicate localAdditionalTaintExprStep(Expr src, Expr sink) {
* This is restricted to cases where the step updates the value of `sink`.
*/
private predicate localAdditionalTaintUpdateStep(Expr src, Expr sink) {
- exists(Assignment assign | assign.getSource() = src |
- sink = assign.getDest().(ArrayAccess).getArray()
- )
- or
containerUpdateStep(src, sink)
or
qualifierToArgumentStep(src, sink)
From ffd52bb673f9cdcc0237bcfb7e93098a6f94110d Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Thu, 22 Apr 2021 11:00:31 +0200
Subject: [PATCH 067/272] Java: Fix bug in matching generic signatures.
---
java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll
index 33a146d07fe..8aa8447f7fb 100644
--- a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll
+++ b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll
@@ -561,7 +561,7 @@ private RefType interpretType(string namespace, string type, boolean subtypes) {
private string paramsStringPart(Callable c, int i) {
i = -1 and result = "("
or
- exists(int n, string p | c.getParameterType(n).toString() = p |
+ exists(int n, string p | c.getParameterType(n).getErasure().toString() = p |
i = 2 * n and result = p
or
i = 2 * n - 1 and result = "," and n != 0
From 3b6cef4f7464a9396483a0022503c82f92ed04e3 Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Fri, 9 Apr 2021 15:32:28 +0200
Subject: [PATCH 068/272] Java: Add container flow models.
---
.../code/java/dataflow/ExternalFlow.qll | 1 +
.../java/dataflow/internal/ContainerFlow.qll | 281 ++++++++++++++++++
2 files changed, 282 insertions(+)
diff --git a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll
index 8aa8447f7fb..34fa8d66dd3 100644
--- a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll
+++ b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll
@@ -74,6 +74,7 @@ private import FlowSummary
* ensuring that they are visible to the taint tracking / data flow library.
*/
private module Frameworks {
+ private import internal.ContainerFlow
private import semmle.code.java.frameworks.ApacheHttp
private import semmle.code.java.frameworks.apache.Lang
private import semmle.code.java.frameworks.guava.Guava
diff --git a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll
index 52a9b1704dd..667ae024b37 100644
--- a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll
+++ b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll
@@ -3,6 +3,7 @@ import semmle.code.java.Collections
import semmle.code.java.Maps
private import semmle.code.java.dataflow.SSA
private import DataFlowUtil
+private import semmle.code.java.dataflow.ExternalFlow
private class EntryType extends RefType {
EntryType() {
@@ -90,6 +91,286 @@ class ContainerType extends RefType {
}
}
+private class ContainerFlowSummaries extends SummaryModelCsv {
+ override predicate row(string row) {
+ row =
+ [
+ "java.util;Map<>$Entry;true;getValue;;;MapValue of Argument[-1];ReturnValue;value",
+ "java.util;Map<>$Entry;true;setValue;;;MapValue of Argument[-1];ReturnValue;value",
+ "java.util;Map<>$Entry;true;setValue;;;Argument[0];MapValue of Argument[-1];value",
+ "java.lang;Iterable;true;iterator;();;Element of Argument[-1];Element of ReturnValue;value",
+ "java.lang;Iterable;true;spliterator;();;Element of Argument[-1];Element of ReturnValue;value",
+ "java.util;Iterator;true;next;;;Element of Argument[-1];ReturnValue;value",
+ "java.util;ListIterator;true;previous;;;Element of Argument[-1];ReturnValue;value",
+ "java.util;ListIterator;true;add;(Object);;Argument[0];Element of Argument[-1];value",
+ "java.util;ListIterator;true;set;(Object);;Argument[0];Element of Argument[-1];value",
+ "java.util;Enumeration;true;asIterator;;;Element of Argument[-1];Element of ReturnValue;value",
+ "java.util;Enumeration;true;nextElement;;;Element of Argument[-1];ReturnValue;value",
+ "java.util;Map;true;computeIfAbsent;;;MapValue of Argument[-1];ReturnValue;value",
+ "java.util;Map;true;computeIfAbsent;;;ReturnValue of Argument[1];ReturnValue;value",
+ "java.util;Map;true;computeIfAbsent;;;ReturnValue of Argument[1];MapValue of Argument[-1];value",
+ "java.util;Map;true;entrySet;;;MapValue of Argument[-1];MapValue of Element of ReturnValue;value",
+ "java.util;Map;true;entrySet;;;MapKey of Argument[-1];MapKey of Element of ReturnValue;value",
+ "java.util;Map;true;get;;;MapValue of Argument[-1];ReturnValue;value",
+ "java.util;Map;true;getOrDefault;;;MapValue of Argument[-1];ReturnValue;value",
+ "java.util;Map;true;getOrDefault;;;Argument[1];ReturnValue;value",
+ "java.util;Map;true;put;;;MapValue of Argument[-1];ReturnValue;value",
+ "java.util;Map;true;put;;;Argument[0];MapKey of Argument[-1];value",
+ "java.util;Map;true;put;;;Argument[1];MapValue of Argument[-1];value",
+ "java.util;Map;true;putIfAbsent;;;MapValue of Argument[-1];ReturnValue;value",
+ "java.util;Map;true;putIfAbsent;;;Argument[0];MapKey of Argument[-1];value",
+ "java.util;Map;true;putIfAbsent;;;Argument[1];MapValue of Argument[-1];value",
+ "java.util;Map;true;remove;(Object);;MapValue of Argument[-1];ReturnValue;value",
+ "java.util;Map;true;replace;(Object,Object);;MapValue of Argument[-1];ReturnValue;value",
+ "java.util;Map;true;replace;(Object,Object);;Argument[0];MapKey of Argument[-1];value",
+ "java.util;Map;true;replace;(Object,Object);;Argument[1];MapValue of Argument[-1];value",
+ "java.util;Map;true;replace;(Object,Object,Object);;Argument[0];MapKey of Argument[-1];value",
+ "java.util;Map;true;replace;(Object,Object,Object);;Argument[2];MapValue of Argument[-1];value",
+ "java.util;Map;true;keySet;();;MapKey of Argument[-1];Element of ReturnValue;value",
+ "java.util;Map;true;values;();;MapValue of Argument[-1];Element of ReturnValue;value",
+ "java.util;Map;true;merge;(Object,Object,BiFunction);;Argument[1];MapValue of Argument[-1];value",
+ "java.util;Map;true;putAll;(Map);;MapKey of Argument[0];MapKey of Argument[-1];value",
+ "java.util;Map;true;putAll;(Map);;MapValue of Argument[0];MapValue of Argument[-1];value",
+ "java.util;Collection;true;parallelStream;();;Element of Argument[-1];Element of ReturnValue;value",
+ "java.util;Collection;true;stream;();;Element of Argument[-1];Element of ReturnValue;value",
+ "java.util;Collection;true;toArray;;;Element of Argument[-1];ArrayElement of ReturnValue;value",
+ "java.util;Collection;true;toArray;;;Element of Argument[-1];ArrayElement of Argument[0];value",
+ "java.util;Collection;true;add;;;Argument[0];Element of Argument[-1];value",
+ "java.util;Collection;true;addAll;;;Element of Argument[0];Element of Argument[-1];value",
+ "java.util;List;true;get;(int);;Element of Argument[-1];ReturnValue;value",
+ "java.util;List;true;listIterator;;;Element of Argument[-1];Element of ReturnValue;value",
+ "java.util;List;true;remove;(int);;Element of Argument[-1];ReturnValue;value",
+ "java.util;List;true;set;(int,Object);;Element of Argument[-1];ReturnValue;value",
+ "java.util;List;true;set;(int,Object);;Argument[1];Element of Argument[-1];value",
+ "java.util;List;true;subList;;;Element of Argument[-1];Element of ReturnValue;value",
+ "java.util;List;true;add;(int,Object);;Argument[1];Element of Argument[-1];value",
+ "java.util;List;true;addAll;(int,Collection);;Element of Argument[1];Element of Argument[-1];value",
+ "java.util;Vector;true;elementAt;(int);;Element of Argument[-1];ReturnValue;value",
+ "java.util;Vector;true;elements;();;Element of Argument[-1];Element of ReturnValue;value",
+ "java.util;Vector;true;firstElement;();;Element of Argument[-1];ReturnValue;value",
+ "java.util;Vector;true;lastElement;();;Element of Argument[-1];ReturnValue;value",
+ "java.util;Vector;true;addElement;(Object);;Argument[0];Element of Argument[-1];value",
+ "java.util;Vector;true;insertElementAt;(Object,int);;Argument[0];Element of Argument[-1];value",
+ "java.util;Vector;true;setElementAt;(Object,int);;Argument[0];Element of Argument[-1];value",
+ "java.util;Vector;true;copyInto;(Object[]);;Element of Argument[-1];ArrayElement of Argument[0];value",
+ "java.util;Stack;true;peek;();;Element of Argument[-1];ReturnValue;value",
+ "java.util;Stack;true;pop;();;Element of Argument[-1];ReturnValue;value",
+ "java.util;Stack;true;push;(Object);;Argument[0];Element of Argument[-1];value",
+ "java.util;Queue;true;element;();;Element of Argument[-1];ReturnValue;value",
+ "java.util;Queue;true;peek;();;Element of Argument[-1];ReturnValue;value",
+ "java.util;Queue;true;poll;();;Element of Argument[-1];ReturnValue;value",
+ "java.util;Queue;true;remove;();;Element of Argument[-1];ReturnValue;value",
+ "java.util;Queue;true;offer;(Object);;Argument[0];Element of Argument[-1];value",
+ "java.util;Deque;true;descendingIterator;();;Element of Argument[-1];Element of ReturnValue;value",
+ "java.util;Deque;true;getFirst;();;Element of Argument[-1];ReturnValue;value",
+ "java.util;Deque;true;getLast;();;Element of Argument[-1];ReturnValue;value",
+ "java.util;Deque;true;peekFirst;();;Element of Argument[-1];ReturnValue;value",
+ "java.util;Deque;true;peekLast;();;Element of Argument[-1];ReturnValue;value",
+ "java.util;Deque;true;pollFirst;();;Element of Argument[-1];ReturnValue;value",
+ "java.util;Deque;true;pollLast;();;Element of Argument[-1];ReturnValue;value",
+ "java.util;Deque;true;pop;();;Element of Argument[-1];ReturnValue;value",
+ "java.util;Deque;true;removeFirst;();;Element of Argument[-1];ReturnValue;value",
+ "java.util;Deque;true;removeLast;();;Element of Argument[-1];ReturnValue;value",
+ "java.util;Deque;true;push;(Object);;Argument[0];Element of Argument[-1];value",
+ "java.util;Deque;true;offerLast;(Object);;Argument[0];Element of Argument[-1];value",
+ "java.util;Deque;true;offerFirst;(Object);;Argument[0];Element of Argument[-1];value",
+ "java.util;Deque;true;addLast;(Object);;Argument[0];Element of Argument[-1];value",
+ "java.util;Deque;true;addFirst;(Object);;Argument[0];Element of Argument[-1];value",
+ "java.util.concurrent;BlockingDeque;true;pollFirst;(long,TimeUnit);;Element of Argument[-1];ReturnValue;value",
+ "java.util.concurrent;BlockingDeque;true;pollLast;(long,TimeUnit);;Element of Argument[-1];ReturnValue;value",
+ "java.util.concurrent;BlockingDeque;true;takeFirst;();;Element of Argument[-1];ReturnValue;value",
+ "java.util.concurrent;BlockingDeque;true;takeLast;();;Element of Argument[-1];ReturnValue;value",
+ "java.util.concurrent;BlockingQueue;true;poll;(long,TimeUnit);;Element of Argument[-1];ReturnValue;value",
+ "java.util.concurrent;BlockingQueue;true;take;();;Element of Argument[-1];ReturnValue;value",
+ "java.util.concurrent;BlockingQueue;true;offer;(Object,long,TimeUnit);;Argument[0];Element of Argument[-1];value",
+ "java.util.concurrent;BlockingQueue;true;put;(Object);;Argument[0];Element of Argument[-1];value",
+ "java.util.concurrent;BlockingDeque;true;offerLast;(Object,long,TimeUnit);;Argument[0];Element of Argument[-1];value",
+ "java.util.concurrent;BlockingDeque;true;offerFirst;(Object,long,TimeUnit);;Argument[0];Element of Argument[-1];value",
+ "java.util.concurrent;BlockingDeque;true;putLast;(Object);;Argument[0];Element of Argument[-1];value",
+ "java.util.concurrent;BlockingDeque;true;putFirst;(Object);;Argument[0];Element of Argument[-1];value",
+ "java.util.concurrent;BlockingQueue;true;drainTo;(Collection,int);;Element of Argument[-1];Element of Argument[0];value",
+ "java.util.concurrent;BlockingQueue;true;drainTo;(Collection);;Element of Argument[-1];Element of Argument[0];value",
+ "java.util.concurrent;ConcurrentHashMap;true;elements;();;MapValue of Argument[-1];Element of ReturnValue;value",
+ "java.util;Dictionary;true;elements;();;MapValue of Argument[-1];Element of ReturnValue;value",
+ "java.util;Dictionary;true;get;(Object);;MapValue of Argument[-1];ReturnValue;value",
+ "java.util;Dictionary;true;put;(Object,Object);;MapValue of Argument[-1];ReturnValue;value",
+ "java.util;Dictionary;true;put;(Object,Object);;Argument[0];MapKey of Argument[-1];value",
+ "java.util;Dictionary;true;put;(Object,Object);;Argument[1];MapValue of Argument[-1];value",
+ "java.util;Dictionary;true;remove;(Object);;MapValue of Argument[-1];ReturnValue;value",
+ "java.util;NavigableMap;true;ceilingEntry;(Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ "java.util;NavigableMap;true;ceilingEntry;(Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ "java.util;NavigableMap;true;descendingMap;();;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ "java.util;NavigableMap;true;descendingMap;();;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ "java.util;NavigableMap;true;firstEntry;();;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ "java.util;NavigableMap;true;firstEntry;();;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ "java.util;NavigableMap;true;floorEntry;(Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ "java.util;NavigableMap;true;floorEntry;(Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ "java.util;NavigableMap;true;headMap;(Object,boolean);;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ "java.util;NavigableMap;true;headMap;(Object,boolean);;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ "java.util;NavigableMap;true;higherEntry;(Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ "java.util;NavigableMap;true;higherEntry;(Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ "java.util;NavigableMap;true;lastEntry;();;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ "java.util;NavigableMap;true;lastEntry;();;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ "java.util;NavigableMap;true;lowerEntry;(Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ "java.util;NavigableMap;true;lowerEntry;(Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ "java.util;NavigableMap;true;pollFirstEntry;();;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ "java.util;NavigableMap;true;pollFirstEntry;();;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ "java.util;NavigableMap;true;pollLastEntry;();;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ "java.util;NavigableMap;true;pollLastEntry;();;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ "java.util;NavigableMap;true;subMap;(Object,boolean,Object,boolean);;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ "java.util;NavigableMap;true;subMap;(Object,boolean,Object,boolean);;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ "java.util;NavigableMap;true;tailMap;(Object,boolean);;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ "java.util;NavigableMap;true;tailMap;(Object,boolean);;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ "java.util;NavigableSet;true;ceiling;(Object);;Element of Argument[-1];ReturnValue;value",
+ "java.util;NavigableSet;true;descendingIterator;();;Element of Argument[-1];Element of ReturnValue;value",
+ "java.util;NavigableSet;true;descendingSet;();;Element of Argument[-1];Element of ReturnValue;value",
+ "java.util;NavigableSet;true;floor;(Object);;Element of Argument[-1];ReturnValue;value",
+ "java.util;NavigableSet;true;headSet;(Object,boolean);;Element of Argument[-1];Element of ReturnValue;value",
+ "java.util;NavigableSet;true;higher;(Object);;Element of Argument[-1];ReturnValue;value",
+ "java.util;NavigableSet;true;lower;(Object);;Element of Argument[-1];ReturnValue;value",
+ "java.util;NavigableSet;true;pollFirst;();;Element of Argument[-1];ReturnValue;value",
+ "java.util;NavigableSet;true;pollLast;();;Element of Argument[-1];ReturnValue;value",
+ "java.util;NavigableSet;true;subSet;(Object,boolean,Object,boolean);;Element of Argument[-1];Element of ReturnValue;value",
+ "java.util;NavigableSet;true;tailSet;(Object,boolean);;Element of Argument[-1];Element of ReturnValue;value",
+ "java.util;Scanner;true;next;(Pattern);;Argument[-1];ReturnValue;taint",
+ "java.util;Scanner;true;next;(String);;Argument[-1];ReturnValue;taint",
+ "java.util;SortedMap;true;headMap;(Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ "java.util;SortedMap;true;headMap;(Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ "java.util;SortedMap;true;subMap;(Object,Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ "java.util;SortedMap;true;subMap;(Object,Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ "java.util;SortedMap;true;tailMap;(Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ "java.util;SortedMap;true;tailMap;(Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ "java.util;SortedSet;true;first;();;Element of Argument[-1];ReturnValue;value",
+ "java.util;SortedSet;true;headSet;(Object);;Element of Argument[-1];Element of ReturnValue;value",
+ "java.util;SortedSet;true;last;();;Element of Argument[-1];ReturnValue;value",
+ "java.util;SortedSet;true;subSet;(Object,Object);;Element of Argument[-1];Element of ReturnValue;value",
+ "java.util;SortedSet;true;tailSet;(Object);;Element of Argument[-1];Element of ReturnValue;value",
+ "java.util.concurrent;TransferQueue;true;tryTransfer;(Object,long,TimeUnit);;Argument[0];Element of Argument[-1];value",
+ "java.util.concurrent;TransferQueue;true;transfer;(Object);;Argument[0];Element of Argument[-1];value",
+ "java.util.concurrent;TransferQueue;true;tryTransfer;(Object);;Argument[0];Element of Argument[-1];value",
+ "java.util;List;false;copyOf;(Collection);;Element of Argument[0];Element of ReturnValue;value",
+ "java.util;List;false;of;(Object[]);;ArrayElement of Argument[0];Element of ReturnValue;value",
+ "java.util;List;false;of;(Object);;Argument[0];Element of ReturnValue;value",
+ "java.util;List;false;of;(Object,Object);;Argument[0..1];Element of ReturnValue;value",
+ "java.util;List;false;of;(Object,Object,Object);;Argument[0..2];Element of ReturnValue;value",
+ "java.util;List;false;of;(Object,Object,Object,Object);;Argument[0..3];Element of ReturnValue;value",
+ "java.util;List;false;of;(Object,Object,Object,Object,Object);;Argument[0..4];Element of ReturnValue;value",
+ "java.util;List;false;of;(Object,Object,Object,Object,Object,Object);;Argument[0..5];Element of ReturnValue;value",
+ "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object);;Argument[0..6];Element of ReturnValue;value",
+ "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object);;Argument[0..7];Element of ReturnValue;value",
+ "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[0..8];Element of ReturnValue;value",
+ "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[0..9];Element of ReturnValue;value",
+ "java.util;Map;false;copyOf;(Map);;MapKey of Argument[0];MapKey of ReturnValue;value",
+ "java.util;Map;false;copyOf;(Map);;MapValue of Argument[0];MapValue of ReturnValue;value",
+ "java.util;Map;false;entry;(Object,Object);;Argument[0];MapKey of ReturnValue;value",
+ "java.util;Map;false;entry;(Object,Object);;Argument[1];MapValue of ReturnValue;value",
+ "java.util;Map;false;of;;;Argument[0];MapKey of ReturnValue;value",
+ "java.util;Map;false;of;;;Argument[1];MapValue of ReturnValue;value",
+ "java.util;Map;false;of;;;Argument[2];MapKey of ReturnValue;value",
+ "java.util;Map;false;of;;;Argument[3];MapValue of ReturnValue;value",
+ "java.util;Map;false;of;;;Argument[4];MapKey of ReturnValue;value",
+ "java.util;Map;false;of;;;Argument[5];MapValue of ReturnValue;value",
+ "java.util;Map;false;of;;;Argument[6];MapKey of ReturnValue;value",
+ "java.util;Map;false;of;;;Argument[7];MapValue of ReturnValue;value",
+ "java.util;Map;false;of;;;Argument[8];MapKey of ReturnValue;value",
+ "java.util;Map;false;of;;;Argument[9];MapValue of ReturnValue;value",
+ "java.util;Map;false;of;;;Argument[10];MapKey of ReturnValue;value",
+ "java.util;Map;false;of;;;Argument[11];MapValue of ReturnValue;value",
+ "java.util;Map;false;of;;;Argument[12];MapKey of ReturnValue;value",
+ "java.util;Map;false;of;;;Argument[13];MapValue of ReturnValue;value",
+ "java.util;Map;false;of;;;Argument[14];MapKey of ReturnValue;value",
+ "java.util;Map;false;of;;;Argument[15];MapValue of ReturnValue;value",
+ "java.util;Map;false;of;;;Argument[16];MapKey of ReturnValue;value",
+ "java.util;Map;false;of;;;Argument[17];MapValue of ReturnValue;value",
+ "java.util;Map;false;of;;;Argument[18];MapKey of ReturnValue;value",
+ "java.util;Map;false;of;;;Argument[19];MapValue of ReturnValue;value",
+ "java.util;Map;false;ofEntries;;;MapKey of ArrayElement of Argument[0];MapKey of ReturnValue;value",
+ "java.util;Map;false;ofEntries;;;MapValue of ArrayElement of Argument[0];MapValue of ReturnValue;value",
+ "java.util;Set;false;copyOf;(Collection);;Element of Argument[0];Element of ReturnValue;value",
+ "java.util;Set;false;of;(Object[]);;ArrayElement of Argument[0];Element of ReturnValue;value",
+ "java.util;Set;false;of;(Object);;Argument[0];Element of ReturnValue;value",
+ "java.util;Set;false;of;(Object,Object);;Argument[0..1];Element of ReturnValue;value",
+ "java.util;Set;false;of;(Object,Object,Object);;Argument[0..2];Element of ReturnValue;value",
+ "java.util;Set;false;of;(Object,Object,Object,Object);;Argument[0..3];Element of ReturnValue;value",
+ "java.util;Set;false;of;(Object,Object,Object,Object,Object);;Argument[0..4];Element of ReturnValue;value",
+ "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object);;Argument[0..5];Element of ReturnValue;value",
+ "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object);;Argument[0..6];Element of ReturnValue;value",
+ "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object);;Argument[0..7];Element of ReturnValue;value",
+ "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[0..8];Element of ReturnValue;value",
+ "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[0..9];Element of ReturnValue;value",
+ "java.util;Arrays;false;stream;;;ArrayElement of Argument[0];Element of ReturnValue;value",
+ "java.util;Arrays;false;spliterator;;;ArrayElement of Argument[0];Element of ReturnValue;value",
+ "java.util;Arrays;false;copyOfRange;;;ArrayElement of Argument[0];ArrayElement of ReturnValue;value",
+ "java.util;Arrays;false;copyOf;;;ArrayElement of Argument[0];ArrayElement of ReturnValue;value",
+ "java.util;Collections;false;list;(Enumeration);;Element of Argument[0];Element of ReturnValue;value",
+ "java.util;Collections;false;enumeration;(Collection);;Element of Argument[0];Element of ReturnValue;value",
+ "java.util;Collections;false;nCopies;(int,Object);;Argument[1];Element of ReturnValue;value",
+ "java.util;Collections;false;singletonMap;(Object,Object);;Argument[0];MapKey of ReturnValue;value",
+ "java.util;Collections;false;singletonMap;(Object,Object);;Argument[1];MapValue of ReturnValue;value",
+ "java.util;Collections;false;singletonList;(Object);;Argument[0];Element of ReturnValue;value",
+ "java.util;Collections;false;singleton;(Object);;Argument[0];Element of ReturnValue;value",
+ "java.util;Collections;false;checkedNavigableMap;(NavigableMap,Class,Class);;MapKey of Argument[0];MapKey of ReturnValue;value",
+ "java.util;Collections;false;checkedNavigableMap;(NavigableMap,Class,Class);;MapValue of Argument[0];MapValue of ReturnValue;value",
+ "java.util;Collections;false;checkedSortedMap;(SortedMap,Class,Class);;MapKey of Argument[0];MapKey of ReturnValue;value",
+ "java.util;Collections;false;checkedSortedMap;(SortedMap,Class,Class);;MapValue of Argument[0];MapValue of ReturnValue;value",
+ "java.util;Collections;false;checkedMap;(Map,Class,Class);;MapKey of Argument[0];MapKey of ReturnValue;value",
+ "java.util;Collections;false;checkedMap;(Map,Class,Class);;MapValue of Argument[0];MapValue of ReturnValue;value",
+ "java.util;Collections;false;checkedList;(List,Class);;Element of Argument[0];Element of ReturnValue;value",
+ "java.util;Collections;false;checkedNavigableSet;(NavigableSet,Class);;Element of Argument[0];Element of ReturnValue;value",
+ "java.util;Collections;false;checkedSortedSet;(SortedSet,Class);;Element of Argument[0];Element of ReturnValue;value",
+ "java.util;Collections;false;checkedSet;(Set,Class);;Element of Argument[0];Element of ReturnValue;value",
+ "java.util;Collections;false;checkedCollection;(Collection,Class);;Element of Argument[0];Element of ReturnValue;value",
+ "java.util;Collections;false;synchronizedNavigableMap;(NavigableMap);;MapKey of Argument[0];MapKey of ReturnValue;value",
+ "java.util;Collections;false;synchronizedNavigableMap;(NavigableMap);;MapValue of Argument[0];MapValue of ReturnValue;value",
+ "java.util;Collections;false;synchronizedSortedMap;(SortedMap);;MapKey of Argument[0];MapKey of ReturnValue;value",
+ "java.util;Collections;false;synchronizedSortedMap;(SortedMap);;MapValue of Argument[0];MapValue of ReturnValue;value",
+ "java.util;Collections;false;synchronizedMap;(Map);;MapKey of Argument[0];MapKey of ReturnValue;value",
+ "java.util;Collections;false;synchronizedMap;(Map);;MapValue of Argument[0];MapValue of ReturnValue;value",
+ "java.util;Collections;false;synchronizedList;(List);;Element of Argument[0];Element of ReturnValue;value",
+ "java.util;Collections;false;synchronizedNavigableSet;(NavigableSet);;Element of Argument[0];Element of ReturnValue;value",
+ "java.util;Collections;false;synchronizedSortedSet;(SortedSet);;Element of Argument[0];Element of ReturnValue;value",
+ "java.util;Collections;false;synchronizedSet;(Set);;Element of Argument[0];Element of ReturnValue;value",
+ "java.util;Collections;false;synchronizedCollection;(Collection);;Element of Argument[0];Element of ReturnValue;value",
+ "java.util;Collections;false;unmodifiableNavigableMap;(NavigableMap);;MapKey of Argument[0];MapKey of ReturnValue;value",
+ "java.util;Collections;false;unmodifiableNavigableMap;(NavigableMap);;MapValue of Argument[0];MapValue of ReturnValue;value",
+ "java.util;Collections;false;unmodifiableSortedMap;(SortedMap);;MapKey of Argument[0];MapKey of ReturnValue;value",
+ "java.util;Collections;false;unmodifiableSortedMap;(SortedMap);;MapValue of Argument[0];MapValue of ReturnValue;value",
+ "java.util;Collections;false;unmodifiableMap;(Map);;MapKey of Argument[0];MapKey of ReturnValue;value",
+ "java.util;Collections;false;unmodifiableMap;(Map);;MapValue of Argument[0];MapValue of ReturnValue;value",
+ "java.util;Collections;false;unmodifiableList;(List);;Element of Argument[0];Element of ReturnValue;value",
+ "java.util;Collections;false;unmodifiableNavigableSet;(NavigableSet);;Element of Argument[0];Element of ReturnValue;value",
+ "java.util;Collections;false;unmodifiableSortedSet;(SortedSet);;Element of Argument[0];Element of ReturnValue;value",
+ "java.util;Collections;false;unmodifiableSet;(Set);;Element of Argument[0];Element of ReturnValue;value",
+ "java.util;Collections;false;unmodifiableCollection;(Collection);;Element of Argument[0];Element of ReturnValue;value",
+ "java.util;Collections;false;max;;;Element of Argument[0];ReturnValue;value",
+ "java.util;Collections;false;min;;;Element of Argument[0];ReturnValue;value",
+ "java.util;Arrays;false;fill;(Object[],int,int,Object);;Argument[3];ArrayElement of Argument[0];value",
+ "java.util;Arrays;false;fill;(Object[],Object);;Argument[1];ArrayElement of Argument[0];value",
+ "java.util;Arrays;false;fill;(float[],int,int,float);;Argument[3];ArrayElement of Argument[0];value",
+ "java.util;Arrays;false;fill;(float[],float);;Argument[1];ArrayElement of Argument[0];value",
+ "java.util;Arrays;false;fill;(double[],int,int,double);;Argument[3];ArrayElement of Argument[0];value",
+ "java.util;Arrays;false;fill;(double[],double);;Argument[1];ArrayElement of Argument[0];value",
+ "java.util;Arrays;false;fill;(boolean[],int,int,boolean);;Argument[3];ArrayElement of Argument[0];value",
+ "java.util;Arrays;false;fill;(boolean[],boolean);;Argument[1];ArrayElement of Argument[0];value",
+ "java.util;Arrays;false;fill;(byte[],int,int,byte);;Argument[3];ArrayElement of Argument[0];value",
+ "java.util;Arrays;false;fill;(byte[],byte);;Argument[1];ArrayElement of Argument[0];value",
+ "java.util;Arrays;false;fill;(char[],int,int,char);;Argument[3];ArrayElement of Argument[0];value",
+ "java.util;Arrays;false;fill;(char[],char);;Argument[1];ArrayElement of Argument[0];value",
+ "java.util;Arrays;false;fill;(short[],int,int,short);;Argument[3];ArrayElement of Argument[0];value",
+ "java.util;Arrays;false;fill;(short[],short);;Argument[1];ArrayElement of Argument[0];value",
+ "java.util;Arrays;false;fill;(int[],int,int,int);;Argument[3];ArrayElement of Argument[0];value",
+ "java.util;Arrays;false;fill;(int[],int);;Argument[1];ArrayElement of Argument[0];value",
+ "java.util;Arrays;false;fill;(long[],int,int,long);;Argument[3];ArrayElement of Argument[0];value",
+ "java.util;Arrays;false;fill;(long[],long);;Argument[1];ArrayElement of Argument[0];value",
+ "java.util;Collections;false;replaceAll;(List,Object,Object);;Argument[2];Element of Argument[0];value",
+ "java.util;Collections;false;copy;(List,List);;Element of Argument[1];Element of Argument[0];value",
+ "java.util;Collections;false;fill;(List,Object);;Argument[1];Element of Argument[0];value",
+ "java.util;Arrays;false;asList;;;ArrayElement of Argument[0];Element of ReturnValue;value",
+ "java.util;Collections;false;addAll;(Collection,Object[]);;ArrayElement of Argument[1];Element of Argument[0];value"
+ ]
+ }
+}
+
private predicate taintPreservingQualifierToMethod(Method m) {
// java.util.Map.Entry
m.getDeclaringType() instanceof EntryType and
From 9e313d0cf6817201b96181463bc01ecc13cb1ead Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Thu, 22 Apr 2021 16:57:41 +0200
Subject: [PATCH 069/272] Java: Remove container taint steps.
---
.../semmle/code/java/dataflow/internal/TaintTrackingUtil.qll | 4 ----
1 file changed, 4 deletions(-)
diff --git a/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll b/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll
index 339e0f05e1e..079d61809db 100644
--- a/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll
+++ b/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll
@@ -99,8 +99,6 @@ private predicate localAdditionalTaintExprStep(Expr src, Expr sink) {
or
sink.(LogicExpr).getAnOperand() = src
or
- containerReturnValueStep(src, sink)
- or
constructorStep(src, sink)
or
qualifierToMethodStep(src, sink)
@@ -123,8 +121,6 @@ private predicate localAdditionalTaintExprStep(Expr src, Expr sink) {
* This is restricted to cases where the step updates the value of `sink`.
*/
private predicate localAdditionalTaintUpdateStep(Expr src, Expr sink) {
- containerUpdateStep(src, sink)
- or
qualifierToArgumentStep(src, sink)
or
argToArgStep(src, sink)
From 3f538e7fac175e8d2778586f4da161105f745962 Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Tue, 4 May 2021 10:48:28 +0200
Subject: [PATCH 070/272] Java: Update some models.
---
.../code/java/frameworks/ApacheHttp.qll | 19 ++--
.../code/java/frameworks/apache/Lang.qll | 107 +++++++++++-------
.../code/java/frameworks/guava/Base.qll | 8 +-
.../semmle/code/java/frameworks/guava/IO.qll | 8 +-
4 files changed, 93 insertions(+), 49 deletions(-)
diff --git a/java/ql/src/semmle/code/java/frameworks/ApacheHttp.qll b/java/ql/src/semmle/code/java/frameworks/ApacheHttp.qll
index 8378f1e7260..80cb589e6f2 100644
--- a/java/ql/src/semmle/code/java/frameworks/ApacheHttp.qll
+++ b/java/ql/src/semmle/code/java/frameworks/ApacheHttp.qll
@@ -165,14 +165,17 @@ private class ApacheHttpFlowStep extends SummaryModelCsv {
"org.apache.http.util;EncodingUtils;true;getAsciiString;;;Argument[0];ReturnValue;taint",
"org.apache.http.util;EncodingUtils;true;getBytes;(String,String);;Argument[0];ReturnValue;taint",
"org.apache.http.util;EncodingUtils;true;getString;;;Argument[0];ReturnValue;taint",
- "org.apache.http.util;Args;true;containsNoBlanks;(T,String);;Argument[0];ReturnValue;value",
- "org.apache.http.util;Args;true;notNull;(T,String);;Argument[0];ReturnValue;value",
- "org.apache.http.util;Args;true;notEmpty;(T,String);;Argument[0];ReturnValue;value",
- "org.apache.http.util;Args;true;notBlank;(T,String);;Argument[0];ReturnValue;value",
- "org.apache.hc.core5.util;Args;true;containsNoBlanks;(T,String);;Argument[0];ReturnValue;value",
- "org.apache.hc.core5.util;Args;true;notNull;(T,String);;Argument[0];ReturnValue;value",
- "org.apache.hc.core5.util;Args;true;notEmpty;(T,String);;Argument[0];ReturnValue;value",
- "org.apache.hc.core5.util;Args;true;notBlank;(T,String);;Argument[0];ReturnValue;value",
+ "org.apache.http.util;Args;true;containsNoBlanks;(CharSequence,String);;Argument[0];ReturnValue;value",
+ "org.apache.http.util;Args;true;notNull;(Object,String);;Argument[0];ReturnValue;value",
+ "org.apache.http.util;Args;true;notEmpty;(CharSequence,String);;Argument[0];ReturnValue;value",
+ "org.apache.http.util;Args;true;notEmpty;(Collection,String);;Argument[0];ReturnValue;value",
+ "org.apache.http.util;Args;true;notBlank;(CharSequence,String);;Argument[0];ReturnValue;value",
+ "org.apache.hc.core5.util;Args;true;containsNoBlanks;(CharSequence,String);;Argument[0];ReturnValue;value",
+ "org.apache.hc.core5.util;Args;true;notNull;(Object,String);;Argument[0];ReturnValue;value",
+ "org.apache.hc.core5.util;Args;true;notEmpty;(Collection,String);;Argument[0];ReturnValue;value",
+ "org.apache.hc.core5.util;Args;true;notEmpty;(CharSequence,String);;Argument[0];ReturnValue;value",
+ "org.apache.hc.core5.util;Args;true;notEmpty;(Object,String);;Argument[0];ReturnValue;value",
+ "org.apache.hc.core5.util;Args;true;notBlank;(CharSequence,String);;Argument[0];ReturnValue;value",
"org.apache.hc.core5.http.io.entity;HttpEntities;true;create;;;Argument[0];ReturnValue;taint",
"org.apache.hc.core5.http.io.entity;HttpEntities;true;createGzipped;;;Argument[0];ReturnValue;taint",
"org.apache.hc.core5.http.io.entity;HttpEntities;true;createUrlEncoded;;;Argument[0];ReturnValue;taint",
diff --git a/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll b/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll
index 670014e0f8f..ab411c5ce48 100644
--- a/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll
+++ b/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll
@@ -94,29 +94,33 @@ private class ApacheStringUtilsModel extends SummaryModelCsv {
"org.apache.commons.lang3;StringUtils;false;defaultString;;;Argument[0..1];ReturnValue;taint",
"org.apache.commons.lang3;StringUtils;false;deleteWhitespace;;;Argument[0];ReturnValue;taint",
"org.apache.commons.lang3;StringUtils;false;difference;;;Argument[0..1];ReturnValue;taint",
- "org.apache.commons.lang3;StringUtils;false;firstNonBlank;;;Argument[0];ReturnValue;taint",
- "org.apache.commons.lang3;StringUtils;false;firstNonEmpty;;;Argument[0];ReturnValue;taint",
+ "org.apache.commons.lang3;StringUtils;false;firstNonBlank;;;ArrayElement of Argument[0];ReturnValue;value",
+ "org.apache.commons.lang3;StringUtils;false;firstNonEmpty;;;ArrayElement of Argument[0];ReturnValue;value",
"org.apache.commons.lang3;StringUtils;false;getBytes;;;Argument[0];ReturnValue;taint",
- "org.apache.commons.lang3;StringUtils;false;getCommonPrefix;;;Argument[0];ReturnValue;taint",
+ "org.apache.commons.lang3;StringUtils;false;getCommonPrefix;;;ArrayElement of Argument[0];ReturnValue;taint",
"org.apache.commons.lang3;StringUtils;false;getDigits;;;Argument[0];ReturnValue;taint",
"org.apache.commons.lang3;StringUtils;false;getIfBlank;;;Argument[0..1];ReturnValue;taint",
"org.apache.commons.lang3;StringUtils;false;getIfEmpty;;;Argument[0..1];ReturnValue;taint",
"org.apache.commons.lang3;StringUtils;false;join;(char[],char);;Argument[0];ReturnValue;taint",
"org.apache.commons.lang3;StringUtils;false;join;(char[],char,int,int);;Argument[0];ReturnValue;taint",
- "org.apache.commons.lang3;StringUtils;false;join;(java.lang.Iterable,char);;Argument[0];ReturnValue;taint",
- "org.apache.commons.lang3;StringUtils;false;join;(java.lang.Iterable,java.lang.String);;Argument[0..1];ReturnValue;taint",
- "org.apache.commons.lang3;StringUtils;false;join;(java.lang.Object[]);;Argument[0];ReturnValue;taint",
- "org.apache.commons.lang3;StringUtils;false;join;(java.lang.Object[],char);;Argument[0];ReturnValue;taint",
- "org.apache.commons.lang3;StringUtils;false;join;(java.lang.Object[],char,int,int);;Argument[0];ReturnValue;taint",
- "org.apache.commons.lang3;StringUtils;false;join;(java.lang.Object[],java.lang.String);;Argument[0..1];ReturnValue;taint",
- "org.apache.commons.lang3;StringUtils;false;join;(java.lang.Object[],java.lang.String,int,int);;Argument[0];ReturnValue;taint",
+ "org.apache.commons.lang3;StringUtils;false;join;(java.lang.Iterable,char);;Element of Argument[0];ReturnValue;taint",
+ "org.apache.commons.lang3;StringUtils;false;join;(java.lang.Iterable,java.lang.String);;Element of Argument[0];ReturnValue;taint",
+ "org.apache.commons.lang3;StringUtils;false;join;(java.lang.Iterable,java.lang.String);;Argument[1];ReturnValue;taint",
+ "org.apache.commons.lang3;StringUtils;false;join;(java.lang.Object[]);;ArrayElement of Argument[0];ReturnValue;taint",
+ "org.apache.commons.lang3;StringUtils;false;join;(java.lang.Object[],char);;ArrayElement of Argument[0];ReturnValue;taint",
+ "org.apache.commons.lang3;StringUtils;false;join;(java.lang.Object[],char,int,int);;ArrayElement of Argument[0];ReturnValue;taint",
+ "org.apache.commons.lang3;StringUtils;false;join;(java.lang.Object[],java.lang.String);;ArrayElement of Argument[0];ReturnValue;taint",
+ "org.apache.commons.lang3;StringUtils;false;join;(java.lang.Object[],java.lang.String);;Argument[1];ReturnValue;taint",
+ "org.apache.commons.lang3;StringUtils;false;join;(java.lang.Object[],java.lang.String,int,int);;ArrayElement of Argument[0];ReturnValue;taint",
"org.apache.commons.lang3;StringUtils;false;join;(java.lang.Object[],java.lang.String,int,int);;Argument[1];ReturnValue;taint",
- "org.apache.commons.lang3;StringUtils;false;join;(java.util.Iterator,char);;Argument[0];ReturnValue;taint",
- "org.apache.commons.lang3;StringUtils;false;join;(java.util.Iterator,java.lang.String);;Argument[0..1];ReturnValue;taint",
- "org.apache.commons.lang3;StringUtils;false;join;(java.util.List,char,int,int);;Argument[0];ReturnValue;taint",
- "org.apache.commons.lang3;StringUtils;false;join;(java.util.List,java.lang.String,int,int);;Argument[0];ReturnValue;taint",
+ "org.apache.commons.lang3;StringUtils;false;join;(java.util.Iterator,char);;Element of Argument[0];ReturnValue;taint",
+ "org.apache.commons.lang3;StringUtils;false;join;(java.util.Iterator,java.lang.String);;Element of Argument[0];ReturnValue;taint",
+ "org.apache.commons.lang3;StringUtils;false;join;(java.util.Iterator,java.lang.String);;Argument[1];ReturnValue;taint",
+ "org.apache.commons.lang3;StringUtils;false;join;(java.util.List,char,int,int);;Element of Argument[0];ReturnValue;taint",
+ "org.apache.commons.lang3;StringUtils;false;join;(java.util.List,java.lang.String,int,int);;Element of Argument[0];ReturnValue;taint",
"org.apache.commons.lang3;StringUtils;false;join;(java.util.List,java.lang.String,int,int);;Argument[1];ReturnValue;taint",
- "org.apache.commons.lang3;StringUtils;false;joinWith;;;Argument[0..1];ReturnValue;taint",
+ "org.apache.commons.lang3;StringUtils;false;joinWith;;;Argument[0];ReturnValue;taint",
+ "org.apache.commons.lang3;StringUtils;false;joinWith;;;ArrayElement of Argument[1];ReturnValue;taint",
"org.apache.commons.lang3;StringUtils;false;left;;;Argument[0];ReturnValue;taint",
"org.apache.commons.lang3;StringUtils;false;leftPad;(java.lang.String,int,java.lang.String);;Argument[2];ReturnValue;taint",
"org.apache.commons.lang3;StringUtils;false;leftPad;;;Argument[0];ReturnValue;taint",
@@ -148,9 +152,9 @@ private class ApacheStringUtilsModel extends SummaryModelCsv {
"org.apache.commons.lang3;StringUtils;false;replaceChars;(java.lang.String,java.lang.String,java.lang.String);;Argument[2];ReturnValue;taint",
"org.apache.commons.lang3;StringUtils;false;replaceChars;;;Argument[0];ReturnValue;taint",
"org.apache.commons.lang3;StringUtils;false;replaceEach;;;Argument[0];ReturnValue;taint",
- "org.apache.commons.lang3;StringUtils;false;replaceEach;;;Argument[2];ReturnValue;taint",
+ "org.apache.commons.lang3;StringUtils;false;replaceEach;;;ArrayElement of Argument[2];ReturnValue;taint",
"org.apache.commons.lang3;StringUtils;false;replaceEachRepeatedly;;;Argument[0];ReturnValue;taint",
- "org.apache.commons.lang3;StringUtils;false;replaceEachRepeatedly;;;Argument[2];ReturnValue;taint",
+ "org.apache.commons.lang3;StringUtils;false;replaceEachRepeatedly;;;ArrayElement of Argument[2];ReturnValue;taint",
"org.apache.commons.lang3;StringUtils;false;replaceFirst;;;Argument[0];ReturnValue;taint",
"org.apache.commons.lang3;StringUtils;false;replaceFirst;;;Argument[2];ReturnValue;taint",
"org.apache.commons.lang3;StringUtils;false;replaceIgnoreCase;;;Argument[0];ReturnValue;taint",
@@ -182,7 +186,7 @@ private class ApacheStringUtilsModel extends SummaryModelCsv {
"org.apache.commons.lang3;StringUtils;false;strip;(java.lang.String);;Argument[0];ReturnValue;taint",
"org.apache.commons.lang3;StringUtils;false;strip;(java.lang.String,java.lang.String);;Argument[0];ReturnValue;taint",
"org.apache.commons.lang3;StringUtils;false;stripAccents;;;Argument[0];ReturnValue;taint",
- "org.apache.commons.lang3;StringUtils;false;stripAll;;;Argument[0];ReturnValue;taint",
+ "org.apache.commons.lang3;StringUtils;false;stripAll;;;ArrayElement of Argument[0];ArrayElement of ReturnValue;taint",
"org.apache.commons.lang3;StringUtils;false;stripEnd;;;Argument[0];ReturnValue;taint",
"org.apache.commons.lang3;StringUtils;false;stripStart;;;Argument[0];ReturnValue;taint",
"org.apache.commons.lang3;StringUtils;false;stripToEmpty;;;Argument[0];ReturnValue;taint",
@@ -229,7 +233,8 @@ private class ApacheStrBuilderModel extends SummaryModelCsv {
"org.apache.commons.lang3.text;StrBuilder;false;append;(java.lang.Object);;Argument[0];Argument[-1];taint",
"org.apache.commons.lang3.text;StrBuilder;false;append;(java.lang.String);;Argument[0];Argument[-1];taint",
"org.apache.commons.lang3.text;StrBuilder;false;append;(java.lang.String,int,int);;Argument[0];Argument[-1];taint",
- "org.apache.commons.lang3.text;StrBuilder;false;append;(java.lang.String,java.lang.Object[]);;Argument[0..1];Argument[-1];taint",
+ "org.apache.commons.lang3.text;StrBuilder;false;append;(java.lang.String,java.lang.Object[]);;Argument[0];Argument[-1];taint",
+ "org.apache.commons.lang3.text;StrBuilder;false;append;(java.lang.String,java.lang.Object[]);;ArrayElement of Argument[1];Argument[-1];taint",
"org.apache.commons.lang3.text;StrBuilder;false;append;(java.lang.StringBuffer);;Argument[0];Argument[-1];taint",
"org.apache.commons.lang3.text;StrBuilder;false;append;(java.lang.StringBuffer,int,int);;Argument[0];Argument[-1];taint",
"org.apache.commons.lang3.text;StrBuilder;false;append;(java.lang.StringBuilder);;Argument[0];Argument[-1];taint",
@@ -238,7 +243,9 @@ private class ApacheStrBuilderModel extends SummaryModelCsv {
"org.apache.commons.lang3.text;StrBuilder;false;append;(java.nio.CharBuffer,int,int);;Argument[0];Argument[-1];taint",
"org.apache.commons.lang3.text;StrBuilder;false;append;(org.apache.commons.lang3.text.StrBuilder);;Argument[0];Argument[-1];taint",
"org.apache.commons.lang3.text;StrBuilder;false;append;;;Argument[-1];ReturnValue;taint",
- "org.apache.commons.lang3.text;StrBuilder;false;appendAll;;;Argument[0];Argument[-1];taint",
+ "org.apache.commons.lang3.text;StrBuilder;false;appendAll;(Iterable);;Element of Argument[0];Argument[-1];taint",
+ "org.apache.commons.lang3.text;StrBuilder;false;appendAll;(Iterator);;Element of Argument[0];Argument[-1];taint",
+ "org.apache.commons.lang3.text;StrBuilder;false;appendAll;(Object[]);;ArrayElement of Argument[0];Argument[-1];taint",
"org.apache.commons.lang3.text;StrBuilder;false;appendAll;;;Argument[-1];ReturnValue;taint",
"org.apache.commons.lang3.text;StrBuilder;false;appendFixedWidthPadLeft;;;Argument[-1];ReturnValue;taint",
"org.apache.commons.lang3.text;StrBuilder;false;appendFixedWidthPadLeft;;;Argument[0];Argument[-1];taint",
@@ -249,14 +256,18 @@ private class ApacheStrBuilderModel extends SummaryModelCsv {
"org.apache.commons.lang3.text;StrBuilder;false;appendSeparator;(java.lang.String,java.lang.String);;Argument[0..1];Argument[-1];taint",
"org.apache.commons.lang3.text;StrBuilder;false;appendSeparator;;;Argument[-1];ReturnValue;taint",
"org.apache.commons.lang3.text;StrBuilder;false;appendTo;;;Argument[-1];Argument[0];taint",
- "org.apache.commons.lang3.text;StrBuilder;false;appendWithSeparators;;;Argument[0..1];Argument[-1];taint",
+ "org.apache.commons.lang3.text;StrBuilder;false;appendWithSeparators;(Iterable,String);;Element of Argument[0];Argument[-1];taint",
+ "org.apache.commons.lang3.text;StrBuilder;false;appendWithSeparators;(Iterator,String);;Element of Argument[0];Argument[-1];taint",
+ "org.apache.commons.lang3.text;StrBuilder;false;appendWithSeparators;(Object[],String);;ArrayElement of Argument[0];Argument[-1];taint",
+ "org.apache.commons.lang3.text;StrBuilder;false;appendWithSeparators;;;Argument[1];Argument[-1];taint",
"org.apache.commons.lang3.text;StrBuilder;false;appendWithSeparators;;;Argument[-1];ReturnValue;taint",
"org.apache.commons.lang3.text;StrBuilder;false;appendln;(char[]);;Argument[0];Argument[-1];taint",
"org.apache.commons.lang3.text;StrBuilder;false;appendln;(char[],int,int);;Argument[0];Argument[-1];taint",
"org.apache.commons.lang3.text;StrBuilder;false;appendln;(java.lang.Object);;Argument[0];Argument[-1];taint",
"org.apache.commons.lang3.text;StrBuilder;false;appendln;(java.lang.String);;Argument[0];Argument[-1];taint",
"org.apache.commons.lang3.text;StrBuilder;false;appendln;(java.lang.String,int,int);;Argument[0];Argument[-1];taint",
- "org.apache.commons.lang3.text;StrBuilder;false;appendln;(java.lang.String,java.lang.Object[]);;Argument[0..1];Argument[-1];taint",
+ "org.apache.commons.lang3.text;StrBuilder;false;appendln;(java.lang.String,java.lang.Object[]);;Argument[0];Argument[-1];taint",
+ "org.apache.commons.lang3.text;StrBuilder;false;appendln;(java.lang.String,java.lang.Object[]);;ArrayElement of Argument[1];Argument[-1];taint",
"org.apache.commons.lang3.text;StrBuilder;false;appendln;(java.lang.StringBuffer);;Argument[0];Argument[-1];taint",
"org.apache.commons.lang3.text;StrBuilder;false;appendln;(java.lang.StringBuffer,int,int);;Argument[0];Argument[-1];taint",
"org.apache.commons.lang3.text;StrBuilder;false;appendln;(java.lang.StringBuilder);;Argument[0];Argument[-1];taint",
@@ -296,7 +307,8 @@ private class ApacheStrBuilderModel extends SummaryModelCsv {
"org.apache.commons.text;StrBuilder;false;append;(java.lang.Object);;Argument[0];Argument[-1];taint",
"org.apache.commons.text;StrBuilder;false;append;(java.lang.String);;Argument[0];Argument[-1];taint",
"org.apache.commons.text;StrBuilder;false;append;(java.lang.String,int,int);;Argument[0];Argument[-1];taint",
- "org.apache.commons.text;StrBuilder;false;append;(java.lang.String,java.lang.Object[]);;Argument[0..1];Argument[-1];taint",
+ "org.apache.commons.text;StrBuilder;false;append;(java.lang.String,java.lang.Object[]);;Argument[0];Argument[-1];taint",
+ "org.apache.commons.text;StrBuilder;false;append;(java.lang.String,java.lang.Object[]);;ArrayElement of Argument[1];Argument[-1];taint",
"org.apache.commons.text;StrBuilder;false;append;(java.lang.StringBuffer);;Argument[0];Argument[-1];taint",
"org.apache.commons.text;StrBuilder;false;append;(java.lang.StringBuffer,int,int);;Argument[0];Argument[-1];taint",
"org.apache.commons.text;StrBuilder;false;append;(java.lang.StringBuilder);;Argument[0];Argument[-1];taint",
@@ -305,7 +317,9 @@ private class ApacheStrBuilderModel extends SummaryModelCsv {
"org.apache.commons.text;StrBuilder;false;append;(java.nio.CharBuffer,int,int);;Argument[0];Argument[-1];taint",
"org.apache.commons.text;StrBuilder;false;append;(org.apache.commons.text.StrBuilder);;Argument[0];Argument[-1];taint",
"org.apache.commons.text;StrBuilder;false;append;;;Argument[-1];ReturnValue;taint",
- "org.apache.commons.text;StrBuilder;false;appendAll;;;Argument[0];Argument[-1];taint",
+ "org.apache.commons.text;StrBuilder;false;appendAll;(Iterable);;Element of Argument[0];Argument[-1];taint",
+ "org.apache.commons.text;StrBuilder;false;appendAll;(Iterator);;Element of Argument[0];Argument[-1];taint",
+ "org.apache.commons.text;StrBuilder;false;appendAll;(Object[]);;ArrayElement of Argument[0];Argument[-1];taint",
"org.apache.commons.text;StrBuilder;false;appendAll;;;Argument[-1];ReturnValue;taint",
"org.apache.commons.text;StrBuilder;false;appendFixedWidthPadLeft;;;Argument[-1];ReturnValue;taint",
"org.apache.commons.text;StrBuilder;false;appendFixedWidthPadLeft;;;Argument[0];Argument[-1];taint",
@@ -316,14 +330,18 @@ private class ApacheStrBuilderModel extends SummaryModelCsv {
"org.apache.commons.text;StrBuilder;false;appendSeparator;(java.lang.String,java.lang.String);;Argument[0..1];Argument[-1];taint",
"org.apache.commons.text;StrBuilder;false;appendSeparator;;;Argument[-1];ReturnValue;taint",
"org.apache.commons.text;StrBuilder;false;appendTo;;;Argument[-1];Argument[0];taint",
- "org.apache.commons.text;StrBuilder;false;appendWithSeparators;;;Argument[0..1];Argument[-1];taint",
+ "org.apache.commons.text;StrBuilder;false;appendWithSeparators;(Iterable,String);;Element of Argument[0];Argument[-1];taint",
+ "org.apache.commons.text;StrBuilder;false;appendWithSeparators;(Iterator,String);;Element of Argument[0];Argument[-1];taint",
+ "org.apache.commons.text;StrBuilder;false;appendWithSeparators;(Object[],String);;ArrayElement of Argument[0];Argument[-1];taint",
+ "org.apache.commons.text;StrBuilder;false;appendWithSeparators;;;Argument[1];Argument[-1];taint",
"org.apache.commons.text;StrBuilder;false;appendWithSeparators;;;Argument[-1];ReturnValue;taint",
"org.apache.commons.text;StrBuilder;false;appendln;(char[]);;Argument[0];Argument[-1];taint",
"org.apache.commons.text;StrBuilder;false;appendln;(char[],int,int);;Argument[0];Argument[-1];taint",
"org.apache.commons.text;StrBuilder;false;appendln;(java.lang.Object);;Argument[0];Argument[-1];taint",
"org.apache.commons.text;StrBuilder;false;appendln;(java.lang.String);;Argument[0];Argument[-1];taint",
"org.apache.commons.text;StrBuilder;false;appendln;(java.lang.String,int,int);;Argument[0];Argument[-1];taint",
- "org.apache.commons.text;StrBuilder;false;appendln;(java.lang.String,java.lang.Object[]);;Argument[0..1];Argument[-1];taint",
+ "org.apache.commons.text;StrBuilder;false;appendln;(java.lang.String,java.lang.Object[]);;Argument[0];Argument[-1];taint",
+ "org.apache.commons.text;StrBuilder;false;appendln;(java.lang.String,java.lang.Object[]);;ArrayElement of Argument[1];Argument[-1];taint",
"org.apache.commons.text;StrBuilder;false;appendln;(java.lang.StringBuffer);;Argument[0];Argument[-1];taint",
"org.apache.commons.text;StrBuilder;false;appendln;(java.lang.StringBuffer,int,int);;Argument[0];Argument[-1];taint",
"org.apache.commons.text;StrBuilder;false;appendln;(java.lang.StringBuilder);;Argument[0];Argument[-1];taint",
@@ -364,7 +382,8 @@ private class ApacheStrBuilderModel extends SummaryModelCsv {
"org.apache.commons.text;TextStringBuilder;false;append;(java.lang.Object);;Argument[0];Argument[-1];taint",
"org.apache.commons.text;TextStringBuilder;false;append;(java.lang.String);;Argument[0];Argument[-1];taint",
"org.apache.commons.text;TextStringBuilder;false;append;(java.lang.String,int,int);;Argument[0];Argument[-1];taint",
- "org.apache.commons.text;TextStringBuilder;false;append;(java.lang.String,java.lang.Object[]);;Argument[0..1];Argument[-1];taint",
+ "org.apache.commons.text;TextStringBuilder;false;append;(java.lang.String,java.lang.Object[]);;Argument[0];Argument[-1];taint",
+ "org.apache.commons.text;TextStringBuilder;false;append;(java.lang.String,java.lang.Object[]);;ArrayElement of Argument[1];Argument[-1];taint",
"org.apache.commons.text;TextStringBuilder;false;append;(java.lang.StringBuffer);;Argument[0];Argument[-1];taint",
"org.apache.commons.text;TextStringBuilder;false;append;(java.lang.StringBuffer,int,int);;Argument[0];Argument[-1];taint",
"org.apache.commons.text;TextStringBuilder;false;append;(java.lang.StringBuilder);;Argument[0];Argument[-1];taint",
@@ -373,7 +392,9 @@ private class ApacheStrBuilderModel extends SummaryModelCsv {
"org.apache.commons.text;TextStringBuilder;false;append;(java.nio.CharBuffer,int,int);;Argument[0];Argument[-1];taint",
"org.apache.commons.text;TextStringBuilder;false;append;(org.apache.commons.text.TextStringBuilder);;Argument[0];Argument[-1];taint",
"org.apache.commons.text;TextStringBuilder;false;append;;;Argument[-1];ReturnValue;taint",
- "org.apache.commons.text;TextStringBuilder;false;appendAll;;;Argument[0];Argument[-1];taint",
+ "org.apache.commons.text;TextStringBuilder;false;appendAll;(Iterable);;Element of Argument[0];Argument[-1];taint",
+ "org.apache.commons.text;TextStringBuilder;false;appendAll;(Iterator);;Element of Argument[0];Argument[-1];taint",
+ "org.apache.commons.text;TextStringBuilder;false;appendAll;(Object[]);;ArrayElement of Argument[0];Argument[-1];taint",
"org.apache.commons.text;TextStringBuilder;false;appendAll;;;Argument[-1];ReturnValue;taint",
"org.apache.commons.text;TextStringBuilder;false;appendFixedWidthPadLeft;;;Argument[-1];ReturnValue;taint",
"org.apache.commons.text;TextStringBuilder;false;appendFixedWidthPadLeft;;;Argument[0];Argument[-1];taint",
@@ -384,14 +405,18 @@ private class ApacheStrBuilderModel extends SummaryModelCsv {
"org.apache.commons.text;TextStringBuilder;false;appendSeparator;(java.lang.String,java.lang.String);;Argument[0..1];Argument[-1];taint",
"org.apache.commons.text;TextStringBuilder;false;appendSeparator;;;Argument[-1];ReturnValue;taint",
"org.apache.commons.text;TextStringBuilder;false;appendTo;;;Argument[-1];Argument[0];taint",
- "org.apache.commons.text;TextStringBuilder;false;appendWithSeparators;;;Argument[0..1];Argument[-1];taint",
+ "org.apache.commons.text;TextStringBuilder;false;appendWithSeparators;(Iterable,String);;Element of Argument[0];Argument[-1];taint",
+ "org.apache.commons.text;TextStringBuilder;false;appendWithSeparators;(Iterator,String);;Element of Argument[0];Argument[-1];taint",
+ "org.apache.commons.text;TextStringBuilder;false;appendWithSeparators;(Object[],String);;ArrayElement of Argument[0];Argument[-1];taint",
+ "org.apache.commons.text;TextStringBuilder;false;appendWithSeparators;;;Argument[1];Argument[-1];taint",
"org.apache.commons.text;TextStringBuilder;false;appendWithSeparators;;;Argument[-1];ReturnValue;taint",
"org.apache.commons.text;TextStringBuilder;false;appendln;(char[]);;Argument[0];Argument[-1];taint",
"org.apache.commons.text;TextStringBuilder;false;appendln;(char[],int,int);;Argument[0];Argument[-1];taint",
"org.apache.commons.text;TextStringBuilder;false;appendln;(java.lang.Object);;Argument[0];Argument[-1];taint",
"org.apache.commons.text;TextStringBuilder;false;appendln;(java.lang.String);;Argument[0];Argument[-1];taint",
"org.apache.commons.text;TextStringBuilder;false;appendln;(java.lang.String,int,int);;Argument[0];Argument[-1];taint",
- "org.apache.commons.text;TextStringBuilder;false;appendln;(java.lang.String,java.lang.Object[]);;Argument[0..1];Argument[-1];taint",
+ "org.apache.commons.text;TextStringBuilder;false;appendln;(java.lang.String,java.lang.Object[]);;Argument[0];Argument[-1];taint",
+ "org.apache.commons.text;TextStringBuilder;false;appendln;(java.lang.String,java.lang.Object[]);;ArrayElement of Argument[1];Argument[-1];taint",
"org.apache.commons.text;TextStringBuilder;false;appendln;(java.lang.StringBuffer);;Argument[0];Argument[-1];taint",
"org.apache.commons.text;TextStringBuilder;false;appendln;(java.lang.StringBuffer,int,int);;Argument[0];Argument[-1];taint",
"org.apache.commons.text;TextStringBuilder;false;appendln;(java.lang.StringBuilder);;Argument[0];Argument[-1];taint",
@@ -525,9 +550,9 @@ private class ApacheStrLookupModel extends SummaryModelCsv {
row =
[
"org.apache.commons.lang3.text;StrLookup;false;lookup;;;Argument[-1];ReturnValue;taint",
- "org.apache.commons.lang3.text;StrLookup;false;mapLookup;;;Argument[0];ReturnValue;taint",
+ "org.apache.commons.lang3.text;StrLookup;false;mapLookup;;;MapValue of Argument[0];ReturnValue;taint",
"org.apache.commons.text.lookup;StringLookup;true;lookup;;;Argument[-1];ReturnValue;taint",
- "org.apache.commons.text.lookup;StringLookupFactory;false;mapStringLookup;;;Argument[0];ReturnValue;taint"
+ "org.apache.commons.text.lookup;StringLookupFactory;false;mapStringLookup;;;MapValue of Argument[0];ReturnValue;taint"
]
}
}
@@ -540,6 +565,7 @@ private class ApacheStrSubstitutorModel extends SummaryModelCsv {
row =
[
"org.apache.commons.lang3.text;StrSubstitutor;false;StrSubstitutor;;;Argument[0];Argument[-1];taint",
+ "org.apache.commons.lang3.text;StrSubstitutor;false;StrSubstitutor;;;MapValue of Argument[0];Argument[-1];taint",
"org.apache.commons.lang3.text;StrSubstitutor;false;replace;;;Argument[-1];ReturnValue;taint",
"org.apache.commons.lang3.text;StrSubstitutor;false;replace;(java.lang.Object);;Argument[0];ReturnValue;taint",
"org.apache.commons.lang3.text;StrSubstitutor;false;replace;(char[]);;Argument[0];ReturnValue;taint",
@@ -552,10 +578,12 @@ private class ApacheStrSubstitutorModel extends SummaryModelCsv {
"org.apache.commons.lang3.text;StrSubstitutor;false;replace;(java.lang.StringBuffer,int,int);;Argument[0];ReturnValue;taint",
"org.apache.commons.lang3.text;StrSubstitutor;false;replace;(java.lang.String,int,int);;Argument[0];ReturnValue;taint",
"org.apache.commons.lang3.text;StrSubstitutor;false;replace;(org.apache.commons.lang3.text.StrBuilder,int,int);;Argument[0];ReturnValue;taint",
- "org.apache.commons.lang3.text;StrSubstitutor;false;replace;(java.lang.Object,java.util.Map);;Argument[0..1];ReturnValue;taint",
+ "org.apache.commons.lang3.text;StrSubstitutor;false;replace;(java.lang.Object,java.util.Map);;Argument[0];ReturnValue;taint",
+ "org.apache.commons.lang3.text;StrSubstitutor;false;replace;(java.lang.Object,java.util.Map);;MapValue of Argument[1];ReturnValue;taint",
"org.apache.commons.lang3.text;StrSubstitutor;false;replace;(java.lang.Object,java.util.Map,java.lang.String,java.lang.String);;Argument[0];ReturnValue;taint",
- "org.apache.commons.lang3.text;StrSubstitutor;false;replace;(java.lang.Object,java.util.Map,java.lang.String,java.lang.String);;Argument[1];ReturnValue;taint",
- "org.apache.commons.lang3.text;StrSubstitutor;false;replace;(java.lang.Object,java.util.Properties);;Argument[0..1];ReturnValue;taint",
+ "org.apache.commons.lang3.text;StrSubstitutor;false;replace;(java.lang.Object,java.util.Map,java.lang.String,java.lang.String);;MapValue of Argument[1];ReturnValue;taint",
+ "org.apache.commons.lang3.text;StrSubstitutor;false;replace;(java.lang.Object,java.util.Properties);;Argument[0];ReturnValue;taint",
+ "org.apache.commons.lang3.text;StrSubstitutor;false;replace;(java.lang.Object,java.util.Properties);;MapValue of Argument[1];ReturnValue;taint",
"org.apache.commons.lang3.text;StrSubstitutor;false;setVariableResolver;;;Argument[0];Argument[-1];taint",
"org.apache.commons.lang3.text;StrSubstitutor;false;replaceIn;(org.apache.commons.lang3.text.StrBuilder);;Argument[-1];Argument[0];taint",
"org.apache.commons.lang3.text;StrSubstitutor;false;replaceIn;(java.lang.StringBuffer);;Argument[-1];Argument[0];taint",
@@ -564,6 +592,7 @@ private class ApacheStrSubstitutorModel extends SummaryModelCsv {
"org.apache.commons.lang3.text;StrSubstitutor;false;replaceIn;(java.lang.StringBuilder,int,int);;Argument[-1];Argument[0];taint",
"org.apache.commons.lang3.text;StrSubstitutor;false;replaceIn;(org.apache.commons.lang3.text.StrBuilder,int,int);;Argument[-1];Argument[0];taint",
"org.apache.commons.text;StringSubstitutor;false;StringSubstitutor;;;Argument[0];Argument[-1];taint",
+ "org.apache.commons.text;StringSubstitutor;false;StringSubstitutor;;;MapValue of Argument[0];Argument[-1];taint",
"org.apache.commons.text;StringSubstitutor;false;replace;;;Argument[-1];ReturnValue;taint",
"org.apache.commons.text;StringSubstitutor;false;replace;(java.lang.Object);;Argument[0];ReturnValue;taint",
"org.apache.commons.text;StringSubstitutor;false;replace;(char[]);;Argument[0];ReturnValue;taint",
@@ -574,10 +603,12 @@ private class ApacheStrSubstitutorModel extends SummaryModelCsv {
"org.apache.commons.text;StringSubstitutor;false;replace;(java.lang.StringBuffer);;Argument[0];ReturnValue;taint",
"org.apache.commons.text;StringSubstitutor;false;replace;(java.lang.StringBuffer,int,int);;Argument[0];ReturnValue;taint",
"org.apache.commons.text;StringSubstitutor;false;replace;(java.lang.String,int,int);;Argument[0];ReturnValue;taint",
- "org.apache.commons.text;StringSubstitutor;false;replace;(java.lang.Object,java.util.Map);;Argument[0..1];ReturnValue;taint",
+ "org.apache.commons.text;StringSubstitutor;false;replace;(java.lang.Object,java.util.Map);;Argument[0];ReturnValue;taint",
+ "org.apache.commons.text;StringSubstitutor;false;replace;(java.lang.Object,java.util.Map);;MapValue of Argument[1];ReturnValue;taint",
"org.apache.commons.text;StringSubstitutor;false;replace;(java.lang.Object,java.util.Map,java.lang.String,java.lang.String);;Argument[0];ReturnValue;taint",
- "org.apache.commons.text;StringSubstitutor;false;replace;(java.lang.Object,java.util.Map,java.lang.String,java.lang.String);;Argument[1];ReturnValue;taint",
- "org.apache.commons.text;StringSubstitutor;false;replace;(java.lang.Object,java.util.Properties);;Argument[0..1];ReturnValue;taint",
+ "org.apache.commons.text;StringSubstitutor;false;replace;(java.lang.Object,java.util.Map,java.lang.String,java.lang.String);;MapValue of Argument[1];ReturnValue;taint",
+ "org.apache.commons.text;StringSubstitutor;false;replace;(java.lang.Object,java.util.Properties);;Argument[0];ReturnValue;taint",
+ "org.apache.commons.text;StringSubstitutor;false;replace;(java.lang.Object,java.util.Properties);;MapValue of Argument[1];ReturnValue;taint",
"org.apache.commons.text;StringSubstitutor;false;replace;(org.apache.commons.text.TextStringBuilder);;Argument[0];ReturnValue;taint",
"org.apache.commons.text;StringSubstitutor;false;replace;(org.apache.commons.text.TextStringBuilder,int,int);;Argument[0];ReturnValue;taint",
"org.apache.commons.text;StringSubstitutor;false;setVariableResolver;;;Argument[0];Argument[-1];taint",
diff --git a/java/ql/src/semmle/code/java/frameworks/guava/Base.qll b/java/ql/src/semmle/code/java/frameworks/guava/Base.qll
index 04a97f79f53..fc1ee0e6cb7 100644
--- a/java/ql/src/semmle/code/java/frameworks/guava/Base.qll
+++ b/java/ql/src/semmle/code/java/frameworks/guava/Base.qll
@@ -30,7 +30,13 @@ private class GuavaBaseCsv extends SummaryModelCsv {
"com.google.common.base;Joiner$MapJoiner;false;useForNull;(String);;Argument[-1];ReturnValue;taint",
"com.google.common.base;Joiner$MapJoiner;false;appendTo;;;Argument[1];Argument[0];taint",
"com.google.common.base;Joiner$MapJoiner;false;appendTo;;;Argument[0];ReturnValue;value",
- "com.google.common.base;Joiner$MapJoiner;false;join;;;Argument[-1..0];ReturnValue;taint",
+ "com.google.common.base;Joiner$MapJoiner;false;join;;;Argument[-1];ReturnValue;taint",
+ "com.google.common.base;Joiner$MapJoiner;false;join;(Iterable);;MapKey of Element of Argument[0];ReturnValue;taint",
+ "com.google.common.base;Joiner$MapJoiner;false;join;(Iterable);;MapValue of Element of Argument[0];ReturnValue;taint",
+ "com.google.common.base;Joiner$MapJoiner;false;join;(Iterator);;MapKey of Element of Argument[0];ReturnValue;taint",
+ "com.google.common.base;Joiner$MapJoiner;false;join;(Iterator);;MapValue of Element of Argument[0];ReturnValue;taint",
+ "com.google.common.base;Joiner$MapJoiner;false;join;(Map);;MapKey of Argument[0];ReturnValue;taint",
+ "com.google.common.base;Joiner$MapJoiner;false;join;(Map);;MapValue of Argument[0];ReturnValue;taint",
"com.google.common.base;Splitter;false;split;(CharSequence);;Argument[0];ReturnValue;taint",
"com.google.common.base;Splitter;false;splitToList;(CharSequence);;Argument[0];ReturnValue;taint",
"com.google.common.base;Splitter;false;splitToStream;(CharSequence);;Argument[0];ReturnValue;taint",
diff --git a/java/ql/src/semmle/code/java/frameworks/guava/IO.qll b/java/ql/src/semmle/code/java/frameworks/guava/IO.qll
index fac9ab9d48d..305b4fbcfb7 100644
--- a/java/ql/src/semmle/code/java/frameworks/guava/IO.qll
+++ b/java/ql/src/semmle/code/java/frameworks/guava/IO.qll
@@ -24,7 +24,9 @@ private class GuavaIoCsv extends SummaryModelCsv {
"com.google.common.io;BaseEncoding;true;omitPadding;();;Argument[-1];ReturnValue;taint",
"com.google.common.io;BaseEncoding;true;encode;(byte[],int,int);;Argument[-1];ReturnValue;taint",
"com.google.common.io;ByteSource;true;asCharSource;(Charset);;Argument[-1];ReturnValue;taint",
- "com.google.common.io;ByteSource;true;concat;;;Argument[0];ReturnValue;taint",
+ "com.google.common.io;ByteSource;true;concat;(ByteSource[]);;ArrayElement of Argument[0];ReturnValue;taint",
+ "com.google.common.io;ByteSource;true;concat;(Iterable);;Element of Argument[0];ReturnValue;taint",
+ "com.google.common.io;ByteSource;true;concat;(Iterator);;Element of Argument[0];ReturnValue;taint",
"com.google.common.io;ByteSource;true;copyTo;(OutputStream);;Argument[-1];Argument[0];taint",
"com.google.common.io;ByteSource;true;openStream;();;Argument[-1];ReturnValue;taint",
"com.google.common.io;ByteSource;true;openBufferedStream;();;Argument[-1];ReturnValue;taint",
@@ -43,7 +45,9 @@ private class GuavaIoCsv extends SummaryModelCsv {
"com.google.common.io;ByteStreams;false;readFully;(InputStream,byte[],int,int);;Argument[0];Argument[1];taint",
"com.google.common.io;ByteStreams;false;toByteArray;(InputStream);;Argument[0];ReturnValue;taint",
"com.google.common.io;CharSource;true;asByteSource;(Charset);;Argument[-1];ReturnValue;taint",
- "com.google.common.io;CharSource;true;concat;;;Argument[0];ReturnValue;taint",
+ "com.google.common.io;CharSource;true;concat;(CharSource[]);;ArrayElement of Argument[0];ReturnValue;taint",
+ "com.google.common.io;CharSource;true;concat;(Iterable);;Element of Argument[0];ReturnValue;taint",
+ "com.google.common.io;CharSource;true;concat;(Iterator);;Element of Argument[0];ReturnValue;taint",
"com.google.common.io;CharSource;true;copyTo;(Appendable);;Argument[-1];Argument[0];taint",
"com.google.common.io;CharSource;true;openStream;();;Argument[-1];ReturnValue;taint",
"com.google.common.io;CharSource;true;openBufferedStream;();;Argument[-1];ReturnValue;taint",
From 2f087e17cb1b328a87c2ea998bf1cdf4f052e663 Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Wed, 5 May 2021 15:07:31 +0200
Subject: [PATCH 071/272] Java: Allow <> in types for now.
---
java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll
index 34fa8d66dd3..d4b2c917148 100644
--- a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll
+++ b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll
@@ -481,7 +481,7 @@ module CsvValidation {
not namespace.regexpMatch("[a-zA-Z0-9_\\.]+") and
msg = "Dubious namespace \"" + namespace + "\" in " + pred + " model."
or
- not type.regexpMatch("[a-zA-Z0-9_\\$]+") and
+ not type.regexpMatch("[a-zA-Z0-9_\\$<>]+") and
msg = "Dubious type \"" + type + "\" in " + pred + " model."
or
not name.regexpMatch("[a-zA-Z0-9_]*") and
From a40880af70524e581811eb87b40b9e3d00e27dd6 Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Wed, 19 May 2021 10:45:54 +0200
Subject: [PATCH 072/272] Java: Add read-as-taint and config-dependent
store-as-taint.
---
.../dataflow/internal/TaintTrackingUtil.qll | 86 +++++++++++++++++++
1 file changed, 86 insertions(+)
diff --git a/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll b/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll
index 079d61809db..719b89fbf2a 100644
--- a/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll
+++ b/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll
@@ -60,6 +60,17 @@ private module Cached {
localAdditionalTaintUpdateStep(src.asExpr(),
sink.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr())
or
+ exists(Content f |
+ readStep(src, f, sink) and
+ not sink.getTypeBound() instanceof PrimitiveType and
+ not sink.getTypeBound() instanceof BoxedType and
+ not sink.getTypeBound() instanceof NumberType
+ |
+ f instanceof ArrayContent or
+ f instanceof CollectionContent or
+ f instanceof MapValueContent
+ )
+ or
FlowSummaryImpl::Private::Steps::summaryLocalStep(src, sink, false)
}
@@ -87,6 +98,81 @@ private module Cached {
import Cached
+private module StoreTaintSteps {
+ private import semmle.code.java.dataflow.TaintTracking
+ private import semmle.code.java.dataflow.TaintTracking2
+
+ private class StoreTaintConfig extends TaintTracking::Configuration {
+ StoreTaintConfig() { this instanceof TaintTracking::Configuration or none() }
+
+ override predicate isSource(DataFlow::Node n) { none() }
+
+ override predicate isSink(DataFlow::Node n) { none() }
+
+ private predicate needsTaintStore(RefType container, Type elem, Content f) {
+ exists(DataFlow::Node arg |
+ (isSink(arg) or isAdditionalTaintStep(arg, _)) and
+ (arg.asExpr() instanceof Argument or arg instanceof ArgumentNode) and
+ arg.getType() = container
+ or
+ needsTaintStore(_, container, _)
+ |
+ container.(Array).getComponentType() = elem and
+ f instanceof ArrayContent
+ or
+ container.(CollectionType).getElementType() = elem and
+ f instanceof CollectionContent
+ or
+ container.(MapType).getValueType() = elem and
+ f instanceof MapValueContent
+ )
+ }
+
+ override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
+ exists(Content f, Type elem |
+ storeStep(node1, f, node2) and
+ needsTaintStore(_, elem, f) and
+ not exists(Type srctyp | srctyp = node1.getTypeBound() | not compatibleTypes(srctyp, elem))
+ )
+ }
+ }
+
+ private class StoreTaintConfig2 extends TaintTracking2::Configuration {
+ StoreTaintConfig2() { this instanceof TaintTracking2::Configuration or none() }
+
+ override predicate isSource(DataFlow::Node n) { none() }
+
+ override predicate isSink(DataFlow::Node n) { none() }
+
+ private predicate needsTaintStore(RefType container, Type elem, Content f) {
+ exists(DataFlow::Node arg |
+ (isSink(arg) or isAdditionalTaintStep(arg, _)) and
+ (arg.asExpr() instanceof Argument or arg instanceof ArgumentNode) and
+ arg.getType() = container
+ or
+ needsTaintStore(_, container, _)
+ |
+ container.(Array).getComponentType() = elem and
+ f instanceof ArrayContent
+ or
+ container.(CollectionType).getElementType() = elem and
+ f instanceof CollectionContent
+ or
+ container.(MapType).getValueType() = elem and
+ f instanceof MapValueContent
+ )
+ }
+
+ override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
+ exists(Content f, Type elem |
+ storeStep(node1, f, node2) and
+ needsTaintStore(_, elem, f) and
+ not exists(Type srctyp | srctyp = node1.getTypeBound() | not compatibleTypes(srctyp, elem))
+ )
+ }
+ }
+}
+
/**
* Holds if taint can flow in one local step from `src` to `sink` excluding
* local data flow steps. That is, `src` and `sink` are likely to represent
From 43d1b0ab273f5b5aea3942174839d60a2b1a3c9f Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Tue, 4 May 2021 10:48:43 +0200
Subject: [PATCH 073/272] Java: Update qltests.
---
...ientSuppliedIpUsedInSecurityCheck.expected | 3 ++
.../CWE-522/InsecureLdapAuth.expected | 40 +++++++++++++++++++
.../CWE-598/SensitiveGetQuery.expected | 6 ++-
.../CWE-927/SensitiveBroadcast.expected | 4 +-
.../dataflow/collections/flow.expected | 3 --
.../library-tests/dataflow/getter/getter.ql | 7 +++-
.../localAdditionalTaintStep.expected | 19 +++++----
.../localAdditionalTaintStep.ql | 28 ++++++++++++-
.../dataflow/taint-ioutils/Test.java | 10 +++--
.../dataflow/taint-ioutils/dataFlow.expected | 8 +---
.../dataflow/taint/test.expected | 1 -
.../apache-commons-lang3/ObjectUtilsTest.java | 30 +++++++-------
.../frameworks/apache-commons-lang3/Test.java | 14 +++----
.../frameworks/guava/TestBase.java | 2 +-
.../CWE-078/ExecTaintedLocal.expected | 6 ++-
15 files changed, 128 insertions(+), 53 deletions(-)
diff --git a/java/ql/test/experimental/query-tests/security/CWE-348/ClientSuppliedIpUsedInSecurityCheck.expected b/java/ql/test/experimental/query-tests/security/CWE-348/ClientSuppliedIpUsedInSecurityCheck.expected
index 5627f9541f1..160654628fe 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-348/ClientSuppliedIpUsedInSecurityCheck.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-348/ClientSuppliedIpUsedInSecurityCheck.expected
@@ -1,7 +1,9 @@
edges
| ClientSuppliedIpUsedInSecurityCheck.java:16:21:16:33 | getClientIP(...) : String | ClientSuppliedIpUsedInSecurityCheck.java:17:37:17:38 | ip |
| ClientSuppliedIpUsedInSecurityCheck.java:24:21:24:33 | getClientIP(...) : String | ClientSuppliedIpUsedInSecurityCheck.java:25:33:25:34 | ip |
+| ClientSuppliedIpUsedInSecurityCheck.java:43:27:43:62 | getHeader(...) : String | ClientSuppliedIpUsedInSecurityCheck.java:47:16:47:34 | split(...) : String[] |
| ClientSuppliedIpUsedInSecurityCheck.java:43:27:43:62 | getHeader(...) : String | ClientSuppliedIpUsedInSecurityCheck.java:47:16:47:37 | ...[...] : String |
+| ClientSuppliedIpUsedInSecurityCheck.java:47:16:47:34 | split(...) : String[] | ClientSuppliedIpUsedInSecurityCheck.java:47:16:47:37 | ...[...] : String |
| ClientSuppliedIpUsedInSecurityCheck.java:47:16:47:37 | ...[...] : String | ClientSuppliedIpUsedInSecurityCheck.java:16:21:16:33 | getClientIP(...) : String |
| ClientSuppliedIpUsedInSecurityCheck.java:47:16:47:37 | ...[...] : String | ClientSuppliedIpUsedInSecurityCheck.java:24:21:24:33 | getClientIP(...) : String |
nodes
@@ -10,6 +12,7 @@ nodes
| ClientSuppliedIpUsedInSecurityCheck.java:24:21:24:33 | getClientIP(...) : String | semmle.label | getClientIP(...) : String |
| ClientSuppliedIpUsedInSecurityCheck.java:25:33:25:34 | ip | semmle.label | ip |
| ClientSuppliedIpUsedInSecurityCheck.java:43:27:43:62 | getHeader(...) : String | semmle.label | getHeader(...) : String |
+| ClientSuppliedIpUsedInSecurityCheck.java:47:16:47:34 | split(...) : String[] | semmle.label | split(...) : String[] |
| ClientSuppliedIpUsedInSecurityCheck.java:47:16:47:37 | ...[...] : String | semmle.label | ...[...] : String |
#select
| ClientSuppliedIpUsedInSecurityCheck.java:17:37:17:38 | ip | ClientSuppliedIpUsedInSecurityCheck.java:43:27:43:62 | getHeader(...) : String | ClientSuppliedIpUsedInSecurityCheck.java:17:37:17:38 | ip | IP address spoofing might include code from $@. | ClientSuppliedIpUsedInSecurityCheck.java:43:27:43:62 | getHeader(...) | this user input |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.expected b/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.expected
index 863e8e55dcf..a3b12510626 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.expected
@@ -1,52 +1,88 @@
edges
+| InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:15:41:15:47 | ldapUrl : String |
| InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:20:49:20:59 | environment |
+| InsecureLdapAuth.java:15:3:15:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:20:49:20:59 | environment |
+| InsecureLdapAuth.java:15:41:15:47 | ldapUrl : String | InsecureLdapAuth.java:15:3:15:13 | environment [post update] : Hashtable |
| InsecureLdapAuth.java:17:3:17:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:20:49:20:59 | environment |
+| InsecureLdapAuth.java:25:20:25:39 | ... + ... : String | InsecureLdapAuth.java:29:41:29:47 | ldapUrl : String |
| InsecureLdapAuth.java:25:20:25:39 | ... + ... : String | InsecureLdapAuth.java:34:49:34:59 | environment |
+| InsecureLdapAuth.java:29:3:29:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:34:49:34:59 | environment |
+| InsecureLdapAuth.java:29:41:29:47 | ldapUrl : String | InsecureLdapAuth.java:29:3:29:13 | environment [post update] : Hashtable |
| InsecureLdapAuth.java:31:3:31:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:34:49:34:59 | environment |
| InsecureLdapAuth.java:45:3:45:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:48:49:48:59 | environment |
+| InsecureLdapAuth.java:53:20:53:50 | "ldap://ad.your-server.com:636" : String | InsecureLdapAuth.java:57:41:57:47 | ldapUrl : String |
| InsecureLdapAuth.java:53:20:53:50 | "ldap://ad.your-server.com:636" : String | InsecureLdapAuth.java:63:49:63:59 | environment |
+| InsecureLdapAuth.java:57:3:57:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:63:49:63:59 | environment |
+| InsecureLdapAuth.java:57:41:57:47 | ldapUrl : String | InsecureLdapAuth.java:57:3:57:13 | environment [post update] : Hashtable |
| InsecureLdapAuth.java:59:3:59:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:63:49:63:59 | environment |
| InsecureLdapAuth.java:62:3:62:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:63:49:63:59 | environment |
+| InsecureLdapAuth.java:68:20:68:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:72:41:72:47 | ldapUrl : String |
| InsecureLdapAuth.java:68:20:68:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:77:49:77:59 | environment |
+| InsecureLdapAuth.java:72:3:72:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:77:49:77:59 | environment |
+| InsecureLdapAuth.java:72:41:72:47 | ldapUrl : String | InsecureLdapAuth.java:72:3:72:13 | environment [post update] : Hashtable |
| InsecureLdapAuth.java:88:3:88:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:91:49:91:59 | environment |
+| InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:100:41:100:47 | ldapUrl : String |
| InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:105:59:105:69 | environment |
+| InsecureLdapAuth.java:100:3:100:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:105:59:105:69 | environment |
+| InsecureLdapAuth.java:100:41:100:47 | ldapUrl : String | InsecureLdapAuth.java:100:3:100:13 | environment [post update] : Hashtable |
| InsecureLdapAuth.java:102:3:102:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:105:59:105:69 | environment |
+| InsecureLdapAuth.java:111:20:111:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:115:47:115:53 | ldapUrl : String |
| InsecureLdapAuth.java:111:20:111:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:120:49:120:59 | environment |
+| InsecureLdapAuth.java:115:3:115:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:120:49:120:59 | environment |
+| InsecureLdapAuth.java:115:47:115:53 | ldapUrl : String | InsecureLdapAuth.java:115:3:115:13 | environment [post update] : Hashtable |
| InsecureLdapAuth.java:117:3:117:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:120:49:120:59 | environment |
| InsecureLdapAuth.java:124:3:124:5 | env [post update] : Hashtable | InsecureLdapAuth.java:137:10:137:20 | environment [post update] : Hashtable |
| InsecureLdapAuth.java:128:3:128:5 | env [post update] : Hashtable | InsecureLdapAuth.java:141:16:141:26 | environment [post update] : Hashtable |
| InsecureLdapAuth.java:128:3:128:5 | env [post update] : Hashtable | InsecureLdapAuth.java:152:16:152:26 | environment [post update] : Hashtable |
+| InsecureLdapAuth.java:135:20:135:39 | ... + ... : String | InsecureLdapAuth.java:140:41:140:47 | ldapUrl : String |
| InsecureLdapAuth.java:135:20:135:39 | ... + ... : String | InsecureLdapAuth.java:142:50:142:60 | environment |
| InsecureLdapAuth.java:137:10:137:20 | environment [post update] : Hashtable | InsecureLdapAuth.java:142:50:142:60 | environment |
+| InsecureLdapAuth.java:140:3:140:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:142:50:142:60 | environment |
+| InsecureLdapAuth.java:140:41:140:47 | ldapUrl : String | InsecureLdapAuth.java:140:3:140:13 | environment [post update] : Hashtable |
| InsecureLdapAuth.java:141:16:141:26 | environment [post update] : Hashtable | InsecureLdapAuth.java:142:50:142:60 | environment |
+| InsecureLdapAuth.java:147:20:147:39 | ... + ... : String | InsecureLdapAuth.java:151:41:151:47 | ldapUrl : String |
| InsecureLdapAuth.java:147:20:147:39 | ... + ... : String | InsecureLdapAuth.java:153:50:153:60 | environment |
+| InsecureLdapAuth.java:151:3:151:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:153:50:153:60 | environment |
+| InsecureLdapAuth.java:151:41:151:47 | ldapUrl : String | InsecureLdapAuth.java:151:3:151:13 | environment [post update] : Hashtable |
| InsecureLdapAuth.java:152:16:152:26 | environment [post update] : Hashtable | InsecureLdapAuth.java:153:50:153:60 | environment |
nodes
| InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" : String | semmle.label | "ldap://ad.your-server.com:389" : String |
+| InsecureLdapAuth.java:15:3:15:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
+| InsecureLdapAuth.java:15:41:15:47 | ldapUrl : String | semmle.label | ldapUrl : String |
| InsecureLdapAuth.java:17:3:17:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
| InsecureLdapAuth.java:20:49:20:59 | environment | semmle.label | environment |
| InsecureLdapAuth.java:20:49:20:59 | environment | semmle.label | environment |
| InsecureLdapAuth.java:25:20:25:39 | ... + ... : String | semmle.label | ... + ... : String |
+| InsecureLdapAuth.java:29:3:29:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
+| InsecureLdapAuth.java:29:41:29:47 | ldapUrl : String | semmle.label | ldapUrl : String |
| InsecureLdapAuth.java:31:3:31:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
| InsecureLdapAuth.java:34:49:34:59 | environment | semmle.label | environment |
| InsecureLdapAuth.java:34:49:34:59 | environment | semmle.label | environment |
| InsecureLdapAuth.java:45:3:45:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
| InsecureLdapAuth.java:48:49:48:59 | environment | semmle.label | environment |
| InsecureLdapAuth.java:53:20:53:50 | "ldap://ad.your-server.com:636" : String | semmle.label | "ldap://ad.your-server.com:636" : String |
+| InsecureLdapAuth.java:57:3:57:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
+| InsecureLdapAuth.java:57:41:57:47 | ldapUrl : String | semmle.label | ldapUrl : String |
| InsecureLdapAuth.java:59:3:59:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
| InsecureLdapAuth.java:62:3:62:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
| InsecureLdapAuth.java:63:49:63:59 | environment | semmle.label | environment |
| InsecureLdapAuth.java:63:49:63:59 | environment | semmle.label | environment |
| InsecureLdapAuth.java:63:49:63:59 | environment | semmle.label | environment |
| InsecureLdapAuth.java:68:20:68:50 | "ldap://ad.your-server.com:389" : String | semmle.label | "ldap://ad.your-server.com:389" : String |
+| InsecureLdapAuth.java:72:3:72:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
+| InsecureLdapAuth.java:72:41:72:47 | ldapUrl : String | semmle.label | ldapUrl : String |
| InsecureLdapAuth.java:77:49:77:59 | environment | semmle.label | environment |
| InsecureLdapAuth.java:88:3:88:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
| InsecureLdapAuth.java:91:49:91:59 | environment | semmle.label | environment |
| InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" : String | semmle.label | "ldap://ad.your-server.com:389" : String |
+| InsecureLdapAuth.java:100:3:100:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
+| InsecureLdapAuth.java:100:41:100:47 | ldapUrl : String | semmle.label | ldapUrl : String |
| InsecureLdapAuth.java:102:3:102:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
| InsecureLdapAuth.java:105:59:105:69 | environment | semmle.label | environment |
| InsecureLdapAuth.java:105:59:105:69 | environment | semmle.label | environment |
| InsecureLdapAuth.java:111:20:111:50 | "ldap://ad.your-server.com:389" : String | semmle.label | "ldap://ad.your-server.com:389" : String |
+| InsecureLdapAuth.java:115:3:115:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
+| InsecureLdapAuth.java:115:47:115:53 | ldapUrl : String | semmle.label | ldapUrl : String |
| InsecureLdapAuth.java:117:3:117:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
| InsecureLdapAuth.java:120:49:120:59 | environment | semmle.label | environment |
| InsecureLdapAuth.java:120:49:120:59 | environment | semmle.label | environment |
@@ -54,11 +90,15 @@ nodes
| InsecureLdapAuth.java:128:3:128:5 | env [post update] : Hashtable | semmle.label | env [post update] : Hashtable |
| InsecureLdapAuth.java:135:20:135:39 | ... + ... : String | semmle.label | ... + ... : String |
| InsecureLdapAuth.java:137:10:137:20 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
+| InsecureLdapAuth.java:140:3:140:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
+| InsecureLdapAuth.java:140:41:140:47 | ldapUrl : String | semmle.label | ldapUrl : String |
| InsecureLdapAuth.java:141:16:141:26 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
| InsecureLdapAuth.java:142:50:142:60 | environment | semmle.label | environment |
| InsecureLdapAuth.java:142:50:142:60 | environment | semmle.label | environment |
| InsecureLdapAuth.java:142:50:142:60 | environment | semmle.label | environment |
| InsecureLdapAuth.java:147:20:147:39 | ... + ... : String | semmle.label | ... + ... : String |
+| InsecureLdapAuth.java:151:3:151:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
+| InsecureLdapAuth.java:151:41:151:47 | ldapUrl : String | semmle.label | ldapUrl : String |
| InsecureLdapAuth.java:152:16:152:26 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
| InsecureLdapAuth.java:153:50:153:60 | environment | semmle.label | environment |
| InsecureLdapAuth.java:153:50:153:60 | environment | semmle.label | environment |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-598/SensitiveGetQuery.expected b/java/ql/test/experimental/query-tests/security/CWE-598/SensitiveGetQuery.expected
index 8e96a3ebfc0..2790e9f77d3 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-598/SensitiveGetQuery.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-598/SensitiveGetQuery.expected
@@ -1,7 +1,9 @@
edges
-| SensitiveGetQuery2.java:12:13:12:37 | getParameterMap(...) : Map | SensitiveGetQuery2.java:14:21:14:48 | (...)... : Object |
+| SensitiveGetQuery2.java:12:13:12:37 | getParameterMap(...) : Map | SensitiveGetQuery2.java:14:30:14:32 | map : Map |
| SensitiveGetQuery2.java:14:21:14:48 | (...)... : Object | SensitiveGetQuery2.java:15:29:15:36 | password |
| SensitiveGetQuery2.java:14:21:14:48 | (...)... : Object | SensitiveGetQuery2.java:15:29:15:36 | password : Object |
+| SensitiveGetQuery2.java:14:30:14:32 | map : Map | SensitiveGetQuery2.java:14:30:14:48 | get(...) : Object |
+| SensitiveGetQuery2.java:14:30:14:48 | get(...) : Object | SensitiveGetQuery2.java:14:21:14:48 | (...)... : Object |
| SensitiveGetQuery2.java:15:29:15:36 | password : Object | SensitiveGetQuery2.java:18:40:18:54 | password : Object |
| SensitiveGetQuery2.java:18:40:18:54 | password : Object | SensitiveGetQuery2.java:19:61:19:68 | password |
| SensitiveGetQuery3.java:12:21:12:60 | getRequestParameter(...) : String | SensitiveGetQuery3.java:13:57:13:64 | password |
@@ -15,6 +17,8 @@ edges
nodes
| SensitiveGetQuery2.java:12:13:12:37 | getParameterMap(...) : Map | semmle.label | getParameterMap(...) : Map |
| SensitiveGetQuery2.java:14:21:14:48 | (...)... : Object | semmle.label | (...)... : Object |
+| SensitiveGetQuery2.java:14:30:14:32 | map : Map | semmle.label | map : Map |
+| SensitiveGetQuery2.java:14:30:14:48 | get(...) : Object | semmle.label | get(...) : Object |
| SensitiveGetQuery2.java:15:29:15:36 | password | semmle.label | password |
| SensitiveGetQuery2.java:15:29:15:36 | password : Object | semmle.label | password : Object |
| SensitiveGetQuery2.java:18:40:18:54 | password : Object | semmle.label | password : Object |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-927/SensitiveBroadcast.expected b/java/ql/test/experimental/query-tests/security/CWE-927/SensitiveBroadcast.expected
index 2b1384b845b..29482ca006d 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-927/SensitiveBroadcast.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-927/SensitiveBroadcast.expected
@@ -3,7 +3,8 @@ edges
| SensitiveBroadcast.java:13:41:13:52 | refreshToken : String | SensitiveBroadcast.java:14:31:14:36 | intent |
| SensitiveBroadcast.java:25:32:25:39 | password : String | SensitiveBroadcast.java:26:31:26:36 | intent |
| SensitiveBroadcast.java:36:35:36:39 | email : String | SensitiveBroadcast.java:38:31:38:36 | intent |
-| SensitiveBroadcast.java:50:22:50:29 | password : String | SensitiveBroadcast.java:52:31:52:36 | intent |
+| SensitiveBroadcast.java:50:9:50:16 | userinfo [post update] : ArrayList | SensitiveBroadcast.java:52:31:52:36 | intent |
+| SensitiveBroadcast.java:50:22:50:29 | password : String | SensitiveBroadcast.java:50:9:50:16 | userinfo [post update] : ArrayList |
| SensitiveBroadcast.java:97:35:97:40 | ticket : String | SensitiveBroadcast.java:98:54:98:59 | intent |
| SensitiveBroadcast.java:109:32:109:39 | passcode : String | SensitiveBroadcast.java:111:54:111:59 | intent |
| SensitiveBroadcast.java:136:33:136:38 | passwd : String | SensitiveBroadcast.java:140:54:140:59 | intent |
@@ -15,6 +16,7 @@ nodes
| SensitiveBroadcast.java:26:31:26:36 | intent | semmle.label | intent |
| SensitiveBroadcast.java:36:35:36:39 | email : String | semmle.label | email : String |
| SensitiveBroadcast.java:38:31:38:36 | intent | semmle.label | intent |
+| SensitiveBroadcast.java:50:9:50:16 | userinfo [post update] : ArrayList | semmle.label | userinfo [post update] : ArrayList |
| SensitiveBroadcast.java:50:22:50:29 | password : String | semmle.label | password : String |
| SensitiveBroadcast.java:52:31:52:36 | intent | semmle.label | intent |
| SensitiveBroadcast.java:97:35:97:40 | ticket : String | semmle.label | ticket : String |
diff --git a/java/ql/test/library-tests/dataflow/collections/flow.expected b/java/ql/test/library-tests/dataflow/collections/flow.expected
index d54e9921856..293e82c9759 100644
--- a/java/ql/test/library-tests/dataflow/collections/flow.expected
+++ b/java/ql/test/library-tests/dataflow/collections/flow.expected
@@ -85,9 +85,6 @@
| ContainterTest.java:46:4:46:38 | navMap | ContainterTest.java:199:8:199:48 | subMap(...) |
| ContainterTest.java:46:4:46:38 | navMap | ContainterTest.java:200:8:200:34 | tailMap(...) |
| ContainterTest.java:47:4:47:48 | syncHashMap | ContainterTest.java:203:8:203:29 | elements(...) |
-| ContainterTest.java:47:4:47:48 | syncHashMap | ContainterTest.java:204:8:204:42 | search(...) |
-| ContainterTest.java:47:4:47:48 | syncHashMap | ContainterTest.java:205:8:205:55 | searchEntries(...) |
-| ContainterTest.java:47:4:47:48 | syncHashMap | ContainterTest.java:206:8:206:43 | searchValues(...) |
| ContainterTest.java:48:4:48:34 | dict | ContainterTest.java:209:8:209:22 | elements(...) |
| ContainterTest.java:48:4:48:34 | dict | ContainterTest.java:210:8:210:25 | get(...) |
| ContainterTest.java:48:4:48:34 | dict | ContainterTest.java:211:8:211:31 | put(...) |
diff --git a/java/ql/test/library-tests/dataflow/getter/getter.ql b/java/ql/test/library-tests/dataflow/getter/getter.ql
index 02e6920fc7e..e218ccbcee5 100644
--- a/java/ql/test/library-tests/dataflow/getter/getter.ql
+++ b/java/ql/test/library-tests/dataflow/getter/getter.ql
@@ -5,6 +5,9 @@ import semmle.code.java.dataflow.internal.DataFlowImplSpecific::Private
from Node n1, Content f, Node n2
where
- read(n1, f, n2) or
- getterStep(n1, f, n2)
+ (
+ read(n1, f, n2) or
+ getterStep(n1, f, n2)
+ ) and
+ n1.getEnclosingCallable().fromSource()
select n1, n2, f
diff --git a/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected b/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected
index 1973eb91fe4..75b743dcaab 100644
--- a/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected
+++ b/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected
@@ -1,8 +1,8 @@
-| ArraysTest.java:7:17:7:21 | "one" | ArraysTest.java:7:3:7:22 | asList(...) |
+| ArraysTest.java:6:3:6:17 | new ..[] { .. } | ArraysTest.java:6:3:6:17 | asList(...) |
+| ArraysTest.java:7:3:7:22 | new ..[] { .. } | ArraysTest.java:7:3:7:22 | asList(...) |
| ArraysTest.java:7:17:7:21 | "one" | ArraysTest.java:7:3:7:22 | new ..[] { .. } |
-| ArraysTest.java:8:17:8:21 | "two" | ArraysTest.java:8:3:8:31 | asList(...) |
+| ArraysTest.java:8:3:8:31 | new ..[] { .. } | ArraysTest.java:8:3:8:31 | asList(...) |
| ArraysTest.java:8:17:8:21 | "two" | ArraysTest.java:8:3:8:31 | new ..[] { .. } |
-| ArraysTest.java:8:24:8:30 | "three" | ArraysTest.java:8:3:8:31 | asList(...) |
| ArraysTest.java:8:24:8:30 | "three" | ArraysTest.java:8:3:8:31 | new ..[] { .. } |
| ArraysTest.java:9:17:9:22 | source | ArraysTest.java:9:3:9:27 | copyOf(...) |
| ArraysTest.java:10:22:10:27 | source | ArraysTest.java:10:3:10:35 | copyOfRange(...) |
@@ -18,14 +18,13 @@
| ArraysTest.java:19:55:19:55 | x | ArraysTest.java:19:38:19:56 | toString(...) |
| ArraysTest.java:20:30:20:36 | Integer | ArraysTest.java:20:30:20:48 | toString(...) |
| ArraysTest.java:20:47:20:47 | x | ArraysTest.java:20:30:20:48 | toString(...) |
+| CollectionsTest.java:9:3:9:26 | new ..[] { .. } | CollectionsTest.java:9:22:9:25 | list [post update] |
+| CollectionsTest.java:10:3:10:33 | new ..[] { .. } | CollectionsTest.java:10:22:10:25 | list [post update] |
| CollectionsTest.java:10:28:10:32 | "one" | CollectionsTest.java:10:3:10:33 | new ..[] { .. } |
-| CollectionsTest.java:10:28:10:32 | "one" | CollectionsTest.java:10:22:10:25 | list [post update] |
+| CollectionsTest.java:11:3:11:42 | new ..[] { .. } | CollectionsTest.java:11:22:11:25 | list [post update] |
| CollectionsTest.java:11:28:11:32 | "two" | CollectionsTest.java:11:3:11:42 | new ..[] { .. } |
-| CollectionsTest.java:11:28:11:32 | "two" | CollectionsTest.java:11:22:11:25 | list [post update] |
| CollectionsTest.java:11:35:11:41 | "three" | CollectionsTest.java:11:3:11:42 | new ..[] { .. } |
-| CollectionsTest.java:11:35:11:41 | "three" | CollectionsTest.java:11:22:11:25 | list [post update] |
| CollectionsTest.java:12:28:12:49 | new String[] | CollectionsTest.java:12:22:12:25 | list [post update] |
-| CollectionsTest.java:12:28:12:49 | {...} | CollectionsTest.java:12:28:12:49 | new String[] |
| CollectionsTest.java:12:42:12:47 | "four" | CollectionsTest.java:12:28:12:49 | {...} |
| CollectionsTest.java:14:27:14:30 | list | CollectionsTest.java:14:3:14:45 | checkedList(...) |
| CollectionsTest.java:15:19:15:22 | list | CollectionsTest.java:15:3:15:23 | min(...) |
@@ -47,14 +46,14 @@
| CollectionsTest.java:33:16:33:19 | "v1" | CollectionsTest.java:33:3:33:32 | of(...) |
| CollectionsTest.java:33:28:33:31 | "v2" | CollectionsTest.java:33:3:33:32 | of(...) |
| CollectionsTest.java:34:14:34:16 | map | CollectionsTest.java:34:3:34:17 | copyOf(...) |
+| CollectionsTest.java:35:3:35:17 | new ..[] { .. } | CollectionsTest.java:35:3:35:17 | ofEntries(...) |
+| CollectionsTest.java:36:3:36:38 | new ..[] { .. } | CollectionsTest.java:36:3:36:38 | ofEntries(...) |
| CollectionsTest.java:36:17:36:37 | entry(...) | CollectionsTest.java:36:3:36:38 | new ..[] { .. } |
-| CollectionsTest.java:36:17:36:37 | entry(...) | CollectionsTest.java:36:3:36:38 | ofEntries(...) |
| CollectionsTest.java:36:33:36:36 | "v3" | CollectionsTest.java:36:17:36:37 | entry(...) |
+| CollectionsTest.java:37:3:37:61 | new ..[] { .. } | CollectionsTest.java:37:3:37:61 | ofEntries(...) |
| CollectionsTest.java:37:17:37:37 | entry(...) | CollectionsTest.java:37:3:37:61 | new ..[] { .. } |
-| CollectionsTest.java:37:17:37:37 | entry(...) | CollectionsTest.java:37:3:37:61 | ofEntries(...) |
| CollectionsTest.java:37:33:37:36 | "v4" | CollectionsTest.java:37:17:37:37 | entry(...) |
| CollectionsTest.java:37:40:37:60 | entry(...) | CollectionsTest.java:37:3:37:61 | new ..[] { .. } |
-| CollectionsTest.java:37:40:37:60 | entry(...) | CollectionsTest.java:37:3:37:61 | ofEntries(...) |
| CollectionsTest.java:37:56:37:59 | "v5" | CollectionsTest.java:37:40:37:60 | entry(...) |
| Test.java:24:32:24:38 | string2 | Test.java:24:17:24:39 | decode(...) |
| Test.java:25:46:25:51 | bytes2 | Test.java:25:31:25:52 | encode(...) |
diff --git a/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.ql b/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.ql
index 1520ea7347d..8e2f6ef2646 100644
--- a/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.ql
+++ b/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.ql
@@ -1,12 +1,38 @@
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.internal.TaintTrackingUtil
+import semmle.code.java.dataflow.internal.DataFlowNodes::Private
import semmle.code.java.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
+predicate taintFlowThrough(DataFlow::ParameterNode p) {
+ exists(ReturnNode ret | localTaint(p, ret))
+}
+
+predicate taintFlowUpdate(DataFlow::ParameterNode p1, DataFlow::ParameterNode p2) {
+ exists(DataFlow::PostUpdateNode ret | localTaint(p1, ret) | ret.getPreUpdateNode() = p2)
+}
+
from DataFlow::Node src, DataFlow::Node sink
where
(
localAdditionalTaintStep(src, sink) or
FlowSummaryImpl::Private::Steps::summaryThroughStep(src, sink, false)
) and
- not FlowSummaryImpl::Private::Steps::summaryLocalStep(src, sink, false)
+ not FlowSummaryImpl::Private::Steps::summaryLocalStep(src, sink, false) and
+ not FlowSummaryImpl::Private::Steps::summaryReadStep(src, _, sink) and
+ not FlowSummaryImpl::Private::Steps::summaryStoreStep(src, _, sink)
+ or
+ exists(ArgumentNode arg, MethodAccess call, DataFlow::ParameterNode p, int i |
+ src = arg and
+ p.isParameterOf(call.getMethod().getSourceDeclaration(), i) and
+ arg.argumentOf(call, i)
+ |
+ sink.asExpr() = call and
+ taintFlowThrough(p)
+ or
+ exists(DataFlow::ParameterNode p2, int j |
+ sink.(DataFlow::PostUpdateNode).getPreUpdateNode().(ArgumentNode).argumentOf(call, j) and
+ taintFlowUpdate(p, p2) and
+ p2.isParameterOf(_, j)
+ )
+ )
select src, sink
diff --git a/java/ql/test/library-tests/dataflow/taint-ioutils/Test.java b/java/ql/test/library-tests/dataflow/taint-ioutils/Test.java
index a6660c87252..3e45989d1c9 100644
--- a/java/ql/test/library-tests/dataflow/taint-ioutils/Test.java
+++ b/java/ql/test/library-tests/dataflow/taint-ioutils/Test.java
@@ -33,14 +33,14 @@ class Test {
byte x;
byte[] tgt = new byte[100];
- x = tgt[0]; // not tainted
+ sink(tgt); // not tainted
IOUtils.read(inp, tgt);
- x = tgt[0]; // tainted
+ sink(tgt); // tainted
tgt = new byte[100];
- x = tgt[0]; // not tainted
+ sink(tgt); // not tainted
IOUtils.readFully(inp, tgt);
- x = tgt[0]; // tainted
+ sink(tgt); // tainted
writer = new StringWriter();
writer.toString(); // not tainted
@@ -62,4 +62,6 @@ class Test {
IOUtils.writeLines(new ArrayList(), s, writer);
writer.toString(); // tainted
}
+
+ static void sink(Object o) { }
}
diff --git a/java/ql/test/library-tests/dataflow/taint-ioutils/dataFlow.expected b/java/ql/test/library-tests/dataflow/taint-ioutils/dataFlow.expected
index 1a736aa24c9..1902605e618 100644
--- a/java/ql/test/library-tests/dataflow/taint-ioutils/dataFlow.expected
+++ b/java/ql/test/library-tests/dataflow/taint-ioutils/dataFlow.expected
@@ -28,14 +28,10 @@
| Test.java:32:3:32:19 | toString(...) |
| Test.java:37:16:37:18 | inp |
| Test.java:37:21:37:23 | tgt [post update] |
-| Test.java:38:3:38:12 | ...=... |
-| Test.java:38:7:38:9 | tgt |
-| Test.java:38:7:38:12 | ...[...] |
+| Test.java:38:8:38:10 | tgt |
| Test.java:42:21:42:23 | inp |
| Test.java:42:26:42:28 | tgt [post update] |
-| Test.java:43:3:43:12 | ...=... |
-| Test.java:43:7:43:9 | tgt |
-| Test.java:43:7:43:12 | ...[...] |
+| Test.java:43:8:43:10 | tgt |
| Test.java:47:17:47:21 | chars |
| Test.java:47:24:47:29 | writer [post update] |
| Test.java:48:3:48:8 | writer |
diff --git a/java/ql/test/library-tests/dataflow/taint/test.expected b/java/ql/test/library-tests/dataflow/taint/test.expected
index 54aafdb26e7..0953353f363 100644
--- a/java/ql/test/library-tests/dataflow/taint/test.expected
+++ b/java/ql/test/library-tests/dataflow/taint/test.expected
@@ -3,7 +3,6 @@
| A.java:33:23:33:29 | taint(...) | A.java:34:10:34:27 | toByteArray(...) |
| A.java:46:27:46:33 | taint(...) | A.java:47:10:47:30 | toByteArray(...) |
| A.java:55:58:55:64 | taint(...) | A.java:61:10:61:16 | dh.data |
-| A.java:72:16:72:22 | taint(...) | A.java:73:10:73:10 | b |
| B.java:15:21:15:27 | taint(...) | B.java:18:10:18:16 | aaaargs |
| B.java:15:21:15:27 | taint(...) | B.java:21:10:21:10 | s |
| B.java:15:21:15:27 | taint(...) | B.java:24:10:24:15 | concat |
diff --git a/java/ql/test/library-tests/frameworks/apache-commons-lang3/ObjectUtilsTest.java b/java/ql/test/library-tests/frameworks/apache-commons-lang3/ObjectUtilsTest.java
index 58ee4d262e6..5b2eda3c30f 100644
--- a/java/ql/test/library-tests/frameworks/apache-commons-lang3/ObjectUtilsTest.java
+++ b/java/ql/test/library-tests/frameworks/apache-commons-lang3/ObjectUtilsTest.java
@@ -17,22 +17,22 @@ public class ObjectUtilsTest {
sink(ObjectUtils.CONST_BYTE(IntSource.taint())); // $hasValueFlow
sink(ObjectUtils.defaultIfNull(taint(), null)); // $hasValueFlow
sink(ObjectUtils.defaultIfNull(null, taint())); // $hasValueFlow
- sink(ObjectUtils.firstNonNull(taint(), null, null)); // $ MISSING:hasValueFlow
- sink(ObjectUtils.firstNonNull(null, taint(), null)); // $ MISSING:hasValueFlow
- sink(ObjectUtils.firstNonNull(null, null, taint())); // $ MISSING:hasValueFlow
+ sink(ObjectUtils.firstNonNull(taint(), null, null)); // $ hasValueFlow
+ sink(ObjectUtils.firstNonNull(null, taint(), null)); // $ hasValueFlow
+ sink(ObjectUtils.firstNonNull(null, null, taint())); // $ hasValueFlow
sink(ObjectUtils.getIfNull(taint(), null)); // $hasValueFlow
- sink(ObjectUtils.max(taint(), null, null)); // $ MISSING:hasValueFlow
- sink(ObjectUtils.max(null, taint(), null)); // $ MISSING:hasValueFlow
- sink(ObjectUtils.max(null, null, taint())); // $ MISSING:hasValueFlow
- sink(ObjectUtils.median(taint(), null, null)); // $ MISSING:hasValueFlow
- sink(ObjectUtils.median((String)null, taint(), null)); // $ MISSING:hasValueFlow
- sink(ObjectUtils.median((String)null, null, taint())); // $ MISSING:hasValueFlow
- sink(ObjectUtils.min(taint(), null, null)); // $ MISSING:hasValueFlow
- sink(ObjectUtils.min(null, taint(), null)); // $ MISSING:hasValueFlow
- sink(ObjectUtils.min(null, null, taint())); // $ MISSING:hasValueFlow
- sink(ObjectUtils.mode(taint(), null, null)); // $ MISSING:hasValueFlow
- sink(ObjectUtils.mode(null, taint(), null)); // $ MISSING:hasValueFlow
- sink(ObjectUtils.mode(null, null, taint())); // $ MISSING:hasValueFlow
+ sink(ObjectUtils.max(taint(), null, null)); // $ hasValueFlow
+ sink(ObjectUtils.max(null, taint(), null)); // $ hasValueFlow
+ sink(ObjectUtils.max(null, null, taint())); // $ hasValueFlow
+ sink(ObjectUtils.median(taint(), null, null)); // $ hasValueFlow
+ sink(ObjectUtils.median((String)null, taint(), null)); // $ hasValueFlow
+ sink(ObjectUtils.median((String)null, null, taint())); // $ hasValueFlow
+ sink(ObjectUtils.min(taint(), null, null)); // $ hasValueFlow
+ sink(ObjectUtils.min(null, taint(), null)); // $ hasValueFlow
+ sink(ObjectUtils.min(null, null, taint())); // $ hasValueFlow
+ sink(ObjectUtils.mode(taint(), null, null)); // $ hasValueFlow
+ sink(ObjectUtils.mode(null, taint(), null)); // $ hasValueFlow
+ sink(ObjectUtils.mode(null, null, taint())); // $ hasValueFlow
sink(ObjectUtils.requireNonEmpty(taint(), "message")); // $hasValueFlow
sink(ObjectUtils.requireNonEmpty("not null", taint())); // GOOD (message doesn't propagate to the return)
sink(ObjectUtils.toString(taint(), "default string")); // GOOD (first argument is stringified)
diff --git a/java/ql/test/library-tests/frameworks/apache-commons-lang3/Test.java b/java/ql/test/library-tests/frameworks/apache-commons-lang3/Test.java
index 28cbf5178b8..3d3e47b2bd0 100644
--- a/java/ql/test/library-tests/frameworks/apache-commons-lang3/Test.java
+++ b/java/ql/test/library-tests/frameworks/apache-commons-lang3/Test.java
@@ -50,10 +50,10 @@ class Test {
sink(StringUtils.deleteWhitespace(taint())); // $hasTaintFlow
sink(StringUtils.difference(taint(), "rhs")); // $hasTaintFlow
sink(StringUtils.difference("lhs", taint())); // $hasTaintFlow
- sink(StringUtils.firstNonBlank(taint(), "second string")); // $hasTaintFlow
- sink(StringUtils.firstNonBlank("first string", taint())); // $hasTaintFlow
- sink(StringUtils.firstNonEmpty(taint(), "second string")); // $hasTaintFlow
- sink(StringUtils.firstNonEmpty("first string", taint())); // $hasTaintFlow
+ sink(StringUtils.firstNonBlank(taint(), "second string")); // $hasValueFlow
+ sink(StringUtils.firstNonBlank("first string", taint())); // $hasValueFlow
+ sink(StringUtils.firstNonEmpty(taint(), "second string")); // $hasValueFlow
+ sink(StringUtils.firstNonEmpty("first string", taint())); // $hasValueFlow
sink(StringUtils.getBytes(taint(), (Charset)null)); // $hasTaintFlow
sink(StringUtils.getBytes(taint(), "some charset")); // $hasTaintFlow
// GOOD: charset names are not a source of taint
@@ -216,12 +216,12 @@ class Test {
sink(StringUtils.strip(taint())); // $hasTaintFlow
sink(StringUtils.strip(taint(), "charstoremove")); // $hasTaintFlow
sink(StringUtils.stripAccents(taint())); // $hasTaintFlow
- sink(StringUtils.stripAll(new String[] { taint() }, "charstoremove")); // $hasTaintFlow
+ sink(StringUtils.stripAll(new String[] { taint() }, "charstoremove")[0]); // $hasTaintFlow
sink(StringUtils.stripEnd(taint(), "charstoremove")); // $hasTaintFlow
sink(StringUtils.stripStart(taint(), "charstoremove")); // $hasTaintFlow
// GOOD (next 4 calls): stripped chars do not flow to the return value.
sink(StringUtils.strip("original text", taint()));
- sink(StringUtils.stripAll(new String[] { "original text" }, taint()));
+ sink(StringUtils.stripAll(new String[] { "original text" }, taint())[0]);
sink(StringUtils.stripEnd("original text", taint()));
sink(StringUtils.stripStart("original text", taint()));
sink(StringUtils.stripToEmpty(taint())); // $hasTaintFlow
@@ -275,4 +275,4 @@ class Test {
}
-}
\ No newline at end of file
+}
diff --git a/java/ql/test/library-tests/frameworks/guava/TestBase.java b/java/ql/test/library-tests/frameworks/guava/TestBase.java
index ad75f5cafbf..8da63965d58 100644
--- a/java/ql/test/library-tests/frameworks/guava/TestBase.java
+++ b/java/ql/test/library-tests/frameworks/guava/TestBase.java
@@ -18,7 +18,7 @@ class TestBase {
sink(Strings.lenientFormat(x, 3)); // $numTaintFlow=1
sink(Strings.commonPrefix(x, "abc"));
sink(Strings.commonSuffix(x, "cde"));
- sink(Strings.lenientFormat("%s = %s", x, 3)); // $ MISSING:numTaintFlow=1
+ sink(Strings.lenientFormat("%s = %s", x, 3)); // $ numTaintFlow=1
}
void test2() {
diff --git a/java/ql/test/query-tests/security/CWE-078/ExecTaintedLocal.expected b/java/ql/test/query-tests/security/CWE-078/ExecTaintedLocal.expected
index 4a29161c63c..ea822181844 100644
--- a/java/ql/test/query-tests/security/CWE-078/ExecTaintedLocal.expected
+++ b/java/ql/test/query-tests/security/CWE-078/ExecTaintedLocal.expected
@@ -1,8 +1,10 @@
edges
| Test.java:6:35:6:44 | arg : String | Test.java:7:44:7:69 | ... + ... |
| Test.java:6:35:6:44 | arg : String | Test.java:10:29:10:74 | new String[] |
-| Test.java:6:35:6:44 | arg : String | Test.java:18:29:18:31 | cmd |
+| Test.java:6:35:6:44 | arg : String | Test.java:16:13:16:25 | ... + ... : String |
| Test.java:6:35:6:44 | arg : String | Test.java:24:29:24:32 | cmd1 |
+| Test.java:16:5:16:7 | cmd [post update] : List | Test.java:18:29:18:31 | cmd |
+| Test.java:16:13:16:25 | ... + ... : String | Test.java:16:5:16:7 | cmd [post update] : List |
| Test.java:28:38:28:47 | arg : String | Test.java:29:44:29:64 | ... + ... |
| Test.java:57:27:57:39 | args : String[] | Test.java:60:20:60:22 | arg : String |
| Test.java:57:27:57:39 | args : String[] | Test.java:61:23:61:25 | arg : String |
@@ -12,6 +14,8 @@ nodes
| Test.java:6:35:6:44 | arg : String | semmle.label | arg : String |
| Test.java:7:44:7:69 | ... + ... | semmle.label | ... + ... |
| Test.java:10:29:10:74 | new String[] | semmle.label | new String[] |
+| Test.java:16:5:16:7 | cmd [post update] : List | semmle.label | cmd [post update] : List |
+| Test.java:16:13:16:25 | ... + ... : String | semmle.label | ... + ... : String |
| Test.java:18:29:18:31 | cmd | semmle.label | cmd |
| Test.java:24:29:24:32 | cmd1 | semmle.label | cmd1 |
| Test.java:28:38:28:47 | arg : String | semmle.label | arg : String |
From 901996f9fdf7eb94c65b8f9e95253f11843539fd Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Wed, 12 May 2021 18:04:20 +0200
Subject: [PATCH 074/272] Java: Add collection flow test.
---
.../library-tests/dataflow/collections/B.java | 1821 +++++++++++++++++
.../dataflow/collections/ContainterTest.java | 2 +-
.../collections/containerflow.expected | 0
.../dataflow/collections/containerflow.ql | 45 +
4 files changed, 1867 insertions(+), 1 deletion(-)
create mode 100644 java/ql/test/library-tests/dataflow/collections/B.java
create mode 100644 java/ql/test/library-tests/dataflow/collections/containerflow.expected
create mode 100644 java/ql/test/library-tests/dataflow/collections/containerflow.ql
diff --git a/java/ql/test/library-tests/dataflow/collections/B.java b/java/ql/test/library-tests/dataflow/collections/B.java
new file mode 100644
index 00000000000..1c0384a71c6
--- /dev/null
+++ b/java/ql/test/library-tests/dataflow/collections/B.java
@@ -0,0 +1,1821 @@
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.regex.*;
+
+public class B {
+ static Object source() { return null; }
+
+ static void sink(Object obj) { }
+
+ static Object[] storeArrayElement(Object obj) { return null; }
+ static Object storeElement(Object obj) { return null; }
+ static Object storeMapKey(Object obj) { return null; }
+ static Object storeMapValue(Object obj) { return null; }
+
+ static Object readArrayElement(Object obj) { return null; }
+ static Object readElement(Object obj) { return null; }
+ static Object readMapKey(Object obj) { return null; }
+ static Object readMapValue(Object obj) { return null; }
+
+ void foo() throws InterruptedException {
+ {
+ // "java.util;Map<>$Entry;true;getValue;;;MapValue of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((Map.Entry)in).getValue(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map<>$Entry;true;setValue;;;MapValue of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((Map.Entry)in).setValue(null); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map<>$Entry;true;setValue;;;Argument[0];MapValue of Argument[-1];value",
+ Map.Entry out = null;
+ Object in = source(); out.setValue(in); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.lang;Iterable;true;iterator;();;Element of Argument[-1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Iterable)in).iterator(); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.lang;Iterable;true;spliterator;();;Element of Argument[-1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Iterable)in).spliterator(); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Iterator;true;next;;;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Iterator)in).next(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;ListIterator;true;previous;;;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((ListIterator)in).previous(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;ListIterator;true;add;(Object);;Argument[0];Element of Argument[-1];value",
+ ListIterator out = null;
+ Object in = source(); out.add(in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;ListIterator;true;set;(Object);;Argument[0];Element of Argument[-1];value",
+ ListIterator out = null;
+ Object in = source(); out.set(in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Enumeration;true;asIterator;;;Element of Argument[-1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Enumeration)in).asIterator(); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Enumeration;true;nextElement;;;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Enumeration)in).nextElement(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;true;computeIfAbsent;;;MapValue of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((Map)in).computeIfAbsent(null,null); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;true;computeIfAbsent;;;ReturnValue of Argument[1];ReturnValue;value",
+ Object out = ((Map)null).computeIfAbsent(null,k -> source()); sink(out);
+ }
+ {
+ // "java.util;Map;true;computeIfAbsent;;;ReturnValue of Argument[1];MapValue of Argument[-1];value",
+ Object out = null;
+ ((Map)out).computeIfAbsent(null,k -> source()); sink(readMapValue(out));
+ }
+ {
+ // "java.util;Map;true;entrySet;;;MapValue of Argument[-1];MapValue of Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((Map)in).entrySet(); sink(readMapValue(readElement(out))); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;true;entrySet;;;MapKey of Argument[-1];MapKey of Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapKey(source()); out = ((Map)in).entrySet(); sink(readMapKey(readElement(out))); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;true;get;;;MapValue of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((Map)in).get(null); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;true;getOrDefault;;;MapValue of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((Map)in).getOrDefault(null,null); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;true;getOrDefault;;;Argument[1];ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = ((Map)null).getOrDefault(null,in); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;true;put;;;MapValue of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((Map)in).put(null,null); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;true;put;;;Argument[0];MapKey of Argument[-1];value",
+ Map out = null;
+ Object in = source(); out.put(in,null); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;true;put;;;Argument[1];MapValue of Argument[-1];value",
+ Map out = null;
+ Object in = source(); out.put(null,in); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;true;putIfAbsent;;;MapValue of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((Map)in).putIfAbsent(null,null); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;true;putIfAbsent;;;Argument[0];MapKey of Argument[-1];value",
+ Map out = null;
+ Object in = source(); out.putIfAbsent(in,null); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;true;putIfAbsent;;;Argument[1];MapValue of Argument[-1];value",
+ Map out = null;
+ Object in = source(); out.putIfAbsent(null,in); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;true;remove;(Object);;MapValue of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((Map)in).remove(null); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;true;replace;(Object,Object);;MapValue of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((Map)in).replace(null,null); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;true;replace;(Object,Object);;Argument[0];MapKey of Argument[-1];value",
+ Map out = null;
+ Object in = source(); out.replace(in,null); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;true;replace;(Object,Object);;Argument[1];MapValue of Argument[-1];value",
+ Map out = null;
+ Object in = source(); out.replace(null,in); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;true;replace;(Object,Object,Object);;Argument[0];MapKey of Argument[-1];value",
+ Map out = null;
+ Object in = source(); out.replace(in,null,null); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;true;replace;(Object,Object,Object);;Argument[2];MapValue of Argument[-1];value",
+ Map out = null;
+ Object in = source(); out.replace(null,null,in); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;true;keySet;();;MapKey of Argument[-1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapKey(source()); out = ((Map)in).keySet(); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;true;values;();;MapValue of Argument[-1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((Map)in).values(); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;true;merge;(Object,Object,BiFunction);;Argument[1];MapValue of Argument[-1];value",
+ Map out = null;
+ Object in = source(); out.merge(null,in,null); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;true;putAll;(Map);;MapKey of Argument[0];MapKey of Argument[-1];value",
+ Map out = null;
+ Object in = storeMapKey(source()); out.putAll((Map)in); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;true;putAll;(Map);;MapValue of Argument[0];MapValue of Argument[-1];value",
+ Map out = null;
+ Object in = storeMapValue(source()); out.putAll((Map)in); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collection;true;parallelStream;();;Element of Argument[-1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Collection)in).parallelStream(); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collection;true;stream;();;Element of Argument[-1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Collection)in).stream(); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collection;true;toArray;;;Element of Argument[-1];ArrayElement of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Collection)in).toArray(); sink(readArrayElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collection;true;toArray;;;Element of Argument[-1];ArrayElement of Argument[0];value",
+ Object[] out = null;
+ Object in = storeElement(source()); ((Collection)in).toArray(out); sink(readArrayElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collection;true;add;;;Argument[0];Element of Argument[-1];value",
+ Collection out = null;
+ Object in = source(); out.add(in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collection;true;addAll;;;Element of Argument[0];Element of Argument[-1];value",
+ Collection out = null;
+ Object in = storeElement(source()); out.addAll((Collection)in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;true;get;(int);;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((List)in).get(0); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;true;listIterator;;;Element of Argument[-1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((List)in).listIterator(); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;true;remove;(int);;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((List)in).remove(0); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;true;set;(int,Object);;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((List)in).set(0,null); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;true;set;(int,Object);;Argument[1];Element of Argument[-1];value",
+ List out = null;
+ Object in = source(); out.set(0,in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;true;subList;;;Element of Argument[-1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((List)in).subList(0,0); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;true;add;(int,Object);;Argument[1];Element of Argument[-1];value",
+ List out = null;
+ Object in = source(); out.add(0,in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;true;addAll;(int,Collection);;Element of Argument[1];Element of Argument[-1];value",
+ List out = null;
+ Object in = storeElement(source()); out.addAll(0,(Collection)in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Vector;true;elementAt;(int);;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Vector)in).elementAt(0); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Vector;true;elements;();;Element of Argument[-1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Vector)in).elements(); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Vector;true;firstElement;();;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Vector)in).firstElement(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Vector;true;lastElement;();;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Vector)in).lastElement(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Vector;true;addElement;(Object);;Argument[0];Element of Argument[-1];value",
+ Vector out = null;
+ Object in = source(); out.addElement(in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Vector;true;insertElementAt;(Object,int);;Argument[0];Element of Argument[-1];value",
+ Vector out = null;
+ Object in = source(); out.insertElementAt(in,0); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Vector;true;setElementAt;(Object,int);;Argument[0];Element of Argument[-1];value",
+ Vector out = null;
+ Object in = source(); out.setElementAt(in,0); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Vector;true;copyInto;(Object[]);;Element of Argument[-1];ArrayElement of Argument[0];value",
+ Object[] out = null;
+ Object in = storeElement(source()); ((Vector)in).copyInto(out); sink(readArrayElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Stack;true;peek;();;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Stack)in).peek(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Stack;true;pop;();;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Stack)in).pop(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Stack;true;push;(Object);;Argument[0];Element of Argument[-1];value",
+ Stack out = null;
+ Object in = source(); out.push(in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Queue;true;element;();;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Queue)in).element(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Queue;true;peek;();;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Queue)in).peek(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Queue;true;poll;();;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Queue)in).poll(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Queue;true;remove;();;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Queue)in).remove(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Queue;true;offer;(Object);;Argument[0];Element of Argument[-1];value",
+ Queue out = null;
+ Object in = source(); out.offer(in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Deque;true;descendingIterator;();;Element of Argument[-1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Deque)in).descendingIterator(); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Deque;true;getFirst;();;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Deque)in).getFirst(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Deque;true;getLast;();;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Deque)in).getLast(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Deque;true;peekFirst;();;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Deque)in).peekFirst(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Deque;true;peekLast;();;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Deque)in).peekLast(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Deque;true;pollFirst;();;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Deque)in).pollFirst(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Deque;true;pollLast;();;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Deque)in).pollLast(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Deque;true;pop;();;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Deque)in).pop(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Deque;true;removeFirst;();;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Deque)in).removeFirst(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Deque;true;removeLast;();;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Deque)in).removeLast(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Deque;true;push;(Object);;Argument[0];Element of Argument[-1];value",
+ Deque out = null;
+ Object in = source(); out.push(in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Deque;true;offerLast;(Object);;Argument[0];Element of Argument[-1];value",
+ Deque out = null;
+ Object in = source(); out.offerLast(in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Deque;true;offerFirst;(Object);;Argument[0];Element of Argument[-1];value",
+ Deque out = null;
+ Object in = source(); out.offerFirst(in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Deque;true;addLast;(Object);;Argument[0];Element of Argument[-1];value",
+ Deque out = null;
+ Object in = source(); out.addLast(in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Deque;true;addFirst;(Object);;Argument[0];Element of Argument[-1];value",
+ Deque out = null;
+ Object in = source(); out.addFirst(in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util.concurrent;BlockingDeque;true;pollFirst;(long,TimeUnit);;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((BlockingDeque)in).pollFirst(0,null); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util.concurrent;BlockingDeque;true;pollLast;(long,TimeUnit);;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((BlockingDeque)in).pollLast(0,null); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util.concurrent;BlockingDeque;true;takeFirst;();;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((BlockingDeque)in).takeFirst(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util.concurrent;BlockingDeque;true;takeLast;();;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((BlockingDeque)in).takeLast(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util.concurrent;BlockingQueue;true;poll;(long,TimeUnit);;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((BlockingQueue)in).poll(0,null); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util.concurrent;BlockingQueue;true;take;();;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((BlockingQueue)in).take(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util.concurrent;BlockingQueue;true;offer;(Object,long,TimeUnit);;Argument[0];Element of Argument[-1];value",
+ BlockingQueue out = null;
+ Object in = source(); out.offer(in,0,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util.concurrent;BlockingQueue;true;put;(Object);;Argument[0];Element of Argument[-1];value",
+ BlockingQueue out = null;
+ Object in = source(); out.put(in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util.concurrent;BlockingDeque;true;offerLast;(Object,long,TimeUnit);;Argument[0];Element of Argument[-1];value",
+ BlockingDeque out = null;
+ Object in = source(); out.offerLast(in,0,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util.concurrent;BlockingDeque;true;offerFirst;(Object,long,TimeUnit);;Argument[0];Element of Argument[-1];value",
+ BlockingDeque out = null;
+ Object in = source(); out.offerFirst(in,0,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util.concurrent;BlockingDeque;true;putLast;(Object);;Argument[0];Element of Argument[-1];value",
+ BlockingDeque out = null;
+ Object in = source(); out.putLast(in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util.concurrent;BlockingDeque;true;putFirst;(Object);;Argument[0];Element of Argument[-1];value",
+ BlockingDeque out = null;
+ Object in = source(); out.putFirst(in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util.concurrent;BlockingQueue;true;drainTo;(Collection,int);;Element of Argument[-1];Element of Argument[0];value",
+ Collection out = null;
+ Object in = storeElement(source()); ((BlockingQueue)in).drainTo(out,0); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util.concurrent;BlockingQueue;true;drainTo;(Collection);;Element of Argument[-1];Element of Argument[0];value",
+ Collection out = null;
+ Object in = storeElement(source()); ((BlockingQueue)in).drainTo(out); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util.concurrent;ConcurrentHashMap;true;elements;();;MapValue of Argument[-1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((ConcurrentHashMap)in).elements(); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Dictionary;true;elements;();;MapValue of Argument[-1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((Dictionary)in).elements(); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Dictionary;true;get;(Object);;MapValue of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((Dictionary)in).get(null); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Dictionary;true;put;(Object,Object);;MapValue of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((Dictionary)in).put(null,null); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Dictionary;true;put;(Object,Object);;Argument[0];MapKey of Argument[-1];value",
+ Dictionary out = null;
+ Object in = source(); out.put(in,null); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Dictionary;true;put;(Object,Object);;Argument[1];MapValue of Argument[-1];value",
+ Dictionary out = null;
+ Object in = source(); out.put(null,in); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Dictionary;true;remove;(Object);;MapValue of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((Dictionary)in).remove(null); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableMap;true;ceilingEntry;(Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapKey(source()); out = ((NavigableMap)in).ceilingEntry(null); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableMap;true;ceilingEntry;(Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((NavigableMap)in).ceilingEntry(null); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableMap;true;descendingMap;();;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapKey(source()); out = ((NavigableMap)in).descendingMap(); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableMap;true;descendingMap;();;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((NavigableMap)in).descendingMap(); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableMap;true;firstEntry;();;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapKey(source()); out = ((NavigableMap)in).firstEntry(); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableMap;true;firstEntry;();;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((NavigableMap)in).firstEntry(); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableMap;true;floorEntry;(Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapKey(source()); out = ((NavigableMap)in).floorEntry(null); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableMap;true;floorEntry;(Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((NavigableMap)in).floorEntry(null); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableMap;true;headMap;(Object,boolean);;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapKey(source()); out = ((NavigableMap)in).headMap(null,true); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableMap;true;headMap;(Object,boolean);;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((NavigableMap)in).headMap(null,true); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableMap;true;higherEntry;(Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapKey(source()); out = ((NavigableMap)in).higherEntry(null); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableMap;true;higherEntry;(Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((NavigableMap)in).higherEntry(null); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableMap;true;lastEntry;();;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapKey(source()); out = ((NavigableMap)in).lastEntry(); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableMap;true;lastEntry;();;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((NavigableMap)in).lastEntry(); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableMap;true;lowerEntry;(Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapKey(source()); out = ((NavigableMap)in).lowerEntry(null); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableMap;true;lowerEntry;(Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((NavigableMap)in).lowerEntry(null); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableMap;true;pollFirstEntry;();;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapKey(source()); out = ((NavigableMap)in).pollFirstEntry(); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableMap;true;pollFirstEntry;();;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((NavigableMap)in).pollFirstEntry(); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableMap;true;pollLastEntry;();;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapKey(source()); out = ((NavigableMap)in).pollLastEntry(); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableMap;true;pollLastEntry;();;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((NavigableMap)in).pollLastEntry(); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableMap;true;subMap;(Object,boolean,Object,boolean);;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapKey(source()); out = ((NavigableMap)in).subMap(null,true,null,true); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableMap;true;subMap;(Object,boolean,Object,boolean);;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((NavigableMap)in).subMap(null,true,null,true); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableMap;true;tailMap;(Object,boolean);;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapKey(source()); out = ((NavigableMap)in).tailMap(null,true); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableMap;true;tailMap;(Object,boolean);;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((NavigableMap)in).tailMap(null,true); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableSet;true;ceiling;(Object);;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((NavigableSet)in).ceiling(null); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableSet;true;descendingIterator;();;Element of Argument[-1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((NavigableSet)in).descendingIterator(); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableSet;true;descendingSet;();;Element of Argument[-1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((NavigableSet)in).descendingSet(); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableSet;true;floor;(Object);;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((NavigableSet)in).floor(null); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableSet;true;headSet;(Object,boolean);;Element of Argument[-1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((NavigableSet)in).headSet(null,true); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableSet;true;higher;(Object);;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((NavigableSet)in).higher(null); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableSet;true;lower;(Object);;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((NavigableSet)in).lower(null); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableSet;true;pollFirst;();;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((NavigableSet)in).pollFirst(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableSet;true;pollLast;();;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((NavigableSet)in).pollLast(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableSet;true;subSet;(Object,boolean,Object,boolean);;Element of Argument[-1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((NavigableSet)in).subSet(null,true,null,true); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;NavigableSet;true;tailSet;(Object,boolean);;Element of Argument[-1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((NavigableSet)in).tailSet(null,true); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;SortedMap;true;headMap;(Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapKey(source()); out = ((SortedMap)in).headMap(null); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;SortedMap;true;headMap;(Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((SortedMap)in).headMap(null); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;SortedMap;true;subMap;(Object,Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapKey(source()); out = ((SortedMap)in).subMap(null,null); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;SortedMap;true;subMap;(Object,Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((SortedMap)in).subMap(null,null); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;SortedMap;true;tailMap;(Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapKey(source()); out = ((SortedMap)in).tailMap(null); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;SortedMap;true;tailMap;(Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((SortedMap)in).tailMap(null); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;SortedSet;true;first;();;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((SortedSet)in).first(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;SortedSet;true;headSet;(Object);;Element of Argument[-1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((SortedSet)in).headSet(null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;SortedSet;true;last;();;Element of Argument[-1];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((SortedSet)in).last(); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;SortedSet;true;subSet;(Object,Object);;Element of Argument[-1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((SortedSet)in).subSet(null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;SortedSet;true;tailSet;(Object);;Element of Argument[-1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((SortedSet)in).tailSet(null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util.concurrent;TransferQueue;true;tryTransfer;(Object,long,TimeUnit);;Argument[0];Element of Argument[-1];value",
+ TransferQueue out = null;
+ Object in = source(); out.tryTransfer(in,0,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util.concurrent;TransferQueue;true;transfer;(Object);;Argument[0];Element of Argument[-1];value",
+ TransferQueue out = null;
+ Object in = source(); out.transfer(in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util.concurrent;TransferQueue;true;tryTransfer;(Object);;Argument[0];Element of Argument[-1];value",
+ TransferQueue out = null;
+ Object in = source(); out.tryTransfer(in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;copyOf;(Collection);;Element of Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = List.copyOf((Collection)in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object[]);;ArrayElement of Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object[] in = storeArrayElement(source()); out = List.of(in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object);;Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object);;Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(in,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object);;Argument[1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object);;Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(in,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object);;Argument[1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,in,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object);;Argument[2];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object);;Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(in,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object);;Argument[1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,in,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object);;Argument[2];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,in,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object);;Argument[3];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object);;Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(in,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object);;Argument[1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,in,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object);;Argument[2];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,in,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object);;Argument[3];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,in,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object);;Argument[4];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,null,in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object);;Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(in,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object);;Argument[1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,in,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object);;Argument[2];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,in,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object);;Argument[3];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,in,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object);;Argument[4];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,null,in,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object);;Argument[5];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,null,null,in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object);;Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(in,null,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object);;Argument[1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,in,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object);;Argument[2];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,in,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object);;Argument[3];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,in,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object);;Argument[4];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,null,in,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object);;Argument[5];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,null,null,in,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object);;Argument[6];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,null,null,null,in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object);;Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(in,null,null,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object);;Argument[1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,in,null,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object);;Argument[2];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,in,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object);;Argument[3];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,in,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object);;Argument[4];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,null,in,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object);;Argument[5];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,null,null,in,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object);;Argument[6];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,null,null,null,in,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object);;Argument[7];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,null,null,null,null,in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(in,null,null,null,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,in,null,null,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[2];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,in,null,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[3];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,in,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[4];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,null,in,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[5];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,null,null,in,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[6];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,null,null,null,in,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[7];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,null,null,null,null,in,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[8];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,null,null,null,null,null,in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(in,null,null,null,null,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,in,null,null,null,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[2];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,in,null,null,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[3];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,in,null,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[4];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,null,in,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[5];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,null,null,in,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[6];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,null,null,null,in,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[7];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,null,null,null,null,in,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[8];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,null,null,null,null,null,in,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[9];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = List.of(null,null,null,null,null,null,null,null,null,in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;false;copyOf;(Map);;MapKey of Argument[0];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapKey(source()); out = Map.copyOf((Map)in); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;false;copyOf;(Map);;MapValue of Argument[0];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = Map.copyOf((Map)in); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;false;entry;(Object,Object);;Argument[0];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Map.entry(in,null); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;false;entry;(Object,Object);;Argument[1];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Map.entry(null,in); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;false;of;;;Argument[0];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Map.of(in,null); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;false;of;;;Argument[1];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Map.of(null,in); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;false;of;;;Argument[2];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Map.of(null,null,in,null); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;false;of;;;Argument[3];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Map.of(null,null,null,in); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;false;of;;;Argument[4];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Map.of(null,null,null,null,in,null); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;false;of;;;Argument[5];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Map.of(null,null,null,null,null,in); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;false;of;;;Argument[6];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Map.of(null,null,null,null,null,null,in,null); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;false;of;;;Argument[7];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Map.of(null,null,null,null,null,null,null,in); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;false;of;;;Argument[8];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Map.of(null,null,null,null,null,null,null,null,in,null); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;false;of;;;Argument[9];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Map.of(null,null,null,null,null,null,null,null,null,in); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;false;of;;;Argument[10];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Map.of(null,null,null,null,null,null,null,null,null,null,in,null); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;false;of;;;Argument[11];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Map.of(null,null,null,null,null,null,null,null,null,null,null,in); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;false;of;;;Argument[12];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Map.of(null,null,null,null,null,null,null,null,null,null,null,null,in,null); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;false;of;;;Argument[13];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Map.of(null,null,null,null,null,null,null,null,null,null,null,null,null,in); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;false;of;;;Argument[14];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Map.of(null,null,null,null,null,null,null,null,null,null,null,null,null,null,in,null); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;false;of;;;Argument[15];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Map.of(null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,in); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;false;of;;;Argument[16];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Map.of(null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,in,null); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;false;of;;;Argument[17];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Map.of(null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,in); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;false;of;;;Argument[18];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Map.of(null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,in,null); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;false;of;;;Argument[19];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Map.of(null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,in); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;false;ofEntries;;;MapKey of ArrayElement of Argument[0];MapKey of ReturnValue;value",
+ Object out = null;
+ Object[] in = storeArrayElement(storeMapKey(source())); out = Map.ofEntries((Map.Entry[])in); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Map;false;ofEntries;;;MapValue of ArrayElement of Argument[0];MapValue of ReturnValue;value",
+ Object out = null;
+ Object[] in = storeArrayElement(storeMapValue(source())); out = Map.ofEntries((Map.Entry[])in); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;copyOf;(Collection);;Element of Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = Set.copyOf((Collection)in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object[]);;ArrayElement of Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object[] in = storeArrayElement(source()); out = Set.of(in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object);;Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object);;Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(in,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object);;Argument[1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object);;Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(in,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object);;Argument[1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,in,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object);;Argument[2];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object);;Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(in,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object);;Argument[1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,in,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object);;Argument[2];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,in,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object);;Argument[3];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object);;Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(in,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object);;Argument[1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,in,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object);;Argument[2];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,in,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object);;Argument[3];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,in,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object);;Argument[4];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,null,in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object);;Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(in,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object);;Argument[1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,in,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object);;Argument[2];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,in,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object);;Argument[3];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,in,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object);;Argument[4];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,null,in,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object);;Argument[5];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,null,null,in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object);;Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(in,null,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object);;Argument[1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,in,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object);;Argument[2];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,in,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object);;Argument[3];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,in,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object);;Argument[4];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,null,in,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object);;Argument[5];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,null,null,in,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object);;Argument[6];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,null,null,null,in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object);;Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(in,null,null,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object);;Argument[1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,in,null,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object);;Argument[2];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,in,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object);;Argument[3];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,in,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object);;Argument[4];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,null,in,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object);;Argument[5];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,null,null,in,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object);;Argument[6];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,null,null,null,in,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object);;Argument[7];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,null,null,null,null,in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(in,null,null,null,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,in,null,null,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[2];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,in,null,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[3];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,in,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[4];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,null,in,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[5];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,null,null,in,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[6];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,null,null,null,in,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[7];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,null,null,null,null,in,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[8];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,null,null,null,null,null,in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(in,null,null,null,null,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,in,null,null,null,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[2];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,in,null,null,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[3];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,in,null,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[4];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,null,in,null,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[5];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,null,null,in,null,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[6];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,null,null,null,in,null,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[7];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,null,null,null,null,in,null,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[8];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,null,null,null,null,null,in,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[9];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = Set.of(null,null,null,null,null,null,null,null,null,in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Arrays;false;stream;;;ArrayElement of Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object[] in = storeArrayElement(source()); out = ((Arrays)null).stream(in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Arrays;false;spliterator;;;ArrayElement of Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object[] in = storeArrayElement(source()); out = ((Arrays)null).spliterator(in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Arrays;false;copyOfRange;;;ArrayElement of Argument[0];ArrayElement of ReturnValue;value",
+ Object out = null;
+ Object[] in = storeArrayElement(source()); out = ((Arrays)null).copyOfRange(in,0,0); sink(readArrayElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Arrays;false;copyOf;;;ArrayElement of Argument[0];ArrayElement of ReturnValue;value",
+ Object out = null;
+ Object[] in = storeArrayElement(source()); out = ((Arrays)null).copyOf(in,0); sink(readArrayElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;list;(Enumeration);;Element of Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Collections)null).list((Enumeration)in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;enumeration;(Collection);;Element of Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Collections)null).enumeration((Collection)in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;nCopies;(int,Object);;Argument[1];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = ((Collections)null).nCopies(0,in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;singletonMap;(Object,Object);;Argument[0];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = ((Collections)null).singletonMap(in,null); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;singletonMap;(Object,Object);;Argument[1];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = ((Collections)null).singletonMap(null,in); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;singletonList;(Object);;Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = ((Collections)null).singletonList(in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;singleton;(Object);;Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = source(); out = ((Collections)null).singleton(in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;checkedNavigableMap;(NavigableMap,Class,Class);;MapKey of Argument[0];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapKey(source()); out = ((Collections)null).checkedNavigableMap((NavigableMap)in,null,null); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;checkedNavigableMap;(NavigableMap,Class,Class);;MapValue of Argument[0];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((Collections)null).checkedNavigableMap((NavigableMap)in,null,null); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;checkedSortedMap;(SortedMap,Class,Class);;MapKey of Argument[0];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapKey(source()); out = ((Collections)null).checkedSortedMap((SortedMap)in,null,null); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;checkedSortedMap;(SortedMap,Class,Class);;MapValue of Argument[0];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((Collections)null).checkedSortedMap((SortedMap)in,null,null); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;checkedMap;(Map,Class,Class);;MapKey of Argument[0];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapKey(source()); out = ((Collections)null).checkedMap((Map)in,null,null); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;checkedMap;(Map,Class,Class);;MapValue of Argument[0];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((Collections)null).checkedMap((Map)in,null,null); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;checkedList;(List,Class);;Element of Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Collections)null).checkedList((List)in,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;checkedNavigableSet;(NavigableSet,Class);;Element of Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Collections)null).checkedNavigableSet((NavigableSet)in,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;checkedSortedSet;(SortedSet,Class);;Element of Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Collections)null).checkedSortedSet((SortedSet)in,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;checkedSet;(Set,Class);;Element of Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Collections)null).checkedSet((Set)in,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;checkedCollection;(Collection,Class);;Element of Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Collections)null).checkedCollection((Collection)in,null); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;synchronizedNavigableMap;(NavigableMap);;MapKey of Argument[0];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapKey(source()); out = ((Collections)null).synchronizedNavigableMap((NavigableMap)in); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;synchronizedNavigableMap;(NavigableMap);;MapValue of Argument[0];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((Collections)null).synchronizedNavigableMap((NavigableMap)in); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;synchronizedSortedMap;(SortedMap);;MapKey of Argument[0];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapKey(source()); out = ((Collections)null).synchronizedSortedMap((SortedMap)in); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;synchronizedSortedMap;(SortedMap);;MapValue of Argument[0];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((Collections)null).synchronizedSortedMap((SortedMap)in); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;synchronizedMap;(Map);;MapKey of Argument[0];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapKey(source()); out = ((Collections)null).synchronizedMap((Map)in); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;synchronizedMap;(Map);;MapValue of Argument[0];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((Collections)null).synchronizedMap((Map)in); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;synchronizedList;(List);;Element of Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Collections)null).synchronizedList((List)in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;synchronizedNavigableSet;(NavigableSet);;Element of Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Collections)null).synchronizedNavigableSet((NavigableSet)in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;synchronizedSortedSet;(SortedSet);;Element of Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Collections)null).synchronizedSortedSet((SortedSet)in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;synchronizedSet;(Set);;Element of Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Collections)null).synchronizedSet((Set)in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;synchronizedCollection;(Collection);;Element of Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Collections)null).synchronizedCollection((Collection)in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;unmodifiableNavigableMap;(NavigableMap);;MapKey of Argument[0];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapKey(source()); out = ((Collections)null).unmodifiableNavigableMap((NavigableMap)in); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;unmodifiableNavigableMap;(NavigableMap);;MapValue of Argument[0];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((Collections)null).unmodifiableNavigableMap((NavigableMap)in); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;unmodifiableSortedMap;(SortedMap);;MapKey of Argument[0];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapKey(source()); out = ((Collections)null).unmodifiableSortedMap((SortedMap)in); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;unmodifiableSortedMap;(SortedMap);;MapValue of Argument[0];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((Collections)null).unmodifiableSortedMap((SortedMap)in); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;unmodifiableMap;(Map);;MapKey of Argument[0];MapKey of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapKey(source()); out = ((Collections)null).unmodifiableMap((Map)in); sink(readMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;unmodifiableMap;(Map);;MapValue of Argument[0];MapValue of ReturnValue;value",
+ Object out = null;
+ Object in = storeMapValue(source()); out = ((Collections)null).unmodifiableMap((Map)in); sink(readMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;unmodifiableList;(List);;Element of Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Collections)null).unmodifiableList((List)in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;unmodifiableNavigableSet;(NavigableSet);;Element of Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Collections)null).unmodifiableNavigableSet((NavigableSet)in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;unmodifiableSortedSet;(SortedSet);;Element of Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Collections)null).unmodifiableSortedSet((SortedSet)in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;unmodifiableSet;(Set);;Element of Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Collections)null).unmodifiableSet((Set)in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;unmodifiableCollection;(Collection);;Element of Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Collections)null).unmodifiableCollection((Collection)in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;max;;;Element of Argument[0];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Collections)null).max((Collection)in); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;min;;;Element of Argument[0];ReturnValue;value",
+ Object out = null;
+ Object in = storeElement(source()); out = ((Collections)null).min((Collection)in); sink(out); // $ hasValueFlow
+ }
+ {
+ // "java.util;Arrays;false;fill;(Object[],int,int,Object);;Argument[3];ArrayElement of Argument[0];value",
+ Object[] out = null;
+ Object in = source(); ((Arrays)null).fill(out,0,0,in); sink(readArrayElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Arrays;false;fill;(Object[],Object);;Argument[1];ArrayElement of Argument[0];value",
+ Object[] out = null;
+ Object in = source(); ((Arrays)null).fill(out,in); sink(readArrayElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Arrays;false;fill;(float[],int,int,float);;Argument[3];ArrayElement of Argument[0];value",
+ float[] out = null;
+ float in = (Float)source(); ((Arrays)null).fill(out,0,0,in); sink(readArrayElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Arrays;false;fill;(float[],float);;Argument[1];ArrayElement of Argument[0];value",
+ float[] out = null;
+ float in = (Float)source(); ((Arrays)null).fill(out,in); sink(readArrayElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Arrays;false;fill;(double[],int,int,double);;Argument[3];ArrayElement of Argument[0];value",
+ double[] out = null;
+ double in = (Double)source(); ((Arrays)null).fill(out,0,0,in); sink(readArrayElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Arrays;false;fill;(double[],double);;Argument[1];ArrayElement of Argument[0];value",
+ double[] out = null;
+ double in = (Double)source(); ((Arrays)null).fill(out,in); sink(readArrayElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Arrays;false;fill;(boolean[],int,int,boolean);;Argument[3];ArrayElement of Argument[0];value",
+ boolean[] out = null;
+ boolean in = (Boolean)source(); ((Arrays)null).fill(out,0,0,in); sink(readArrayElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Arrays;false;fill;(boolean[],boolean);;Argument[1];ArrayElement of Argument[0];value",
+ boolean[] out = null;
+ boolean in = (Boolean)source(); ((Arrays)null).fill(out,in); sink(readArrayElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Arrays;false;fill;(byte[],int,int,byte);;Argument[3];ArrayElement of Argument[0];value",
+ byte[] out = null;
+ byte in = (Byte)source(); ((Arrays)null).fill(out,0,0,in); sink(readArrayElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Arrays;false;fill;(byte[],byte);;Argument[1];ArrayElement of Argument[0];value",
+ byte[] out = null;
+ byte in = (Byte)source(); ((Arrays)null).fill(out,in); sink(readArrayElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Arrays;false;fill;(char[],int,int,char);;Argument[3];ArrayElement of Argument[0];value",
+ char[] out = null;
+ char in = (Character)source(); ((Arrays)null).fill(out,0,0,in); sink(readArrayElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Arrays;false;fill;(char[],char);;Argument[1];ArrayElement of Argument[0];value",
+ char[] out = null;
+ char in = (Character)source(); ((Arrays)null).fill(out,in); sink(readArrayElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Arrays;false;fill;(short[],int,int,short);;Argument[3];ArrayElement of Argument[0];value",
+ short[] out = null;
+ short in = (Short)source(); ((Arrays)null).fill(out,0,0,in); sink(readArrayElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Arrays;false;fill;(short[],short);;Argument[1];ArrayElement of Argument[0];value",
+ short[] out = null;
+ short in = (Short)source(); ((Arrays)null).fill(out,in); sink(readArrayElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Arrays;false;fill;(int[],int,int,int);;Argument[3];ArrayElement of Argument[0];value",
+ int[] out = null;
+ int in = (Integer)source(); ((Arrays)null).fill(out,0,0,in); sink(readArrayElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Arrays;false;fill;(int[],int);;Argument[1];ArrayElement of Argument[0];value",
+ int[] out = null;
+ int in = (Integer)source(); ((Arrays)null).fill(out,in); sink(readArrayElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Arrays;false;fill;(long[],int,int,long);;Argument[3];ArrayElement of Argument[0];value",
+ long[] out = null;
+ long in = (Long)source(); ((Arrays)null).fill(out,0,0,in); sink(readArrayElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Arrays;false;fill;(long[],long);;Argument[1];ArrayElement of Argument[0];value",
+ long[] out = null;
+ long in = (Long)source(); ((Arrays)null).fill(out,in); sink(readArrayElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;replaceAll;(List,Object,Object);;Argument[2];Element of Argument[0];value",
+ List out = null;
+ Object in = source(); ((Collections)null).replaceAll(out,null,in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;copy;(List,List);;Element of Argument[1];Element of Argument[0];value",
+ List out = null;
+ Object in = storeElement(source()); ((Collections)null).copy(out,(List)in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;fill;(List,Object);;Argument[1];Element of Argument[0];value",
+ List out = null;
+ Object in = source(); ((Collections)null).fill(out,in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Arrays;false;asList;;;ArrayElement of Argument[0];Element of ReturnValue;value",
+ Object out = null;
+ Object[] in = storeArrayElement(source()); out = ((Arrays)null).asList(in); sink(readElement(out)); // $ hasValueFlow
+ }
+ {
+ // "java.util;Collections;false;addAll;(Collection,Object[]);;ArrayElement of Argument[1];Element of Argument[0];value"
+ Collection out = null;
+ Object[] in = storeArrayElement(source()); ((Collections)null).addAll(out,in); sink(readElement(out)); // $ hasValueFlow
+ }
+ }
+}
diff --git a/java/ql/test/library-tests/dataflow/collections/ContainterTest.java b/java/ql/test/library-tests/dataflow/collections/ContainterTest.java
index ab9b699be9b..9c291a7b2a3 100644
--- a/java/ql/test/library-tests/dataflow/collections/ContainterTest.java
+++ b/java/ql/test/library-tests/dataflow/collections/ContainterTest.java
@@ -89,7 +89,7 @@ class ContainerTest {
sink(stack.peek());
sink(stack.pop());
sink(stack.push("value")); // not tainted
- sink(new Stack().push(source("value")));
+ sink(new Stack().push(source("value"))); // $ hasValueFlow
mkSink(Stack.class).push(source("value"));
// java.util.Queue
diff --git a/java/ql/test/library-tests/dataflow/collections/containerflow.expected b/java/ql/test/library-tests/dataflow/collections/containerflow.expected
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/java/ql/test/library-tests/dataflow/collections/containerflow.ql b/java/ql/test/library-tests/dataflow/collections/containerflow.ql
new file mode 100644
index 00000000000..409ff5c0107
--- /dev/null
+++ b/java/ql/test/library-tests/dataflow/collections/containerflow.ql
@@ -0,0 +1,45 @@
+import java
+import semmle.code.java.dataflow.DataFlow
+import semmle.code.java.dataflow.ExternalFlow
+import TestUtilities.InlineExpectationsTest
+import DataFlow
+
+class SummaryModelTest extends SummaryModelCsv {
+ override predicate row(string row) {
+ row =
+ [
+ //"package;type;overrides;name;signature;ext;inputspec;outputspec;kind",
+ ";B;false;storeArrayElement;(Object);;Argument[0];ArrayElement of ReturnValue;value",
+ ";B;false;storeElement;(Object);;Argument[0];Element of ReturnValue;value",
+ ";B;false;storeMapKey;(Object);;Argument[0];MapKey of ReturnValue;value",
+ ";B;false;storeMapValue;(Object);;Argument[0];MapValue of ReturnValue;value",
+ ";B;false;readArrayElement;(Object);;ArrayElement of Argument[0];ReturnValue;value",
+ ";B;false;readElement;(Object);;Element of Argument[0];ReturnValue;value",
+ ";B;false;readMapKey;(Object);;MapKey of Argument[0];ReturnValue;value",
+ ";B;false;readMapValue;(Object);;MapValue of Argument[0];ReturnValue;value"
+ ]
+ }
+}
+
+class ContainerFlowConf extends Configuration {
+ ContainerFlowConf() { this = "qltest:ContainerFlowConf" }
+
+ override predicate isSource(Node n) { n.asExpr().(MethodAccess).getMethod().hasName("source") }
+
+ override predicate isSink(Node n) { n.asExpr().(Argument).getCall().getCallee().hasName("sink") }
+}
+
+class HasFlowTest extends InlineExpectationsTest {
+ HasFlowTest() { this = "HasFlowTest" }
+
+ override string getARelevantTag() { result = "hasValueFlow" }
+
+ override predicate hasActualResult(Location location, string element, string tag, string value) {
+ tag = "hasValueFlow" and
+ exists(Node src, Node sink, ContainerFlowConf conf | conf.hasFlow(src, sink) |
+ sink.getLocation() = location and
+ element = sink.toString() and
+ value = ""
+ )
+ }
+}
From dbe352f3ff11bbe9b7c55a9556d42dc9bc7a7822 Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Thu, 20 May 2021 12:55:31 +0200
Subject: [PATCH 075/272] Java: Remove deprecated tests.
---
.../local-additional-taint/ArraysTest.java | 18 +++----
.../CollectionsTest.java | 40 ---------------
.../localAdditionalTaintStep.expected | 49 -------------------
3 files changed, 9 insertions(+), 98 deletions(-)
delete mode 100644 java/ql/test/library-tests/dataflow/local-additional-taint/CollectionsTest.java
diff --git a/java/ql/test/library-tests/dataflow/local-additional-taint/ArraysTest.java b/java/ql/test/library-tests/dataflow/local-additional-taint/ArraysTest.java
index f016cb63fd3..45190248a45 100644
--- a/java/ql/test/library-tests/dataflow/local-additional-taint/ArraysTest.java
+++ b/java/ql/test/library-tests/dataflow/local-additional-taint/ArraysTest.java
@@ -3,17 +3,17 @@ import java.util.List;
class ArraysTest {
public static void taintSteps(String[] source) {
- Arrays.asList();
- Arrays.asList("one");
- Arrays.asList("two", "three");
- Arrays.copyOf(source, 10);
- Arrays.copyOfRange(source, 0, 10);
+
+
+
+
+
Arrays.deepToString(source);
- Arrays.spliterator(source);
- Arrays.stream(source);
+
+
Arrays.toString(source);
- Arrays.fill(source, "value");
- Arrays.fill(source, 0, 10, "data");
+
+
Arrays.parallelPrefix(source, (x, y) -> x + y);
Arrays.parallelPrefix(source, 0, 10, (x, y) -> x + y);
Arrays.parallelSetAll(source, x -> Integer.toString(x));
diff --git a/java/ql/test/library-tests/dataflow/local-additional-taint/CollectionsTest.java b/java/ql/test/library-tests/dataflow/local-additional-taint/CollectionsTest.java
deleted file mode 100644
index 7132e0051f0..00000000000
--- a/java/ql/test/library-tests/dataflow/local-additional-taint/CollectionsTest.java
+++ /dev/null
@@ -1,40 +0,0 @@
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.Set;
-import java.util.Map;
-
-class CollectionsTest {
- public static void taintSteps(List list, List other, Enumeration enumeration, Map map) {
- Collections.addAll(list);
- Collections.addAll(list, "one");
- Collections.addAll(list, "two", "three");
- Collections.addAll(list, new String[]{ "four" });
-
- Collections.checkedList(list, String.class);
- Collections.min(list);
- Collections.enumeration(list);
- Collections.list(enumeration);
- Collections.singletonMap("key", "value");
- Collections.copy(list, other);
- Collections.nCopies(10, "item");
- Collections.replaceAll(list, "search", "replace");
-
- List.of();
- java.util.List.of("a");
- List.of("b", "c");
- java.util.List.copyOf(list);
- Set.of();
- Set.of("d");
- Set.of("e" , "f");
- Set.copyOf(list);
- Map.of();
- Map.of("k", "v");
- Map.of("k1", "v1", "k2", "v2");
- Map.copyOf(map);
- Map.ofEntries();
- Map.ofEntries(Map.entry("k3", "v3"));
- Map.ofEntries(Map.entry("k4", "v4"), Map.entry("k5", "v5"));
- }
-}
-
diff --git a/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected b/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected
index 75b743dcaab..b55cd7f9a5c 100644
--- a/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected
+++ b/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected
@@ -1,15 +1,3 @@
-| ArraysTest.java:6:3:6:17 | new ..[] { .. } | ArraysTest.java:6:3:6:17 | asList(...) |
-| ArraysTest.java:7:3:7:22 | new ..[] { .. } | ArraysTest.java:7:3:7:22 | asList(...) |
-| ArraysTest.java:7:17:7:21 | "one" | ArraysTest.java:7:3:7:22 | new ..[] { .. } |
-| ArraysTest.java:8:3:8:31 | new ..[] { .. } | ArraysTest.java:8:3:8:31 | asList(...) |
-| ArraysTest.java:8:17:8:21 | "two" | ArraysTest.java:8:3:8:31 | new ..[] { .. } |
-| ArraysTest.java:8:24:8:30 | "three" | ArraysTest.java:8:3:8:31 | new ..[] { .. } |
-| ArraysTest.java:9:17:9:22 | source | ArraysTest.java:9:3:9:27 | copyOf(...) |
-| ArraysTest.java:10:22:10:27 | source | ArraysTest.java:10:3:10:35 | copyOfRange(...) |
-| ArraysTest.java:12:22:12:27 | source | ArraysTest.java:12:3:12:28 | spliterator(...) |
-| ArraysTest.java:13:17:13:22 | source | ArraysTest.java:13:3:13:23 | stream(...) |
-| ArraysTest.java:15:23:15:29 | "value" | ArraysTest.java:15:15:15:20 | source [post update] |
-| ArraysTest.java:16:30:16:35 | "data" | ArraysTest.java:16:15:16:20 | source [post update] |
| ArraysTest.java:17:43:17:43 | x | ArraysTest.java:17:43:17:47 | ... + ... |
| ArraysTest.java:17:47:17:47 | y | ArraysTest.java:17:43:17:47 | ... + ... |
| ArraysTest.java:18:50:18:50 | x | ArraysTest.java:18:50:18:54 | ... + ... |
@@ -18,43 +6,6 @@
| ArraysTest.java:19:55:19:55 | x | ArraysTest.java:19:38:19:56 | toString(...) |
| ArraysTest.java:20:30:20:36 | Integer | ArraysTest.java:20:30:20:48 | toString(...) |
| ArraysTest.java:20:47:20:47 | x | ArraysTest.java:20:30:20:48 | toString(...) |
-| CollectionsTest.java:9:3:9:26 | new ..[] { .. } | CollectionsTest.java:9:22:9:25 | list [post update] |
-| CollectionsTest.java:10:3:10:33 | new ..[] { .. } | CollectionsTest.java:10:22:10:25 | list [post update] |
-| CollectionsTest.java:10:28:10:32 | "one" | CollectionsTest.java:10:3:10:33 | new ..[] { .. } |
-| CollectionsTest.java:11:3:11:42 | new ..[] { .. } | CollectionsTest.java:11:22:11:25 | list [post update] |
-| CollectionsTest.java:11:28:11:32 | "two" | CollectionsTest.java:11:3:11:42 | new ..[] { .. } |
-| CollectionsTest.java:11:35:11:41 | "three" | CollectionsTest.java:11:3:11:42 | new ..[] { .. } |
-| CollectionsTest.java:12:28:12:49 | new String[] | CollectionsTest.java:12:22:12:25 | list [post update] |
-| CollectionsTest.java:12:42:12:47 | "four" | CollectionsTest.java:12:28:12:49 | {...} |
-| CollectionsTest.java:14:27:14:30 | list | CollectionsTest.java:14:3:14:45 | checkedList(...) |
-| CollectionsTest.java:15:19:15:22 | list | CollectionsTest.java:15:3:15:23 | min(...) |
-| CollectionsTest.java:16:27:16:30 | list | CollectionsTest.java:16:3:16:31 | enumeration(...) |
-| CollectionsTest.java:17:20:17:30 | enumeration | CollectionsTest.java:17:3:17:31 | list(...) |
-| CollectionsTest.java:18:35:18:41 | "value" | CollectionsTest.java:18:3:18:42 | singletonMap(...) |
-| CollectionsTest.java:19:26:19:30 | other | CollectionsTest.java:19:20:19:23 | list [post update] |
-| CollectionsTest.java:20:27:20:32 | "item" | CollectionsTest.java:20:3:20:33 | nCopies(...) |
-| CollectionsTest.java:21:42:21:50 | "replace" | CollectionsTest.java:21:26:21:29 | list [post update] |
-| CollectionsTest.java:24:21:24:23 | "a" | CollectionsTest.java:24:3:24:24 | of(...) |
-| CollectionsTest.java:25:11:25:13 | "b" | CollectionsTest.java:25:3:25:19 | of(...) |
-| CollectionsTest.java:25:16:25:18 | "c" | CollectionsTest.java:25:3:25:19 | of(...) |
-| CollectionsTest.java:26:25:26:28 | list | CollectionsTest.java:26:3:26:29 | copyOf(...) |
-| CollectionsTest.java:28:10:28:12 | "d" | CollectionsTest.java:28:3:28:13 | of(...) |
-| CollectionsTest.java:29:10:29:12 | "e" | CollectionsTest.java:29:3:29:19 | of(...) |
-| CollectionsTest.java:29:16:29:18 | "f" | CollectionsTest.java:29:3:29:19 | of(...) |
-| CollectionsTest.java:30:14:30:17 | list | CollectionsTest.java:30:3:30:18 | copyOf(...) |
-| CollectionsTest.java:32:15:32:17 | "v" | CollectionsTest.java:32:3:32:18 | of(...) |
-| CollectionsTest.java:33:16:33:19 | "v1" | CollectionsTest.java:33:3:33:32 | of(...) |
-| CollectionsTest.java:33:28:33:31 | "v2" | CollectionsTest.java:33:3:33:32 | of(...) |
-| CollectionsTest.java:34:14:34:16 | map | CollectionsTest.java:34:3:34:17 | copyOf(...) |
-| CollectionsTest.java:35:3:35:17 | new ..[] { .. } | CollectionsTest.java:35:3:35:17 | ofEntries(...) |
-| CollectionsTest.java:36:3:36:38 | new ..[] { .. } | CollectionsTest.java:36:3:36:38 | ofEntries(...) |
-| CollectionsTest.java:36:17:36:37 | entry(...) | CollectionsTest.java:36:3:36:38 | new ..[] { .. } |
-| CollectionsTest.java:36:33:36:36 | "v3" | CollectionsTest.java:36:17:36:37 | entry(...) |
-| CollectionsTest.java:37:3:37:61 | new ..[] { .. } | CollectionsTest.java:37:3:37:61 | ofEntries(...) |
-| CollectionsTest.java:37:17:37:37 | entry(...) | CollectionsTest.java:37:3:37:61 | new ..[] { .. } |
-| CollectionsTest.java:37:33:37:36 | "v4" | CollectionsTest.java:37:17:37:37 | entry(...) |
-| CollectionsTest.java:37:40:37:60 | entry(...) | CollectionsTest.java:37:3:37:61 | new ..[] { .. } |
-| CollectionsTest.java:37:56:37:59 | "v5" | CollectionsTest.java:37:40:37:60 | entry(...) |
| Test.java:24:32:24:38 | string2 | Test.java:24:17:24:39 | decode(...) |
| Test.java:25:46:25:51 | bytes2 | Test.java:25:31:25:52 | encode(...) |
| Test.java:27:34:27:40 | string2 | Test.java:27:13:27:41 | decode(...) |
From fc913e744e57fb16a54de16668278fa61dbe5f52 Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Thu, 20 May 2021 15:29:08 +0200
Subject: [PATCH 076/272] Java: Minor model fix.
---
.../code/java/frameworks/jackson/JacksonSerializability.qll | 1 +
1 file changed, 1 insertion(+)
diff --git a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll
index 6cda84512a0..618fc1d2710 100644
--- a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll
+++ b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll
@@ -280,6 +280,7 @@ private class JacksonModel extends SummaryModelCsv {
row =
[
"com.fasterxml.jackson.databind;ObjectMapper;true;valueToTree;;;Argument[0];ReturnValue;taint",
+ "com.fasterxml.jackson.databind;ObjectMapper;true;valueToTree;;;MapValue of Argument[0];ReturnValue;taint",
"com.fasterxml.jackson.databind;ObjectMapper;true;convertValue;;;Argument[0];ReturnValue;taint"
]
}
From 14f9a5c280b65e1e32868176cfe32a6cd2fe2490 Mon Sep 17 00:00:00 2001
From: Tom Hvitved
Date: Tue, 1 Jun 2021 13:08:42 +0200
Subject: [PATCH 077/272] Java: Move some CSV flow summary code into shared
library
---
.../code/java/dataflow/ExternalFlow.qll | 147 +++-------------
.../dataflow/internal/FlowSummaryImpl.qll | 162 ++++++++++--------
.../internal/FlowSummaryImplSpecific.qll | 26 +++
3 files changed, 143 insertions(+), 192 deletions(-)
diff --git a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll
index 33a146d07fe..57ab641483f 100644
--- a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll
+++ b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll
@@ -67,6 +67,7 @@
import java
private import semmle.code.java.dataflow.DataFlow::DataFlow
private import internal.DataFlowPrivate
+private import internal.FlowSummaryImpl::Private::External
private import FlowSummary
/**
@@ -498,10 +499,15 @@ module CsvValidation {
or
summaryModel(_, _, _, _, _, _, input, _, _) and pred = "summary"
|
- specSplit(input, part, _) and
- not part.regexpMatch("|ReturnValue|ArrayElement|Element|MapKey|MapValue") and
- not (part = "Argument" and pred = "sink") and
- not parseArg(part, _) and
+ (
+ invalidSpecComponent(input, part) and
+ not part = "" and
+ not (part = "Argument" and pred = "sink") and
+ not parseArg(part, _)
+ or
+ specSplit(input, part, _) and
+ parseParam(part, _)
+ ) and
msg = "Unrecognized input specification \"" + part + "\" in " + pred + " model."
)
or
@@ -510,11 +516,9 @@ module CsvValidation {
or
summaryModel(_, _, _, _, _, _, _, output, _) and pred = "summary"
|
- specSplit(output, part, _) and
- not part.regexpMatch("|ReturnValue|ArrayElement|Element|MapKey|MapValue") and
+ invalidSpecComponent(output, part) and
+ not part = "" and
not (part = ["Argument", "Parameter"] and pred = "source") and
- not parseArg(part, _) and
- not parseParam(part, _) and
msg = "Unrecognized output specification \"" + part + "\" in " + pred + " model."
)
or
@@ -624,7 +628,11 @@ private predicate sinkElement(Element e, string input, string kind) {
)
}
-private predicate summaryElement(Element e, string input, string output, string kind) {
+/**
+ * Holds if an external flow summary exists for `e` with input specification
+ * `input`, output specification `output`, and kind `kind`.
+ */
+predicate summaryElement(Element e, string input, string output, string kind) {
exists(
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
@@ -633,52 +641,14 @@ private predicate summaryElement(Element e, string input, string output, string
)
}
-private string inOutSpec() {
+/** Gets a specification used in a source model, sink model, or summary model. */
+string inOutSpec() {
sourceModel(_, _, _, _, _, _, result, _) or
sinkModel(_, _, _, _, _, _, result, _) or
summaryModel(_, _, _, _, _, _, result, _, _) or
summaryModel(_, _, _, _, _, _, _, result, _)
}
-private predicate specSplit(string s, string c, int n) {
- inOutSpec() = s and s.splitAt(" of ", n) = c
-}
-
-private predicate len(string s, int len) { len = 1 + max(int n | specSplit(s, _, n)) }
-
-private string getLast(string s) {
- exists(int len |
- len(s, len) and
- specSplit(s, result, len - 1)
- )
-}
-
-private predicate parseParam(string c, int n) {
- specSplit(_, c, _) and
- (
- c.regexpCapture("Parameter\\[([-0-9]+)\\]", 1).toInt() = n
- or
- exists(int n1, int n2 |
- c.regexpCapture("Parameter\\[([-0-9]+)\\.\\.([0-9]+)\\]", 1).toInt() = n1 and
- c.regexpCapture("Parameter\\[([-0-9]+)\\.\\.([0-9]+)\\]", 2).toInt() = n2 and
- n = [n1 .. n2]
- )
- )
-}
-
-private predicate parseArg(string c, int n) {
- specSplit(_, c, _) and
- (
- c.regexpCapture("Argument\\[([-0-9]+)\\]", 1).toInt() = n
- or
- exists(int n1, int n2 |
- c.regexpCapture("Argument\\[([-0-9]+)\\.\\.([0-9]+)\\]", 1).toInt() = n1 and
- c.regexpCapture("Argument\\[([-0-9]+)\\.\\.([0-9]+)\\]", 2).toInt() = n2 and
- n = [n1 .. n2]
- )
- )
-}
-
private predicate inputNeedsReference(string c) {
c = "Argument" or
parseArg(c, _)
@@ -693,7 +663,7 @@ private predicate outputNeedsReference(string c) {
private predicate sourceElementRef(Top ref, string output, string kind) {
exists(Element e |
sourceElement(e, output, kind) and
- if outputNeedsReference(getLast(output))
+ if outputNeedsReference(specLast(output))
then ref.(Call).getCallee().getSourceDeclaration() = e
else ref = e
)
@@ -702,7 +672,7 @@ private predicate sourceElementRef(Top ref, string output, string kind) {
private predicate sinkElementRef(Top ref, string input, string kind) {
exists(Element e |
sinkElement(e, input, kind) and
- if inputNeedsReference(getLast(input))
+ if inputNeedsReference(specLast(input))
then ref.(Call).getCallee().getSourceDeclaration() = e
else ref = e
)
@@ -711,81 +681,12 @@ private predicate sinkElementRef(Top ref, string input, string kind) {
private predicate summaryElementRef(Top ref, string input, string output, string kind) {
exists(Element e |
summaryElement(e, input, output, kind) and
- if inputNeedsReference(getLast(input))
+ if inputNeedsReference(specLast(input))
then ref.(Call).getCallee().getSourceDeclaration() = e
else ref = e
)
}
-private SummaryComponent interpretComponent(string c) {
- specSplit(_, c, _) and
- (
- exists(int pos | parseArg(c, pos) and result = SummaryComponent::argument(pos))
- or
- exists(int pos | parseParam(c, pos) and result = SummaryComponent::parameter(pos))
- or
- c = "ReturnValue" and result = SummaryComponent::return()
- or
- c = "ArrayElement" and result = SummaryComponent::content(any(ArrayContent c0))
- or
- c = "Element" and result = SummaryComponent::content(any(CollectionContent c0))
- or
- c = "MapKey" and result = SummaryComponent::content(any(MapKeyContent c0))
- or
- c = "MapValue" and result = SummaryComponent::content(any(MapValueContent c0))
- )
-}
-
-private predicate interpretSpec(string spec, int idx, SummaryComponentStack stack) {
- exists(string c |
- summaryElement(_, spec, _, _) or
- summaryElement(_, _, spec, _)
- |
- len(spec, idx + 1) and
- specSplit(spec, c, idx) and
- stack = SummaryComponentStack::singleton(interpretComponent(c))
- )
- or
- exists(SummaryComponent head, SummaryComponentStack tail |
- interpretSpec(spec, idx, head, tail) and
- stack = SummaryComponentStack::push(head, tail)
- )
-}
-
-private predicate interpretSpec(
- string output, int idx, SummaryComponent head, SummaryComponentStack tail
-) {
- exists(string c |
- interpretSpec(output, idx + 1, tail) and
- specSplit(output, c, idx) and
- head = interpretComponent(c)
- )
-}
-
-private class MkStack extends RequiredSummaryComponentStack {
- MkStack() { interpretSpec(_, _, _, this) }
-
- override predicate required(SummaryComponent c) { interpretSpec(_, _, c, this) }
-}
-
-private class SummarizedCallableExternal extends SummarizedCallable {
- SummarizedCallableExternal() { summaryElement(this, _, _, _) }
-
- override predicate propagatesFlow(
- SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
- ) {
- exists(string inSpec, string outSpec, string kind |
- summaryElement(this, inSpec, outSpec, kind) and
- interpretSpec(inSpec, 0, input) and
- interpretSpec(outSpec, 0, output)
- |
- kind = "value" and preservesValue = true
- or
- kind = "taint" and preservesValue = false
- )
- }
-}
-
private newtype TAstOrNode =
TAst(Top t) or
TNode(Node n)
@@ -795,7 +696,7 @@ private predicate interpretOutput(string output, int idx, Top ref, TAstOrNode no
sourceElementRef(ref, output, _) or
summaryElementRef(ref, _, output, _)
) and
- len(output, idx) and
+ specLength(output, idx) and
node = TAst(ref)
or
exists(Top mid, string c, Node n |
@@ -827,7 +728,7 @@ private predicate interpretInput(string input, int idx, Top ref, TAstOrNode node
sinkElementRef(ref, input, _) or
summaryElementRef(ref, input, _, _)
) and
- len(input, idx) and
+ specLength(input, idx) and
node = TAst(ref)
or
exists(Top mid, string c, Node n |
diff --git a/java/ql/src/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll b/java/ql/src/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll
index bdf5498d8c6..da6f89071ef 100644
--- a/java/ql/src/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll
+++ b/java/ql/src/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll
@@ -580,88 +580,112 @@ module Private {
* summaries into a `SummarizedCallable`s.
*/
module External {
- /**
- * Provides a means of translating an externally (e.g., CSV) defined flow
- * summary into a `SummarizedCallable`.
- */
- abstract class ExternalSummaryCompilation extends string {
- bindingset[this]
- ExternalSummaryCompilation() { any() }
+ /** Holds if the `n`th component of specification `s` is `c`. */
+ predicate specSplit(string s, string c, int n) { relevantSpec(s) and s.splitAt(" of ", n) = c }
- /** Holds if this flow summary is for callable `c`. */
- abstract predicate callable(DataFlowCallable c, boolean preservesValue);
+ /** Holds if specification `s` has length `len`. */
+ predicate specLength(string s, int len) { len = 1 + max(int n | specSplit(s, _, n)) }
- /** Holds if the `i`th input component is `c`. */
- abstract predicate input(int i, SummaryComponent c);
-
- /** Holds if the `i`th output component is `c`. */
- abstract predicate output(int i, SummaryComponent c);
-
- /**
- * Holds if the input components starting from index `i` translate into `suffix`.
- */
- final predicate translateInput(int i, SummaryComponentStack suffix) {
- exists(SummaryComponent comp | this.input(i, comp) |
- i = max(int j | this.input(j, _)) and
- suffix = TSingletonSummaryComponentStack(comp)
- or
- exists(TSummaryComponent head, SummaryComponentStack tail |
- this.translateInputCons(i, head, tail) and
- suffix = TConsSummaryComponentStack(head, tail)
- )
- )
- }
-
- final predicate translateInputCons(int i, SummaryComponent head, SummaryComponentStack tail) {
- this.input(i, head) and
- this.translateInput(i + 1, tail)
- }
-
- /**
- * Holds if the output components starting from index `i` translate into `suffix`.
- */
- predicate translateOutput(int i, SummaryComponentStack suffix) {
- exists(SummaryComponent comp | this.output(i, comp) |
- i = max(int j | this.output(j, _)) and
- suffix = TSingletonSummaryComponentStack(comp)
- or
- exists(TSummaryComponent head, SummaryComponentStack tail |
- this.translateOutputCons(i, head, tail) and
- suffix = TConsSummaryComponentStack(head, tail)
- )
- )
- }
-
- predicate translateOutputCons(int i, SummaryComponent head, SummaryComponentStack tail) {
- this.output(i, head) and
- this.translateOutput(i + 1, tail)
- }
+ /** Gets the last component of specification `s`. */
+ string specLast(string s) {
+ exists(int len |
+ specLength(s, len) and
+ specSplit(s, result, len - 1)
+ )
}
- private class ExternalRequiredSummaryComponentStack extends RequiredSummaryComponentStack {
- private SummaryComponent head;
-
- ExternalRequiredSummaryComponentStack() {
- any(ExternalSummaryCompilation s).translateInputCons(_, head, this) or
- any(ExternalSummaryCompilation s).translateOutputCons(_, head, this)
- }
-
- override predicate required(SummaryComponent c) { c = head }
+ /** Holds if specification component `c` parses as parameter `n`. */
+ predicate parseParam(string c, int n) {
+ specSplit(_, c, _) and
+ (
+ c.regexpCapture("Parameter\\[([-0-9]+)\\]", 1).toInt() = n
+ or
+ exists(int n1, int n2 |
+ c.regexpCapture("Parameter\\[([-0-9]+)\\.\\.([0-9]+)\\]", 1).toInt() = n1 and
+ c.regexpCapture("Parameter\\[([-0-9]+)\\.\\.([0-9]+)\\]", 2).toInt() = n2 and
+ n = [n1 .. n2]
+ )
+ )
}
- class ExternalSummarizedCallableAdaptor extends SummarizedCallable {
- ExternalSummarizedCallableAdaptor() { any(ExternalSummaryCompilation s).callable(this, _) }
+ /** Holds if specification component `c` parses as argument `n`. */
+ predicate parseArg(string c, int n) {
+ specSplit(_, c, _) and
+ (
+ c.regexpCapture("Argument\\[([-0-9]+)\\]", 1).toInt() = n
+ or
+ exists(int n1, int n2 |
+ c.regexpCapture("Argument\\[([-0-9]+)\\.\\.([0-9]+)\\]", 1).toInt() = n1 and
+ c.regexpCapture("Argument\\[([-0-9]+)\\.\\.([0-9]+)\\]", 2).toInt() = n2 and
+ n = [n1 .. n2]
+ )
+ )
+ }
+
+ private SummaryComponent interpretComponent(string c) {
+ specSplit(_, c, _) and
+ (
+ exists(int pos | parseArg(c, pos) and result = SummaryComponent::argument(pos))
+ or
+ exists(int pos | parseParam(c, pos) and result = SummaryComponent::parameter(pos))
+ or
+ result = interpretComponentSpecific(c)
+ )
+ }
+
+ private predicate interpretSpec(string spec, int idx, SummaryComponentStack stack) {
+ exists(string c |
+ relevantSpec(spec) and
+ specLength(spec, idx + 1) and
+ specSplit(spec, c, idx) and
+ stack = SummaryComponentStack::singleton(interpretComponent(c))
+ )
+ or
+ exists(SummaryComponent head, SummaryComponentStack tail |
+ interpretSpec(spec, idx, head, tail) and
+ stack = SummaryComponentStack::push(head, tail)
+ )
+ }
+
+ private predicate interpretSpec(
+ string output, int idx, SummaryComponent head, SummaryComponentStack tail
+ ) {
+ exists(string c |
+ interpretSpec(output, idx + 1, tail) and
+ specSplit(output, c, idx) and
+ head = interpretComponent(c)
+ )
+ }
+
+ private class MkStack extends RequiredSummaryComponentStack {
+ MkStack() { interpretSpec(_, _, _, this) }
+
+ override predicate required(SummaryComponent c) { interpretSpec(_, _, c, this) }
+ }
+
+ private class SummarizedCallableExternal extends SummarizedCallable {
+ SummarizedCallableExternal() { externalSummary(this, _, _, _) }
override predicate propagatesFlow(
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
) {
- exists(ExternalSummaryCompilation s |
- s.callable(this, preservesValue) and
- s.translateInput(0, input) and
- s.translateOutput(0, output)
+ exists(string inSpec, string outSpec, string kind |
+ externalSummary(this, inSpec, outSpec, kind) and
+ interpretSpec(inSpec, 0, input) and
+ interpretSpec(outSpec, 0, output)
+ |
+ kind = "value" and preservesValue = true
+ or
+ kind = "taint" and preservesValue = false
)
}
}
+
+ /** Holds if component `c` of specification `spec` cannot be parsed. */
+ predicate invalidSpecComponent(string spec, string c) {
+ specSplit(spec, c, _) and
+ not exists(interpretComponent(c))
+ }
}
/** Provides a query predicate for outputting a set of relevant flow summaries. */
diff --git a/java/ql/src/semmle/code/java/dataflow/internal/FlowSummaryImplSpecific.qll b/java/ql/src/semmle/code/java/dataflow/internal/FlowSummaryImplSpecific.qll
index dae0571f0fa..327ddfb50dd 100644
--- a/java/ql/src/semmle/code/java/dataflow/internal/FlowSummaryImplSpecific.qll
+++ b/java/ql/src/semmle/code/java/dataflow/internal/FlowSummaryImplSpecific.qll
@@ -7,6 +7,7 @@ private import DataFlowPrivate
private import DataFlowUtil
private import FlowSummaryImpl::Private
private import FlowSummaryImpl::Public
+private import semmle.code.java.dataflow.ExternalFlow
private module FlowSummaries {
private import semmle.code.java.dataflow.FlowSummary as F
@@ -49,3 +50,28 @@ DataFlowType getCallbackParameterType(DataFlowType t, int i) { none() }
* callback of type `t`.
*/
DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk) { none() }
+
+/** Holds if `spec` is a relevant external specification. */
+predicate relevantSpec(string spec) { spec = inOutSpec() }
+
+/**
+ * Holds if an external flow summary exists for `c` with input specification
+ * `input`, output specification `output`, and kind `kind`.
+ */
+predicate externalSummary(DataFlowCallable c, string input, string output, string kind) {
+ summaryElement(c, input, output, kind)
+}
+
+/** Gets the summary component for specification component `c`, if any. */
+bindingset[c]
+SummaryComponent interpretComponentSpecific(string c) {
+ c = "ReturnValue" and result = SummaryComponent::return(_)
+ or
+ c = "ArrayElement" and result = SummaryComponent::content(any(ArrayContent c0))
+ or
+ c = "Element" and result = SummaryComponent::content(any(CollectionContent c0))
+ or
+ c = "MapKey" and result = SummaryComponent::content(any(MapKeyContent c0))
+ or
+ c = "MapValue" and result = SummaryComponent::content(any(MapValueContent c0))
+}
From ecf7f24cde1a3b721f006733e6a5f57878c55f1e Mon Sep 17 00:00:00 2001
From: Tom Hvitved
Date: Tue, 1 Jun 2021 13:09:27 +0200
Subject: [PATCH 078/272] C#: Sync latest `FlowSummaryImpl.qll` changes
---
.../dataflow/internal/FlowSummaryImpl.qll | 162 ++++++++++--------
.../internal/FlowSummaryImplSpecific.qll | 27 +++
2 files changed, 120 insertions(+), 69 deletions(-)
diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll
index bdf5498d8c6..da6f89071ef 100644
--- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll
+++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll
@@ -580,88 +580,112 @@ module Private {
* summaries into a `SummarizedCallable`s.
*/
module External {
- /**
- * Provides a means of translating an externally (e.g., CSV) defined flow
- * summary into a `SummarizedCallable`.
- */
- abstract class ExternalSummaryCompilation extends string {
- bindingset[this]
- ExternalSummaryCompilation() { any() }
+ /** Holds if the `n`th component of specification `s` is `c`. */
+ predicate specSplit(string s, string c, int n) { relevantSpec(s) and s.splitAt(" of ", n) = c }
- /** Holds if this flow summary is for callable `c`. */
- abstract predicate callable(DataFlowCallable c, boolean preservesValue);
+ /** Holds if specification `s` has length `len`. */
+ predicate specLength(string s, int len) { len = 1 + max(int n | specSplit(s, _, n)) }
- /** Holds if the `i`th input component is `c`. */
- abstract predicate input(int i, SummaryComponent c);
-
- /** Holds if the `i`th output component is `c`. */
- abstract predicate output(int i, SummaryComponent c);
-
- /**
- * Holds if the input components starting from index `i` translate into `suffix`.
- */
- final predicate translateInput(int i, SummaryComponentStack suffix) {
- exists(SummaryComponent comp | this.input(i, comp) |
- i = max(int j | this.input(j, _)) and
- suffix = TSingletonSummaryComponentStack(comp)
- or
- exists(TSummaryComponent head, SummaryComponentStack tail |
- this.translateInputCons(i, head, tail) and
- suffix = TConsSummaryComponentStack(head, tail)
- )
- )
- }
-
- final predicate translateInputCons(int i, SummaryComponent head, SummaryComponentStack tail) {
- this.input(i, head) and
- this.translateInput(i + 1, tail)
- }
-
- /**
- * Holds if the output components starting from index `i` translate into `suffix`.
- */
- predicate translateOutput(int i, SummaryComponentStack suffix) {
- exists(SummaryComponent comp | this.output(i, comp) |
- i = max(int j | this.output(j, _)) and
- suffix = TSingletonSummaryComponentStack(comp)
- or
- exists(TSummaryComponent head, SummaryComponentStack tail |
- this.translateOutputCons(i, head, tail) and
- suffix = TConsSummaryComponentStack(head, tail)
- )
- )
- }
-
- predicate translateOutputCons(int i, SummaryComponent head, SummaryComponentStack tail) {
- this.output(i, head) and
- this.translateOutput(i + 1, tail)
- }
+ /** Gets the last component of specification `s`. */
+ string specLast(string s) {
+ exists(int len |
+ specLength(s, len) and
+ specSplit(s, result, len - 1)
+ )
}
- private class ExternalRequiredSummaryComponentStack extends RequiredSummaryComponentStack {
- private SummaryComponent head;
-
- ExternalRequiredSummaryComponentStack() {
- any(ExternalSummaryCompilation s).translateInputCons(_, head, this) or
- any(ExternalSummaryCompilation s).translateOutputCons(_, head, this)
- }
-
- override predicate required(SummaryComponent c) { c = head }
+ /** Holds if specification component `c` parses as parameter `n`. */
+ predicate parseParam(string c, int n) {
+ specSplit(_, c, _) and
+ (
+ c.regexpCapture("Parameter\\[([-0-9]+)\\]", 1).toInt() = n
+ or
+ exists(int n1, int n2 |
+ c.regexpCapture("Parameter\\[([-0-9]+)\\.\\.([0-9]+)\\]", 1).toInt() = n1 and
+ c.regexpCapture("Parameter\\[([-0-9]+)\\.\\.([0-9]+)\\]", 2).toInt() = n2 and
+ n = [n1 .. n2]
+ )
+ )
}
- class ExternalSummarizedCallableAdaptor extends SummarizedCallable {
- ExternalSummarizedCallableAdaptor() { any(ExternalSummaryCompilation s).callable(this, _) }
+ /** Holds if specification component `c` parses as argument `n`. */
+ predicate parseArg(string c, int n) {
+ specSplit(_, c, _) and
+ (
+ c.regexpCapture("Argument\\[([-0-9]+)\\]", 1).toInt() = n
+ or
+ exists(int n1, int n2 |
+ c.regexpCapture("Argument\\[([-0-9]+)\\.\\.([0-9]+)\\]", 1).toInt() = n1 and
+ c.regexpCapture("Argument\\[([-0-9]+)\\.\\.([0-9]+)\\]", 2).toInt() = n2 and
+ n = [n1 .. n2]
+ )
+ )
+ }
+
+ private SummaryComponent interpretComponent(string c) {
+ specSplit(_, c, _) and
+ (
+ exists(int pos | parseArg(c, pos) and result = SummaryComponent::argument(pos))
+ or
+ exists(int pos | parseParam(c, pos) and result = SummaryComponent::parameter(pos))
+ or
+ result = interpretComponentSpecific(c)
+ )
+ }
+
+ private predicate interpretSpec(string spec, int idx, SummaryComponentStack stack) {
+ exists(string c |
+ relevantSpec(spec) and
+ specLength(spec, idx + 1) and
+ specSplit(spec, c, idx) and
+ stack = SummaryComponentStack::singleton(interpretComponent(c))
+ )
+ or
+ exists(SummaryComponent head, SummaryComponentStack tail |
+ interpretSpec(spec, idx, head, tail) and
+ stack = SummaryComponentStack::push(head, tail)
+ )
+ }
+
+ private predicate interpretSpec(
+ string output, int idx, SummaryComponent head, SummaryComponentStack tail
+ ) {
+ exists(string c |
+ interpretSpec(output, idx + 1, tail) and
+ specSplit(output, c, idx) and
+ head = interpretComponent(c)
+ )
+ }
+
+ private class MkStack extends RequiredSummaryComponentStack {
+ MkStack() { interpretSpec(_, _, _, this) }
+
+ override predicate required(SummaryComponent c) { interpretSpec(_, _, c, this) }
+ }
+
+ private class SummarizedCallableExternal extends SummarizedCallable {
+ SummarizedCallableExternal() { externalSummary(this, _, _, _) }
override predicate propagatesFlow(
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
) {
- exists(ExternalSummaryCompilation s |
- s.callable(this, preservesValue) and
- s.translateInput(0, input) and
- s.translateOutput(0, output)
+ exists(string inSpec, string outSpec, string kind |
+ externalSummary(this, inSpec, outSpec, kind) and
+ interpretSpec(inSpec, 0, input) and
+ interpretSpec(outSpec, 0, output)
+ |
+ kind = "value" and preservesValue = true
+ or
+ kind = "taint" and preservesValue = false
)
}
}
+
+ /** Holds if component `c` of specification `spec` cannot be parsed. */
+ predicate invalidSpecComponent(string spec, string c) {
+ specSplit(spec, c, _) and
+ not exists(interpretComponent(c))
+ }
}
/** Provides a query predicate for outputting a set of relevant flow summaries. */
diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/FlowSummaryImplSpecific.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/FlowSummaryImplSpecific.qll
index 01e3a2e1633..5568b8d3368 100644
--- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/FlowSummaryImplSpecific.qll
+++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/FlowSummaryImplSpecific.qll
@@ -77,3 +77,30 @@ DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk) {
result = Gvn::getGlobalValueNumber(dt.getDelegateType().getReturnType())
)
}
+
+/** Holds if `spec` is a relevant external specification. */
+predicate relevantSpec(string spec) { none() }
+
+/**
+ * Holds if an external flow summary exists for `c` with input specification
+ * `input`, output specification `output`, and kind `kind`.
+ */
+predicate externalSummary(DataFlowCallable c, string input, string output, string kind) { none() }
+
+/** Gets the summary component for specification component `c`, if any. */
+bindingset[c]
+SummaryComponent interpretComponentSpecific(string c) {
+ c = "ReturnValue" and result = SummaryComponent::return(any(NormalReturnKind nrk))
+ or
+ c = "Element" and result = SummaryComponent::content(any(ElementContent ec))
+ or
+ exists(Field f |
+ c.regexpCapture("Field\\[(.+)\\]", 1) = f.getQualifiedName() and
+ result = SummaryComponent::content(any(FieldContent fc | fc.getField() = f))
+ )
+ or
+ exists(Property p |
+ c.regexpCapture("Property\\[(.+)\\]", 1) = p.getQualifiedName() and
+ result = SummaryComponent::content(any(PropertyContent pc | pc.getProperty() = p))
+ )
+}
From 0fb692400cff3f22a2929f673da18b4b91379b00 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alvaro=20Mun=CC=83oz?=
Date: Tue, 1 Jun 2021 13:57:13 +0200
Subject: [PATCH 079/272] fix failing test
---
.../Security/CWE/CWE-209/StackTraceExposure.ql | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql b/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql
index b82edc2efc6..4f2753b9723 100644
--- a/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql
+++ b/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql
@@ -109,7 +109,7 @@ predicate printsStackExternally(MethodAccess call, Expr stackTrace) {
/**
* A stringified stack trace flows to an external sink.
*/
-predicate stringifiedStackFlowsExternally(XssSink externalExpr, Expr stackTrace) {
+predicate stringifiedStackFlowsExternally(DataFlow::Node externalExpr, Expr stackTrace) {
exists(MethodAccess stackTraceString, StackTraceStringToHTTPResponseSinkFlowConfig conf |
stackTraceExpr(stackTrace, stackTraceString) and
conf.hasFlow(DataFlow::exprNode(stackTraceString), externalExpr)
@@ -127,21 +127,24 @@ class GetMessageFlowSource extends MethodAccess {
}
}
-class GetMessageFlowSourceToXssSinkFlowConfig extends TaintTracking::Configuration {
- GetMessageFlowSourceToXssSinkFlowConfig() {
- this = "StackTraceExposure::GetMessageFlowSourceToXssSinkFlowConfig"
+class GetMessageFlowSourceToHTTPResponseSinkFlowConfig extends TaintTracking::Configuration {
+ GetMessageFlowSourceToHTTPResponseSinkFlowConfig() {
+ this = "StackTraceExposure::GetMessageFlowSourceToHTTPResponseSinkFlowConfig"
}
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof GetMessageFlowSource }
- override predicate isSink(DataFlow::Node sink) { sink instanceof XssSink }
+ override predicate isSink(DataFlow::Node sink) {
+ sink instanceof XssSink or
+ sink instanceof InformationLeakSink
+ }
}
/**
* A call to `getMessage()` that then flows to a servlet response.
*/
-predicate getMessageFlowsExternally(XssSink externalExpr, GetMessageFlowSource getMessage) {
- any(GetMessageFlowSourceToXssSinkFlowConfig conf)
+predicate getMessageFlowsExternally(DataFlow::Node externalExpr, GetMessageFlowSource getMessage) {
+ any(GetMessageFlowSourceToHTTPResponseSinkFlowConfig conf)
.hasFlow(DataFlow::exprNode(getMessage), externalExpr)
}
From 1c081eeaedb27332623e67231bf21c1f5d8a62d5 Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Tue, 1 Jun 2021 13:51:16 +0200
Subject: [PATCH 080/272] Java: Update coverage.
---
.../library-coverage/flow-model-coverage.csv | 18 +++++++++---------
.../library-coverage/flow-model-coverage.rst | 10 +++++-----
2 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/java/documentation/library-coverage/flow-model-coverage.csv b/java/documentation/library-coverage/flow-model-coverage.csv
index cf97071ad92..a66f7880221 100644
--- a/java/documentation/library-coverage/flow-model-coverage.csv
+++ b/java/documentation/library-coverage/flow-model-coverage.csv
@@ -3,16 +3,16 @@ android.util,,16,,,,,,,,,,,16,,
android.webkit,3,2,,,,,,,,,,3,2,,
com.esotericsoftware.kryo.io,,,1,,,,,,,,,,,1,
com.esotericsoftware.kryo5.io,,,1,,,,,,,,,,,1,
-com.fasterxml.jackson.databind,,,2,,,,,,,,,,,2,
-com.google.common.base,,,28,,,,,,,,,,,22,6
-com.google.common.io,6,,69,,,,,,,6,,,,68,1
+com.fasterxml.jackson.databind,,,3,,,,,,,,,,,3,
+com.google.common.base,,,34,,,,,,,,,,,28,6
+com.google.common.io,6,,73,,,,,,,6,,,,72,1
com.unboundid.ldap.sdk,17,,,,,,17,,,,,,,,
java.beans,,,1,,,,,,,,,,,1,
java.io,3,,20,,3,,,,,,,,,20,
-java.lang,,,1,,,,,,,,,,,1,
+java.lang,,,3,,,,,,,,,,,1,2
java.net,2,3,4,,,,,2,,,,,3,4,
java.nio,10,,2,,10,,,,,,,,,2,
-java.util,,,13,,,,,,,,,,,13,
+java.util,,,283,,,,,,,,,,,15,268
javax.naming.directory,1,,,,,,1,,,,,,,,
javax.net.ssl,2,,,,,,,,2,,,,,,
javax.servlet,4,21,2,,,3,,,,,,1,21,2,
@@ -23,14 +23,14 @@ javax.xml.transform.stream,,,2,,,,,,,,,,,2,
javax.xml.xpath,3,,,,,,,,,,3,,,,
org.apache.commons.codec,,,2,,,,,,,,,,,2,
org.apache.commons.io,,,22,,,,,,,,,,,22,
-org.apache.commons.lang3,,,313,,,,,,,,,,,299,14
-org.apache.commons.text,,,203,,,,,,,,,,,203,
+org.apache.commons.lang3,,,327,,,,,,,,,,,311,16
+org.apache.commons.text,,,220,,,,,,,,,,,220,
org.apache.directory.ldap.client.api,1,,,,,,1,,,,,,,,
org.apache.hc.core5.function,,,1,,,,,,,,,,,1,
org.apache.hc.core5.http,1,2,39,,,,,,,,,1,2,39,
org.apache.hc.core5.net,,,2,,,,,,,,,,,2,
-org.apache.hc.core5.util,,,22,,,,,,,,,,,18,4
-org.apache.http,2,3,66,,,,,,,,,2,3,59,7
+org.apache.hc.core5.util,,,24,,,,,,,,,,,18,6
+org.apache.http,2,3,67,,,,,,,,,2,3,59,8
org.dom4j,20,,,,,,,,,,20,,,,
org.springframework.ldap.core,14,,,,,,14,,,,,,,,
org.springframework.security.web.savedrequest,,6,,,,,,,,,,,6,,
diff --git a/java/documentation/library-coverage/flow-model-coverage.rst b/java/documentation/library-coverage/flow-model-coverage.rst
index 8bb20a9b6c3..6e46c906b07 100644
--- a/java/documentation/library-coverage/flow-model-coverage.rst
+++ b/java/documentation/library-coverage/flow-model-coverage.rst
@@ -8,12 +8,12 @@ Java framework & library support
Framework / library,Package,Remote flow sources,Taint & value steps,Sinks (total),`CWE‑022` :sub:`Path injection`,`CWE‑036` :sub:`Path traversal`,`CWE‑079` :sub:`Cross-site scripting`,`CWE‑089` :sub:`SQL injection`,`CWE‑090` :sub:`LDAP injection`,`CWE‑094` :sub:`Code injection`,`CWE‑319` :sub:`Cleartext transmission`
Android,``android.*``,18,,3,,,3,,,,
- Apache,``org.apache.*``,5,648,4,,,3,,1,,
+ Apache,``org.apache.*``,5,682,4,,,3,,1,,
`Apache Commons IO `_,``org.apache.commons.io``,,22,,,,,,,,
- Google,``com.google.common.*``,,97,6,,6,,,,,
- Java Standard Library,``java.*``,3,41,15,13,,,,,,2
+ Google,``com.google.common.*``,,107,6,,6,,,,,
+ Java Standard Library,``java.*``,3,313,15,13,,,,,,2
Java extensions,``javax.*``,22,8,12,,,1,,1,1,
`Spring `_,``org.springframework.*``,29,,14,,,,,14,,
- Others,"``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.databind``, ``com.unboundid.ldap.sdk``, ``org.dom4j``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.mvc``",7,5,37,,,,,17,,
- Totals,,84,821,91,13,6,7,,33,1,2
+ Others,"``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.databind``, ``com.unboundid.ldap.sdk``, ``org.dom4j``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.mvc``",7,6,37,,,,,17,,
+ Totals,,84,1138,91,13,6,7,,33,1,2
From 922b421a45388be29efdb5d91b2031add1133df4 Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Tue, 1 Jun 2021 14:33:52 +0200
Subject: [PATCH 081/272] Java: Add change note.
---
java/change-notes/2021-06-01-collection-flow.md | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 java/change-notes/2021-06-01-collection-flow.md
diff --git a/java/change-notes/2021-06-01-collection-flow.md b/java/change-notes/2021-06-01-collection-flow.md
new file mode 100644
index 00000000000..5da2e78d1df
--- /dev/null
+++ b/java/change-notes/2021-06-01-collection-flow.md
@@ -0,0 +1,5 @@
+lgtm,codescanning
+* Data flow now tracks steps through collections and arrays more precisely.
+ That means that collection and array read steps are now matched up with
+ preceding store steps. This results in increased precision for all flow-based
+ queries, in particular most of the security queries.
From 970b4e7d6a49a95f6d6a5577cd7ac315065f5553 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alvaro=20Mun=CC=83oz?=
Date: Tue, 1 Jun 2021 14:54:31 +0200
Subject: [PATCH 082/272] update java library coverage documentation
---
.../library-coverage/flow-model-coverage.csv | 84 +++++++++----------
.../library-coverage/flow-model-coverage.rst | 4 +-
2 files changed, 44 insertions(+), 44 deletions(-)
diff --git a/java/documentation/library-coverage/flow-model-coverage.csv b/java/documentation/library-coverage/flow-model-coverage.csv
index 73f06306193..464228bb022 100644
--- a/java/documentation/library-coverage/flow-model-coverage.csv
+++ b/java/documentation/library-coverage/flow-model-coverage.csv
@@ -1,42 +1,42 @@
-package,sink,source,summary,sink:bean-validation,sink:create-file,sink:header-splitting,sink:ldap,sink:open-url,sink:set-hostname-verifier,sink:url-open-stream,sink:xpath,sink:xss,source:remote,summary:taint,summary:value
-android.util,,16,,,,,,,,,,,16,,
-android.webkit,3,2,,,,,,,,,,3,2,,
-com.esotericsoftware.kryo.io,,,1,,,,,,,,,,,1,
-com.esotericsoftware.kryo5.io,,,1,,,,,,,,,,,1,
-com.fasterxml.jackson.databind,,,2,,,,,,,,,,,2,
-com.google.common.base,,,28,,,,,,,,,,,22,6
-com.google.common.io,6,,69,,,,,,,6,,,,68,1
-com.unboundid.ldap.sdk,17,,,,,,17,,,,,,,,
-java.beans,,,1,,,,,,,,,,,1,
-java.io,3,,20,,3,,,,,,,,,20,
-java.lang,,,1,,,,,,,,,,,1,
-java.net,2,3,4,,,,,2,,,,,3,4,
-java.nio,10,,2,,10,,,,,,,,,2,
-java.util,,,13,,,,,,,,,,,13,
-javax.naming.directory,1,,,,,,1,,,,,,,,
-javax.net.ssl,2,,,,,,,,2,,,,,,
-javax.servlet,3,21,2,,,3,,,,,,,21,2,
-javax.validation,1,1,,1,,,,,,,,,1,,
-javax.ws.rs.core,1,,,,,1,,,,,,,,,
-javax.xml.transform.sax,,,4,,,,,,,,,,,4,
-javax.xml.transform.stream,,,2,,,,,,,,,,,2,
-javax.xml.xpath,3,,,,,,,,,,3,,,,
-org.apache.commons.codec,,,2,,,,,,,,,,,2,
-org.apache.commons.io,,,22,,,,,,,,,,,22,
-org.apache.commons.lang3,,,313,,,,,,,,,,,299,14
-org.apache.commons.text,,,203,,,,,,,,,,,203,
-org.apache.directory.ldap.client.api,1,,,,,,1,,,,,,,,
-org.apache.hc.core5.function,,,1,,,,,,,,,,,1,
-org.apache.hc.core5.http,1,2,39,,,,,,,,,1,2,39,
-org.apache.hc.core5.net,,,2,,,,,,,,,,,2,
-org.apache.hc.core5.util,,,22,,,,,,,,,,,18,4
-org.apache.http,2,3,66,,,,,,,,,2,3,59,7
-org.dom4j,20,,,,,,,,,,20,,,,
-org.springframework.ldap.core,14,,,,,,14,,,,,,,,
-org.springframework.security.web.savedrequest,,6,,,,,,,,,,,6,,
-org.springframework.web.client,,3,,,,,,,,,,,3,,
-org.springframework.web.context.request,,8,,,,,,,,,,,8,,
-org.springframework.web.multipart,,12,,,,,,,,,,,12,,
-org.xml.sax,,,1,,,,,,,,,,,1,
-org.xmlpull.v1,,3,,,,,,,,,,,3,,
-play.mvc,,4,,,,,,,,,,,4,,
+package,sink,source,summary,sink:bean-validation,sink:create-file,sink:header-splitting,sink:information-leak,sink:ldap,sink:open-url,sink:set-hostname-verifier,sink:url-open-stream,sink:xpath,sink:xss,source:remote,summary:taint,summary:value
+android.util,,16,,,,,,,,,,,,16,,
+android.webkit,3,2,,,,,,,,,,,3,2,,
+com.esotericsoftware.kryo.io,,,1,,,,,,,,,,,,1,
+com.esotericsoftware.kryo5.io,,,1,,,,,,,,,,,,1,
+com.fasterxml.jackson.databind,,,2,,,,,,,,,,,,2,
+com.google.common.base,,,28,,,,,,,,,,,,22,6
+com.google.common.io,6,,69,,,,,,,,6,,,,68,1
+com.unboundid.ldap.sdk,17,,,,,,,17,,,,,,,,
+java.beans,,,1,,,,,,,,,,,,1,
+java.io,3,,20,,3,,,,,,,,,,20,
+java.lang,,,1,,,,,,,,,,,,1,
+java.net,2,3,4,,,,,,2,,,,,3,4,
+java.nio,10,,2,,10,,,,,,,,,,2,
+java.util,,,13,,,,,,,,,,,,13,
+javax.naming.directory,1,,,,,,,1,,,,,,,,
+javax.net.ssl,2,,,,,,,,,2,,,,,,
+javax.servlet,4,21,2,,,3,1,,,,,,,21,2,
+javax.validation,1,1,,1,,,,,,,,,,1,,
+javax.ws.rs.core,1,,,,,1,,,,,,,,,,
+javax.xml.transform.sax,,,4,,,,,,,,,,,,4,
+javax.xml.transform.stream,,,2,,,,,,,,,,,,2,
+javax.xml.xpath,3,,,,,,,,,,,3,,,,
+org.apache.commons.codec,,,2,,,,,,,,,,,,2,
+org.apache.commons.io,,,22,,,,,,,,,,,,22,
+org.apache.commons.lang3,,,313,,,,,,,,,,,,299,14
+org.apache.commons.text,,,203,,,,,,,,,,,,203,
+org.apache.directory.ldap.client.api,1,,,,,,,1,,,,,,,,
+org.apache.hc.core5.function,,,1,,,,,,,,,,,,1,
+org.apache.hc.core5.http,1,2,39,,,,,,,,,,1,2,39,
+org.apache.hc.core5.net,,,2,,,,,,,,,,,,2,
+org.apache.hc.core5.util,,,22,,,,,,,,,,,,18,4
+org.apache.http,2,3,66,,,,,,,,,,2,3,59,7
+org.dom4j,20,,,,,,,,,,,20,,,,
+org.springframework.ldap.core,14,,,,,,,14,,,,,,,,
+org.springframework.security.web.savedrequest,,6,,,,,,,,,,,,6,,
+org.springframework.web.client,,3,,,,,,,,,,,,3,,
+org.springframework.web.context.request,,8,,,,,,,,,,,,8,,
+org.springframework.web.multipart,,12,,,,,,,,,,,,12,,
+org.xml.sax,,,1,,,,,,,,,,,,1,
+org.xmlpull.v1,,3,,,,,,,,,,,,3,,
+play.mvc,,4,,,,,,,,,,,,4,,
diff --git a/java/documentation/library-coverage/flow-model-coverage.rst b/java/documentation/library-coverage/flow-model-coverage.rst
index bad79a93c06..b9b54aad158 100644
--- a/java/documentation/library-coverage/flow-model-coverage.rst
+++ b/java/documentation/library-coverage/flow-model-coverage.rst
@@ -12,8 +12,8 @@ Java framework & library support
`Apache Commons IO `_,``org.apache.commons.io``,,22,,,,,,,,
Google,``com.google.common.*``,,97,6,,6,,,,,
Java Standard Library,``java.*``,3,41,15,13,,,,,,2
- Java extensions,``javax.*``,22,8,11,,,,,1,1,
+ Java extensions,``javax.*``,22,8,12,,,,,1,1,
`Spring `_,``org.springframework.*``,29,,14,,,,,14,,
Others,"``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.databind``, ``com.unboundid.ldap.sdk``, ``org.dom4j``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.mvc``",7,5,37,,,,,17,,
- Totals,,84,821,90,13,6,6,,33,1,2
+ Totals,,84,821,91,13,6,6,,33,1,2
From 650c4f19d2088b446523b3ec29ed3aae13ccad72 Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Tue, 1 Jun 2021 16:09:17 +0200
Subject: [PATCH 083/272] Java: More qldoc.
---
.../code/java/dataflow/internal/ContainerFlow.qll | 14 ++++++++++++++
.../java/dataflow/internal/TaintTrackingUtil.qll | 11 +++++++++++
2 files changed, 25 insertions(+)
diff --git a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll
index 667ae024b37..d57c68bf007 100644
--- a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll
+++ b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll
@@ -710,6 +710,11 @@ predicate containerStep(Expr n1, Expr n2) {
containerUpdateStep(n1, n2)
}
+/**
+ * Holds if the step from `node1` to `node2` stores a value in an array.
+ * This covers array assignments and initializers as well as implicit array
+ * creations for varargs.
+ */
predicate arrayStoreStep(Node node1, Node node2) {
exists(Argument arg |
node1.asExpr() = arg and
@@ -734,6 +739,11 @@ private predicate enhancedForStmtStep(Node node1, Node node2, Type containerType
)
}
+/**
+ * Holds if the step from `node1` to `node2` reads a value from an array.
+ * This covers ordinary array reads as well as array iteration through enhanced
+ * `for` statements.
+ */
predicate arrayReadStep(Node node1, Node node2, Type elemType) {
exists(ArrayAccess aa |
aa.getArray() = node1.asExpr() and
@@ -747,6 +757,10 @@ predicate arrayReadStep(Node node1, Node node2, Type elemType) {
)
}
+/**
+ * Holds if the step from `node1` to `node2` reads a value from a collection.
+ * This only covers iteration through enhanced `for` statements.
+ */
predicate collectionReadStep(Node node1, Node node2) {
enhancedForStmtStep(node1, node2, any(Type t | not t instanceof Array))
}
diff --git a/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll b/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll
index 719b89fbf2a..d2322eca983 100644
--- a/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll
+++ b/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll
@@ -98,6 +98,17 @@ private module Cached {
import Cached
+/**
+ * These configurations add a number of configuration-dependent additional taint
+ * steps to all taint configurations. For each sink or additional step provided
+ * by a given configuration the types are inspected to find those implicit
+ * collection or array read steps that might be required at the sink or step
+ * input. The corresponding store steps are then added as additional taint steps
+ * to provide backwards-compatible taint flow to such sinks and steps.
+ *
+ * This is a temporary measure until support is added for such sinks that
+ * require implicit read steps.
+ */
private module StoreTaintSteps {
private import semmle.code.java.dataflow.TaintTracking
private import semmle.code.java.dataflow.TaintTracking2
From 9aba92397dbc686eb15e83e0df401d8eb7299606 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alvaro=20Mun=CC=83oz?=
Date: Tue, 1 Jun 2021 17:16:41 +0200
Subject: [PATCH 084/272] lift XssSink check to InformationLeakSink
---
.../ql/src/Security/CWE/CWE-209/StackTraceExposure.ql | 11 ++---------
.../src/semmle/code/java/security/InformationLeak.qll | 6 +++++-
2 files changed, 7 insertions(+), 10 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql b/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql
index 4f2753b9723..78a6b9108e3 100644
--- a/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql
+++ b/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql
@@ -15,7 +15,6 @@
import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.TaintTracking
-import semmle.code.java.security.XSS
import semmle.code.java.security.InformationLeak
/**
@@ -91,10 +90,7 @@ class StackTraceStringToHTTPResponseSinkFlowConfig extends TaintTracking::Config
override predicate isSource(DataFlow::Node src) { stackTraceExpr(_, src.asExpr()) }
- override predicate isSink(DataFlow::Node sink) {
- sink instanceof XssSink or
- sink instanceof InformationLeakSink
- }
+ override predicate isSink(DataFlow::Node sink) { sink instanceof InformationLeakSink }
}
/**
@@ -134,10 +130,7 @@ class GetMessageFlowSourceToHTTPResponseSinkFlowConfig extends TaintTracking::Co
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof GetMessageFlowSource }
- override predicate isSink(DataFlow::Node sink) {
- sink instanceof XssSink or
- sink instanceof InformationLeakSink
- }
+ override predicate isSink(DataFlow::Node sink) { sink instanceof InformationLeakSink }
}
/**
diff --git a/java/ql/src/semmle/code/java/security/InformationLeak.qll b/java/ql/src/semmle/code/java/security/InformationLeak.qll
index 4cb12c3d3d9..f68ddd5b121 100644
--- a/java/ql/src/semmle/code/java/security/InformationLeak.qll
+++ b/java/ql/src/semmle/code/java/security/InformationLeak.qll
@@ -3,6 +3,7 @@
import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.ExternalFlow
+import semmle.code.java.security.XSS
/** CSV sink models representing methods not susceptible to XSS but outputing to an HTTP response body. */
private class DefaultInformationLeakSinkModel extends SinkModelCsv {
@@ -19,5 +20,8 @@ abstract class InformationLeakSink extends DataFlow::Node { }
/** A default sink representing methods outputing data to an HTTP response. */
private class DefaultInformationLeakSink extends InformationLeakSink {
- DefaultInformationLeakSink() { sinkNode(this, "information-leak") }
+ DefaultInformationLeakSink() {
+ sinkNode(this, "information-leak") or
+ this instanceof XssSink
+ }
}
From 5e96e28792037cb8a289bab7c6c47f16342f7c4e Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Wed, 2 Jun 2021 10:24:46 +0200
Subject: [PATCH 085/272] Java: Add missing metadata.
---
.../ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql | 2 ++
1 file changed, 2 insertions(+)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql
index c44eee23602..c6a7f583b14 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql
@@ -3,6 +3,8 @@
* @description Evaluation of a user-controlled malicious expression in Java Python
* interpreter may lead to remote code execution.
* @kind path-problem
+ * @problem.severity error
+ * @precision high
* @id java/jython-injection
* @tags security
* external/cwe/cwe-094
From a3a215afea122df02b5a52bb2ff21e7c45affb18 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alvaro=20Mun=CC=83oz?=
Date: Wed, 2 Jun 2021 11:12:39 +0200
Subject: [PATCH 086/272] HTTP -> Http
---
.../Security/CWE/CWE-209/StackTraceExposure.ql | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql b/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql
index 78a6b9108e3..233e2694afa 100644
--- a/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql
+++ b/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql
@@ -83,9 +83,9 @@ predicate stackTraceExpr(Expr exception, MethodAccess stackTraceString) {
)
}
-class StackTraceStringToHTTPResponseSinkFlowConfig extends TaintTracking::Configuration {
- StackTraceStringToHTTPResponseSinkFlowConfig() {
- this = "StackTraceExposure::StackTraceStringToHTTPResponseSinkFlowConfig"
+class StackTraceStringToHttpResponseSinkFlowConfig extends TaintTracking::Configuration {
+ StackTraceStringToHttpResponseSinkFlowConfig() {
+ this = "StackTraceExposure::StackTraceStringToHttpResponseSinkFlowConfig"
}
override predicate isSource(DataFlow::Node src) { stackTraceExpr(_, src.asExpr()) }
@@ -106,7 +106,7 @@ predicate printsStackExternally(MethodAccess call, Expr stackTrace) {
* A stringified stack trace flows to an external sink.
*/
predicate stringifiedStackFlowsExternally(DataFlow::Node externalExpr, Expr stackTrace) {
- exists(MethodAccess stackTraceString, StackTraceStringToHTTPResponseSinkFlowConfig conf |
+ exists(MethodAccess stackTraceString, StackTraceStringToHttpResponseSinkFlowConfig conf |
stackTraceExpr(stackTrace, stackTraceString) and
conf.hasFlow(DataFlow::exprNode(stackTraceString), externalExpr)
)
@@ -123,9 +123,9 @@ class GetMessageFlowSource extends MethodAccess {
}
}
-class GetMessageFlowSourceToHTTPResponseSinkFlowConfig extends TaintTracking::Configuration {
- GetMessageFlowSourceToHTTPResponseSinkFlowConfig() {
- this = "StackTraceExposure::GetMessageFlowSourceToHTTPResponseSinkFlowConfig"
+class GetMessageFlowSourceToHttpResponseSinkFlowConfig extends TaintTracking::Configuration {
+ GetMessageFlowSourceToHttpResponseSinkFlowConfig() {
+ this = "StackTraceExposure::GetMessageFlowSourceToHttpResponseSinkFlowConfig"
}
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof GetMessageFlowSource }
@@ -137,7 +137,7 @@ class GetMessageFlowSourceToHTTPResponseSinkFlowConfig extends TaintTracking::Co
* A call to `getMessage()` that then flows to a servlet response.
*/
predicate getMessageFlowsExternally(DataFlow::Node externalExpr, GetMessageFlowSource getMessage) {
- any(GetMessageFlowSourceToHTTPResponseSinkFlowConfig conf)
+ any(GetMessageFlowSourceToHttpResponseSinkFlowConfig conf)
.hasFlow(DataFlow::exprNode(getMessage), externalExpr)
}
From f9ede137f9fc86548fdc24af45034f4b317f70a4 Mon Sep 17 00:00:00 2001
From: AlonaHlobina <54394529+AlonaHlobina@users.noreply.github.com>
Date: Wed, 2 Jun 2021 14:19:18 +0200
Subject: [PATCH 087/272] Update versions-compilers.rst
---
docs/codeql/support/reusables/versions-compilers.rst | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/codeql/support/reusables/versions-compilers.rst b/docs/codeql/support/reusables/versions-compilers.rst
index 9bc3c855e58..330922fd876 100644
--- a/docs/codeql/support/reusables/versions-compilers.rst
+++ b/docs/codeql/support/reusables/versions-compilers.rst
@@ -17,11 +17,11 @@
.NET 5","``.sln``, ``.csproj``, ``.cs``, ``.cshtml``, ``.xaml``"
Go (aka Golang), "Go up to 1.16", "Go 1.11 or more recent", ``.go``
- Java,"Java 7 to 15 [3]_","javac (OpenJDK and Oracle JDK),
+ Java,"Java 7 to 15 [3]_, Java 16 (partially)","javac (OpenJDK and Oracle JDK),
Eclipse compiler for Java (ECJ) [4]_",``.java``
JavaScript,ECMAScript 2021 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhm``, ``.xhtml``, ``.vue``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [5]_"
- Python,"2.7, 3.5, 3.6, 3.7, 3.8",Not applicable,``.py``
+ Python,"2.7, 3.5, 3.6, 3.7, 3.8, 3.9",Not applicable,``.py``
TypeScript [6]_,"2.6-4.2",Standard TypeScript compiler,"``.ts``, ``.tsx``"
.. container:: footnote-group
From 788c5ba70124c4f766c1c49d1217f612ca63da11 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 2 Jun 2021 15:33:08 +0200
Subject: [PATCH 088/272] add support for the prettier API
---
javascript/ql/src/javascript.qll | 1 +
.../semmle/javascript/frameworks/Prettier.qll | 52 +++++++++++++++++++
.../CWE-022/TaintedPath/TaintedPath.expected | 45 ++++++++++++++++
.../Security/CWE-022/TaintedPath/prettier.js | 14 +++++
.../ReflectedXss/ReflectedXss.expected | 12 +++++
.../ReflectedXssWithCustomSanitizer.expected | 1 +
.../Security/CWE-079/ReflectedXss/tst3.js | 6 +++
7 files changed, 131 insertions(+)
create mode 100644 javascript/ql/src/semmle/javascript/frameworks/Prettier.qll
create mode 100644 javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/prettier.js
diff --git a/javascript/ql/src/javascript.qll b/javascript/ql/src/javascript.qll
index 95db948bbd4..e3b202bf89b 100644
--- a/javascript/ql/src/javascript.qll
+++ b/javascript/ql/src/javascript.qll
@@ -104,6 +104,7 @@ import semmle.javascript.frameworks.Nest
import semmle.javascript.frameworks.Next
import semmle.javascript.frameworks.NoSQL
import semmle.javascript.frameworks.PkgCloud
+import semmle.javascript.frameworks.Prettier
import semmle.javascript.frameworks.PropertyProjection
import semmle.javascript.frameworks.Puppeteer
import semmle.javascript.frameworks.React
diff --git a/javascript/ql/src/semmle/javascript/frameworks/Prettier.qll b/javascript/ql/src/semmle/javascript/frameworks/Prettier.qll
new file mode 100644
index 00000000000..eeb8d1cdb87
--- /dev/null
+++ b/javascript/ql/src/semmle/javascript/frameworks/Prettier.qll
@@ -0,0 +1,52 @@
+/**
+ * Provides classes and predicates for working with the [prettier](https://www.npmjs.com/package/prettier) library.
+ */
+
+import javascript
+
+/** Provides classes and predicates modelling aspects of the [prettier](https://www.npmjs.com/package/prettier) library. */
+private module Prettier {
+ /**
+ * A taint step from the [prettier API](https://prettier.io/docs/en/api.html).
+ */
+ private class PrettierTaintStep extends TaintTracking::SharedTaintStep {
+ override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
+ exists(API::CallNode call |
+ call = API::moduleImport("prettier").getMember("format").getACall()
+ |
+ pred = call.getArgument(0) and
+ succ = call
+ )
+ or
+ exists(API::CallNode call |
+ call = API::moduleImport("prettier").getMember("formatWithCursor").getACall()
+ |
+ pred = call.getArgument(0) and
+ succ = call.getReturn().getMember("formatted").getAnImmediateUse()
+ )
+ }
+ }
+
+ private import semmle.javascript.security.dataflow.TaintedPathCustomizations::TaintedPath as TaintedPath
+
+ /**
+ * An argument given to the `prettier` library specificing the location of a config file.
+ */
+ private class PrettierFileSink extends TaintedPath::Sink {
+ PrettierFileSink() {
+ this =
+ API::moduleImport("prettier")
+ .getMember(["resolveConfig", "resolveConfigFile", "getFileInfo"])
+ .getACall()
+ .getArgument(0)
+ or
+ this =
+ API::moduleImport("prettier")
+ .getMember("resolveConfig")
+ .getACall()
+ .getParameter(1)
+ .getMember("config")
+ .getARhs()
+ }
+ }
+}
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 5d8806c0501..24beed4007f 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
@@ -2271,6 +2271,25 @@ nodes
| other-fs-libraries.js:52:24:52:27 | path |
| other-fs-libraries.js:52:24:52:27 | path |
| other-fs-libraries.js:52:24:52:27 | path |
+| prettier.js:6:11:6:28 | p |
+| prettier.js:6:11:6:28 | p |
+| prettier.js:6:11:6:28 | p |
+| prettier.js:6:11:6:28 | p |
+| prettier.js:6:13:6:13 | p |
+| prettier.js:6:13:6:13 | p |
+| prettier.js:6:13:6:13 | p |
+| prettier.js:6:13:6:13 | p |
+| prettier.js:6:13:6:13 | p |
+| prettier.js:7:28:7:28 | p |
+| prettier.js:7:28:7:28 | p |
+| prettier.js:7:28:7:28 | p |
+| prettier.js:7:28:7:28 | p |
+| prettier.js:7:28:7:28 | p |
+| prettier.js:11:44:11:44 | p |
+| prettier.js:11:44:11:44 | p |
+| prettier.js:11:44:11:44 | p |
+| prettier.js:11:44:11:44 | p |
+| prettier.js:11:44:11:44 | p |
| pupeteer.js:5:9:5:71 | tainted |
| pupeteer.js:5:9:5:71 | tainted |
| pupeteer.js:5:9:5:71 | tainted |
@@ -6668,6 +6687,30 @@ edges
| other-fs-libraries.js:49:24:49:30 | req.url | other-fs-libraries.js:49:14:49:37 | url.par ... , true) |
| other-fs-libraries.js:49:24:49:30 | req.url | other-fs-libraries.js:49:14:49:37 | url.par ... , true) |
| other-fs-libraries.js:49:24:49:30 | req.url | other-fs-libraries.js:49:14:49:37 | url.par ... , true) |
+| prettier.js:6:11:6:28 | p | prettier.js:7:28:7:28 | p |
+| prettier.js:6:11:6:28 | p | prettier.js:7:28:7:28 | p |
+| prettier.js:6:11:6:28 | p | prettier.js:7:28:7:28 | p |
+| prettier.js:6:11:6:28 | p | prettier.js:7:28:7:28 | p |
+| prettier.js:6:11:6:28 | p | prettier.js:7:28:7:28 | p |
+| prettier.js:6:11:6:28 | p | prettier.js:7:28:7:28 | p |
+| prettier.js:6:11:6:28 | p | prettier.js:7:28:7:28 | p |
+| prettier.js:6:11:6:28 | p | prettier.js:7:28:7:28 | p |
+| prettier.js:6:11:6:28 | p | prettier.js:11:44:11:44 | p |
+| prettier.js:6:11:6:28 | p | prettier.js:11:44:11:44 | p |
+| prettier.js:6:11:6:28 | p | prettier.js:11:44:11:44 | p |
+| prettier.js:6:11:6:28 | p | prettier.js:11:44:11:44 | p |
+| prettier.js:6:11:6:28 | p | prettier.js:11:44:11:44 | p |
+| prettier.js:6:11:6:28 | p | prettier.js:11:44:11:44 | p |
+| prettier.js:6:11:6:28 | p | prettier.js:11:44:11:44 | p |
+| prettier.js:6:11:6:28 | p | prettier.js:11:44:11:44 | p |
+| prettier.js:6:13:6:13 | p | prettier.js:6:11:6:28 | p |
+| prettier.js:6:13:6:13 | p | prettier.js:6:11:6:28 | p |
+| prettier.js:6:13:6:13 | p | prettier.js:6:11:6:28 | p |
+| prettier.js:6:13:6:13 | p | prettier.js:6:11:6:28 | p |
+| prettier.js:6:13:6:13 | p | prettier.js:6:11:6:28 | p |
+| prettier.js:6:13:6:13 | p | prettier.js:6:11:6:28 | p |
+| prettier.js:6:13:6:13 | p | prettier.js:6:11:6:28 | p |
+| prettier.js:6:13:6:13 | p | prettier.js:6:11:6:28 | p |
| pupeteer.js:5:9:5:71 | tainted | pupeteer.js:9:28:9:34 | tainted |
| pupeteer.js:5:9:5:71 | tainted | pupeteer.js:9:28:9:34 | tainted |
| pupeteer.js:5:9:5:71 | tainted | pupeteer.js:9:28:9:34 | tainted |
@@ -8295,6 +8338,8 @@ edges
| other-fs-libraries.js:42:53:42:56 | path | other-fs-libraries.js:38:24:38:30 | req.url | other-fs-libraries.js:42:53:42:56 | path | This path depends on $@. | other-fs-libraries.js:38:24:38:30 | req.url | a user-provided value |
| other-fs-libraries.js:51:19:51:22 | path | other-fs-libraries.js:49:24:49:30 | req.url | other-fs-libraries.js:51:19:51:22 | path | This path depends on $@. | other-fs-libraries.js:49:24:49:30 | req.url | a user-provided value |
| other-fs-libraries.js:52:24:52:27 | path | other-fs-libraries.js:49:24:49:30 | req.url | other-fs-libraries.js:52:24:52:27 | path | This path depends on $@. | other-fs-libraries.js:49:24:49:30 | req.url | a user-provided value |
+| prettier.js:7:28:7:28 | p | prettier.js:6:13:6:13 | p | prettier.js:7:28:7:28 | p | This path depends on $@. | prettier.js:6:13:6:13 | p | a user-provided value |
+| prettier.js:11:44:11:44 | p | prettier.js:6:13:6:13 | p | prettier.js:11:44:11:44 | p | This path depends on $@. | prettier.js:6:13:6:13 | p | a user-provided value |
| pupeteer.js:9:28:9:34 | tainted | pupeteer.js:5:28:5:53 | parseTo ... t).name | pupeteer.js:9:28:9:34 | tainted | This path depends on $@. | pupeteer.js:5:28:5:53 | parseTo ... t).name | a user-provided value |
| pupeteer.js:13:37:13:43 | tainted | pupeteer.js:5:28:5:53 | parseTo ... t).name | pupeteer.js:13:37:13:43 | tainted | This path depends on $@. | pupeteer.js:5:28:5:53 | parseTo ... t).name | a user-provided value |
| tainted-access-paths.js:8:19:8:22 | path | tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:8:19:8:22 | path | This path depends on $@. | tainted-access-paths.js:6:24:6:30 | req.url | a user-provided value |
diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/prettier.js b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/prettier.js
new file mode 100644
index 00000000000..7546bb2c293
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/prettier.js
@@ -0,0 +1,14 @@
+const express = require('express');
+const prettier = require("prettier");
+
+const app = express();
+app.get('/some/path', function (req, res) {
+ const { p } = req.params;
+ prettier.resolveConfig(p).then((options) => { // NOT OK
+ const formatted = prettier.format("foo", options);
+ });
+
+ prettier.resolveConfig("foo", {config: p}).then((options) => { // NOT OK
+ const formatted = prettier.format("bar", options);
+ });
+});
diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXss.expected b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXss.expected
index b1d940b437c..c0955082e00 100644
--- a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXss.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXss.expected
@@ -187,6 +187,12 @@ nodes
| tst3.js:5:9:5:9 | p |
| tst3.js:6:12:6:12 | p |
| tst3.js:6:12:6:12 | p |
+| tst3.js:11:9:11:74 | code |
+| tst3.js:11:16:11:74 | prettie ... bel" }) |
+| tst3.js:11:32:11:39 | reg.body |
+| tst3.js:11:32:11:39 | reg.body |
+| tst3.js:12:12:12:15 | code |
+| tst3.js:12:12:12:15 | code |
edges
| ReflectedXss.js:8:33:8:45 | req.params.id | ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id |
| ReflectedXss.js:8:33:8:45 | req.params.id | ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id |
@@ -342,6 +348,11 @@ edges
| tst3.js:5:7:5:24 | p | tst3.js:6:12:6:12 | p |
| tst3.js:5:9:5:9 | p | tst3.js:5:7:5:24 | p |
| tst3.js:5:9:5:9 | p | tst3.js:5:7:5:24 | p |
+| tst3.js:11:9:11:74 | code | tst3.js:12:12:12:15 | code |
+| tst3.js:11:9:11:74 | code | tst3.js:12:12:12:15 | code |
+| tst3.js:11:16:11:74 | prettie ... bel" }) | tst3.js:11:9:11:74 | code |
+| tst3.js:11:32:11:39 | reg.body | tst3.js:11:16:11:74 | prettie ... bel" }) |
+| tst3.js:11:32:11:39 | reg.body | tst3.js:11:16:11:74 | prettie ... bel" }) |
#select
| ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id | ReflectedXss.js:8:33:8:45 | req.params.id | ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:8:33:8:45 | req.params.id | user-provided value |
| ReflectedXss.js:17:12:17:39 | "Unknow ... rams.id | ReflectedXss.js:17:31:17:39 | params.id | ReflectedXss.js:17:12:17:39 | "Unknow ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:17:31:17:39 | params.id | user-provided value |
@@ -386,3 +397,4 @@ edges
| tst2.js:36:12:36:12 | p | tst2.js:30:9:30:9 | p | tst2.js:36:12:36:12 | p | Cross-site scripting vulnerability due to $@. | tst2.js:30:9:30:9 | p | user-provided value |
| tst2.js:37:12:37:18 | other.p | tst2.js:30:9:30:9 | p | tst2.js:37:12:37:18 | other.p | Cross-site scripting vulnerability due to $@. | tst2.js:30:9:30:9 | p | user-provided value |
| tst3.js:6:12:6:12 | p | tst3.js:5:9:5:9 | p | tst3.js:6:12:6:12 | p | Cross-site scripting vulnerability due to $@. | tst3.js:5:9:5:9 | p | user-provided value |
+| tst3.js:12:12:12:15 | code | tst3.js:11:32:11:39 | reg.body | tst3.js:12:12:12:15 | code | Cross-site scripting vulnerability due to $@. | tst3.js:11:32:11:39 | reg.body | user-provided value |
diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXssWithCustomSanitizer.expected b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXssWithCustomSanitizer.expected
index 6a919f7b43a..db284911aaf 100644
--- a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXssWithCustomSanitizer.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXssWithCustomSanitizer.expected
@@ -40,3 +40,4 @@
| tst2.js:36:12:36:12 | p | Cross-site scripting vulnerability due to $@. | tst2.js:30:9:30:9 | p | user-provided value |
| tst2.js:37:12:37:18 | other.p | Cross-site scripting vulnerability due to $@. | tst2.js:30:9:30:9 | p | user-provided value |
| tst3.js:6:12:6:12 | p | Cross-site scripting vulnerability due to $@. | tst3.js:5:9:5:9 | p | user-provided value |
+| tst3.js:12:12:12:15 | code | Cross-site scripting vulnerability due to $@. | tst3.js:11:32:11:39 | reg.body | user-provided value |
diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/tst3.js b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/tst3.js
index 1fa1758c41c..c7d0fd91a4a 100644
--- a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/tst3.js
+++ b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/tst3.js
@@ -5,3 +5,9 @@ app.enable('x-powered-by').disable('x-powered-by').get('/', function (req, res)
let { p } = req.params;
res.send(p); // NOT OK
});
+
+const prettier = require("prettier");
+app.post("foobar", function (reg, res) {
+ const code = prettier.format(reg.body, { semi: false, parser: "babel" });
+ res.send(code); // NOT OK
+});
\ No newline at end of file
From 27ff256b0e86c71f5a7f2483433029dfbb712945 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 2 Jun 2021 15:34:01 +0200
Subject: [PATCH 089/272] add change note
---
javascript/change-notes/2021-06-02-prettier.md | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 javascript/change-notes/2021-06-02-prettier.md
diff --git a/javascript/change-notes/2021-06-02-prettier.md b/javascript/change-notes/2021-06-02-prettier.md
new file mode 100644
index 00000000000..ce4277c5e33
--- /dev/null
+++ b/javascript/change-notes/2021-06-02-prettier.md
@@ -0,0 +1,4 @@
+lgtm,codescanning
+* The dataflow libraries now model dataflow in the prettier library.
+ Affected packages are
+ [prettier](https://npmjs.com/package/prettier)
From daf2cc3d53d61b933a96f4f89f41af0812e62a47 Mon Sep 17 00:00:00 2001
From: Tom Hvitved
Date: Wed, 2 Jun 2021 20:39:05 +0200
Subject: [PATCH 090/272] Java: Improve performance of `isUnreachableInCall()`
---
.../semmle/code/java/dataflow/internal/DataFlowPrivate.qll | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll
index e3ab07fce1f..c20fa87af6c 100644
--- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll
+++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll
@@ -296,7 +296,9 @@ predicate isUnreachableInCall(Node n, DataFlowCall call) {
// which is used in a guard
param.getAUse() = guard and
// which controls `n` with the opposite value of `arg`
- guard.controls(n.asExpr().getBasicBlock(), arg.getBooleanValue().booleanNot())
+ guard
+ .controls(n.asExpr().getBasicBlock(),
+ pragma[only_bind_out](arg.getBooleanValue()).booleanNot())
)
}
From 98ee763d576c24bcca83f5ea30115e9f4d68b41d Mon Sep 17 00:00:00 2001
From: AlonaHlobina <54394529+AlonaHlobina@users.noreply.github.com>
Date: Wed, 2 Jun 2021 20:56:06 +0200
Subject: [PATCH 091/272] Update
docs/codeql/support/reusables/versions-compilers.rst
Co-authored-by: yo-h <55373593+yo-h@users.noreply.github.com>
---
docs/codeql/support/reusables/versions-compilers.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/codeql/support/reusables/versions-compilers.rst b/docs/codeql/support/reusables/versions-compilers.rst
index 330922fd876..f983b5e0114 100644
--- a/docs/codeql/support/reusables/versions-compilers.rst
+++ b/docs/codeql/support/reusables/versions-compilers.rst
@@ -17,7 +17,7 @@
.NET 5","``.sln``, ``.csproj``, ``.cs``, ``.cshtml``, ``.xaml``"
Go (aka Golang), "Go up to 1.16", "Go 1.11 or more recent", ``.go``
- Java,"Java 7 to 15 [3]_, Java 16 (partially)","javac (OpenJDK and Oracle JDK),
+ Java,"Java 7 to 16 [3]_","javac (OpenJDK and Oracle JDK),
Eclipse compiler for Java (ECJ) [4]_",``.java``
JavaScript,ECMAScript 2021 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhm``, ``.xhtml``, ``.vue``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [5]_"
From 1e19da155c6a0785156d4fb4255979480000b570 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 2 Jun 2021 21:25:48 +0200
Subject: [PATCH 092/272] move TaintedPath sink into TaintedPathCustomizations
to avoid side-effects
---
.../semmle/javascript/frameworks/Prettier.qll | 23 -------------------
.../dataflow/TaintedPathCustomizations.qll | 21 +++++++++++++++++
2 files changed, 21 insertions(+), 23 deletions(-)
diff --git a/javascript/ql/src/semmle/javascript/frameworks/Prettier.qll b/javascript/ql/src/semmle/javascript/frameworks/Prettier.qll
index eeb8d1cdb87..81a1d596c14 100644
--- a/javascript/ql/src/semmle/javascript/frameworks/Prettier.qll
+++ b/javascript/ql/src/semmle/javascript/frameworks/Prettier.qll
@@ -26,27 +26,4 @@ private module Prettier {
)
}
}
-
- private import semmle.javascript.security.dataflow.TaintedPathCustomizations::TaintedPath as TaintedPath
-
- /**
- * An argument given to the `prettier` library specificing the location of a config file.
- */
- private class PrettierFileSink extends TaintedPath::Sink {
- PrettierFileSink() {
- this =
- API::moduleImport("prettier")
- .getMember(["resolveConfig", "resolveConfigFile", "getFileInfo"])
- .getACall()
- .getArgument(0)
- or
- this =
- API::moduleImport("prettier")
- .getMember("resolveConfig")
- .getACall()
- .getParameter(1)
- .getMember("config")
- .getARhs()
- }
- }
}
diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll
index ebc877e0e25..d0814cd51d3 100644
--- a/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll
+++ b/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll
@@ -650,6 +650,27 @@ module TaintedPath {
}
}
+ /**
+ * An argument given to the `prettier` library specificing the location of a config file.
+ */
+ private class PrettierFileSink extends TaintedPath::Sink {
+ PrettierFileSink() {
+ this =
+ API::moduleImport("prettier")
+ .getMember(["resolveConfig", "resolveConfigFile", "getFileInfo"])
+ .getACall()
+ .getArgument(0)
+ or
+ this =
+ API::moduleImport("prettier")
+ .getMember("resolveConfig")
+ .getACall()
+ .getParameter(1)
+ .getMember("config")
+ .getARhs()
+ }
+ }
+
/**
* Holds if there is a step `src -> dst` mapping `srclabel` to `dstlabel` relevant for path traversal vulnerabilities.
*/
From 69d6c74e7e8eb193f51fc7bb27a4b083b7da56fc Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 2 Jun 2021 21:56:47 +0200
Subject: [PATCH 093/272] fix typescript version
---
javascript/extractor/lib/typescript/package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/extractor/lib/typescript/package.json b/javascript/extractor/lib/typescript/package.json
index 953f69edb1e..22b130f3bec 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.3.2"
+ "typescript": "4.3.2"
},
"scripts": {
"build": "tsc --project tsconfig.json",
From 431c9951319a254f9ead87d76c9997d9da7eb970 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 2 Jun 2021 23:11:15 +0200
Subject: [PATCH 094/272] add support for the debug library
---
javascript/change-notes/2021-06-02-debug.md | 4 ++++
.../semmle/javascript/frameworks/Logging.qll | 9 +++++++++
.../Security/CWE-312/CleartextLogging.expected | 18 ++++++++++++++++++
.../query-tests/Security/CWE-312/passwords.js | 8 +++++++-
4 files changed, 38 insertions(+), 1 deletion(-)
create mode 100644 javascript/change-notes/2021-06-02-debug.md
diff --git a/javascript/change-notes/2021-06-02-debug.md b/javascript/change-notes/2021-06-02-debug.md
new file mode 100644
index 00000000000..324ea96e2bf
--- /dev/null
+++ b/javascript/change-notes/2021-06-02-debug.md
@@ -0,0 +1,4 @@
+lgtm,codescanning
+* Logging calls using the [debug](https://npmjs.com/package/immutable) library are now recognized.
+ Affected packages are
+ [debug](https://npmjs.com/package/debug)
diff --git a/javascript/ql/src/semmle/javascript/frameworks/Logging.qll b/javascript/ql/src/semmle/javascript/frameworks/Logging.qll
index cf632cc6e1e..f6c02a54e8c 100644
--- a/javascript/ql/src/semmle/javascript/frameworks/Logging.qll
+++ b/javascript/ql/src/semmle/javascript/frameworks/Logging.qll
@@ -192,3 +192,12 @@ private module Fancylog {
override DataFlow::Node getAMessageComponent() { result = getAnArgument() }
}
}
+
+/**
+ * A class modelling [debug](https://npmjs.org/package/debug) as a logging mechanism.
+ */
+private class DebugLoggerCall extends LoggerCall, API::CallNode {
+ DebugLoggerCall() { this = API::moduleImport("debug").getReturn().getACall() }
+
+ override DataFlow::Node getAMessageComponent() { result = getAnArgument() }
+}
diff --git a/javascript/ql/test/query-tests/Security/CWE-312/CleartextLogging.expected b/javascript/ql/test/query-tests/Security/CWE-312/CleartextLogging.expected
index 85b1f872b52..6fc52f66868 100644
--- a/javascript/ql/test/query-tests/Security/CWE-312/CleartextLogging.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-312/CleartextLogging.expected
@@ -125,6 +125,14 @@ nodes
| passwords.js:164:14:164:21 | password |
| passwords.js:164:14:164:42 | passwor ... g, "*") |
| passwords.js:164:14:164:42 | passwor ... g, "*") |
+| passwords.js:169:17:169:24 | password |
+| passwords.js:169:17:169:24 | password |
+| passwords.js:169:17:169:45 | passwor ... g, "*") |
+| passwords.js:169:17:169:45 | passwor ... g, "*") |
+| passwords.js:170:11:170:18 | password |
+| passwords.js:170:11:170:18 | password |
+| passwords.js:170:11:170:39 | passwor ... g, "*") |
+| passwords.js:170:11:170:39 | passwor ... g, "*") |
| passwords_in_browser1.js:2:13:2:20 | password |
| passwords_in_browser1.js:2:13:2:20 | password |
| passwords_in_browser1.js:2:13:2:20 | password |
@@ -261,6 +269,14 @@ edges
| passwords.js:164:14:164:21 | password | passwords.js:164:14:164:42 | passwor ... g, "*") |
| passwords.js:164:14:164:21 | password | passwords.js:164:14:164:42 | passwor ... g, "*") |
| passwords.js:164:14:164:21 | password | passwords.js:164:14:164:42 | passwor ... g, "*") |
+| passwords.js:169:17:169:24 | password | passwords.js:169:17:169:45 | passwor ... g, "*") |
+| passwords.js:169:17:169:24 | password | passwords.js:169:17:169:45 | passwor ... g, "*") |
+| passwords.js:169:17:169:24 | password | passwords.js:169:17:169:45 | passwor ... g, "*") |
+| passwords.js:169:17:169:24 | password | passwords.js:169:17:169:45 | passwor ... g, "*") |
+| passwords.js:170:11:170:18 | password | passwords.js:170:11:170:39 | passwor ... g, "*") |
+| passwords.js:170:11:170:18 | password | passwords.js:170:11:170:39 | passwor ... g, "*") |
+| passwords.js:170:11:170:18 | password | passwords.js:170:11:170:39 | passwor ... g, "*") |
+| passwords.js:170:11:170:18 | password | passwords.js:170:11:170:39 | passwor ... g, "*") |
| passwords_in_browser1.js:2:13:2:20 | password | passwords_in_browser1.js:2:13:2:20 | password |
| passwords_in_browser2.js:2:13:2:20 | password | passwords_in_browser2.js:2:13:2:20 | password |
| passwords_in_server_1.js:6:13:6:20 | password | passwords_in_server_1.js:6:13:6:20 | password |
@@ -304,6 +320,8 @@ edges
| passwords.js:156:17:156:27 | process.env | passwords.js:156:17:156:27 | process.env | passwords.js:156:17:156:27 | process.env | Sensitive data returned by $@ is logged here. | passwords.js:156:17:156:27 | process.env | process environment |
| passwords.js:163:14:163:41 | passwor ... g, "*") | passwords.js:163:14:163:21 | password | passwords.js:163:14:163:41 | passwor ... g, "*") | Sensitive data returned by $@ is logged here. | passwords.js:163:14:163:21 | password | an access to password |
| passwords.js:164:14:164:42 | passwor ... g, "*") | passwords.js:164:14:164:21 | password | passwords.js:164:14:164:42 | passwor ... g, "*") | Sensitive data returned by $@ is logged here. | passwords.js:164:14:164:21 | password | an access to password |
+| passwords.js:169:17:169:45 | passwor ... g, "*") | passwords.js:169:17:169:24 | password | passwords.js:169:17:169:45 | passwor ... g, "*") | Sensitive data returned by $@ is logged here. | passwords.js:169:17:169:24 | password | an access to password |
+| passwords.js:170:11:170:39 | passwor ... g, "*") | passwords.js:170:11:170:18 | password | passwords.js:170:11:170:39 | passwor ... g, "*") | Sensitive data returned by $@ is logged here. | passwords.js:170:11:170:18 | password | an access to password |
| passwords_in_server_1.js:6:13:6:20 | password | passwords_in_server_1.js:6:13:6:20 | password | passwords_in_server_1.js:6:13:6:20 | password | Sensitive data returned by $@ is logged here. | passwords_in_server_1.js:6:13:6:20 | password | an access to password |
| passwords_in_server_2.js:3:13:3:20 | password | passwords_in_server_2.js:3:13:3:20 | password | passwords_in_server_2.js:3:13:3:20 | password | Sensitive data returned by $@ is logged here. | passwords_in_server_2.js:3:13:3:20 | password | an access to password |
| passwords_in_server_3.js:2:13:2:20 | password | passwords_in_server_3.js:2:13:2:20 | password | passwords_in_server_3.js:2:13:2:20 | password | Sensitive data returned by $@ is logged here. | passwords_in_server_3.js:2:13:2:20 | password | an access to password |
diff --git a/javascript/ql/test/query-tests/Security/CWE-312/passwords.js b/javascript/ql/test/query-tests/Security/CWE-312/passwords.js
index 50dc80a65d0..3c9f7d32464 100644
--- a/javascript/ql/test/query-tests/Security/CWE-312/passwords.js
+++ b/javascript/ql/test/query-tests/Security/CWE-312/passwords.js
@@ -162,4 +162,10 @@ var Util = require('util');
console.log(password.replace(/./g, "*")); // OK!
console.log(password.replace(/\./g, "*")); // NOT OK!
console.log(password.replace(/foo/g, "*")); // NOT OK!
-})();
\ No newline at end of file
+})();
+
+const debug = require('debug')('test');
+(function () {
+ console.log(password.replace(/foo/g, "*")); // NOT OK
+ debug(password.replace(/foo/g, "*")); // NOT OK
+});
\ No newline at end of file
From 185811ee227ad7f212b530fa8d29dac80e67ea6f Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 2 Jun 2021 23:23:30 +0200
Subject: [PATCH 095/272] make MongooseFunction abstract
---
javascript/ql/src/semmle/javascript/frameworks/NoSQL.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/src/semmle/javascript/frameworks/NoSQL.qll b/javascript/ql/src/semmle/javascript/frameworks/NoSQL.qll
index f48dacea700..b27641e313b 100644
--- a/javascript/ql/src/semmle/javascript/frameworks/NoSQL.qll
+++ b/javascript/ql/src/semmle/javascript/frameworks/NoSQL.qll
@@ -166,7 +166,7 @@ private module Mongoose {
/**
* A Mongoose function.
*/
- private class MongooseFunction extends API::Node {
+ abstract private class MongooseFunction extends API::Node {
/**
* Gets the API-graph node for the result from this function (if the function returns a `Query`).
*/
From 48ab6305592dd9691e971f229caf4b31d17fccbc Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 2 Jun 2021 23:43:40 +0200
Subject: [PATCH 096/272] model webpack-merge as an extend call
---
.../ql/src/semmle/javascript/Extend.qll | 20 +++++++++++++++++++
.../library-tests/Extend/ExtendCalls.expected | 2 ++
.../ql/test/library-tests/Extend/tst.js | 5 +++++
3 files changed, 27 insertions(+)
diff --git a/javascript/ql/src/semmle/javascript/Extend.qll b/javascript/ql/src/semmle/javascript/Extend.qll
index d50054fc4ee..d6c2087e39b 100644
--- a/javascript/ql/src/semmle/javascript/Extend.qll
+++ b/javascript/ql/src/semmle/javascript/Extend.qll
@@ -188,3 +188,23 @@ private class CloneStep extends PreCallGraphStep {
)
}
}
+
+/**
+ * A deep extend call from the [webpack-merge](https://npmjs.org/package/webpack-merge) library.
+ */
+private class WebpackMergeDeep extends ExtendCall, DataFlow::CallNode {
+ WebpackMergeDeep() {
+ this = DataFlow::moduleMember("webpack-merge", "merge").getACall()
+ or
+ this =
+ DataFlow::moduleMember("webpack-merge", ["mergeWithCustomize", "mergeWithRules"])
+ .getACall()
+ .getACall()
+ }
+
+ override DataFlow::Node getASourceOperand() { result = getAnArgument() }
+
+ override DataFlow::Node getDestinationOperand() { none() }
+
+ override predicate isDeep() { any() }
+}
diff --git a/javascript/ql/test/library-tests/Extend/ExtendCalls.expected b/javascript/ql/test/library-tests/Extend/ExtendCalls.expected
index 89449bc4ff1..a4c66634f7c 100644
--- a/javascript/ql/test/library-tests/Extend/ExtendCalls.expected
+++ b/javascript/ql/test/library-tests/Extend/ExtendCalls.expected
@@ -41,3 +41,5 @@
| tst.js:79:1:79:45 | checkSh ... arg())) | OK |
| tst.js:80:1:80:55 | checkSh ... arg())) | OK |
| tst.js:81:1:81:51 | checkSh ... arg())) | OK |
+| tst.js:85:1:85:44 | checkDe ... arg())) | OK |
+| tst.js:86:1:86:61 | checkDe ... arg())) | OK |
diff --git a/javascript/ql/test/library-tests/Extend/tst.js b/javascript/ql/test/library-tests/Extend/tst.js
index 287fe57cdba..500178e3e5d 100644
--- a/javascript/ql/test/library-tests/Extend/tst.js
+++ b/javascript/ql/test/library-tests/Extend/tst.js
@@ -79,3 +79,8 @@ checkShallow(require('lodash').extend(base(), arg()));
checkShallow(require("xtend")(base(), arg()));
checkShallow(require("xtend/immutable")(base(), arg()));
checkShallow(require("ramda").merge(base(), arg()));
+
+// webpack-merge. deep.
+const webpackMerge = require('webpack-merge');
+checkDeep(webpackMerge.merge(base(), arg()));
+checkDeep(webpackMerge.mergeWithCustomize({})(base(), arg()));
From 143bf9de14730c032db33226bda612345054cd26 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 2 Jun 2021 23:48:29 +0200
Subject: [PATCH 097/272] add change note
---
javascript/change-notes/2021-06-02-webpack-merge.md | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 javascript/change-notes/2021-06-02-webpack-merge.md
diff --git a/javascript/change-notes/2021-06-02-webpack-merge.md b/javascript/change-notes/2021-06-02-webpack-merge.md
new file mode 100644
index 00000000000..87ab83fffa6
--- /dev/null
+++ b/javascript/change-notes/2021-06-02-webpack-merge.md
@@ -0,0 +1,4 @@
+lgtm,codescanning
+* The security queries recognize the merge call from [webpack-merge](https://npmjs.com/package/webpack-merge).
+ Affected packages are
+ [webpack-merge](https://npmjs.com/package/webpack-merge)
From 3bda1f2e264c3d470339a78ac8d6b9ca08b70e9f Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 3 Jun 2021 00:43:41 +0200
Subject: [PATCH 098/272] update expected test output
---
.../CallResolution/CallResolution.expected | 8 +-
.../TypeScript/Types/printAst.expected | 561 +++++++++++++++++-
2 files changed, 539 insertions(+), 30 deletions(-)
diff --git a/javascript/ql/test/library-tests/TypeScript/CallResolution/CallResolution.expected b/javascript/ql/test/library-tests/TypeScript/CallResolution/CallResolution.expected
index 96b0086480e..a942a68076b 100644
--- a/javascript/ql/test/library-tests/TypeScript/CallResolution/CallResolution.expected
+++ b/javascript/ql/test/library-tests/TypeScript/CallResolution/CallResolution.expected
@@ -4,16 +4,16 @@
| tst.ts:55:3:55:27 | obj.ove ... od(num) | (x: number): number | 0 |
| tst.ts:56:3:56:27 | obj.ove ... od(str) | (x: string): string | 1 |
| tst.ts:57:3:57:26 | obj.ove ... hod([]) | (x: any): any | 2 |
-| tst.ts:58:3:58:36 | obj.gen ... ([num]) | (x: number[]): T | 0 |
-| tst.ts:59:3:59:39 | obj.gen ... : str}) | (x: Box): T | 1 |
+| tst.ts:58:3:58:36 | obj.gen ... ([num]) | (x: number[]): number | 0 |
+| tst.ts:59:3:59:39 | obj.gen ... : str}) | (x: Box): string | 1 |
| tst.ts:60:3:60:34 | obj.gen ... od(num) | (x: any): any | 2 |
| tst.ts:64:3:64:23 | obj.sim ... od(str) | (x: string): number | 0 |
| tst.ts:65:3:65:24 | obj.gen ... od(str) | (x: string): string | 0 |
| tst.ts:66:3:66:24 | obj.gen ... od(num) | (x: number): number | 0 |
| tst.ts:67:3:67:27 | obj.ove ... od(num) | (x: number): number | 0 |
| tst.ts:68:3:68:27 | obj.ove ... od(str) | (x: string): string | 1 |
-| tst.ts:69:3:69:36 | obj.gen ... ([num]) | (x: number[]): T | 0 |
-| tst.ts:70:3:70:39 | obj.gen ... : str}) | (x: Box): T | 1 |
+| tst.ts:69:3:69:36 | obj.gen ... ([num]) | (x: number[]): number | 0 |
+| tst.ts:70:3:70:39 | obj.gen ... : str}) | (x: Box): string | 1 |
| tst.ts:74:3:74:28 | new Sim ... or(str) | new (x: string): SimpleConstructor | 0 |
| tst.ts:75:3:75:29 | new Gen ... or(str) | new (x: string): GenericConstructor | 0 |
| tst.ts:76:3:76:29 | new Gen ... or(num) | new (x: number): GenericConstructor | 0 |
diff --git a/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected b/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected
index 1f39b308479..d05b3228025 100644
--- a/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected
+++ b/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected
@@ -85,6 +85,12 @@ nodes
| dummy.ts:4:20:4:20 | [RegExpNormalConstant] b | semmle.label | [RegExpNormalConstant] b |
| dummy.ts:4:20:4:21 | [RegExpPlus] b+ | semmle.label | [RegExpPlus] b+ |
| dummy.ts:4:22:4:22 | [RegExpNormalConstant] c | semmle.label | [RegExpNormalConstant] c |
+| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
+| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
+| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
+| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
+| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
+| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
@@ -420,17 +426,182 @@ nodes
| tst.ts:69:25:69:38 | [Label] yetAnotherType | semmle.label | [Label] yetAnotherType |
| tst.ts:69:25:69:44 | [Property] yetAnotherType: true | semmle.label | [Property] yetAnotherType: true |
| tst.ts:69:41:69:44 | [Literal] true | semmle.label | [Literal] true |
+| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | semmle.label | [NamespaceDeclaration] module ... } } |
+| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | semmle.order | 55 |
+| tst.ts:71:8:71:11 | [VarDecl] TS43 | semmle.label | [VarDecl] TS43 |
+| tst.ts:73:3:76:3 | [InterfaceDeclaration,TypeDefinition] interfa ... n); } | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... n); } |
+| tst.ts:73:13:73:18 | [Identifier] ThingI | semmle.label | [Identifier] ThingI |
+| tst.ts:74:5:74:22 | [FunctionExpr] get size(): number | semmle.label | [FunctionExpr] get size(): number |
+| tst.ts:74:5:74:22 | [GetterMethodSignature] get size(): number | semmle.label | [GetterMethodSignature] get size(): number |
+| tst.ts:74:9:74:12 | [Label] size | semmle.label | [Label] size |
+| tst.ts:74:17:74:22 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number |
+| tst.ts:75:5:75:47 | [FunctionExpr] set siz ... olean); | semmle.label | [FunctionExpr] set siz ... olean); |
+| tst.ts:75:5:75:47 | [SetterMethodSignature] set siz ... olean); | semmle.label | [SetterMethodSignature] set siz ... olean); |
+| tst.ts:75:9:75:12 | [Label] size | semmle.label | [Label] size |
+| tst.ts:75:14:75:18 | [SimpleParameter] value | semmle.label | [SimpleParameter] value |
+| tst.ts:75:21:75:26 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number |
+| tst.ts:75:21:75:45 | [UnionTypeExpr] number ... boolean | semmle.label | [UnionTypeExpr] number ... boolean |
+| tst.ts:75:30:75:35 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string |
+| tst.ts:75:39:75:45 | [KeywordTypeExpr] boolean | semmle.label | [KeywordTypeExpr] boolean |
+| tst.ts:78:3:88:3 | [ExportDeclaration] export ... } } | semmle.label | [ExportDeclaration] export ... } } |
+| tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | semmle.label | [ClassDefinition,TypeDefinition] class T ... } } |
+| tst.ts:78:16:78:20 | [VarDecl] Thing | semmle.label | [VarDecl] Thing |
+| tst.ts:78:33:78:38 | [LocalTypeAccess] ThingI | semmle.label | [LocalTypeAccess] ThingI |
+| tst.ts:78:40:78:39 | [BlockStmt] {} | semmle.label | [BlockStmt] {} |
+| tst.ts:78:40:78:39 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | [ClassInitializedMember,ConstructorDefinition] constructor() {} |
+| tst.ts:78:40:78:39 | [FunctionExpr] () {} | semmle.label | [FunctionExpr] () {} |
+| tst.ts:78:40:78:39 | [Label] constructor | semmle.label | [Label] constructor |
+| tst.ts:79:5:79:9 | [Label] #size | semmle.label | [Label] #size |
+| tst.ts:79:5:79:14 | [FieldDeclaration] #size = 0; | semmle.label | [FieldDeclaration] #size = 0; |
+| tst.ts:79:13:79:13 | [Literal] 0 | semmle.label | [Literal] 0 |
+| tst.ts:81:5:83:5 | [ClassInitializedMember,GetterMethodDefinition] get siz ... ; } | semmle.label | [ClassInitializedMember,GetterMethodDefinition] get siz ... ; } |
+| tst.ts:81:5:83:5 | [FunctionExpr] get siz ... ; } | semmle.label | [FunctionExpr] get siz ... ; } |
+| tst.ts:81:9:81:12 | [Label] size | semmle.label | [Label] size |
+| tst.ts:81:17:81:22 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number |
+| tst.ts:81:24:83:5 | [BlockStmt] { ... ; } | semmle.label | [BlockStmt] { ... ; } |
+| tst.ts:82:7:82:24 | [ReturnStmt] return this.#size; | semmle.label | [ReturnStmt] return this.#size; |
+| tst.ts:82:14:82:17 | [ThisExpr] this | semmle.label | [ThisExpr] this |
+| tst.ts:82:14:82:23 | [DotExpr] this.#size | semmle.label | [DotExpr] this.#size |
+| tst.ts:82:19:82:23 | [Label] #size | semmle.label | [Label] #size |
+| tst.ts:85:5:87:5 | [ClassInitializedMember,SetterMethodDefinition] set siz ... ; } | semmle.label | [ClassInitializedMember,SetterMethodDefinition] set siz ... ; } |
+| tst.ts:85:5:87:5 | [FunctionExpr] set siz ... ; } | semmle.label | [FunctionExpr] set siz ... ; } |
+| tst.ts:85:9:85:12 | [Label] size | semmle.label | [Label] size |
+| tst.ts:85:14:85:18 | [SimpleParameter] value | semmle.label | [SimpleParameter] value |
+| tst.ts:85:21:85:26 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string |
+| tst.ts:85:21:85:45 | [UnionTypeExpr] string ... boolean | semmle.label | [UnionTypeExpr] string ... boolean |
+| tst.ts:85:30:85:35 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number |
+| tst.ts:85:39:85:45 | [KeywordTypeExpr] boolean | semmle.label | [KeywordTypeExpr] boolean |
+| tst.ts:85:48:87:5 | [BlockStmt] { ... ; } | semmle.label | [BlockStmt] { ... ; } |
+| tst.ts:86:7:86:10 | [ThisExpr] this | semmle.label | [ThisExpr] this |
+| tst.ts:86:7:86:16 | [DotExpr] this.#size | semmle.label | [DotExpr] this.#size |
+| tst.ts:86:7:86:32 | [AssignExpr] this.#s ... (value) | semmle.label | [AssignExpr] this.#s ... (value) |
+| tst.ts:86:7:86:33 | [ExprStmt] this.#s ... value); | semmle.label | [ExprStmt] this.#s ... value); |
+| tst.ts:86:12:86:16 | [Label] #size | semmle.label | [Label] #size |
+| tst.ts:86:20:86:25 | [VarRef] Number | semmle.label | [VarRef] Number |
+| tst.ts:86:20:86:32 | [CallExpr] Number(value) | semmle.label | [CallExpr] Number(value) |
+| tst.ts:86:27:86:31 | [VarRef] value | semmle.label | [VarRef] value |
+| tst.ts:91:3:95:3 | [ClassDefinition,TypeDefinition] class S ... } } | semmle.label | [ClassDefinition,TypeDefinition] class S ... } } |
+| tst.ts:91:9:91:13 | [VarDecl] Super | semmle.label | [VarDecl] Super |
+| tst.ts:91:15:91:14 | [BlockStmt] {} | semmle.label | [BlockStmt] {} |
+| tst.ts:91:15:91:14 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | [ClassInitializedMember,ConstructorDefinition] constructor() {} |
+| tst.ts:91:15:91:14 | [FunctionExpr] () {} | semmle.label | [FunctionExpr] () {} |
+| tst.ts:91:15:91:14 | [Label] constructor | semmle.label | [Label] constructor |
+| tst.ts:92:5:92:10 | [Label] random | semmle.label | [Label] random |
+| tst.ts:92:5:94:5 | [ClassInitializedMember,MethodDefinition] random( ... ; } | semmle.label | [ClassInitializedMember,MethodDefinition] random( ... ; } |
+| tst.ts:92:5:94:5 | [FunctionExpr] random( ... ; } | semmle.label | [FunctionExpr] random( ... ; } |
+| tst.ts:92:15:92:20 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number |
+| tst.ts:92:22:94:5 | [BlockStmt] { ... ; } | semmle.label | [BlockStmt] { ... ; } |
+| tst.ts:93:7:93:15 | [ReturnStmt] return 4; | semmle.label | [ReturnStmt] return 4; |
+| tst.ts:93:14:93:14 | [Literal] 4 | semmle.label | [Literal] 4 |
+| tst.ts:97:3:101:3 | [ClassDefinition,TypeDefinition] class S ... } } | semmle.label | [ClassDefinition,TypeDefinition] class S ... } } |
+| tst.ts:97:9:97:11 | [VarDecl] Sub | semmle.label | [VarDecl] Sub |
+| tst.ts:97:21:97:25 | [VarRef] Super | semmle.label | [VarRef] Super |
+| tst.ts:97:27:97:26 | [BlockStmt] { super(...args); } | semmle.label | [BlockStmt] { super(...args); } |
+| tst.ts:97:27:97:26 | [CallExpr] super(...args) | semmle.label | [CallExpr] super(...args) |
+| tst.ts:97:27:97:26 | [ClassInitializedMember,ConstructorDefinition] constru ... rgs); } | semmle.label | [ClassInitializedMember,ConstructorDefinition] constru ... rgs); } |
+| tst.ts:97:27:97:26 | [ExprStmt] super(...args); | semmle.label | [ExprStmt] super(...args); |
+| tst.ts:97:27:97:26 | [FunctionExpr] (...arg ... rgs); } | semmle.label | [FunctionExpr] (...arg ... rgs); } |
+| tst.ts:97:27:97:26 | [Label] constructor | semmle.label | [Label] constructor |
+| tst.ts:97:27:97:26 | [SimpleParameter] args | semmle.label | [SimpleParameter] args |
+| tst.ts:97:27:97:26 | [SpreadElement] ...args | semmle.label | [SpreadElement] ...args |
+| tst.ts:97:27:97:26 | [SuperExpr] super | semmle.label | [SuperExpr] super |
+| tst.ts:97:27:97:26 | [VarRef] args | semmle.label | [VarRef] args |
+| tst.ts:98:5:100:5 | [ClassInitializedMember,MethodDefinition] overrid ... ; } | semmle.label | [ClassInitializedMember,MethodDefinition] overrid ... ; } |
+| tst.ts:98:5:100:5 | [FunctionExpr] overrid ... ; } | semmle.label | [FunctionExpr] overrid ... ; } |
+| tst.ts:98:14:98:19 | [Label] random | semmle.label | [Label] random |
+| tst.ts:98:24:98:29 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number |
+| tst.ts:98:31:100:5 | [BlockStmt] { ... ; } | semmle.label | [BlockStmt] { ... ; } |
+| tst.ts:99:7:99:33 | [ReturnStmt] return ... ) * 10; | semmle.label | [ReturnStmt] return ... ) * 10; |
+| tst.ts:99:14:99:18 | [SuperExpr] super | semmle.label | [SuperExpr] super |
+| tst.ts:99:14:99:25 | [DotExpr] super.random | semmle.label | [DotExpr] super.random |
+| tst.ts:99:14:99:27 | [MethodCallExpr] super.random() | semmle.label | [MethodCallExpr] super.random() |
+| tst.ts:99:14:99:32 | [BinaryExpr] super.random() * 10 | semmle.label | [BinaryExpr] super.random() * 10 |
+| tst.ts:99:20:99:25 | [Label] random | semmle.label | [Label] random |
+| tst.ts:99:31:99:32 | [Literal] 10 | semmle.label | [Literal] 10 |
+| tst.ts:104:3:107:3 | [FunctionDeclStmt] functio ... }`; } | semmle.label | [FunctionDeclStmt] functio ... }`; } |
+| tst.ts:104:12:104:14 | [VarDecl] bar | semmle.label | [VarDecl] bar |
+| tst.ts:104:16:104:16 | [SimpleParameter] s | semmle.label | [SimpleParameter] s |
+| tst.ts:104:19:104:24 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string |
+| tst.ts:104:28:104:44 | [TemplateLiteralTypeExpr] `hello ${string}` | semmle.label | [TemplateLiteralTypeExpr] `hello ${string}` |
+| tst.ts:104:29:104:34 | [LiteralTypeExpr] hello | semmle.label | [LiteralTypeExpr] hello |
+| tst.ts:104:37:104:42 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string |
+| tst.ts:104:46:107:3 | [BlockStmt] { / ... }`; } | semmle.label | [BlockStmt] { / ... }`; } |
+| tst.ts:106:5:106:24 | [ReturnStmt] return `hello ${s}`; | semmle.label | [ReturnStmt] return `hello ${s}`; |
+| tst.ts:106:12:106:23 | [TemplateLiteral] `hello ${s}` | semmle.label | [TemplateLiteral] `hello ${s}` |
+| tst.ts:106:13:106:18 | [TemplateElement] hello | semmle.label | [TemplateElement] hello |
+| tst.ts:106:21:106:21 | [VarRef] s | semmle.label | [VarRef] s |
+| tst.ts:109:3:109:50 | [DeclStmt] let s1 = ... | semmle.label | [DeclStmt] let s1 = ... |
+| tst.ts:109:15:109:16 | [VarDecl] s1 | semmle.label | [VarDecl] s1 |
+| tst.ts:109:15:109:49 | [VariableDeclarator] s1: `${ ... umber}` | semmle.label | [VariableDeclarator] s1: `${ ... umber}` |
+| tst.ts:109:19:109:49 | [TemplateLiteralTypeExpr] `${numb ... umber}` | semmle.label | [TemplateLiteralTypeExpr] `${numb ... umber}` |
+| tst.ts:109:22:109:27 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number |
+| tst.ts:109:29:109:29 | [LiteralTypeExpr] - | semmle.label | [LiteralTypeExpr] - |
+| tst.ts:109:32:109:37 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number |
+| tst.ts:109:39:109:39 | [LiteralTypeExpr] - | semmle.label | [LiteralTypeExpr] - |
+| tst.ts:109:42:109:47 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number |
+| tst.ts:110:3:110:26 | [DeclStmt] let s2 = ... | semmle.label | [DeclStmt] let s2 = ... |
+| tst.ts:110:15:110:16 | [VarDecl] s2 | semmle.label | [VarDecl] s2 |
+| tst.ts:110:15:110:25 | [VariableDeclarator] s2: `1-2-3` | semmle.label | [VariableDeclarator] s2: `1-2-3` |
+| tst.ts:110:19:110:25 | [LiteralTypeExpr] `1-2-3` | semmle.label | [LiteralTypeExpr] `1-2-3` |
+| tst.ts:110:19:110:25 | [TemplateLiteralTypeExpr] `1-2-3` | semmle.label | [TemplateLiteralTypeExpr] `1-2-3` |
+| tst.ts:111:3:111:34 | [DeclStmt] let s3 = ... | semmle.label | [DeclStmt] let s3 = ... |
+| tst.ts:111:15:111:16 | [VarDecl] s3 | semmle.label | [VarDecl] s3 |
+| tst.ts:111:15:111:33 | [VariableDeclarator] s3: `${number}-2-3` | semmle.label | [VariableDeclarator] s3: `${number}-2-3` |
+| tst.ts:111:19:111:33 | [TemplateLiteralTypeExpr] `${number}-2-3` | semmle.label | [TemplateLiteralTypeExpr] `${number}-2-3` |
+| tst.ts:111:22:111:27 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number |
+| tst.ts:111:29:111:32 | [LiteralTypeExpr] -2-3 | semmle.label | [LiteralTypeExpr] -2-3 |
+| tst.ts:112:3:112:4 | [VarRef] s1 | semmle.label | [VarRef] s1 |
+| tst.ts:112:3:112:9 | [AssignExpr] s1 = s2 | semmle.label | [AssignExpr] s1 = s2 |
+| tst.ts:112:3:112:10 | [ExprStmt] s1 = s2; | semmle.label | [ExprStmt] s1 = s2; |
+| tst.ts:112:8:112:9 | [VarRef] s2 | semmle.label | [VarRef] s2 |
+| tst.ts:113:3:113:4 | [VarRef] s1 | semmle.label | [VarRef] s1 |
+| tst.ts:113:3:113:9 | [AssignExpr] s1 = s3 | semmle.label | [AssignExpr] s1 = s3 |
+| tst.ts:113:3:113:10 | [ExprStmt] s1 = s3; | semmle.label | [ExprStmt] s1 = s3; |
+| tst.ts:113:8:113:9 | [VarRef] s3 | semmle.label | [VarRef] s3 |
+| tst.ts:116:3:129:3 | [ClassDefinition,TypeDefinition] class F ... } } | semmle.label | [ClassDefinition,TypeDefinition] class F ... } } |
+| tst.ts:116:9:116:11 | [VarDecl] Foo | semmle.label | [VarDecl] Foo |
+| tst.ts:116:13:116:12 | [BlockStmt] {} | semmle.label | [BlockStmt] {} |
+| tst.ts:116:13:116:12 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | [ClassInitializedMember,ConstructorDefinition] constructor() {} |
+| tst.ts:116:13:116:12 | [FunctionExpr] () {} | semmle.label | [FunctionExpr] () {} |
+| tst.ts:116:13:116:12 | [Label] constructor | semmle.label | [Label] constructor |
+| tst.ts:117:5:117:15 | [Label] #someMethod | semmle.label | [Label] #someMethod |
+| tst.ts:117:5:119:5 | [ClassInitializedMember,MethodDefinition] #someMe ... ; } | semmle.label | [ClassInitializedMember,MethodDefinition] #someMe ... ; } |
+| tst.ts:117:5:119:5 | [FunctionExpr] #someMe ... ; } | semmle.label | [FunctionExpr] #someMe ... ; } |
+| tst.ts:117:20:117:25 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number |
+| tst.ts:117:27:119:5 | [BlockStmt] { ... ; } | semmle.label | [BlockStmt] { ... ; } |
+| tst.ts:118:7:118:16 | [ReturnStmt] return 42; | semmle.label | [ReturnStmt] return 42; |
+| tst.ts:118:14:118:15 | [Literal] 42 | semmle.label | [Literal] 42 |
+| tst.ts:121:5:123:5 | [ClassInitializedMember,GetterMethodDefinition] get #so ... ; } | semmle.label | [ClassInitializedMember,GetterMethodDefinition] get #so ... ; } |
+| tst.ts:121:5:123:5 | [FunctionExpr] get #so ... ; } | semmle.label | [FunctionExpr] get #so ... ; } |
+| tst.ts:121:9:121:18 | [Label] #someValue | semmle.label | [Label] #someValue |
+| tst.ts:121:23:121:28 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number |
+| tst.ts:121:30:123:5 | [BlockStmt] { ... ; } | semmle.label | [BlockStmt] { ... ; } |
+| tst.ts:122:7:122:17 | [ReturnStmt] return 100; | semmle.label | [ReturnStmt] return 100; |
+| tst.ts:122:14:122:16 | [Literal] 100 | semmle.label | [Literal] 100 |
+| tst.ts:125:5:125:16 | [Label] publicMethod | semmle.label | [Label] publicMethod |
+| tst.ts:125:5:128:5 | [ClassInitializedMember,MethodDefinition] publicM ... ; } | semmle.label | [ClassInitializedMember,MethodDefinition] publicM ... ; } |
+| tst.ts:125:5:128:5 | [FunctionExpr] publicM ... ; } | semmle.label | [FunctionExpr] publicM ... ; } |
+| tst.ts:125:20:128:5 | [BlockStmt] { ... ; } | semmle.label | [BlockStmt] { ... ; } |
+| tst.ts:126:7:126:10 | [ThisExpr] this | semmle.label | [ThisExpr] this |
+| tst.ts:126:7:126:22 | [DotExpr] this.#someMethod | semmle.label | [DotExpr] this.#someMethod |
+| tst.ts:126:7:126:24 | [MethodCallExpr] this.#someMethod() | semmle.label | [MethodCallExpr] this.#someMethod() |
+| tst.ts:126:7:126:25 | [ExprStmt] this.#someMethod(); | semmle.label | [ExprStmt] this.#someMethod(); |
+| tst.ts:126:12:126:22 | [Label] #someMethod | semmle.label | [Label] #someMethod |
+| tst.ts:127:7:127:29 | [ReturnStmt] return ... eValue; | semmle.label | [ReturnStmt] return ... eValue; |
+| tst.ts:127:14:127:17 | [ThisExpr] this | semmle.label | [ThisExpr] this |
+| tst.ts:127:14:127:28 | [DotExpr] this.#someValue | semmle.label | [DotExpr] this.#someValue |
+| tst.ts:127:19:127:28 | [Label] #someValue | semmle.label | [Label] #someValue |
| type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type B = boolean; |
-| type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | semmle.order | 55 |
+| type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | semmle.order | 56 |
| type_alias.ts:1:6:1:6 | [Identifier] B | semmle.label | [Identifier] B |
| type_alias.ts:1:10:1:16 | [KeywordTypeExpr] boolean | semmle.label | [KeywordTypeExpr] boolean |
| type_alias.ts:3:1:3:9 | [DeclStmt] var b = ... | semmle.label | [DeclStmt] var b = ... |
-| type_alias.ts:3:1:3:9 | [DeclStmt] var b = ... | semmle.order | 56 |
+| type_alias.ts:3:1:3:9 | [DeclStmt] var b = ... | semmle.order | 57 |
| type_alias.ts:3:5:3:5 | [VarDecl] b | semmle.label | [VarDecl] b |
| type_alias.ts:3:5:3:8 | [VariableDeclarator] b: B | semmle.label | [VariableDeclarator] b: B |
| type_alias.ts:3:8:3:8 | [LocalTypeAccess] B | semmle.label | [LocalTypeAccess] B |
| type_alias.ts:5:1:5:50 | [TypeAliasDeclaration,TypeDefinition] type Va ... ay>; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Va ... ay>; |
-| type_alias.ts:5:1:5:50 | [TypeAliasDeclaration,TypeDefinition] type Va ... ay>; | semmle.order | 57 |
+| type_alias.ts:5:1:5:50 | [TypeAliasDeclaration,TypeDefinition] type Va ... ay>; | semmle.order | 58 |
| type_alias.ts:5:6:5:17 | [Identifier] ValueOrArray | semmle.label | [Identifier] ValueOrArray |
| type_alias.ts:5:19:5:19 | [Identifier] T | semmle.label | [Identifier] T |
| type_alias.ts:5:19:5:19 | [TypeParameter] T | semmle.label | [TypeParameter] T |
@@ -442,14 +613,14 @@ nodes
| type_alias.ts:5:34:5:48 | [GenericTypeExpr] ValueOrArray | semmle.label | [GenericTypeExpr] ValueOrArray |
| type_alias.ts:5:47:5:47 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T |
| type_alias.ts:7:1:7:28 | [DeclStmt] var c = ... | semmle.label | [DeclStmt] var c = ... |
-| type_alias.ts:7:1:7:28 | [DeclStmt] var c = ... | semmle.order | 58 |
+| type_alias.ts:7:1:7:28 | [DeclStmt] var c = ... | semmle.order | 59 |
| type_alias.ts:7:5:7:5 | [VarDecl] c | semmle.label | [VarDecl] c |
| type_alias.ts:7:5:7:27 | [VariableDeclarator] c: Valu ... number> | semmle.label | [VariableDeclarator] c: Valu ... number> |
| type_alias.ts:7:8:7:19 | [LocalTypeAccess] ValueOrArray | semmle.label | [LocalTypeAccess] ValueOrArray |
| type_alias.ts:7:8:7:27 | [GenericTypeExpr] ValueOrArray | semmle.label | [GenericTypeExpr] ValueOrArray |
| type_alias.ts:7:21:7:26 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number |
| type_alias.ts:9:1:15:13 | [TypeAliasDeclaration,TypeDefinition] type Js ... Json[]; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Js ... Json[]; |
-| type_alias.ts:9:1:15:13 | [TypeAliasDeclaration,TypeDefinition] type Js ... Json[]; | semmle.order | 59 |
+| type_alias.ts:9:1:15:13 | [TypeAliasDeclaration,TypeDefinition] type Js ... Json[]; | semmle.order | 60 |
| type_alias.ts:9:6:9:9 | [Identifier] Json | semmle.label | [Identifier] Json |
| type_alias.ts:10:5:15:12 | [UnionTypeExpr] \| strin ... Json[] | semmle.label | [UnionTypeExpr] \| strin ... Json[] |
| type_alias.ts:10:7:10:12 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string |
@@ -465,12 +636,12 @@ nodes
| type_alias.ts:15:7:15:10 | [LocalTypeAccess] Json | semmle.label | [LocalTypeAccess] Json |
| type_alias.ts:15:7:15:12 | [ArrayTypeExpr] Json[] | semmle.label | [ArrayTypeExpr] Json[] |
| type_alias.ts:17:1:17:15 | [DeclStmt] var json = ... | semmle.label | [DeclStmt] var json = ... |
-| type_alias.ts:17:1:17:15 | [DeclStmt] var json = ... | semmle.order | 60 |
+| type_alias.ts:17:1:17:15 | [DeclStmt] var json = ... | semmle.order | 61 |
| type_alias.ts:17:5:17:8 | [VarDecl] json | semmle.label | [VarDecl] json |
| type_alias.ts:17:5:17:14 | [VariableDeclarator] json: Json | semmle.label | [VariableDeclarator] json: Json |
| type_alias.ts:17:11:17:14 | [LocalTypeAccess] Json | semmle.label | [LocalTypeAccess] Json |
| type_alias.ts:19:1:21:57 | [TypeAliasDeclaration,TypeDefinition] type Vi ... ode[]]; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Vi ... ode[]]; |
-| type_alias.ts:19:1:21:57 | [TypeAliasDeclaration,TypeDefinition] type Vi ... ode[]]; | semmle.order | 61 |
+| type_alias.ts:19:1:21:57 | [TypeAliasDeclaration,TypeDefinition] type Vi ... ode[]]; | semmle.order | 62 |
| type_alias.ts:19:6:19:16 | [Identifier] VirtualNode | semmle.label | [Identifier] VirtualNode |
| type_alias.ts:20:5:21:56 | [UnionTypeExpr] \| strin ... Node[]] | semmle.label | [UnionTypeExpr] \| strin ... Node[]] |
| type_alias.ts:20:7:20:12 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string |
@@ -486,7 +657,7 @@ nodes
| type_alias.ts:21:43:21:53 | [LocalTypeAccess] VirtualNode | semmle.label | [LocalTypeAccess] VirtualNode |
| type_alias.ts:21:43:21:55 | [ArrayTypeExpr] VirtualNode[] | semmle.label | [ArrayTypeExpr] VirtualNode[] |
| type_alias.ts:23:1:27:6 | [DeclStmt] const myNode = ... | semmle.label | [DeclStmt] const myNode = ... |
-| type_alias.ts:23:1:27:6 | [DeclStmt] const myNode = ... | semmle.order | 62 |
+| type_alias.ts:23:1:27:6 | [DeclStmt] const myNode = ... | semmle.order | 63 |
| type_alias.ts:23:7:23:12 | [VarDecl] myNode | semmle.label | [VarDecl] myNode |
| type_alias.ts:23:7:27:5 | [VariableDeclarator] myNode: ... ] ] | semmle.label | [VariableDeclarator] myNode: ... ] ] |
| type_alias.ts:23:15:23:25 | [LocalTypeAccess] VirtualNode | semmle.label | [LocalTypeAccess] VirtualNode |
@@ -511,12 +682,12 @@ nodes
| type_alias.ts:26:23:26:36 | [Literal] "second-child" | semmle.label | [Literal] "second-child" |
| type_alias.ts:26:41:26:62 | [Literal] "I'm the second child" | semmle.label | [Literal] "I'm the second child" |
| type_definition_objects.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.label | [ImportDeclaration] import ... dummy"; |
-| type_definition_objects.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 63 |
+| type_definition_objects.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 64 |
| type_definition_objects.ts:1:8:1:17 | [ImportSpecifier] * as dummy | semmle.label | [ImportSpecifier] * as dummy |
| type_definition_objects.ts:1:13:1:17 | [VarDecl] dummy | semmle.label | [VarDecl] dummy |
| type_definition_objects.ts:1:24:1:32 | [Literal] "./dummy" | semmle.label | [Literal] "./dummy" |
| type_definition_objects.ts:3:1:3:17 | [ExportDeclaration] export class C {} | semmle.label | [ExportDeclaration] export class C {} |
-| type_definition_objects.ts:3:1:3:17 | [ExportDeclaration] export class C {} | semmle.order | 64 |
+| type_definition_objects.ts:3:1:3:17 | [ExportDeclaration] export class C {} | semmle.order | 65 |
| type_definition_objects.ts:3:8:3:17 | [ClassDefinition,TypeDefinition] class C {} | semmle.label | [ClassDefinition,TypeDefinition] class C {} |
| type_definition_objects.ts:3:14:3:14 | [VarDecl] C | semmle.label | [VarDecl] C |
| type_definition_objects.ts:3:16:3:15 | [BlockStmt] {} | semmle.label | [BlockStmt] {} |
@@ -524,36 +695,36 @@ nodes
| type_definition_objects.ts:3:16:3:15 | [FunctionExpr] () {} | semmle.label | [FunctionExpr] () {} |
| type_definition_objects.ts:3:16:3:15 | [Label] constructor | semmle.label | [Label] constructor |
| type_definition_objects.ts:4:1:4:17 | [DeclStmt] let classObj = ... | semmle.label | [DeclStmt] let classObj = ... |
-| type_definition_objects.ts:4:1:4:17 | [DeclStmt] let classObj = ... | semmle.order | 65 |
+| type_definition_objects.ts:4:1:4:17 | [DeclStmt] let classObj = ... | semmle.order | 66 |
| type_definition_objects.ts:4:5:4:12 | [VarDecl] classObj | semmle.label | [VarDecl] classObj |
| type_definition_objects.ts:4:5:4:16 | [VariableDeclarator] classObj = C | semmle.label | [VariableDeclarator] classObj = C |
| type_definition_objects.ts:4:16:4:16 | [VarRef] C | semmle.label | [VarRef] C |
| type_definition_objects.ts:6:1:6:16 | [ExportDeclaration] export enum E {} | semmle.label | [ExportDeclaration] export enum E {} |
-| type_definition_objects.ts:6:1:6:16 | [ExportDeclaration] export enum E {} | semmle.order | 66 |
+| type_definition_objects.ts:6:1:6:16 | [ExportDeclaration] export enum E {} | semmle.order | 67 |
| type_definition_objects.ts:6:8:6:16 | [EnumDeclaration,TypeDefinition] enum E {} | semmle.label | [EnumDeclaration,TypeDefinition] enum E {} |
| type_definition_objects.ts:6:13:6:13 | [VarDecl] E | semmle.label | [VarDecl] E |
| type_definition_objects.ts:7:1:7:16 | [DeclStmt] let enumObj = ... | semmle.label | [DeclStmt] let enumObj = ... |
-| type_definition_objects.ts:7:1:7:16 | [DeclStmt] let enumObj = ... | semmle.order | 67 |
+| type_definition_objects.ts:7:1:7:16 | [DeclStmt] let enumObj = ... | semmle.order | 68 |
| type_definition_objects.ts:7:5:7:11 | [VarDecl] enumObj | semmle.label | [VarDecl] enumObj |
| type_definition_objects.ts:7:5:7:15 | [VariableDeclarator] enumObj = E | semmle.label | [VariableDeclarator] enumObj = E |
| type_definition_objects.ts:7:15:7:15 | [VarRef] E | semmle.label | [VarRef] E |
| type_definition_objects.ts:9:1:9:22 | [ExportDeclaration] export ... e N {;} | semmle.label | [ExportDeclaration] export ... e N {;} |
-| type_definition_objects.ts:9:1:9:22 | [ExportDeclaration] export ... e N {;} | semmle.order | 68 |
+| type_definition_objects.ts:9:1:9:22 | [ExportDeclaration] export ... e N {;} | semmle.order | 69 |
| type_definition_objects.ts:9:8:9:22 | [NamespaceDeclaration] namespace N {;} | semmle.label | [NamespaceDeclaration] namespace N {;} |
| type_definition_objects.ts:9:18:9:18 | [VarDecl] N | semmle.label | [VarDecl] N |
| type_definition_objects.ts:9:21:9:21 | [EmptyStmt] ; | semmle.label | [EmptyStmt] ; |
| type_definition_objects.ts:10:1:10:21 | [DeclStmt] let namespaceObj = ... | semmle.label | [DeclStmt] let namespaceObj = ... |
-| type_definition_objects.ts:10:1:10:21 | [DeclStmt] let namespaceObj = ... | semmle.order | 69 |
+| type_definition_objects.ts:10:1:10:21 | [DeclStmt] let namespaceObj = ... | semmle.order | 70 |
| type_definition_objects.ts:10:5:10:16 | [VarDecl] namespaceObj | semmle.label | [VarDecl] namespaceObj |
| type_definition_objects.ts:10:5:10:20 | [VariableDeclarator] namespaceObj = N | semmle.label | [VariableDeclarator] namespaceObj = N |
| type_definition_objects.ts:10:20:10:20 | [VarRef] N | semmle.label | [VarRef] N |
| type_definitions.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.label | [ImportDeclaration] import ... dummy"; |
-| type_definitions.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 70 |
+| type_definitions.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 71 |
| type_definitions.ts:1:8:1:17 | [ImportSpecifier] * as dummy | semmle.label | [ImportSpecifier] * as dummy |
| type_definitions.ts:1:13:1:17 | [VarDecl] dummy | semmle.label | [VarDecl] dummy |
| type_definitions.ts:1:24:1:32 | [Literal] "./dummy" | semmle.label | [Literal] "./dummy" |
| type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } |
-| type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | semmle.order | 71 |
+| type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | semmle.order | 72 |
| type_definitions.ts:3:11:3:11 | [Identifier] I | semmle.label | [Identifier] I |
| type_definitions.ts:3:13:3:13 | [Identifier] S | semmle.label | [Identifier] S |
| type_definitions.ts:3:13:3:13 | [TypeParameter] S | semmle.label | [TypeParameter] S |
@@ -561,14 +732,14 @@ nodes
| type_definitions.ts:4:3:4:7 | [FieldDeclaration] x: S; | semmle.label | [FieldDeclaration] x: S; |
| type_definitions.ts:4:6:4:6 | [LocalTypeAccess] S | semmle.label | [LocalTypeAccess] S |
| type_definitions.ts:6:1:6:16 | [DeclStmt] let i = ... | semmle.label | [DeclStmt] let i = ... |
-| type_definitions.ts:6:1:6:16 | [DeclStmt] let i = ... | semmle.order | 72 |
+| type_definitions.ts:6:1:6:16 | [DeclStmt] let i = ... | semmle.order | 73 |
| type_definitions.ts:6:5:6:5 | [VarDecl] i | semmle.label | [VarDecl] i |
| type_definitions.ts:6:5:6:16 | [VariableDeclarator] i: I | semmle.label | [VariableDeclarator] i: I |
| type_definitions.ts:6:8:6:8 | [LocalTypeAccess] I | semmle.label | [LocalTypeAccess] I |
| type_definitions.ts:6:8:6:16 | [GenericTypeExpr] I | semmle.label | [GenericTypeExpr] I |
| type_definitions.ts:6:10:6:15 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number |
| type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | semmle.label | [ClassDefinition,TypeDefinition] class C ... x: T } |
-| type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | semmle.order | 73 |
+| type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | semmle.order | 74 |
| type_definitions.ts:8:7:8:7 | [VarDecl] C | semmle.label | [VarDecl] C |
| type_definitions.ts:8:8:8:7 | [BlockStmt] {} | semmle.label | [BlockStmt] {} |
| type_definitions.ts:8:8:8:7 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | [ClassInitializedMember,ConstructorDefinition] constructor() {} |
@@ -580,14 +751,14 @@ nodes
| type_definitions.ts:9:3:9:6 | [FieldDeclaration] x: T | semmle.label | [FieldDeclaration] x: T |
| type_definitions.ts:9:6:9:6 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T |
| type_definitions.ts:11:1:11:17 | [DeclStmt] let c = ... | semmle.label | [DeclStmt] let c = ... |
-| type_definitions.ts:11:1:11:17 | [DeclStmt] let c = ... | semmle.order | 74 |
+| type_definitions.ts:11:1:11:17 | [DeclStmt] let c = ... | semmle.order | 75 |
| type_definitions.ts:11:5:11:5 | [VarDecl] c | semmle.label | [VarDecl] c |
| type_definitions.ts:11:5:11:16 | [VariableDeclarator] c: C | semmle.label | [VariableDeclarator] c: C |
| type_definitions.ts:11:8:11:8 | [LocalTypeAccess] C | semmle.label | [LocalTypeAccess] C |
| type_definitions.ts:11:8:11:16 | [GenericTypeExpr] C | semmle.label | [GenericTypeExpr] C |
| type_definitions.ts:11:10:11:15 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number |
| type_definitions.ts:13:1:15:1 | [EnumDeclaration,TypeDefinition] enum Co ... blue } | semmle.label | [EnumDeclaration,TypeDefinition] enum Co ... blue } |
-| type_definitions.ts:13:1:15:1 | [EnumDeclaration,TypeDefinition] enum Co ... blue } | semmle.order | 75 |
+| type_definitions.ts:13:1:15:1 | [EnumDeclaration,TypeDefinition] enum Co ... blue } | semmle.order | 76 |
| type_definitions.ts:13:6:13:10 | [VarDecl] Color | semmle.label | [VarDecl] Color |
| type_definitions.ts:14:3:14:5 | [EnumMember,TypeDefinition] red | semmle.label | [EnumMember,TypeDefinition] red |
| type_definitions.ts:14:3:14:5 | [VarDecl] red | semmle.label | [VarDecl] red |
@@ -596,29 +767,29 @@ nodes
| type_definitions.ts:14:15:14:18 | [EnumMember,TypeDefinition] blue | semmle.label | [EnumMember,TypeDefinition] blue |
| type_definitions.ts:14:15:14:18 | [VarDecl] blue | semmle.label | [VarDecl] blue |
| type_definitions.ts:16:1:16:17 | [DeclStmt] let color = ... | semmle.label | [DeclStmt] let color = ... |
-| type_definitions.ts:16:1:16:17 | [DeclStmt] let color = ... | semmle.order | 76 |
+| type_definitions.ts:16:1:16:17 | [DeclStmt] let color = ... | semmle.order | 77 |
| type_definitions.ts:16:5:16:9 | [VarDecl] color | semmle.label | [VarDecl] color |
| type_definitions.ts:16:5:16:16 | [VariableDeclarator] color: Color | semmle.label | [VariableDeclarator] color: Color |
| type_definitions.ts:16:12:16:16 | [LocalTypeAccess] Color | semmle.label | [LocalTypeAccess] Color |
| type_definitions.ts:18:1:18:33 | [EnumDeclaration,TypeDefinition] enum En ... ember } | semmle.label | [EnumDeclaration,TypeDefinition] enum En ... ember } |
-| type_definitions.ts:18:1:18:33 | [EnumDeclaration,TypeDefinition] enum En ... ember } | semmle.order | 77 |
+| type_definitions.ts:18:1:18:33 | [EnumDeclaration,TypeDefinition] enum En ... ember } | semmle.order | 78 |
| type_definitions.ts:18:6:18:22 | [VarDecl] EnumWithOneMember | semmle.label | [VarDecl] EnumWithOneMember |
| type_definitions.ts:18:26:18:31 | [EnumMember,TypeDefinition] member | semmle.label | [EnumMember,TypeDefinition] member |
| type_definitions.ts:18:26:18:31 | [VarDecl] member | semmle.label | [VarDecl] member |
| type_definitions.ts:19:1:19:25 | [DeclStmt] let e = ... | semmle.label | [DeclStmt] let e = ... |
-| type_definitions.ts:19:1:19:25 | [DeclStmt] let e = ... | semmle.order | 78 |
+| type_definitions.ts:19:1:19:25 | [DeclStmt] let e = ... | semmle.order | 79 |
| type_definitions.ts:19:5:19:5 | [VarDecl] e | semmle.label | [VarDecl] e |
| type_definitions.ts:19:5:19:24 | [VariableDeclarator] e: EnumWithOneMember | semmle.label | [VariableDeclarator] e: EnumWithOneMember |
| type_definitions.ts:19:8:19:24 | [LocalTypeAccess] EnumWithOneMember | semmle.label | [LocalTypeAccess] EnumWithOneMember |
| type_definitions.ts:21:1:21:20 | [TypeAliasDeclaration,TypeDefinition] type Alias = T[]; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Alias = T[]; |
-| type_definitions.ts:21:1:21:20 | [TypeAliasDeclaration,TypeDefinition] type Alias = T[]; | semmle.order | 79 |
+| type_definitions.ts:21:1:21:20 | [TypeAliasDeclaration,TypeDefinition] type Alias = T[]; | semmle.order | 80 |
| type_definitions.ts:21:6:21:10 | [Identifier] Alias | semmle.label | [Identifier] Alias |
| type_definitions.ts:21:12:21:12 | [Identifier] T | semmle.label | [Identifier] T |
| type_definitions.ts:21:12:21:12 | [TypeParameter] T | semmle.label | [TypeParameter] T |
| type_definitions.ts:21:17:21:17 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T |
| type_definitions.ts:21:17:21:19 | [ArrayTypeExpr] T[] | semmle.label | [ArrayTypeExpr] T[] |
| type_definitions.ts:22:1:22:39 | [DeclStmt] let aliasForNumberArray = ... | semmle.label | [DeclStmt] let aliasForNumberArray = ... |
-| type_definitions.ts:22:1:22:39 | [DeclStmt] let aliasForNumberArray = ... | semmle.order | 80 |
+| type_definitions.ts:22:1:22:39 | [DeclStmt] let aliasForNumberArray = ... | semmle.order | 81 |
| type_definitions.ts:22:5:22:23 | [VarDecl] aliasForNumberArray | semmle.label | [VarDecl] aliasForNumberArray |
| type_definitions.ts:22:5:22:38 | [VariableDeclarator] aliasFo ... number> | semmle.label | [VariableDeclarator] aliasFo ... number> |
| type_definitions.ts:22:26:22:30 | [LocalTypeAccess] Alias | semmle.label | [LocalTypeAccess] Alias |
@@ -745,6 +916,10 @@ edges
| dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | dummy.ts:4:22:4:22 | [RegExpNormalConstant] c | semmle.order | 2 |
| dummy.ts:4:20:4:21 | [RegExpPlus] b+ | dummy.ts:4:20:4:20 | [RegExpNormalConstant] b | semmle.label | 0 |
| dummy.ts:4:20:4:21 | [RegExpPlus] b+ | dummy.ts:4:20:4:20 | [RegExpNormalConstant] b | semmle.order | 0 |
+| file://:0:0:0:0 | (Arguments) | tst.ts:86:27:86:31 | [VarRef] value | semmle.label | 0 |
+| file://:0:0:0:0 | (Arguments) | tst.ts:86:27:86:31 | [VarRef] value | semmle.order | 0 |
+| file://:0:0:0:0 | (Arguments) | tst.ts:97:27:97:26 | [SpreadElement] ...args | semmle.label | 0 |
+| file://:0:0:0:0 | (Arguments) | tst.ts:97:27:97:26 | [SpreadElement] ...args | semmle.order | 0 |
| file://:0:0:0:0 | (Parameters) | tst.ts:14:17:14:17 | [SimpleParameter] x | semmle.label | 0 |
| file://:0:0:0:0 | (Parameters) | tst.ts:14:17:14:17 | [SimpleParameter] x | semmle.order | 0 |
| file://:0:0:0:0 | (Parameters) | tst.ts:14:28:14:28 | [SimpleParameter] y | semmle.label | 1 |
@@ -761,6 +936,14 @@ edges
| file://:0:0:0:0 | (Parameters) | tst.ts:20:23:20:23 | [SimpleParameter] x | semmle.order | 0 |
| file://:0:0:0:0 | (Parameters) | tst.ts:20:26:20:26 | [SimpleParameter] y | semmle.label | 1 |
| file://:0:0:0:0 | (Parameters) | tst.ts:20:26:20:26 | [SimpleParameter] y | semmle.order | 1 |
+| file://:0:0:0:0 | (Parameters) | tst.ts:75:14:75:18 | [SimpleParameter] value | semmle.label | 0 |
+| file://:0:0:0:0 | (Parameters) | tst.ts:75:14:75:18 | [SimpleParameter] value | semmle.order | 0 |
+| file://:0:0:0:0 | (Parameters) | tst.ts:85:14:85:18 | [SimpleParameter] value | semmle.label | 0 |
+| file://:0:0:0:0 | (Parameters) | tst.ts:85:14:85:18 | [SimpleParameter] value | semmle.order | 0 |
+| file://:0:0:0:0 | (Parameters) | tst.ts:97:27:97:26 | [SimpleParameter] args | semmle.label | 0 |
+| file://:0:0:0:0 | (Parameters) | tst.ts:97:27:97:26 | [SimpleParameter] args | semmle.order | 0 |
+| file://:0:0:0:0 | (Parameters) | tst.ts:104:16:104:16 | [SimpleParameter] s | semmle.label | 0 |
+| file://:0:0:0:0 | (Parameters) | tst.ts:104:16:104:16 | [SimpleParameter] s | semmle.order | 0 |
| file://:0:0:0:0 | (Parameters) | type_alias.ts:14:10:14:17 | [SimpleParameter] property | semmle.label | 0 |
| file://:0:0:0:0 | (Parameters) | type_alias.ts:14:10:14:17 | [SimpleParameter] property | semmle.order | 0 |
| file://:0:0:0:0 | (Parameters) | type_alias.ts:21:19:21:21 | [SimpleParameter] key | semmle.label | 0 |
@@ -1251,6 +1434,332 @@ edges
| tst.ts:69:25:69:44 | [Property] yetAnotherType: true | tst.ts:69:25:69:38 | [Label] yetAnotherType | semmle.order | 1 |
| tst.ts:69:25:69:44 | [Property] yetAnotherType: true | tst.ts:69:41:69:44 | [Literal] true | semmle.label | 2 |
| tst.ts:69:25:69:44 | [Property] yetAnotherType: true | tst.ts:69:41:69:44 | [Literal] true | semmle.order | 2 |
+| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:71:8:71:11 | [VarDecl] TS43 | semmle.label | 1 |
+| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:71:8:71:11 | [VarDecl] TS43 | semmle.order | 1 |
+| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:73:3:76:3 | [InterfaceDeclaration,TypeDefinition] interfa ... n); } | semmle.label | 2 |
+| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:73:3:76:3 | [InterfaceDeclaration,TypeDefinition] interfa ... n); } | semmle.order | 2 |
+| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:78:3:88:3 | [ExportDeclaration] export ... } } | semmle.label | 3 |
+| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:78:3:88:3 | [ExportDeclaration] export ... } } | semmle.order | 3 |
+| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:91:3:95:3 | [ClassDefinition,TypeDefinition] class S ... } } | semmle.label | 4 |
+| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:91:3:95:3 | [ClassDefinition,TypeDefinition] class S ... } } | semmle.order | 4 |
+| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:97:3:101:3 | [ClassDefinition,TypeDefinition] class S ... } } | semmle.label | 5 |
+| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:97:3:101:3 | [ClassDefinition,TypeDefinition] class S ... } } | semmle.order | 5 |
+| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:104:3:107:3 | [FunctionDeclStmt] functio ... }`; } | semmle.label | 6 |
+| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:104:3:107:3 | [FunctionDeclStmt] functio ... }`; } | semmle.order | 6 |
+| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:109:3:109:50 | [DeclStmt] let s1 = ... | semmle.label | 7 |
+| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:109:3:109:50 | [DeclStmt] let s1 = ... | semmle.order | 7 |
+| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:110:3:110:26 | [DeclStmt] let s2 = ... | semmle.label | 8 |
+| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:110:3:110:26 | [DeclStmt] let s2 = ... | semmle.order | 8 |
+| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:111:3:111:34 | [DeclStmt] let s3 = ... | semmle.label | 9 |
+| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:111:3:111:34 | [DeclStmt] let s3 = ... | semmle.order | 9 |
+| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:112:3:112:10 | [ExprStmt] s1 = s2; | semmle.label | 10 |
+| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:112:3:112:10 | [ExprStmt] s1 = s2; | semmle.order | 10 |
+| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:113:3:113:10 | [ExprStmt] s1 = s3; | semmle.label | 11 |
+| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:113:3:113:10 | [ExprStmt] s1 = s3; | semmle.order | 11 |
+| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:116:3:129:3 | [ClassDefinition,TypeDefinition] class F ... } } | semmle.label | 12 |
+| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:116:3:129:3 | [ClassDefinition,TypeDefinition] class F ... } } | semmle.order | 12 |
+| tst.ts:73:3:76:3 | [InterfaceDeclaration,TypeDefinition] interfa ... n); } | tst.ts:73:13:73:18 | [Identifier] ThingI | semmle.label | 1 |
+| tst.ts:73:3:76:3 | [InterfaceDeclaration,TypeDefinition] interfa ... n); } | tst.ts:73:13:73:18 | [Identifier] ThingI | semmle.order | 1 |
+| tst.ts:73:3:76:3 | [InterfaceDeclaration,TypeDefinition] interfa ... n); } | tst.ts:74:5:74:22 | [GetterMethodSignature] get size(): number | semmle.label | 2 |
+| tst.ts:73:3:76:3 | [InterfaceDeclaration,TypeDefinition] interfa ... n); } | tst.ts:74:5:74:22 | [GetterMethodSignature] get size(): number | semmle.order | 2 |
+| tst.ts:73:3:76:3 | [InterfaceDeclaration,TypeDefinition] interfa ... n); } | tst.ts:75:5:75:47 | [SetterMethodSignature] set siz ... olean); | semmle.label | 3 |
+| tst.ts:73:3:76:3 | [InterfaceDeclaration,TypeDefinition] interfa ... n); } | tst.ts:75:5:75:47 | [SetterMethodSignature] set siz ... olean); | semmle.order | 3 |
+| tst.ts:74:5:74:22 | [FunctionExpr] get size(): number | tst.ts:74:17:74:22 | [KeywordTypeExpr] number | semmle.label | 4 |
+| tst.ts:74:5:74:22 | [FunctionExpr] get size(): number | tst.ts:74:17:74:22 | [KeywordTypeExpr] number | semmle.order | 4 |
+| tst.ts:74:5:74:22 | [GetterMethodSignature] get size(): number | tst.ts:74:5:74:22 | [FunctionExpr] get size(): number | semmle.label | 1 |
+| tst.ts:74:5:74:22 | [GetterMethodSignature] get size(): number | tst.ts:74:5:74:22 | [FunctionExpr] get size(): number | semmle.order | 1 |
+| tst.ts:74:5:74:22 | [GetterMethodSignature] get size(): number | tst.ts:74:9:74:12 | [Label] size | semmle.label | 2 |
+| tst.ts:74:5:74:22 | [GetterMethodSignature] get size(): number | tst.ts:74:9:74:12 | [Label] size | semmle.order | 2 |
+| tst.ts:75:5:75:47 | [FunctionExpr] set siz ... olean); | file://:0:0:0:0 | (Parameters) | semmle.label | 1 |
+| tst.ts:75:5:75:47 | [FunctionExpr] set siz ... olean); | file://:0:0:0:0 | (Parameters) | semmle.order | 1 |
+| tst.ts:75:5:75:47 | [SetterMethodSignature] set siz ... olean); | tst.ts:75:5:75:47 | [FunctionExpr] set siz ... olean); | semmle.label | 1 |
+| tst.ts:75:5:75:47 | [SetterMethodSignature] set siz ... olean); | tst.ts:75:5:75:47 | [FunctionExpr] set siz ... olean); | semmle.order | 1 |
+| tst.ts:75:5:75:47 | [SetterMethodSignature] set siz ... olean); | tst.ts:75:9:75:12 | [Label] size | semmle.label | 2 |
+| tst.ts:75:5:75:47 | [SetterMethodSignature] set siz ... olean); | tst.ts:75:9:75:12 | [Label] size | semmle.order | 2 |
+| tst.ts:75:14:75:18 | [SimpleParameter] value | tst.ts:75:21:75:45 | [UnionTypeExpr] number ... boolean | semmle.label | 0 |
+| tst.ts:75:14:75:18 | [SimpleParameter] value | tst.ts:75:21:75:45 | [UnionTypeExpr] number ... boolean | semmle.order | 0 |
+| tst.ts:75:21:75:45 | [UnionTypeExpr] number ... boolean | tst.ts:75:21:75:26 | [KeywordTypeExpr] number | semmle.label | 1 |
+| tst.ts:75:21:75:45 | [UnionTypeExpr] number ... boolean | tst.ts:75:21:75:26 | [KeywordTypeExpr] number | semmle.order | 1 |
+| tst.ts:75:21:75:45 | [UnionTypeExpr] number ... boolean | tst.ts:75:30:75:35 | [KeywordTypeExpr] string | semmle.label | 2 |
+| tst.ts:75:21:75:45 | [UnionTypeExpr] number ... boolean | tst.ts:75:30:75:35 | [KeywordTypeExpr] string | semmle.order | 2 |
+| tst.ts:75:21:75:45 | [UnionTypeExpr] number ... boolean | tst.ts:75:39:75:45 | [KeywordTypeExpr] boolean | semmle.label | 3 |
+| tst.ts:75:21:75:45 | [UnionTypeExpr] number ... boolean | tst.ts:75:39:75:45 | [KeywordTypeExpr] boolean | semmle.order | 3 |
+| tst.ts:78:3:88:3 | [ExportDeclaration] export ... } } | tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | semmle.label | 1 |
+| tst.ts:78:3:88:3 | [ExportDeclaration] export ... } } | tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | semmle.order | 1 |
+| tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | tst.ts:78:16:78:20 | [VarDecl] Thing | semmle.label | 1 |
+| tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | tst.ts:78:16:78:20 | [VarDecl] Thing | semmle.order | 1 |
+| tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | tst.ts:78:33:78:38 | [LocalTypeAccess] ThingI | semmle.label | 2 |
+| tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | tst.ts:78:33:78:38 | [LocalTypeAccess] ThingI | semmle.order | 2 |
+| tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | tst.ts:78:40:78:39 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | 3 |
+| tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | tst.ts:78:40:78:39 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.order | 3 |
+| tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | tst.ts:79:5:79:14 | [FieldDeclaration] #size = 0; | semmle.label | 4 |
+| tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | tst.ts:79:5:79:14 | [FieldDeclaration] #size = 0; | semmle.order | 4 |
+| tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | tst.ts:81:5:83:5 | [ClassInitializedMember,GetterMethodDefinition] get siz ... ; } | semmle.label | 5 |
+| tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | tst.ts:81:5:83:5 | [ClassInitializedMember,GetterMethodDefinition] get siz ... ; } | semmle.order | 5 |
+| tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | tst.ts:85:5:87:5 | [ClassInitializedMember,SetterMethodDefinition] set siz ... ; } | semmle.label | 6 |
+| tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | tst.ts:85:5:87:5 | [ClassInitializedMember,SetterMethodDefinition] set siz ... ; } | semmle.order | 6 |
+| tst.ts:78:40:78:39 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:78:40:78:39 | [FunctionExpr] () {} | semmle.label | 2 |
+| tst.ts:78:40:78:39 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:78:40:78:39 | [FunctionExpr] () {} | semmle.order | 2 |
+| tst.ts:78:40:78:39 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:78:40:78:39 | [Label] constructor | semmle.label | 1 |
+| tst.ts:78:40:78:39 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:78:40:78:39 | [Label] constructor | semmle.order | 1 |
+| tst.ts:78:40:78:39 | [FunctionExpr] () {} | tst.ts:78:40:78:39 | [BlockStmt] {} | semmle.label | 5 |
+| tst.ts:78:40:78:39 | [FunctionExpr] () {} | tst.ts:78:40:78:39 | [BlockStmt] {} | semmle.order | 5 |
+| tst.ts:79:5:79:14 | [FieldDeclaration] #size = 0; | tst.ts:79:5:79:9 | [Label] #size | semmle.label | 1 |
+| tst.ts:79:5:79:14 | [FieldDeclaration] #size = 0; | tst.ts:79:5:79:9 | [Label] #size | semmle.order | 1 |
+| tst.ts:79:5:79:14 | [FieldDeclaration] #size = 0; | tst.ts:79:13:79:13 | [Literal] 0 | semmle.label | 2 |
+| tst.ts:79:5:79:14 | [FieldDeclaration] #size = 0; | tst.ts:79:13:79:13 | [Literal] 0 | semmle.order | 2 |
+| tst.ts:81:5:83:5 | [ClassInitializedMember,GetterMethodDefinition] get siz ... ; } | tst.ts:81:5:83:5 | [FunctionExpr] get siz ... ; } | semmle.label | 1 |
+| tst.ts:81:5:83:5 | [ClassInitializedMember,GetterMethodDefinition] get siz ... ; } | tst.ts:81:5:83:5 | [FunctionExpr] get siz ... ; } | semmle.order | 1 |
+| tst.ts:81:5:83:5 | [ClassInitializedMember,GetterMethodDefinition] get siz ... ; } | tst.ts:81:9:81:12 | [Label] size | semmle.label | 2 |
+| tst.ts:81:5:83:5 | [ClassInitializedMember,GetterMethodDefinition] get siz ... ; } | tst.ts:81:9:81:12 | [Label] size | semmle.order | 2 |
+| tst.ts:81:5:83:5 | [FunctionExpr] get siz ... ; } | tst.ts:81:17:81:22 | [KeywordTypeExpr] number | semmle.label | 4 |
+| tst.ts:81:5:83:5 | [FunctionExpr] get siz ... ; } | tst.ts:81:17:81:22 | [KeywordTypeExpr] number | semmle.order | 4 |
+| tst.ts:81:5:83:5 | [FunctionExpr] get siz ... ; } | tst.ts:81:24:83:5 | [BlockStmt] { ... ; } | semmle.label | 5 |
+| tst.ts:81:5:83:5 | [FunctionExpr] get siz ... ; } | tst.ts:81:24:83:5 | [BlockStmt] { ... ; } | semmle.order | 5 |
+| tst.ts:81:24:83:5 | [BlockStmt] { ... ; } | tst.ts:82:7:82:24 | [ReturnStmt] return this.#size; | semmle.label | 1 |
+| tst.ts:81:24:83:5 | [BlockStmt] { ... ; } | tst.ts:82:7:82:24 | [ReturnStmt] return this.#size; | semmle.order | 1 |
+| tst.ts:82:7:82:24 | [ReturnStmt] return this.#size; | tst.ts:82:14:82:23 | [DotExpr] this.#size | semmle.label | 1 |
+| tst.ts:82:7:82:24 | [ReturnStmt] return this.#size; | tst.ts:82:14:82:23 | [DotExpr] this.#size | semmle.order | 1 |
+| tst.ts:82:14:82:23 | [DotExpr] this.#size | tst.ts:82:14:82:17 | [ThisExpr] this | semmle.label | 1 |
+| tst.ts:82:14:82:23 | [DotExpr] this.#size | tst.ts:82:14:82:17 | [ThisExpr] this | semmle.order | 1 |
+| tst.ts:82:14:82:23 | [DotExpr] this.#size | tst.ts:82:19:82:23 | [Label] #size | semmle.label | 2 |
+| tst.ts:82:14:82:23 | [DotExpr] this.#size | tst.ts:82:19:82:23 | [Label] #size | semmle.order | 2 |
+| tst.ts:85:5:87:5 | [ClassInitializedMember,SetterMethodDefinition] set siz ... ; } | tst.ts:85:5:87:5 | [FunctionExpr] set siz ... ; } | semmle.label | 1 |
+| tst.ts:85:5:87:5 | [ClassInitializedMember,SetterMethodDefinition] set siz ... ; } | tst.ts:85:5:87:5 | [FunctionExpr] set siz ... ; } | semmle.order | 1 |
+| tst.ts:85:5:87:5 | [ClassInitializedMember,SetterMethodDefinition] set siz ... ; } | tst.ts:85:9:85:12 | [Label] size | semmle.label | 2 |
+| tst.ts:85:5:87:5 | [ClassInitializedMember,SetterMethodDefinition] set siz ... ; } | tst.ts:85:9:85:12 | [Label] size | semmle.order | 2 |
+| tst.ts:85:5:87:5 | [FunctionExpr] set siz ... ; } | file://:0:0:0:0 | (Parameters) | semmle.label | 1 |
+| tst.ts:85:5:87:5 | [FunctionExpr] set siz ... ; } | file://:0:0:0:0 | (Parameters) | semmle.order | 1 |
+| tst.ts:85:5:87:5 | [FunctionExpr] set siz ... ; } | tst.ts:85:48:87:5 | [BlockStmt] { ... ; } | semmle.label | 5 |
+| tst.ts:85:5:87:5 | [FunctionExpr] set siz ... ; } | tst.ts:85:48:87:5 | [BlockStmt] { ... ; } | semmle.order | 5 |
+| tst.ts:85:14:85:18 | [SimpleParameter] value | tst.ts:85:21:85:45 | [UnionTypeExpr] string ... boolean | semmle.label | 0 |
+| tst.ts:85:14:85:18 | [SimpleParameter] value | tst.ts:85:21:85:45 | [UnionTypeExpr] string ... boolean | semmle.order | 0 |
+| tst.ts:85:21:85:45 | [UnionTypeExpr] string ... boolean | tst.ts:85:21:85:26 | [KeywordTypeExpr] string | semmle.label | 1 |
+| tst.ts:85:21:85:45 | [UnionTypeExpr] string ... boolean | tst.ts:85:21:85:26 | [KeywordTypeExpr] string | semmle.order | 1 |
+| tst.ts:85:21:85:45 | [UnionTypeExpr] string ... boolean | tst.ts:85:30:85:35 | [KeywordTypeExpr] number | semmle.label | 2 |
+| tst.ts:85:21:85:45 | [UnionTypeExpr] string ... boolean | tst.ts:85:30:85:35 | [KeywordTypeExpr] number | semmle.order | 2 |
+| tst.ts:85:21:85:45 | [UnionTypeExpr] string ... boolean | tst.ts:85:39:85:45 | [KeywordTypeExpr] boolean | semmle.label | 3 |
+| tst.ts:85:21:85:45 | [UnionTypeExpr] string ... boolean | tst.ts:85:39:85:45 | [KeywordTypeExpr] boolean | semmle.order | 3 |
+| tst.ts:85:48:87:5 | [BlockStmt] { ... ; } | tst.ts:86:7:86:33 | [ExprStmt] this.#s ... value); | semmle.label | 1 |
+| tst.ts:85:48:87:5 | [BlockStmt] { ... ; } | tst.ts:86:7:86:33 | [ExprStmt] this.#s ... value); | semmle.order | 1 |
+| tst.ts:86:7:86:16 | [DotExpr] this.#size | tst.ts:86:7:86:10 | [ThisExpr] this | semmle.label | 1 |
+| tst.ts:86:7:86:16 | [DotExpr] this.#size | tst.ts:86:7:86:10 | [ThisExpr] this | semmle.order | 1 |
+| tst.ts:86:7:86:16 | [DotExpr] this.#size | tst.ts:86:12:86:16 | [Label] #size | semmle.label | 2 |
+| tst.ts:86:7:86:16 | [DotExpr] this.#size | tst.ts:86:12:86:16 | [Label] #size | semmle.order | 2 |
+| tst.ts:86:7:86:32 | [AssignExpr] this.#s ... (value) | tst.ts:86:7:86:16 | [DotExpr] this.#size | semmle.label | 1 |
+| tst.ts:86:7:86:32 | [AssignExpr] this.#s ... (value) | tst.ts:86:7:86:16 | [DotExpr] this.#size | semmle.order | 1 |
+| tst.ts:86:7:86:32 | [AssignExpr] this.#s ... (value) | tst.ts:86:20:86:32 | [CallExpr] Number(value) | semmle.label | 2 |
+| tst.ts:86:7:86:32 | [AssignExpr] this.#s ... (value) | tst.ts:86:20:86:32 | [CallExpr] Number(value) | semmle.order | 2 |
+| tst.ts:86:7:86:33 | [ExprStmt] this.#s ... value); | tst.ts:86:7:86:32 | [AssignExpr] this.#s ... (value) | semmle.label | 1 |
+| tst.ts:86:7:86:33 | [ExprStmt] this.#s ... value); | tst.ts:86:7:86:32 | [AssignExpr] this.#s ... (value) | semmle.order | 1 |
+| tst.ts:86:20:86:32 | [CallExpr] Number(value) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 |
+| tst.ts:86:20:86:32 | [CallExpr] Number(value) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
+| tst.ts:86:20:86:32 | [CallExpr] Number(value) | tst.ts:86:20:86:25 | [VarRef] Number | semmle.label | 0 |
+| tst.ts:86:20:86:32 | [CallExpr] Number(value) | tst.ts:86:20:86:25 | [VarRef] Number | semmle.order | 0 |
+| tst.ts:91:3:95:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:91:9:91:13 | [VarDecl] Super | semmle.label | 1 |
+| tst.ts:91:3:95:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:91:9:91:13 | [VarDecl] Super | semmle.order | 1 |
+| tst.ts:91:3:95:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:91:15:91:14 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | 2 |
+| tst.ts:91:3:95:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:91:15:91:14 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.order | 2 |
+| tst.ts:91:3:95:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:92:5:94:5 | [ClassInitializedMember,MethodDefinition] random( ... ; } | semmle.label | 3 |
+| tst.ts:91:3:95:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:92:5:94:5 | [ClassInitializedMember,MethodDefinition] random( ... ; } | semmle.order | 3 |
+| tst.ts:91:15:91:14 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:91:15:91:14 | [FunctionExpr] () {} | semmle.label | 2 |
+| tst.ts:91:15:91:14 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:91:15:91:14 | [FunctionExpr] () {} | semmle.order | 2 |
+| tst.ts:91:15:91:14 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:91:15:91:14 | [Label] constructor | semmle.label | 1 |
+| tst.ts:91:15:91:14 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:91:15:91:14 | [Label] constructor | semmle.order | 1 |
+| tst.ts:91:15:91:14 | [FunctionExpr] () {} | tst.ts:91:15:91:14 | [BlockStmt] {} | semmle.label | 5 |
+| tst.ts:91:15:91:14 | [FunctionExpr] () {} | tst.ts:91:15:91:14 | [BlockStmt] {} | semmle.order | 5 |
+| tst.ts:92:5:94:5 | [ClassInitializedMember,MethodDefinition] random( ... ; } | tst.ts:92:5:92:10 | [Label] random | semmle.label | 1 |
+| tst.ts:92:5:94:5 | [ClassInitializedMember,MethodDefinition] random( ... ; } | tst.ts:92:5:92:10 | [Label] random | semmle.order | 1 |
+| tst.ts:92:5:94:5 | [ClassInitializedMember,MethodDefinition] random( ... ; } | tst.ts:92:5:94:5 | [FunctionExpr] random( ... ; } | semmle.label | 2 |
+| tst.ts:92:5:94:5 | [ClassInitializedMember,MethodDefinition] random( ... ; } | tst.ts:92:5:94:5 | [FunctionExpr] random( ... ; } | semmle.order | 2 |
+| tst.ts:92:5:94:5 | [FunctionExpr] random( ... ; } | tst.ts:92:15:92:20 | [KeywordTypeExpr] number | semmle.label | 4 |
+| tst.ts:92:5:94:5 | [FunctionExpr] random( ... ; } | tst.ts:92:15:92:20 | [KeywordTypeExpr] number | semmle.order | 4 |
+| tst.ts:92:5:94:5 | [FunctionExpr] random( ... ; } | tst.ts:92:22:94:5 | [BlockStmt] { ... ; } | semmle.label | 5 |
+| tst.ts:92:5:94:5 | [FunctionExpr] random( ... ; } | tst.ts:92:22:94:5 | [BlockStmt] { ... ; } | semmle.order | 5 |
+| tst.ts:92:22:94:5 | [BlockStmt] { ... ; } | tst.ts:93:7:93:15 | [ReturnStmt] return 4; | semmle.label | 1 |
+| tst.ts:92:22:94:5 | [BlockStmt] { ... ; } | tst.ts:93:7:93:15 | [ReturnStmt] return 4; | semmle.order | 1 |
+| tst.ts:93:7:93:15 | [ReturnStmt] return 4; | tst.ts:93:14:93:14 | [Literal] 4 | semmle.label | 1 |
+| tst.ts:93:7:93:15 | [ReturnStmt] return 4; | tst.ts:93:14:93:14 | [Literal] 4 | semmle.order | 1 |
+| tst.ts:97:3:101:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:97:9:97:11 | [VarDecl] Sub | semmle.label | 1 |
+| tst.ts:97:3:101:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:97:9:97:11 | [VarDecl] Sub | semmle.order | 1 |
+| tst.ts:97:3:101:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:97:21:97:25 | [VarRef] Super | semmle.label | 2 |
+| tst.ts:97:3:101:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:97:21:97:25 | [VarRef] Super | semmle.order | 2 |
+| tst.ts:97:3:101:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:97:27:97:26 | [ClassInitializedMember,ConstructorDefinition] constru ... rgs); } | semmle.label | 3 |
+| tst.ts:97:3:101:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:97:27:97:26 | [ClassInitializedMember,ConstructorDefinition] constru ... rgs); } | semmle.order | 3 |
+| tst.ts:97:3:101:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:98:5:100:5 | [ClassInitializedMember,MethodDefinition] overrid ... ; } | semmle.label | 4 |
+| tst.ts:97:3:101:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:98:5:100:5 | [ClassInitializedMember,MethodDefinition] overrid ... ; } | semmle.order | 4 |
+| tst.ts:97:27:97:26 | [BlockStmt] { super(...args); } | tst.ts:97:27:97:26 | [ExprStmt] super(...args); | semmle.label | 1 |
+| tst.ts:97:27:97:26 | [BlockStmt] { super(...args); } | tst.ts:97:27:97:26 | [ExprStmt] super(...args); | semmle.order | 1 |
+| tst.ts:97:27:97:26 | [CallExpr] super(...args) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 |
+| tst.ts:97:27:97:26 | [CallExpr] super(...args) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
+| tst.ts:97:27:97:26 | [CallExpr] super(...args) | tst.ts:97:27:97:26 | [SuperExpr] super | semmle.label | 0 |
+| tst.ts:97:27:97:26 | [CallExpr] super(...args) | tst.ts:97:27:97:26 | [SuperExpr] super | semmle.order | 0 |
+| tst.ts:97:27:97:26 | [ClassInitializedMember,ConstructorDefinition] constru ... rgs); } | tst.ts:97:27:97:26 | [FunctionExpr] (...arg ... rgs); } | semmle.label | 2 |
+| tst.ts:97:27:97:26 | [ClassInitializedMember,ConstructorDefinition] constru ... rgs); } | tst.ts:97:27:97:26 | [FunctionExpr] (...arg ... rgs); } | semmle.order | 2 |
+| tst.ts:97:27:97:26 | [ClassInitializedMember,ConstructorDefinition] constru ... rgs); } | tst.ts:97:27:97:26 | [Label] constructor | semmle.label | 1 |
+| tst.ts:97:27:97:26 | [ClassInitializedMember,ConstructorDefinition] constru ... rgs); } | tst.ts:97:27:97:26 | [Label] constructor | semmle.order | 1 |
+| tst.ts:97:27:97:26 | [ExprStmt] super(...args); | tst.ts:97:27:97:26 | [CallExpr] super(...args) | semmle.label | 1 |
+| tst.ts:97:27:97:26 | [ExprStmt] super(...args); | tst.ts:97:27:97:26 | [CallExpr] super(...args) | semmle.order | 1 |
+| tst.ts:97:27:97:26 | [FunctionExpr] (...arg ... rgs); } | file://:0:0:0:0 | (Parameters) | semmle.label | 1 |
+| tst.ts:97:27:97:26 | [FunctionExpr] (...arg ... rgs); } | file://:0:0:0:0 | (Parameters) | semmle.order | 1 |
+| tst.ts:97:27:97:26 | [FunctionExpr] (...arg ... rgs); } | tst.ts:97:27:97:26 | [BlockStmt] { super(...args); } | semmle.label | 5 |
+| tst.ts:97:27:97:26 | [FunctionExpr] (...arg ... rgs); } | tst.ts:97:27:97:26 | [BlockStmt] { super(...args); } | semmle.order | 5 |
+| tst.ts:97:27:97:26 | [SpreadElement] ...args | tst.ts:97:27:97:26 | [VarRef] args | semmle.label | 1 |
+| tst.ts:97:27:97:26 | [SpreadElement] ...args | tst.ts:97:27:97:26 | [VarRef] args | semmle.order | 1 |
+| tst.ts:98:5:100:5 | [ClassInitializedMember,MethodDefinition] overrid ... ; } | tst.ts:98:5:100:5 | [FunctionExpr] overrid ... ; } | semmle.label | 1 |
+| tst.ts:98:5:100:5 | [ClassInitializedMember,MethodDefinition] overrid ... ; } | tst.ts:98:5:100:5 | [FunctionExpr] overrid ... ; } | semmle.order | 1 |
+| tst.ts:98:5:100:5 | [ClassInitializedMember,MethodDefinition] overrid ... ; } | tst.ts:98:14:98:19 | [Label] random | semmle.label | 2 |
+| tst.ts:98:5:100:5 | [ClassInitializedMember,MethodDefinition] overrid ... ; } | tst.ts:98:14:98:19 | [Label] random | semmle.order | 2 |
+| tst.ts:98:5:100:5 | [FunctionExpr] overrid ... ; } | tst.ts:98:24:98:29 | [KeywordTypeExpr] number | semmle.label | 4 |
+| tst.ts:98:5:100:5 | [FunctionExpr] overrid ... ; } | tst.ts:98:24:98:29 | [KeywordTypeExpr] number | semmle.order | 4 |
+| tst.ts:98:5:100:5 | [FunctionExpr] overrid ... ; } | tst.ts:98:31:100:5 | [BlockStmt] { ... ; } | semmle.label | 5 |
+| tst.ts:98:5:100:5 | [FunctionExpr] overrid ... ; } | tst.ts:98:31:100:5 | [BlockStmt] { ... ; } | semmle.order | 5 |
+| tst.ts:98:31:100:5 | [BlockStmt] { ... ; } | tst.ts:99:7:99:33 | [ReturnStmt] return ... ) * 10; | semmle.label | 1 |
+| tst.ts:98:31:100:5 | [BlockStmt] { ... ; } | tst.ts:99:7:99:33 | [ReturnStmt] return ... ) * 10; | semmle.order | 1 |
+| tst.ts:99:7:99:33 | [ReturnStmt] return ... ) * 10; | tst.ts:99:14:99:32 | [BinaryExpr] super.random() * 10 | semmle.label | 1 |
+| tst.ts:99:7:99:33 | [ReturnStmt] return ... ) * 10; | tst.ts:99:14:99:32 | [BinaryExpr] super.random() * 10 | semmle.order | 1 |
+| tst.ts:99:14:99:25 | [DotExpr] super.random | tst.ts:99:14:99:18 | [SuperExpr] super | semmle.label | 1 |
+| tst.ts:99:14:99:25 | [DotExpr] super.random | tst.ts:99:14:99:18 | [SuperExpr] super | semmle.order | 1 |
+| tst.ts:99:14:99:25 | [DotExpr] super.random | tst.ts:99:20:99:25 | [Label] random | semmle.label | 2 |
+| tst.ts:99:14:99:25 | [DotExpr] super.random | tst.ts:99:20:99:25 | [Label] random | semmle.order | 2 |
+| tst.ts:99:14:99:27 | [MethodCallExpr] super.random() | tst.ts:99:14:99:25 | [DotExpr] super.random | semmle.label | 0 |
+| tst.ts:99:14:99:27 | [MethodCallExpr] super.random() | tst.ts:99:14:99:25 | [DotExpr] super.random | semmle.order | 0 |
+| tst.ts:99:14:99:32 | [BinaryExpr] super.random() * 10 | tst.ts:99:14:99:27 | [MethodCallExpr] super.random() | semmle.label | 1 |
+| tst.ts:99:14:99:32 | [BinaryExpr] super.random() * 10 | tst.ts:99:14:99:27 | [MethodCallExpr] super.random() | semmle.order | 1 |
+| tst.ts:99:14:99:32 | [BinaryExpr] super.random() * 10 | tst.ts:99:31:99:32 | [Literal] 10 | semmle.label | 2 |
+| tst.ts:99:14:99:32 | [BinaryExpr] super.random() * 10 | tst.ts:99:31:99:32 | [Literal] 10 | semmle.order | 2 |
+| tst.ts:104:3:107:3 | [FunctionDeclStmt] functio ... }`; } | file://:0:0:0:0 | (Parameters) | semmle.label | 1 |
+| tst.ts:104:3:107:3 | [FunctionDeclStmt] functio ... }`; } | file://:0:0:0:0 | (Parameters) | semmle.order | 1 |
+| tst.ts:104:3:107:3 | [FunctionDeclStmt] functio ... }`; } | tst.ts:104:12:104:14 | [VarDecl] bar | semmle.label | 0 |
+| tst.ts:104:3:107:3 | [FunctionDeclStmt] functio ... }`; } | tst.ts:104:12:104:14 | [VarDecl] bar | semmle.order | 0 |
+| tst.ts:104:3:107:3 | [FunctionDeclStmt] functio ... }`; } | tst.ts:104:28:104:44 | [TemplateLiteralTypeExpr] `hello ${string}` | semmle.label | 4 |
+| tst.ts:104:3:107:3 | [FunctionDeclStmt] functio ... }`; } | tst.ts:104:28:104:44 | [TemplateLiteralTypeExpr] `hello ${string}` | semmle.order | 4 |
+| tst.ts:104:3:107:3 | [FunctionDeclStmt] functio ... }`; } | tst.ts:104:46:107:3 | [BlockStmt] { / ... }`; } | semmle.label | 5 |
+| tst.ts:104:3:107:3 | [FunctionDeclStmt] functio ... }`; } | tst.ts:104:46:107:3 | [BlockStmt] { / ... }`; } | semmle.order | 5 |
+| tst.ts:104:16:104:16 | [SimpleParameter] s | tst.ts:104:19:104:24 | [KeywordTypeExpr] string | semmle.label | 0 |
+| tst.ts:104:16:104:16 | [SimpleParameter] s | tst.ts:104:19:104:24 | [KeywordTypeExpr] string | semmle.order | 0 |
+| tst.ts:104:28:104:44 | [TemplateLiteralTypeExpr] `hello ${string}` | tst.ts:104:29:104:34 | [LiteralTypeExpr] hello | semmle.label | 1 |
+| tst.ts:104:28:104:44 | [TemplateLiteralTypeExpr] `hello ${string}` | tst.ts:104:29:104:34 | [LiteralTypeExpr] hello | semmle.order | 1 |
+| tst.ts:104:28:104:44 | [TemplateLiteralTypeExpr] `hello ${string}` | tst.ts:104:37:104:42 | [KeywordTypeExpr] string | semmle.label | 2 |
+| tst.ts:104:28:104:44 | [TemplateLiteralTypeExpr] `hello ${string}` | tst.ts:104:37:104:42 | [KeywordTypeExpr] string | semmle.order | 2 |
+| tst.ts:104:46:107:3 | [BlockStmt] { / ... }`; } | tst.ts:106:5:106:24 | [ReturnStmt] return `hello ${s}`; | semmle.label | 1 |
+| tst.ts:104:46:107:3 | [BlockStmt] { / ... }`; } | tst.ts:106:5:106:24 | [ReturnStmt] return `hello ${s}`; | semmle.order | 1 |
+| tst.ts:106:5:106:24 | [ReturnStmt] return `hello ${s}`; | tst.ts:106:12:106:23 | [TemplateLiteral] `hello ${s}` | semmle.label | 1 |
+| tst.ts:106:5:106:24 | [ReturnStmt] return `hello ${s}`; | tst.ts:106:12:106:23 | [TemplateLiteral] `hello ${s}` | semmle.order | 1 |
+| tst.ts:106:12:106:23 | [TemplateLiteral] `hello ${s}` | tst.ts:106:13:106:18 | [TemplateElement] hello | semmle.label | 1 |
+| tst.ts:106:12:106:23 | [TemplateLiteral] `hello ${s}` | tst.ts:106:13:106:18 | [TemplateElement] hello | semmle.order | 1 |
+| tst.ts:106:12:106:23 | [TemplateLiteral] `hello ${s}` | tst.ts:106:21:106:21 | [VarRef] s | semmle.label | 2 |
+| tst.ts:106:12:106:23 | [TemplateLiteral] `hello ${s}` | tst.ts:106:21:106:21 | [VarRef] s | semmle.order | 2 |
+| tst.ts:109:3:109:50 | [DeclStmt] let s1 = ... | tst.ts:109:15:109:49 | [VariableDeclarator] s1: `${ ... umber}` | semmle.label | 1 |
+| tst.ts:109:3:109:50 | [DeclStmt] let s1 = ... | tst.ts:109:15:109:49 | [VariableDeclarator] s1: `${ ... umber}` | semmle.order | 1 |
+| tst.ts:109:15:109:49 | [VariableDeclarator] s1: `${ ... umber}` | tst.ts:109:15:109:16 | [VarDecl] s1 | semmle.label | 1 |
+| tst.ts:109:15:109:49 | [VariableDeclarator] s1: `${ ... umber}` | tst.ts:109:15:109:16 | [VarDecl] s1 | semmle.order | 1 |
+| tst.ts:109:15:109:49 | [VariableDeclarator] s1: `${ ... umber}` | tst.ts:109:19:109:49 | [TemplateLiteralTypeExpr] `${numb ... umber}` | semmle.label | 2 |
+| tst.ts:109:15:109:49 | [VariableDeclarator] s1: `${ ... umber}` | tst.ts:109:19:109:49 | [TemplateLiteralTypeExpr] `${numb ... umber}` | semmle.order | 2 |
+| tst.ts:109:19:109:49 | [TemplateLiteralTypeExpr] `${numb ... umber}` | tst.ts:109:22:109:27 | [KeywordTypeExpr] number | semmle.label | 1 |
+| tst.ts:109:19:109:49 | [TemplateLiteralTypeExpr] `${numb ... umber}` | tst.ts:109:22:109:27 | [KeywordTypeExpr] number | semmle.order | 1 |
+| tst.ts:109:19:109:49 | [TemplateLiteralTypeExpr] `${numb ... umber}` | tst.ts:109:29:109:29 | [LiteralTypeExpr] - | semmle.label | 2 |
+| tst.ts:109:19:109:49 | [TemplateLiteralTypeExpr] `${numb ... umber}` | tst.ts:109:29:109:29 | [LiteralTypeExpr] - | semmle.order | 2 |
+| tst.ts:109:19:109:49 | [TemplateLiteralTypeExpr] `${numb ... umber}` | tst.ts:109:32:109:37 | [KeywordTypeExpr] number | semmle.label | 3 |
+| tst.ts:109:19:109:49 | [TemplateLiteralTypeExpr] `${numb ... umber}` | tst.ts:109:32:109:37 | [KeywordTypeExpr] number | semmle.order | 3 |
+| tst.ts:109:19:109:49 | [TemplateLiteralTypeExpr] `${numb ... umber}` | tst.ts:109:39:109:39 | [LiteralTypeExpr] - | semmle.label | 4 |
+| tst.ts:109:19:109:49 | [TemplateLiteralTypeExpr] `${numb ... umber}` | tst.ts:109:39:109:39 | [LiteralTypeExpr] - | semmle.order | 4 |
+| tst.ts:109:19:109:49 | [TemplateLiteralTypeExpr] `${numb ... umber}` | tst.ts:109:42:109:47 | [KeywordTypeExpr] number | semmle.label | 5 |
+| tst.ts:109:19:109:49 | [TemplateLiteralTypeExpr] `${numb ... umber}` | tst.ts:109:42:109:47 | [KeywordTypeExpr] number | semmle.order | 5 |
+| tst.ts:110:3:110:26 | [DeclStmt] let s2 = ... | tst.ts:110:15:110:25 | [VariableDeclarator] s2: `1-2-3` | semmle.label | 1 |
+| tst.ts:110:3:110:26 | [DeclStmt] let s2 = ... | tst.ts:110:15:110:25 | [VariableDeclarator] s2: `1-2-3` | semmle.order | 1 |
+| tst.ts:110:15:110:25 | [VariableDeclarator] s2: `1-2-3` | tst.ts:110:15:110:16 | [VarDecl] s2 | semmle.label | 1 |
+| tst.ts:110:15:110:25 | [VariableDeclarator] s2: `1-2-3` | tst.ts:110:15:110:16 | [VarDecl] s2 | semmle.order | 1 |
+| tst.ts:110:15:110:25 | [VariableDeclarator] s2: `1-2-3` | tst.ts:110:19:110:25 | [TemplateLiteralTypeExpr] `1-2-3` | semmle.label | 2 |
+| tst.ts:110:15:110:25 | [VariableDeclarator] s2: `1-2-3` | tst.ts:110:19:110:25 | [TemplateLiteralTypeExpr] `1-2-3` | semmle.order | 2 |
+| tst.ts:110:19:110:25 | [TemplateLiteralTypeExpr] `1-2-3` | tst.ts:110:19:110:25 | [LiteralTypeExpr] `1-2-3` | semmle.label | 1 |
+| tst.ts:110:19:110:25 | [TemplateLiteralTypeExpr] `1-2-3` | tst.ts:110:19:110:25 | [LiteralTypeExpr] `1-2-3` | semmle.order | 1 |
+| tst.ts:111:3:111:34 | [DeclStmt] let s3 = ... | tst.ts:111:15:111:33 | [VariableDeclarator] s3: `${number}-2-3` | semmle.label | 1 |
+| tst.ts:111:3:111:34 | [DeclStmt] let s3 = ... | tst.ts:111:15:111:33 | [VariableDeclarator] s3: `${number}-2-3` | semmle.order | 1 |
+| tst.ts:111:15:111:33 | [VariableDeclarator] s3: `${number}-2-3` | tst.ts:111:15:111:16 | [VarDecl] s3 | semmle.label | 1 |
+| tst.ts:111:15:111:33 | [VariableDeclarator] s3: `${number}-2-3` | tst.ts:111:15:111:16 | [VarDecl] s3 | semmle.order | 1 |
+| tst.ts:111:15:111:33 | [VariableDeclarator] s3: `${number}-2-3` | tst.ts:111:19:111:33 | [TemplateLiteralTypeExpr] `${number}-2-3` | semmle.label | 2 |
+| tst.ts:111:15:111:33 | [VariableDeclarator] s3: `${number}-2-3` | tst.ts:111:19:111:33 | [TemplateLiteralTypeExpr] `${number}-2-3` | semmle.order | 2 |
+| tst.ts:111:19:111:33 | [TemplateLiteralTypeExpr] `${number}-2-3` | tst.ts:111:22:111:27 | [KeywordTypeExpr] number | semmle.label | 1 |
+| tst.ts:111:19:111:33 | [TemplateLiteralTypeExpr] `${number}-2-3` | tst.ts:111:22:111:27 | [KeywordTypeExpr] number | semmle.order | 1 |
+| tst.ts:111:19:111:33 | [TemplateLiteralTypeExpr] `${number}-2-3` | tst.ts:111:29:111:32 | [LiteralTypeExpr] -2-3 | semmle.label | 2 |
+| tst.ts:111:19:111:33 | [TemplateLiteralTypeExpr] `${number}-2-3` | tst.ts:111:29:111:32 | [LiteralTypeExpr] -2-3 | semmle.order | 2 |
+| tst.ts:112:3:112:9 | [AssignExpr] s1 = s2 | tst.ts:112:3:112:4 | [VarRef] s1 | semmle.label | 1 |
+| tst.ts:112:3:112:9 | [AssignExpr] s1 = s2 | tst.ts:112:3:112:4 | [VarRef] s1 | semmle.order | 1 |
+| tst.ts:112:3:112:9 | [AssignExpr] s1 = s2 | tst.ts:112:8:112:9 | [VarRef] s2 | semmle.label | 2 |
+| tst.ts:112:3:112:9 | [AssignExpr] s1 = s2 | tst.ts:112:8:112:9 | [VarRef] s2 | semmle.order | 2 |
+| tst.ts:112:3:112:10 | [ExprStmt] s1 = s2; | tst.ts:112:3:112:9 | [AssignExpr] s1 = s2 | semmle.label | 1 |
+| tst.ts:112:3:112:10 | [ExprStmt] s1 = s2; | tst.ts:112:3:112:9 | [AssignExpr] s1 = s2 | semmle.order | 1 |
+| tst.ts:113:3:113:9 | [AssignExpr] s1 = s3 | tst.ts:113:3:113:4 | [VarRef] s1 | semmle.label | 1 |
+| tst.ts:113:3:113:9 | [AssignExpr] s1 = s3 | tst.ts:113:3:113:4 | [VarRef] s1 | semmle.order | 1 |
+| tst.ts:113:3:113:9 | [AssignExpr] s1 = s3 | tst.ts:113:8:113:9 | [VarRef] s3 | semmle.label | 2 |
+| tst.ts:113:3:113:9 | [AssignExpr] s1 = s3 | tst.ts:113:8:113:9 | [VarRef] s3 | semmle.order | 2 |
+| tst.ts:113:3:113:10 | [ExprStmt] s1 = s3; | tst.ts:113:3:113:9 | [AssignExpr] s1 = s3 | semmle.label | 1 |
+| tst.ts:113:3:113:10 | [ExprStmt] s1 = s3; | tst.ts:113:3:113:9 | [AssignExpr] s1 = s3 | semmle.order | 1 |
+| tst.ts:116:3:129:3 | [ClassDefinition,TypeDefinition] class F ... } } | tst.ts:116:9:116:11 | [VarDecl] Foo | semmle.label | 1 |
+| tst.ts:116:3:129:3 | [ClassDefinition,TypeDefinition] class F ... } } | tst.ts:116:9:116:11 | [VarDecl] Foo | semmle.order | 1 |
+| tst.ts:116:3:129:3 | [ClassDefinition,TypeDefinition] class F ... } } | tst.ts:116:13:116:12 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | 2 |
+| tst.ts:116:3:129:3 | [ClassDefinition,TypeDefinition] class F ... } } | tst.ts:116:13:116:12 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.order | 2 |
+| tst.ts:116:3:129:3 | [ClassDefinition,TypeDefinition] class F ... } } | tst.ts:117:5:119:5 | [ClassInitializedMember,MethodDefinition] #someMe ... ; } | semmle.label | 3 |
+| tst.ts:116:3:129:3 | [ClassDefinition,TypeDefinition] class F ... } } | tst.ts:117:5:119:5 | [ClassInitializedMember,MethodDefinition] #someMe ... ; } | semmle.order | 3 |
+| tst.ts:116:3:129:3 | [ClassDefinition,TypeDefinition] class F ... } } | tst.ts:121:5:123:5 | [ClassInitializedMember,GetterMethodDefinition] get #so ... ; } | semmle.label | 4 |
+| tst.ts:116:3:129:3 | [ClassDefinition,TypeDefinition] class F ... } } | tst.ts:121:5:123:5 | [ClassInitializedMember,GetterMethodDefinition] get #so ... ; } | semmle.order | 4 |
+| tst.ts:116:3:129:3 | [ClassDefinition,TypeDefinition] class F ... } } | tst.ts:125:5:128:5 | [ClassInitializedMember,MethodDefinition] publicM ... ; } | semmle.label | 5 |
+| tst.ts:116:3:129:3 | [ClassDefinition,TypeDefinition] class F ... } } | tst.ts:125:5:128:5 | [ClassInitializedMember,MethodDefinition] publicM ... ; } | semmle.order | 5 |
+| tst.ts:116:13:116:12 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:116:13:116:12 | [FunctionExpr] () {} | semmle.label | 2 |
+| tst.ts:116:13:116:12 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:116:13:116:12 | [FunctionExpr] () {} | semmle.order | 2 |
+| tst.ts:116:13:116:12 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:116:13:116:12 | [Label] constructor | semmle.label | 1 |
+| tst.ts:116:13:116:12 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:116:13:116:12 | [Label] constructor | semmle.order | 1 |
+| tst.ts:116:13:116:12 | [FunctionExpr] () {} | tst.ts:116:13:116:12 | [BlockStmt] {} | semmle.label | 5 |
+| tst.ts:116:13:116:12 | [FunctionExpr] () {} | tst.ts:116:13:116:12 | [BlockStmt] {} | semmle.order | 5 |
+| tst.ts:117:5:119:5 | [ClassInitializedMember,MethodDefinition] #someMe ... ; } | tst.ts:117:5:117:15 | [Label] #someMethod | semmle.label | 1 |
+| tst.ts:117:5:119:5 | [ClassInitializedMember,MethodDefinition] #someMe ... ; } | tst.ts:117:5:117:15 | [Label] #someMethod | semmle.order | 1 |
+| tst.ts:117:5:119:5 | [ClassInitializedMember,MethodDefinition] #someMe ... ; } | tst.ts:117:5:119:5 | [FunctionExpr] #someMe ... ; } | semmle.label | 2 |
+| tst.ts:117:5:119:5 | [ClassInitializedMember,MethodDefinition] #someMe ... ; } | tst.ts:117:5:119:5 | [FunctionExpr] #someMe ... ; } | semmle.order | 2 |
+| tst.ts:117:5:119:5 | [FunctionExpr] #someMe ... ; } | tst.ts:117:20:117:25 | [KeywordTypeExpr] number | semmle.label | 4 |
+| tst.ts:117:5:119:5 | [FunctionExpr] #someMe ... ; } | tst.ts:117:20:117:25 | [KeywordTypeExpr] number | semmle.order | 4 |
+| tst.ts:117:5:119:5 | [FunctionExpr] #someMe ... ; } | tst.ts:117:27:119:5 | [BlockStmt] { ... ; } | semmle.label | 5 |
+| tst.ts:117:5:119:5 | [FunctionExpr] #someMe ... ; } | tst.ts:117:27:119:5 | [BlockStmt] { ... ; } | semmle.order | 5 |
+| tst.ts:117:27:119:5 | [BlockStmt] { ... ; } | tst.ts:118:7:118:16 | [ReturnStmt] return 42; | semmle.label | 1 |
+| tst.ts:117:27:119:5 | [BlockStmt] { ... ; } | tst.ts:118:7:118:16 | [ReturnStmt] return 42; | semmle.order | 1 |
+| tst.ts:118:7:118:16 | [ReturnStmt] return 42; | tst.ts:118:14:118:15 | [Literal] 42 | semmle.label | 1 |
+| tst.ts:118:7:118:16 | [ReturnStmt] return 42; | tst.ts:118:14:118:15 | [Literal] 42 | semmle.order | 1 |
+| tst.ts:121:5:123:5 | [ClassInitializedMember,GetterMethodDefinition] get #so ... ; } | tst.ts:121:5:123:5 | [FunctionExpr] get #so ... ; } | semmle.label | 1 |
+| tst.ts:121:5:123:5 | [ClassInitializedMember,GetterMethodDefinition] get #so ... ; } | tst.ts:121:5:123:5 | [FunctionExpr] get #so ... ; } | semmle.order | 1 |
+| tst.ts:121:5:123:5 | [ClassInitializedMember,GetterMethodDefinition] get #so ... ; } | tst.ts:121:9:121:18 | [Label] #someValue | semmle.label | 2 |
+| tst.ts:121:5:123:5 | [ClassInitializedMember,GetterMethodDefinition] get #so ... ; } | tst.ts:121:9:121:18 | [Label] #someValue | semmle.order | 2 |
+| tst.ts:121:5:123:5 | [FunctionExpr] get #so ... ; } | tst.ts:121:23:121:28 | [KeywordTypeExpr] number | semmle.label | 4 |
+| tst.ts:121:5:123:5 | [FunctionExpr] get #so ... ; } | tst.ts:121:23:121:28 | [KeywordTypeExpr] number | semmle.order | 4 |
+| tst.ts:121:5:123:5 | [FunctionExpr] get #so ... ; } | tst.ts:121:30:123:5 | [BlockStmt] { ... ; } | semmle.label | 5 |
+| tst.ts:121:5:123:5 | [FunctionExpr] get #so ... ; } | tst.ts:121:30:123:5 | [BlockStmt] { ... ; } | semmle.order | 5 |
+| tst.ts:121:30:123:5 | [BlockStmt] { ... ; } | tst.ts:122:7:122:17 | [ReturnStmt] return 100; | semmle.label | 1 |
+| tst.ts:121:30:123:5 | [BlockStmt] { ... ; } | tst.ts:122:7:122:17 | [ReturnStmt] return 100; | semmle.order | 1 |
+| tst.ts:122:7:122:17 | [ReturnStmt] return 100; | tst.ts:122:14:122:16 | [Literal] 100 | semmle.label | 1 |
+| tst.ts:122:7:122:17 | [ReturnStmt] return 100; | tst.ts:122:14:122:16 | [Literal] 100 | semmle.order | 1 |
+| tst.ts:125:5:128:5 | [ClassInitializedMember,MethodDefinition] publicM ... ; } | tst.ts:125:5:125:16 | [Label] publicMethod | semmle.label | 1 |
+| tst.ts:125:5:128:5 | [ClassInitializedMember,MethodDefinition] publicM ... ; } | tst.ts:125:5:125:16 | [Label] publicMethod | semmle.order | 1 |
+| tst.ts:125:5:128:5 | [ClassInitializedMember,MethodDefinition] publicM ... ; } | tst.ts:125:5:128:5 | [FunctionExpr] publicM ... ; } | semmle.label | 2 |
+| tst.ts:125:5:128:5 | [ClassInitializedMember,MethodDefinition] publicM ... ; } | tst.ts:125:5:128:5 | [FunctionExpr] publicM ... ; } | semmle.order | 2 |
+| tst.ts:125:5:128:5 | [FunctionExpr] publicM ... ; } | tst.ts:125:20:128:5 | [BlockStmt] { ... ; } | semmle.label | 5 |
+| tst.ts:125:5:128:5 | [FunctionExpr] publicM ... ; } | tst.ts:125:20:128:5 | [BlockStmt] { ... ; } | semmle.order | 5 |
+| tst.ts:125:20:128:5 | [BlockStmt] { ... ; } | tst.ts:126:7:126:25 | [ExprStmt] this.#someMethod(); | semmle.label | 1 |
+| tst.ts:125:20:128:5 | [BlockStmt] { ... ; } | tst.ts:126:7:126:25 | [ExprStmt] this.#someMethod(); | semmle.order | 1 |
+| tst.ts:125:20:128:5 | [BlockStmt] { ... ; } | tst.ts:127:7:127:29 | [ReturnStmt] return ... eValue; | semmle.label | 2 |
+| tst.ts:125:20:128:5 | [BlockStmt] { ... ; } | tst.ts:127:7:127:29 | [ReturnStmt] return ... eValue; | semmle.order | 2 |
+| tst.ts:126:7:126:22 | [DotExpr] this.#someMethod | tst.ts:126:7:126:10 | [ThisExpr] this | semmle.label | 1 |
+| tst.ts:126:7:126:22 | [DotExpr] this.#someMethod | tst.ts:126:7:126:10 | [ThisExpr] this | semmle.order | 1 |
+| tst.ts:126:7:126:22 | [DotExpr] this.#someMethod | tst.ts:126:12:126:22 | [Label] #someMethod | semmle.label | 2 |
+| tst.ts:126:7:126:22 | [DotExpr] this.#someMethod | tst.ts:126:12:126:22 | [Label] #someMethod | semmle.order | 2 |
+| tst.ts:126:7:126:24 | [MethodCallExpr] this.#someMethod() | tst.ts:126:7:126:22 | [DotExpr] this.#someMethod | semmle.label | 0 |
+| tst.ts:126:7:126:24 | [MethodCallExpr] this.#someMethod() | tst.ts:126:7:126:22 | [DotExpr] this.#someMethod | semmle.order | 0 |
+| tst.ts:126:7:126:25 | [ExprStmt] this.#someMethod(); | tst.ts:126:7:126:24 | [MethodCallExpr] this.#someMethod() | semmle.label | 1 |
+| tst.ts:126:7:126:25 | [ExprStmt] this.#someMethod(); | tst.ts:126:7:126:24 | [MethodCallExpr] this.#someMethod() | semmle.order | 1 |
+| tst.ts:127:7:127:29 | [ReturnStmt] return ... eValue; | tst.ts:127:14:127:28 | [DotExpr] this.#someValue | semmle.label | 1 |
+| tst.ts:127:7:127:29 | [ReturnStmt] return ... eValue; | tst.ts:127:14:127:28 | [DotExpr] this.#someValue | semmle.order | 1 |
+| tst.ts:127:14:127:28 | [DotExpr] this.#someValue | tst.ts:127:14:127:17 | [ThisExpr] this | semmle.label | 1 |
+| tst.ts:127:14:127:28 | [DotExpr] this.#someValue | tst.ts:127:14:127:17 | [ThisExpr] this | semmle.order | 1 |
+| tst.ts:127:14:127:28 | [DotExpr] this.#someValue | tst.ts:127:19:127:28 | [Label] #someValue | semmle.label | 2 |
+| tst.ts:127:14:127:28 | [DotExpr] this.#someValue | tst.ts:127:19:127:28 | [Label] #someValue | semmle.order | 2 |
| type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | type_alias.ts:1:6:1:6 | [Identifier] B | semmle.label | 1 |
| type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | type_alias.ts:1:6:1:6 | [Identifier] B | semmle.order | 1 |
| type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | type_alias.ts:1:10:1:16 | [KeywordTypeExpr] boolean | semmle.label | 2 |
From 374adc88194f6e82ddd72fa3a021fc9342adc0e9 Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Thu, 3 Jun 2021 08:17:17 +0200
Subject: [PATCH 099/272] Temporarily disable CSV coverage PR file comparison
step
---
.github/workflows/csv-coverage.yml | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/csv-coverage.yml b/.github/workflows/csv-coverage.yml
index 093d6ead060..54c172d66d3 100644
--- a/.github/workflows/csv-coverage.yml
+++ b/.github/workflows/csv-coverage.yml
@@ -70,8 +70,8 @@ jobs:
with:
name: rst-flow-model-coverage
path: flow-model-coverage-*.rst
- - name: Check coverage files
- if: github.event.pull_request
- run: |
- python script/misc/scripts/library-coverage/compare-files.py codeqlModels
+ # - name: Check coverage files
+ # if: github.event.pull_request
+ # run: |
+ # python script/misc/scripts/library-coverage/compare-files.py codeqlModels
From e86c534c48d6362be4d58a2e50cfa122f8315212 Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Thu, 3 Jun 2021 09:02:49 +0200
Subject: [PATCH 100/272] Revert "Java: Update coverage."
This reverts commit 1c081eeaedb27332623e67231bf21c1f5d8a62d5.
---
.../library-coverage/flow-model-coverage.csv | 18 +++++++++---------
.../library-coverage/flow-model-coverage.rst | 10 +++++-----
2 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/java/documentation/library-coverage/flow-model-coverage.csv b/java/documentation/library-coverage/flow-model-coverage.csv
index a66f7880221..cf97071ad92 100644
--- a/java/documentation/library-coverage/flow-model-coverage.csv
+++ b/java/documentation/library-coverage/flow-model-coverage.csv
@@ -3,16 +3,16 @@ android.util,,16,,,,,,,,,,,16,,
android.webkit,3,2,,,,,,,,,,3,2,,
com.esotericsoftware.kryo.io,,,1,,,,,,,,,,,1,
com.esotericsoftware.kryo5.io,,,1,,,,,,,,,,,1,
-com.fasterxml.jackson.databind,,,3,,,,,,,,,,,3,
-com.google.common.base,,,34,,,,,,,,,,,28,6
-com.google.common.io,6,,73,,,,,,,6,,,,72,1
+com.fasterxml.jackson.databind,,,2,,,,,,,,,,,2,
+com.google.common.base,,,28,,,,,,,,,,,22,6
+com.google.common.io,6,,69,,,,,,,6,,,,68,1
com.unboundid.ldap.sdk,17,,,,,,17,,,,,,,,
java.beans,,,1,,,,,,,,,,,1,
java.io,3,,20,,3,,,,,,,,,20,
-java.lang,,,3,,,,,,,,,,,1,2
+java.lang,,,1,,,,,,,,,,,1,
java.net,2,3,4,,,,,2,,,,,3,4,
java.nio,10,,2,,10,,,,,,,,,2,
-java.util,,,283,,,,,,,,,,,15,268
+java.util,,,13,,,,,,,,,,,13,
javax.naming.directory,1,,,,,,1,,,,,,,,
javax.net.ssl,2,,,,,,,,2,,,,,,
javax.servlet,4,21,2,,,3,,,,,,1,21,2,
@@ -23,14 +23,14 @@ javax.xml.transform.stream,,,2,,,,,,,,,,,2,
javax.xml.xpath,3,,,,,,,,,,3,,,,
org.apache.commons.codec,,,2,,,,,,,,,,,2,
org.apache.commons.io,,,22,,,,,,,,,,,22,
-org.apache.commons.lang3,,,327,,,,,,,,,,,311,16
-org.apache.commons.text,,,220,,,,,,,,,,,220,
+org.apache.commons.lang3,,,313,,,,,,,,,,,299,14
+org.apache.commons.text,,,203,,,,,,,,,,,203,
org.apache.directory.ldap.client.api,1,,,,,,1,,,,,,,,
org.apache.hc.core5.function,,,1,,,,,,,,,,,1,
org.apache.hc.core5.http,1,2,39,,,,,,,,,1,2,39,
org.apache.hc.core5.net,,,2,,,,,,,,,,,2,
-org.apache.hc.core5.util,,,24,,,,,,,,,,,18,6
-org.apache.http,2,3,67,,,,,,,,,2,3,59,8
+org.apache.hc.core5.util,,,22,,,,,,,,,,,18,4
+org.apache.http,2,3,66,,,,,,,,,2,3,59,7
org.dom4j,20,,,,,,,,,,20,,,,
org.springframework.ldap.core,14,,,,,,14,,,,,,,,
org.springframework.security.web.savedrequest,,6,,,,,,,,,,,6,,
diff --git a/java/documentation/library-coverage/flow-model-coverage.rst b/java/documentation/library-coverage/flow-model-coverage.rst
index 6e46c906b07..8bb20a9b6c3 100644
--- a/java/documentation/library-coverage/flow-model-coverage.rst
+++ b/java/documentation/library-coverage/flow-model-coverage.rst
@@ -8,12 +8,12 @@ Java framework & library support
Framework / library,Package,Remote flow sources,Taint & value steps,Sinks (total),`CWE‑022` :sub:`Path injection`,`CWE‑036` :sub:`Path traversal`,`CWE‑079` :sub:`Cross-site scripting`,`CWE‑089` :sub:`SQL injection`,`CWE‑090` :sub:`LDAP injection`,`CWE‑094` :sub:`Code injection`,`CWE‑319` :sub:`Cleartext transmission`
Android,``android.*``,18,,3,,,3,,,,
- Apache,``org.apache.*``,5,682,4,,,3,,1,,
+ Apache,``org.apache.*``,5,648,4,,,3,,1,,
`Apache Commons IO `_,``org.apache.commons.io``,,22,,,,,,,,
- Google,``com.google.common.*``,,107,6,,6,,,,,
- Java Standard Library,``java.*``,3,313,15,13,,,,,,2
+ Google,``com.google.common.*``,,97,6,,6,,,,,
+ Java Standard Library,``java.*``,3,41,15,13,,,,,,2
Java extensions,``javax.*``,22,8,12,,,1,,1,1,
`Spring `_,``org.springframework.*``,29,,14,,,,,14,,
- Others,"``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.databind``, ``com.unboundid.ldap.sdk``, ``org.dom4j``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.mvc``",7,6,37,,,,,17,,
- Totals,,84,1138,91,13,6,7,,33,1,2
+ Others,"``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.databind``, ``com.unboundid.ldap.sdk``, ``org.dom4j``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.mvc``",7,5,37,,,,,17,,
+ Totals,,84,821,91,13,6,7,,33,1,2
From 99708c33fd5347d9b7edba7346a04774dbab1e51 Mon Sep 17 00:00:00 2001
From: AlonaHlobina <54394529+AlonaHlobina@users.noreply.github.com>
Date: Thu, 3 Jun 2021 09:50:18 +0200
Subject: [PATCH 101/272] Update versions-compilers.rst
---
docs/codeql/support/reusables/versions-compilers.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/codeql/support/reusables/versions-compilers.rst b/docs/codeql/support/reusables/versions-compilers.rst
index f983b5e0114..52253cb5ab4 100644
--- a/docs/codeql/support/reusables/versions-compilers.rst
+++ b/docs/codeql/support/reusables/versions-compilers.rst
@@ -28,7 +28,7 @@
.. [1] Support for the clang-cl compiler is preliminary.
.. [2] Support for the Arm Compiler (armcc) is preliminary.
- .. [3] Builds that execute on Java 7 to 15 can be analyzed. The analysis understands Java 15 standard language features.
+ .. [3] Builds that execute on Java 7 to 16 can be analyzed. The analysis understands Java 16 standard language features.
.. [4] ECJ is supported when the build invokes it via the Maven Compiler plugin or the Takari Lifecycle plugin.
.. [5] JSX and Flow code, YAML, JSON, HTML, and XML files may also be analyzed with JavaScript files.
.. [6] TypeScript analysis is performed by running the JavaScript extractor with TypeScript enabled. This is the default for LGTM.
From 2833f8daa4092aa496c204c7313a1f42f19b3693 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Thu, 3 Jun 2021 10:42:41 +0200
Subject: [PATCH 102/272] Change predicate isUnsafeEngine -> isSafeEngine to
improve performance
---
.../src/semmle/code/java/security/JexlInjection.qll | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/java/ql/src/semmle/code/java/security/JexlInjection.qll b/java/ql/src/semmle/code/java/security/JexlInjection.qll
index d36fa0aad9b..b975e6f2bdb 100644
--- a/java/ql/src/semmle/code/java/security/JexlInjection.qll
+++ b/java/ql/src/semmle/code/java/security/JexlInjection.qll
@@ -83,7 +83,7 @@ private class DefaultJexlInjectionAdditionalTaintStep extends JexlInjectionAddit
*/
private predicate createJexlScriptStep(DataFlow::Node n1, DataFlow::Node n2) {
exists(MethodAccess ma, Method m | m = ma.getMethod() and n2.asExpr() = ma |
- isUnsafeEngine(ma.getQualifier()) and
+ not isSafeEngine(ma.getQualifier()) and
m instanceof CreateJexlScriptMethod and
n1.asExpr() = ma.getArgument(0) and
n1.asExpr().getType() instanceof TypeString
@@ -96,7 +96,7 @@ private predicate createJexlScriptStep(DataFlow::Node n1, DataFlow::Node n2) {
*/
private predicate createJexlExpressionStep(DataFlow::Node n1, DataFlow::Node n2) {
exists(MethodAccess ma, Method m | m = ma.getMethod() and n2.asExpr() = ma |
- isUnsafeEngine(ma.getQualifier()) and
+ not isSafeEngine(ma.getQualifier()) and
m instanceof CreateJexlExpressionMethod and
n1.asExpr() = ma.getAnArgument() and
n1.asExpr().getType() instanceof TypeString
@@ -111,7 +111,7 @@ private predicate createJexlTemplateStep(DataFlow::Node n1, DataFlow::Node n2) {
exists(MethodAccess ma, Method m, RefType taintType |
m = ma.getMethod() and n2.asExpr() = ma and taintType = n1.asExpr().getType()
|
- isUnsafeEngine(ma.getQualifier()) and
+ not isSafeEngine(ma.getQualifier()) and
m instanceof CreateJexlTemplateMethod and
n1.asExpr() = ma.getArgument([0, 1]) and
(taintType instanceof TypeString or taintType instanceof Reader)
@@ -119,10 +119,10 @@ private predicate createJexlTemplateStep(DataFlow::Node n1, DataFlow::Node n2) {
}
/**
- * Holds if `expr` is a JEXL engine that is not configured with a sandbox.
+ * Holds if `expr` is a JEXL engine that is configured with a sandbox.
*/
-private predicate isUnsafeEngine(Expr expr) {
- not exists(SandboxedJexlFlowConfig config | config.hasFlowTo(DataFlow::exprNode(expr)))
+private predicate isSafeEngine(Expr expr) {
+ exists(SandboxedJexlFlowConfig config | config.hasFlowTo(DataFlow::exprNode(expr)))
}
/**
From 00836c4bacbbd2ad127849eacc57fafb71b99f14 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Thu, 3 Jun 2021 10:52:52 +0200
Subject: [PATCH 103/272] Fix QLDocs
---
java/ql/src/semmle/code/java/security/JexlInjection.qll | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/java/ql/src/semmle/code/java/security/JexlInjection.qll b/java/ql/src/semmle/code/java/security/JexlInjection.qll
index b975e6f2bdb..8e8923a746b 100644
--- a/java/ql/src/semmle/code/java/security/JexlInjection.qll
+++ b/java/ql/src/semmle/code/java/security/JexlInjection.qll
@@ -6,7 +6,7 @@ private import semmle.code.java.dataflow.ExternalFlow
/**
* A sink for Expresssion Language injection vulnerabilities via Jexl,
- * i.e. method calls that run evaluation of a JEXL expression.
+ * that is, method calls that run evaluation of a JEXL expression.
*/
abstract class JexlEvaluationSink extends DataFlow::ExprNode { }
@@ -79,7 +79,7 @@ private class DefaultJexlInjectionAdditionalTaintStep extends JexlInjectionAddit
/**
* Holds if `n1` to `n2` is a dataflow step that creates a JEXL script using an unsafe engine
- * i.e. `tainted.createScript(jexlExpr)`.
+ * by calling `tainted.createScript(jexlExpr)`.
*/
private predicate createJexlScriptStep(DataFlow::Node n1, DataFlow::Node n2) {
exists(MethodAccess ma, Method m | m = ma.getMethod() and n2.asExpr() = ma |
@@ -92,7 +92,7 @@ private predicate createJexlScriptStep(DataFlow::Node n1, DataFlow::Node n2) {
/**
* Holds if `n1` to `n2` is a dataflow step that creates a JEXL expression using an unsafe engine
- * i.e. `tainted.createExpression(jexlExpr)`.
+ * by calling `tainted.createExpression(jexlExpr)`.
*/
private predicate createJexlExpressionStep(DataFlow::Node n1, DataFlow::Node n2) {
exists(MethodAccess ma, Method m | m = ma.getMethod() and n2.asExpr() = ma |
@@ -105,7 +105,7 @@ private predicate createJexlExpressionStep(DataFlow::Node n1, DataFlow::Node n2)
/**
* Holds if `n1` to `n2` is a dataflow step that creates a JEXL template using an unsafe engine
- * i.e. `tainted.createTemplate(jexlExpr)`.
+ * by calling `tainted.createTemplate(jexlExpr)`.
*/
private predicate createJexlTemplateStep(DataFlow::Node n1, DataFlow::Node n2) {
exists(MethodAccess ma, Method m, RefType taintType |
From 85d9483c7b412728eb7624570d07299c15070fe8 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 12 May 2021 12:11:25 +0200
Subject: [PATCH 104/272] Python: Add basic aiohttp tests
---
.../frameworks/aiohttp/ConceptsTest.expected | 0
.../frameworks/aiohttp/ConceptsTest.ql | 12 ++
.../aiohttp/InlineTaintTest.expected | 3 +
.../frameworks/aiohttp/InlineTaintTest.ql | 1 +
.../library-tests/frameworks/aiohttp/options | 1 +
.../frameworks/aiohttp/response_test.py | 0
.../frameworks/aiohttp/routing_test.py | 169 ++++++++++++++++++
.../frameworks/aiohttp/taint_test.py | 0
8 files changed, 186 insertions(+)
create mode 100644 python/ql/test/library-tests/frameworks/aiohttp/ConceptsTest.expected
create mode 100644 python/ql/test/library-tests/frameworks/aiohttp/ConceptsTest.ql
create mode 100644 python/ql/test/library-tests/frameworks/aiohttp/InlineTaintTest.expected
create mode 100644 python/ql/test/library-tests/frameworks/aiohttp/InlineTaintTest.ql
create mode 100644 python/ql/test/library-tests/frameworks/aiohttp/options
create mode 100644 python/ql/test/library-tests/frameworks/aiohttp/response_test.py
create mode 100644 python/ql/test/library-tests/frameworks/aiohttp/routing_test.py
create mode 100644 python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/aiohttp/ConceptsTest.expected
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/aiohttp/ConceptsTest.ql
new file mode 100644
index 00000000000..1e2c1fab3ee
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/aiohttp/ConceptsTest.ql
@@ -0,0 +1,12 @@
+import python
+import experimental.meta.ConceptsTest
+
+class DedicatedResponseTest extends HttpServerHttpResponseTest {
+ DedicatedResponseTest() { file.getShortName() = "response_test.py" }
+}
+
+class OtherResponseTest extends HttpServerHttpResponseTest {
+ OtherResponseTest() { not this instanceof DedicatedResponseTest }
+
+ override string getARelevantTag() { result = "HttpResponse" }
+}
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/aiohttp/InlineTaintTest.expected
new file mode 100644
index 00000000000..79d760d87f4
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/aiohttp/InlineTaintTest.expected
@@ -0,0 +1,3 @@
+argumentToEnsureNotTaintedNotMarkedAsSpurious
+untaintedArgumentToEnsureTaintedNotMarkedAsMissing
+failures
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/aiohttp/InlineTaintTest.ql
new file mode 100644
index 00000000000..027ad8667be
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/aiohttp/InlineTaintTest.ql
@@ -0,0 +1 @@
+import experimental.meta.InlineTaintTest
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/options b/python/ql/test/library-tests/frameworks/aiohttp/options
new file mode 100644
index 00000000000..cfef58cf2b2
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/aiohttp/options
@@ -0,0 +1 @@
+semmle-extractor-options: --max-import-depth=1 --lang=3
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/response_test.py b/python/ql/test/library-tests/frameworks/aiohttp/response_test.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/routing_test.py b/python/ql/test/library-tests/frameworks/aiohttp/routing_test.py
new file mode 100644
index 00000000000..6a8b98cfca6
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/aiohttp/routing_test.py
@@ -0,0 +1,169 @@
+# Inspired by https://docs.aiohttp.org/en/stable/web_quickstart.html
+# and https://docs.aiohttp.org/en/stable/web_quickstart.html#resources-and-routes
+
+from aiohttp import web
+
+
+app = web.Application()
+
+
+## ================================= ##
+## Ways to specify routes / handlers ##
+## ================================= ##
+
+## Using coroutines
+if True:
+
+ # `app.add_routes` with list
+ async def foo(request): # $ MISSING: requestHandler
+ return web.Response(text="foo")
+
+ async def foo2(request): # $ MISSING: requestHandler
+ return web.Response(text="foo2")
+
+ async def foo3(request): # $ MISSING: requestHandler
+ return web.Response(text="foo3")
+
+ app.add_routes([
+ web.get("/foo", foo), # $ MISSING: routeSetup
+ web.route("*", "/foo2", foo2), # $ MISSING: routeSetup
+ web.get(path="/foo3", handler=foo3), # $ MISSING: routeSetup
+ ])
+
+
+ # using decorator
+ routes = web.RouteTableDef()
+
+ @routes.get("/bar") # $ MISSING: routeSetup
+ async def bar(request): # $ MISSING: requestHandler
+ return web.Response(text="bar")
+
+ @routes.route("*", "/bar2") # $ MISSING: routeSetup
+ async def bar2(request): # $ MISSING: requestHandler
+ return web.Response(text="bar2")
+
+ @routes.get(path="/bar3") # $ MISSING: routeSetup
+ async def bar3(request): # $ MISSING: requestHandler
+ return web.Response(text="bar3")
+
+ app.add_routes(routes)
+
+
+ # `app.router.add_get` / `app.router.add_route`
+ async def baz(request): # $ MISSING: requestHandler
+ return web.Response(text="baz")
+
+ app.router.add_get("/baz", baz) # $ MISSING: routeSetup
+
+ async def baz2(request): # $ MISSING: requestHandler
+ return web.Response(text="baz2")
+
+ app.router.add_route("*", "/baz2", baz2) # $ MISSING: routeSetup
+
+ async def baz3(request): # $ MISSING: requestHandler
+ return web.Response(text="baz3")
+
+ app.router.add_get(path="/baz3", handler=baz3) # $ MISSING: routeSetup
+
+
+## Using classes / views
+if True:
+ # see https://docs.aiohttp.org/en/stable/web_quickstart.html#organizing-handlers-in-classes
+
+ class MyCustomHandlerClass:
+
+ async def foo_handler(self, request): # $ MISSING: requestHandler
+ return web.Response(text="MyCustomHandlerClass.foo")
+
+ my_custom_handler = MyCustomHandlerClass()
+ app.router.add_get("/MyCustomHandlerClass/foo", my_custom_handler.foo_handler) # $ MISSING: routeSetup
+
+ # Using `web.View`
+ # ---------------
+
+ # `app.add_routes` with list
+ class MyWebView1(web.View):
+ async def get(self): # $ MISSING: requestHandler
+ return web.Response(text="MyWebView1.get")
+
+ app.add_routes([
+ web.view("/MyWebView1", MyWebView1) # $ MISSING: routeSetup
+ ])
+
+
+ # using decorator
+ routes = web.RouteTableDef()
+
+ @routes.view("/MyWebView2") # $ MISSING: routeSetup
+ class MyWebView2(web.View):
+ async def get(self): # $ MISSING: requestHandler
+ return web.Response(text="MyWebView2.get")
+
+ app.add_routes(routes)
+
+
+ # `app.router.add_view`
+ class MyWebView3(web.View):
+ async def get(self): # $ MISSING: requestHandler
+ return web.Response(text="MyWebView3.get")
+
+ app.router.add_view("/MyWebView3", MyWebView3) # $ MISSING: routeSetup
+
+## =================== ##
+## "Routed parameters" ##
+## =================== ##
+
+if True:
+ # see https://docs.aiohttp.org/en/stable/web_quickstart.html#variable-resources
+
+ async def matching(request: web.Request): # $ MISSING: requestHandler
+ name = request.match_info['name']
+ number = request.match_info['number']
+ return web.Response(text="matching name={} number={}".format(name, number))
+
+ app.router.add_get("/matching/{name}/{number:\d+}", matching) # $ MISSING: routeSetup
+
+## ======= ##
+## subapps ##
+## ======= ##
+
+if True:
+ subapp = web.Application()
+
+ async def subapp_handler(request): # $ MISSING: requestHandler
+ return web.Response(text="subapp_handler")
+
+ subapp.router.add_get("/subapp_handler", subapp_handler) # $ MISSING: routeSetup
+
+ app.add_subapp("/my_subapp", subapp)
+
+ # similar behavior is possible with `app.add_domain`, but since I don't think we'll have special handling
+ # for any kind of subapps, I have not created a test for this.
+
+
+## ================================ ##
+## Constructing UrlDispatcher first ##
+## ================================ ##
+
+if True:
+ async def manual_dispatcher_instance(request): # $ MISSING: requestHandler
+ return web.Response(text="manual_dispatcher_instance")
+
+ url_dispatcher = web.UrlDispatcher()
+ url_dispatcher.add_get("/manual_dispatcher_instance", manual_dispatcher_instance) # $ MISSING: routeSetup
+
+ subapp2 = web.Application(router=url_dispatcher)
+ app.add_subapp("/manual_dispatcher_instance_app", subapp2)
+
+
+## =========== ##
+## Run the app ##
+## =========== ##
+
+if __name__ == "__main__":
+ print("For auto-reloading server you can use:")
+ print(f"aiohttp-devtools runserver {__file__}")
+ print("after doing `pip install aiohttp-devtools`")
+ print()
+
+ web.run_app(app)
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py b/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
new file mode 100644
index 00000000000..e69de29bb2d
From fa1d4e6de781c935ba5894a52a6ce758fdfff17e Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Thu, 20 May 2021 10:59:06 +0200
Subject: [PATCH 105/272] Python: Extract poor mans function resolution (from
django)
Since I also want to use this for aiohttp.web modeling
---
.../src/semmle/python/frameworks/Django.qll | 51 +----------
.../internal/PoorMansFunctionResolution.qll | 90 +++++++++++++++++++
2 files changed, 93 insertions(+), 48 deletions(-)
create mode 100644 python/ql/src/semmle/python/frameworks/internal/PoorMansFunctionResolution.qll
diff --git a/python/ql/src/semmle/python/frameworks/Django.qll b/python/ql/src/semmle/python/frameworks/Django.qll
index 93aa921e496..f1ae74bc8b5 100644
--- a/python/ql/src/semmle/python/frameworks/Django.qll
+++ b/python/ql/src/semmle/python/frameworks/Django.qll
@@ -11,6 +11,7 @@ private import semmle.python.Concepts
private import semmle.python.ApiGraphs
private import semmle.python.frameworks.PEP249
private import semmle.python.regex
+private import semmle.python.frameworks.internal.PoorMansFunctionResolution
/**
* Provides models for the `django` PyPI package.
@@ -1386,13 +1387,6 @@ private module PrivateDjango {
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
- /**
- * Gets the last decorator call for the function `func`, if `func` has decorators.
- */
- private Expr lastDecoratorCall(Function func) {
- result = func.getDefinition().(FunctionExpr).getADecoratorCall() and
- not exists(Call other_decorator | other_decorator.getArg(0) = result)
- }
/** Adds the `getASelfRef` member predicate when modeling a class. */
abstract private class SelfRefMixin extends Class {
@@ -1487,45 +1481,6 @@ private module PrivateDjango {
// ---------------------------------------------------------------------------
// routing modeling
// ---------------------------------------------------------------------------
- /**
- * Gets a reference to the Function `func`.
- *
- * The idea is that this function should be used as a route handler when setting up a
- * route, but currently it just tracks all functions, since we can't do type-tracking
- * backwards yet (TODO).
- */
- private DataFlow::LocalSourceNode djangoRouteHandlerFunctionTracker(
- DataFlow::TypeTracker t, Function func
- ) {
- t.start() and
- (
- not exists(func.getADecorator()) and
- result.asExpr() = func.getDefinition()
- or
- // If the function has decorators, we still want to model the function as being
- // the request handler for a route setup. In such situations, we must track the
- // last decorator call instead of the function itself.
- //
- // Note that this means that we blindly ignore what the decorator actually does to
- // the function, which seems like an OK tradeoff.
- result.asExpr() = lastDecoratorCall(func)
- )
- or
- exists(DataFlow::TypeTracker t2 |
- result = djangoRouteHandlerFunctionTracker(t2, func).track(t2, t)
- )
- }
-
- /**
- * Gets a reference to the Function `func`.
- *
- * The idea is that this function should be used as a route handler when setting up a
- * route, but currently it just tracks all functions, since we can't do type-tracking
- * backwards yet (TODO).
- */
- private DataFlow::Node djangoRouteHandlerFunctionTracker(Function func) {
- djangoRouteHandlerFunctionTracker(DataFlow::TypeTracker::end(), func).flowsTo(result)
- }
/**
* In order to recognize a class as being a django view class, based on the `as_view`
@@ -1613,7 +1568,7 @@ private module PrivateDjango {
*/
private class DjangoRouteHandler extends Function {
DjangoRouteHandler() {
- exists(DjangoRouteSetup route | route.getViewArg() = djangoRouteHandlerFunctionTracker(this))
+ exists(DjangoRouteSetup route | route.getViewArg() = poorMansFunctionTracker(this))
or
any(DjangoViewClass vc).getARequestHandler() = this
}
@@ -1663,7 +1618,7 @@ private module PrivateDjango {
abstract DataFlow::Node getViewArg();
final override DjangoRouteHandler getARequestHandler() {
- djangoRouteHandlerFunctionTracker(result) = getViewArg()
+ poorMansFunctionTracker(result) = getViewArg()
or
exists(DjangoViewClass vc |
getViewArg() = vc.asViewResult() and
diff --git a/python/ql/src/semmle/python/frameworks/internal/PoorMansFunctionResolution.qll b/python/ql/src/semmle/python/frameworks/internal/PoorMansFunctionResolution.qll
new file mode 100644
index 00000000000..78764bf53bc
--- /dev/null
+++ b/python/ql/src/semmle/python/frameworks/internal/PoorMansFunctionResolution.qll
@@ -0,0 +1,90 @@
+/**
+ * INTERNAL: Do not use.
+ *
+ * Notice: The predicates provided in this module is a poor mans solution for function
+ * resolution, and does not handle anything but the most simple cases.
+ *
+ * For example, in the code below, we're not able to tell anything about
+ * `inst.my_method` (which is a bound-method)
+ * ```py
+ * class MyClass:
+ * def my_method(self):
+ * pass
+ *
+ * inst = MyClass()
+ * print(inst.my_method)
+ * ```
+ */
+
+private import python
+private import semmle.python.dataflow.new.DataFlow
+
+/**
+ * Gets the last decorator call for the function `func`, if `func` has decorators.
+ */
+private Expr lastDecoratorCall(Function func) {
+ result = func.getDefinition().(FunctionExpr).getADecoratorCall() and
+ not exists(Call other_decorator | other_decorator.getArg(0) = result)
+}
+
+/**
+ * Gets a reference to the Function `func`.
+ *
+ * Notice: This is a poor mans solution for function resolution, and does not handle
+ * anything but the most simple cases.
+ *
+ * For example, in the code below, we're not able to tell anything about
+ * `inst.my_method` (which is a bound-method)
+ * ```py
+ * class MyClass:
+ * def my_method(self):
+ * pass
+ *
+ * inst = MyClass()
+ * print(inst.my_method)
+ * ```
+ */
+private DataFlow::LocalSourceNode poorMansFunctionTracker(
+ DataFlow::TypeTracker t, Function func
+) {
+ t.start() and
+ (
+ not exists(func.getADecorator()) and
+ result.asExpr() = func.getDefinition()
+ or
+ // If the function has decorators, we still want to model the function as being
+ // the request handler for a route setup. In such situations, we must track the
+ // last decorator call instead of the function itself.
+ //
+ // Note that this means that we blindly ignore what the decorator actually does to
+ // the function, which seems like an OK tradeoff.
+ result.asExpr() = lastDecoratorCall(func)
+ )
+ or
+ exists(DataFlow::TypeTracker t2 |
+ result = poorMansFunctionTracker(t2, func).track(t2, t)
+ )
+}
+
+/**
+ * INTERNAL: Do not use.
+ *
+ * Gets a reference to the Function `func`.
+ *
+ * Notice: This is a poor mans solution for function resolution, and does not handle
+ * anything but the most simple cases.
+ *
+ * For example, in the code below, we're not able to tell anything about
+ * `inst.my_method` (which is a bound-method)
+ * ```py
+ * class MyClass:
+ * def my_method(self):
+ * pass
+ *
+ * inst = MyClass()
+ * print(inst.my_method)
+ * ```
+ */
+DataFlow::Node poorMansFunctionTracker(Function func) {
+ poorMansFunctionTracker(DataFlow::TypeTracker::end(), func).flowsTo(result)
+}
From 3cbb909a3a4ae8f736d10bd3e1175f1a388c6af3 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Thu, 20 May 2021 11:12:28 +0200
Subject: [PATCH 106/272] Python: Add modeling of coroutine routes in
aiohttp.web
---
docs/codeql/support/reusables/frameworks.rst | 1 +
python/ql/src/semmle/python/Frameworks.qll | 1 +
.../src/semmle/python/frameworks/Aiohttp.qll | 141 ++++++++++++++++++
.../frameworks/aiohttp/routing_test.py | 50 +++----
4 files changed, 168 insertions(+), 25 deletions(-)
create mode 100644 python/ql/src/semmle/python/frameworks/Aiohttp.qll
diff --git a/docs/codeql/support/reusables/frameworks.rst b/docs/codeql/support/reusables/frameworks.rst
index be9949aad77..41680cffe0e 100644
--- a/docs/codeql/support/reusables/frameworks.rst
+++ b/docs/codeql/support/reusables/frameworks.rst
@@ -152,6 +152,7 @@ Python built-in support
:widths: auto
Name, Category
+ aiohttp.web, Web framework
Django, Web framework
Flask, Web framework
Tornado, Web framework
diff --git a/python/ql/src/semmle/python/Frameworks.qll b/python/ql/src/semmle/python/Frameworks.qll
index 3115c3ffac6..172f3d11bfb 100644
--- a/python/ql/src/semmle/python/Frameworks.qll
+++ b/python/ql/src/semmle/python/Frameworks.qll
@@ -4,6 +4,7 @@
// If you add modeling of a new framework/library, remember to add it it to the docs in
// `docs/codeql/support/reusables/frameworks.rst`
+private import semmle.python.frameworks.Aiohttp
private import semmle.python.frameworks.Cryptodome
private import semmle.python.frameworks.Cryptography
private import semmle.python.frameworks.Dill
diff --git a/python/ql/src/semmle/python/frameworks/Aiohttp.qll b/python/ql/src/semmle/python/frameworks/Aiohttp.qll
new file mode 100644
index 00000000000..b73610c06b1
--- /dev/null
+++ b/python/ql/src/semmle/python/frameworks/Aiohttp.qll
@@ -0,0 +1,141 @@
+/**
+ * Provides classes modeling security-relevant aspects of the `aiohttp` PyPI package.
+ * See https://docs.aiohttp.org/en/stable/index.html
+ */
+
+private import python
+private import semmle.python.dataflow.new.DataFlow
+private import semmle.python.Concepts
+private import semmle.python.ApiGraphs
+private import semmle.python.frameworks.internal.PoorMansFunctionResolution
+
+/**
+ * INTERNAL: Do not use.
+ *
+ * Provides models for the web server part (`aiohttp.web`) of the `aiohttp` PyPI package.
+ * See https://docs.aiohttp.org/en/stable/web.html
+ */
+module AiohttpWebModel {
+ /** Gets a reference to a `aiohttp.web.Application` instance. */
+ API::Node applicationInstance() {
+ // Not sure whether you're allowed to add routes _after_ starting the app, for
+ // example in the middle of handling a http request... but I'm guessing that for 99%
+ // for all code, not modeling that `request.app` is a reference to an application
+ // should be good enough for the route-setup part of the modeling :+1:
+ result = API::moduleImport("aiohttp").getMember("web").getMember("Application").getReturn()
+ }
+
+ /** Gets a reference to a `aiohttp.web.UrlDispatcher` instance. */
+ API::Node urlDispathcerInstance() {
+ result = API::moduleImport("aiohttp").getMember("web").getMember("UrlDispatcher").getReturn()
+ or
+ result = applicationInstance().getMember("router")
+ }
+
+ // -- route modeling --
+
+ /** A route setup in `aiohttp.web` */
+ abstract class AiohttpRouteSetup extends HTTP::Server::RouteSetup::Range {
+ override Parameter getARoutedParameter() { none() }
+
+ override string getFramework() { result = "aiohttp.web" }
+ }
+
+ /** An aiohttp route setup that uses coroutines (async function) as request handler. */
+ abstract class AiohttpCoroutineRouteSetup extends AiohttpRouteSetup {
+ /** Gets the argument specifying the request handler (which is a coroutine/async function) */
+ abstract DataFlow::Node getRequestHandlerArg();
+
+ override Function getARequestHandler() {
+ this.getRequestHandlerArg() = poorMansFunctionTracker(result)
+ }
+ }
+
+ /**
+ * A route-setup from `add_route` or any of `add_get`, `add_post`, etc. on an
+ * `aiohttp.web.UrlDispatcher`.
+ */
+ class AiohttpUrlDispatcherAddRouteCall extends AiohttpCoroutineRouteSetup, DataFlow::CallCfgNode {
+ /** At what index route arguments starts, so we can handle "route" version together with get/post/... */
+ int routeArgsStart;
+
+ AiohttpUrlDispatcherAddRouteCall() {
+ this = urlDispathcerInstance().getMember("add_" + HTTP::httpVerbLower()).getACall() and
+ routeArgsStart = 0
+ or
+ this = urlDispathcerInstance().getMember("add_route").getACall() and
+ routeArgsStart = 1
+ }
+
+ override DataFlow::Node getUrlPatternArg() {
+ result in [this.getArg(routeArgsStart + 0), this.getArgByName("path")]
+ }
+
+ override DataFlow::Node getRequestHandlerArg() {
+ result in [this.getArg(routeArgsStart + 1), this.getArgByName("handler")]
+ }
+ }
+
+ /**
+ * A route-setup from using `route` or any of `get`, `post`, etc. functions from `aiohttp.web`.
+ *
+ * Note that technically, this does not set up a route in itself (since it needs to be added to an application first).
+ * However, modeling this way makes it easier, and we don't expect it to lead to many problems.
+ */
+ class AiohttpWebRouteCall extends AiohttpCoroutineRouteSetup, DataFlow::CallCfgNode {
+ /** At what index route arguments starts, so we can handle "route" version together with get/post/... */
+ int routeArgsStart;
+
+ AiohttpWebRouteCall() {
+ exists(string funcName |
+ funcName = HTTP::httpVerbLower() and
+ routeArgsStart = 0
+ or
+ funcName = "route" and
+ routeArgsStart = 1
+ |
+ this = API::moduleImport("aiohttp").getMember("web").getMember(funcName).getACall()
+ )
+ }
+
+ override DataFlow::Node getUrlPatternArg() {
+ result in [this.getArg(routeArgsStart + 0), this.getArgByName("path")]
+ }
+
+ override DataFlow::Node getRequestHandlerArg() {
+ result in [this.getArg(routeArgsStart + 1), this.getArgByName("handler")]
+ }
+ }
+
+ /** A route-setup from using a `route` or any of `get`, `post`, etc. decorators from a `aiohttp.web.RouteTableDef`. */
+ class AiohttpRouteTableDefRouteCall extends AiohttpCoroutineRouteSetup, DataFlow::CallCfgNode {
+ /** At what index route arguments starts, so we can handle "route" version together with get/post/... */
+ int routeArgsStart;
+
+ AiohttpRouteTableDefRouteCall() {
+ exists(string decoratorName |
+ decoratorName = HTTP::httpVerbLower() and
+ routeArgsStart = 0
+ or
+ decoratorName = "route" and
+ routeArgsStart = 1
+ |
+ this =
+ API::moduleImport("aiohttp")
+ .getMember("web")
+ .getMember("RouteTableDef")
+ .getReturn()
+ .getMember(decoratorName)
+ .getACall()
+ )
+ }
+
+ override DataFlow::Node getUrlPatternArg() {
+ result in [this.getArg(routeArgsStart + 0), this.getArgByName("path")]
+ }
+
+ override DataFlow::Node getRequestHandlerArg() { none() }
+
+ override Function getARequestHandler() { result.getADecorator() = this.asExpr() }
+ }
+}
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/routing_test.py b/python/ql/test/library-tests/frameworks/aiohttp/routing_test.py
index 6a8b98cfca6..880a7f8cb87 100644
--- a/python/ql/test/library-tests/frameworks/aiohttp/routing_test.py
+++ b/python/ql/test/library-tests/frameworks/aiohttp/routing_test.py
@@ -15,55 +15,55 @@ app = web.Application()
if True:
# `app.add_routes` with list
- async def foo(request): # $ MISSING: requestHandler
+ async def foo(request): # $ requestHandler
return web.Response(text="foo")
- async def foo2(request): # $ MISSING: requestHandler
+ async def foo2(request): # $ requestHandler
return web.Response(text="foo2")
- async def foo3(request): # $ MISSING: requestHandler
+ async def foo3(request): # $ requestHandler
return web.Response(text="foo3")
app.add_routes([
- web.get("/foo", foo), # $ MISSING: routeSetup
- web.route("*", "/foo2", foo2), # $ MISSING: routeSetup
- web.get(path="/foo3", handler=foo3), # $ MISSING: routeSetup
+ web.get("/foo", foo), # $ routeSetup="/foo"
+ web.route("*", "/foo2", foo2), # $ routeSetup="/foo2"
+ web.get(path="/foo3", handler=foo3), # $ routeSetup="/foo3"
])
# using decorator
routes = web.RouteTableDef()
- @routes.get("/bar") # $ MISSING: routeSetup
- async def bar(request): # $ MISSING: requestHandler
+ @routes.get("/bar") # $ routeSetup="/bar"
+ async def bar(request): # $ requestHandler
return web.Response(text="bar")
- @routes.route("*", "/bar2") # $ MISSING: routeSetup
- async def bar2(request): # $ MISSING: requestHandler
+ @routes.route("*", "/bar2") # $ routeSetup="/bar2"
+ async def bar2(request): # $ requestHandler
return web.Response(text="bar2")
- @routes.get(path="/bar3") # $ MISSING: routeSetup
- async def bar3(request): # $ MISSING: requestHandler
+ @routes.get(path="/bar3") # $ routeSetup="/bar3"
+ async def bar3(request): # $ requestHandler
return web.Response(text="bar3")
app.add_routes(routes)
# `app.router.add_get` / `app.router.add_route`
- async def baz(request): # $ MISSING: requestHandler
+ async def baz(request): # $ requestHandler
return web.Response(text="baz")
- app.router.add_get("/baz", baz) # $ MISSING: routeSetup
+ app.router.add_get("/baz", baz) # $ routeSetup="/baz"
- async def baz2(request): # $ MISSING: requestHandler
+ async def baz2(request): # $ requestHandler
return web.Response(text="baz2")
- app.router.add_route("*", "/baz2", baz2) # $ MISSING: routeSetup
+ app.router.add_route("*", "/baz2", baz2) # $ routeSetup="/baz2"
- async def baz3(request): # $ MISSING: requestHandler
+ async def baz3(request): # $ requestHandler
return web.Response(text="baz3")
- app.router.add_get(path="/baz3", handler=baz3) # $ MISSING: routeSetup
+ app.router.add_get(path="/baz3", handler=baz3) # $ routeSetup="/baz3"
## Using classes / views
@@ -76,7 +76,7 @@ if True:
return web.Response(text="MyCustomHandlerClass.foo")
my_custom_handler = MyCustomHandlerClass()
- app.router.add_get("/MyCustomHandlerClass/foo", my_custom_handler.foo_handler) # $ MISSING: routeSetup
+ app.router.add_get("/MyCustomHandlerClass/foo", my_custom_handler.foo_handler) # $ routeSetup="/MyCustomHandlerClass/foo"
# Using `web.View`
# ---------------
@@ -116,12 +116,12 @@ if True:
if True:
# see https://docs.aiohttp.org/en/stable/web_quickstart.html#variable-resources
- async def matching(request: web.Request): # $ MISSING: requestHandler
+ async def matching(request: web.Request): # $ requestHandler
name = request.match_info['name']
number = request.match_info['number']
return web.Response(text="matching name={} number={}".format(name, number))
- app.router.add_get("/matching/{name}/{number:\d+}", matching) # $ MISSING: routeSetup
+ app.router.add_get(r"/matching/{name}/{number:\d+}", matching) # $ routeSetup="/matching/{name}/{number:\d+}"
## ======= ##
## subapps ##
@@ -130,10 +130,10 @@ if True:
if True:
subapp = web.Application()
- async def subapp_handler(request): # $ MISSING: requestHandler
+ async def subapp_handler(request): # $ requestHandler
return web.Response(text="subapp_handler")
- subapp.router.add_get("/subapp_handler", subapp_handler) # $ MISSING: routeSetup
+ subapp.router.add_get("/subapp_handler", subapp_handler) # $ routeSetup="/subapp_handler"
app.add_subapp("/my_subapp", subapp)
@@ -146,11 +146,11 @@ if True:
## ================================ ##
if True:
- async def manual_dispatcher_instance(request): # $ MISSING: requestHandler
+ async def manual_dispatcher_instance(request): # $ requestHandler
return web.Response(text="manual_dispatcher_instance")
url_dispatcher = web.UrlDispatcher()
- url_dispatcher.add_get("/manual_dispatcher_instance", manual_dispatcher_instance) # $ MISSING: routeSetup
+ url_dispatcher.add_get("/manual_dispatcher_instance", manual_dispatcher_instance) # $ routeSetup="/manual_dispatcher_instance"
subapp2 = web.Application(router=url_dispatcher)
app.add_subapp("/manual_dispatcher_instance_app", subapp2)
From 2b992a635a1c3ff42d45b826353a919f7f0d4aa5 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 26 May 2021 14:54:20 +0200
Subject: [PATCH 107/272] Python: Add aiohttp taint tests
---
.../frameworks/aiohttp/taint_test.py | 196 ++++++++++++++++++
1 file changed, 196 insertions(+)
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py b/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
index e69de29bb2d..ba24f59a702 100644
--- a/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
+++ b/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
@@ -0,0 +1,196 @@
+from aiohttp import web
+
+async def test_taint(request: web.Request): # $ requestHandler
+
+ ensure_tainted(
+ request, # $ MISSING: tainted
+
+ # yarl.URL instances
+ # https://yarl.readthedocs.io/en/stable/api.html#yarl.URL
+ # see below
+ request.url, # $ MISSING: tainted
+ request.rel_url, # $ MISSING: tainted
+
+ request.forwarded, # $ MISSING: tainted
+
+ request.host, # $ MISSING: tainted
+ request.remote, # $ MISSING: tainted
+ request.path, # $ MISSING: tainted
+ request.path_qs, # $ MISSING: tainted
+ request.raw_path, # $ MISSING: tainted
+
+ # multidict.MultiDictProxy[str]
+ # see https://multidict.readthedocs.io/en/stable/multidict.html#multidict.MultiDictProxy
+ # TODO: Should have a better way to capture that we in fact _do_ model this as a
+ # an instance of the right class, and have the actual taint_test for that in a
+ # different file!
+ request.query, # $ MISSING: tainted
+ request.query["key"], # $ MISSING: tainted
+ request.query.get("key"), # $ MISSING: tainted
+ request.query.getone("key"), # $ MISSING: tainted
+ request.query.getall("key"), # $ MISSING: tainted
+ request.query.keys(), # $ MISSING: tainted
+ request.query.values(), # $ MISSING: tainted
+ request.query.items(), # $ MISSING: tainted
+ request.query.copy(), # $ MISSING: tainted
+ list(request.query), # $ MISSING: tainted
+ iter(request.query), # $ MISSING: tainted
+
+ # multidict.CIMultiDictProxy[str]
+ # see https://multidict.readthedocs.io/en/stable/multidict.html#multidict.CIMultiDictProxy
+ # TODO: Should have a better way to capture that we in fact _do_ model this as a
+ # an instance of the right class, and have the actual taint_test for that in a
+ # different file!
+ request.headers, # $ MISSING: tainted
+ request.query.getone("key"), # $ MISSING: tainted
+
+ # https://docs.python.org/3/library/asyncio-protocol.html#asyncio-transport
+ # TODO
+ request.transport, # $ MISSING: tainted
+ request.transport.get_extra_info("key"), # $ MISSING: tainted
+
+ # dict-like (readonly)
+ request.cookies, # $ MISSING: tainted
+ request.cookies["key"], # $ MISSING: tainted
+ request.cookies.get("key"), # $ MISSING: tainted
+ request.cookies.keys(), # $ MISSING: tainted
+ request.cookies.values(), # $ MISSING: tainted
+ request.cookies.items(), # $ MISSING: tainted
+ list(request.cookies), # $ MISSING: tainted
+ iter(request.cookies), # $ MISSING: tainted
+
+
+ # aiohttp.StreamReader
+ # see https://docs.aiohttp.org/en/stable/streams.html#aiohttp.StreamReader
+ # TODO
+ request.content, # $ MISSING: tainted
+ request._payload, # $ MISSING: tainted
+
+ request.body_exists, # $ MISSING: tainted
+ request.has_body, # $ MISSING: tainted
+
+ request.content_type, # $ MISSING: tainted
+ request.charset, # $ MISSING: tainted
+
+ request.http_range, # $ MISSING: tainted
+
+ # Optional[datetime]
+ request.if_modified_since, # $ MISSING: tainted
+ request.if_unmodified_since, # $ MISSING: tainted
+ request.if_range, # $ MISSING: tainted
+
+ request.clone(scheme="https"), # $ MISSING: tainted
+
+ # TODO: like request.transport.get_extra_info
+ request.get_extra_info("key"), # $ MISSING: tainted
+
+ # bytes
+ await request.read(), # $ MISSING: tainted
+
+ # str
+ await request.text(), # $ MISSING: tainted
+
+ # obj
+ await request.json(), # $ MISSING: tainted
+
+ # aiohttp.multipart.MultipartReader
+ await request.multipart(), # $ MISSING: tainted
+
+ # multidict.MultiDictProxy[str]
+ await request.post(), # $ MISSING: tainted
+ (await request.post()).getone("key"), # $ MISSING: tainted
+ )
+
+ import yarl
+ assert isinstance(request.url, yarl.URL)
+ assert isinstance(request.rel_url, yarl.URL)
+
+
+ # things that are technically controlled by sender of request,
+ # but doesn't seem that likely for exploitation.
+ ensure_not_tainted(
+ request.method,
+ request.version,
+ request.scheme,
+ request.secure,
+ request.keep_alive,
+
+ request.content_length,
+ )
+
+ ensure_not_tainted(
+ request.loop,
+
+ request.match_info,
+ request.app,
+ request.config_dict,
+ )
+
+ # TODO: Should have a better way to capture that we in fact _do_ model this as a
+ # an instance of the right class, and have the actual taint_test for that in a
+ # different file!
+ import yarl
+
+ ensure_tainted(
+ request.url.user, # $ MISSING: tainted
+ request.url.raw_user, # $ MISSING: tainted
+
+ request.url.password, # $ MISSING: tainted
+ request.url.raw_password, # $ MISSING: tainted
+
+ request.url.host, # $ MISSING: tainted
+ request.url.raw_host, # $ MISSING: tainted
+
+ request.url.port, # $ MISSING: tainted
+ request.url.explicit_port, # $ MISSING: tainted
+
+ request.url.authority, # $ MISSING: tainted
+ request.url.raw_authority, # $ MISSING: tainted
+
+ request.url.path, # $ MISSING: tainted
+ request.url.raw_path, # $ MISSING: tainted
+
+ request.url.path_qs, # $ MISSING: tainted
+ request.url.raw_path_qs, # $ MISSING: tainted
+
+ request.url.query_string, # $ MISSING: tainted
+ request.url.raw_query_string, # $ MISSING: tainted
+
+ request.url.fragment, # $ MISSING: tainted
+ request.url.raw_fragment, # $ MISSING: tainted
+
+ request.url.parts, # $ MISSING: tainted
+ request.url.raw_parts, # $ MISSING: tainted
+
+ request.url.name, # $ MISSING: tainted
+ request.url.raw_name, # $ MISSING: tainted
+
+ # multidict.MultiDictProxy[str]
+ request.url.query, # $ MISSING: tainted
+ request.url.query.getone("key"), # $ MISSING: tainted
+
+ request.url.with_scheme("foo"), # $ MISSING: tainted
+ request.url.with_user("foo"), # $ MISSING: tainted
+ request.url.with_password("foo"), # $ MISSING: tainted
+ request.url.with_host("foo"), # $ MISSING: tainted
+ request.url.with_port("foo"), # $ MISSING: tainted
+ request.url.with_path("foo"), # $ MISSING: tainted
+ request.url.with_query({"foo": 42}), # $ MISSING: tainted
+ request.url.with_query(foo=42), # $ MISSING: tainted
+ request.url.update_query({"foo": 42}), # $ MISSING: tainted
+ request.url.update_query(foo=42), # $ MISSING: tainted
+ request.url.with_fragment("foo"), # $ MISSING: tainted
+ request.url.with_name("foo"), # $ MISSING: tainted
+
+ request.url.join(yarl.URL("wat.html")), # $ MISSING: tainted
+
+ request.url.human_repr(), # $ MISSING: tainted
+ )
+
+
+app = web.Application()
+app.router.add_get(r"/test_taint/{name}/{number:\d+}", test_taint) # $ routeSetup="/test_taint/{name}/{number:\d+}"
+
+
+if __name__ == "__main__":
+ web.run_app(app)
From 88158e7414ff45206a71b8f9a7cc84cea33d653c Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 26 May 2021 15:24:52 +0200
Subject: [PATCH 108/272] Python: Add basic model setup for aiohttp.web.Request
---
.../src/semmle/python/frameworks/Aiohttp.qll | 67 ++++++++++++++++++-
.../frameworks/aiohttp/taint_test.py | 2 +-
2 files changed, 67 insertions(+), 2 deletions(-)
diff --git a/python/ql/src/semmle/python/frameworks/Aiohttp.qll b/python/ql/src/semmle/python/frameworks/Aiohttp.qll
index b73610c06b1..e7131e2c409 100644
--- a/python/ql/src/semmle/python/frameworks/Aiohttp.qll
+++ b/python/ql/src/semmle/python/frameworks/Aiohttp.qll
@@ -5,6 +5,8 @@
private import python
private import semmle.python.dataflow.new.DataFlow
+private import semmle.python.dataflow.new.RemoteFlowSources
+private import semmle.python.dataflow.new.TaintTracking
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
private import semmle.python.frameworks.internal.PoorMansFunctionResolution
@@ -33,7 +35,6 @@ module AiohttpWebModel {
}
// -- route modeling --
-
/** A route setup in `aiohttp.web` */
abstract class AiohttpRouteSetup extends HTTP::Server::RouteSetup::Range {
override Parameter getARoutedParameter() { none() }
@@ -138,4 +139,68 @@ module AiohttpWebModel {
override Function getARequestHandler() { result.getADecorator() = this.asExpr() }
}
+
+ // ---------------------------------------------------------------------------
+ // aiohttp.web.Request taint modeling
+ // ---------------------------------------------------------------------------
+ /**
+ * Provides models for the `aiohttp.web.Request` class
+ *
+ * See https://docs.aiohttp.org/en/stable/web_reference.html#request-and-base-request
+ */
+ module Request {
+ /**
+ * A source of instances of `aiohttp.web.Request`, extend this class to model new instances.
+ *
+ * This can include instantiations of the class, return values from function
+ * calls, or a special parameter that will be set when functions are called by an external
+ * library.
+ *
+ * Use `Request::instance()` predicate to get
+ * references to instances of `aiohttp.web.Request`.
+ */
+ abstract class InstanceSource extends DataFlow::Node { }
+
+ /** Gets a reference to an instance of `aiohttp.web.Request`. */
+ private DataFlow::LocalSourceNode instance(DataFlow::TypeTracker t) {
+ t.start() and
+ result instanceof InstanceSource
+ or
+ exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
+ }
+
+ /** Gets a reference to an instance of `aiohttp.web.Request`. */
+ DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
+ }
+
+ /**
+ * A parameter that will receive a `aiohttp.web.Request` instance when a request
+ * handler is invoked.
+ */
+ class AiohttpRequestHandlerRequestParam extends Request::InstanceSource, RemoteFlowSource::Range,
+ DataFlow::ParameterNode {
+ AiohttpRequestHandlerRequestParam() {
+ exists(Function requestHandler |
+ requestHandler = any(AiohttpRouteSetup setup).getARequestHandler() and
+ // We select the _last_ parameter for the request since that is what they do in
+ // `aiohttp-jinja2`.
+ // https://github.com/aio-libs/aiohttp-jinja2/blob/7fb4daf2c3003921d34031d38c2311ee0e02c18b/aiohttp_jinja2/__init__.py#L235
+ //
+ // I assume that is just to handle cases such as the one below
+ // ```py
+ // class MyCustomHandlerClass:
+ // async def foo_handler(self, request):
+ // ...
+ //
+ // my_custom_handler = MyCustomHandlerClass()
+ // app.router.add_get("/MyCustomHandlerClass/foo", my_custom_handler.foo_handler)
+ // ```
+ this.getParameter() =
+ max(Parameter param, int i | param = requestHandler.getArg(i) | param order by i)
+
+ )
+ }
+
+ override string getSourceType() { result = "aiohttp.web.Request" }
+ }
}
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py b/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
index ba24f59a702..d996d07ead2 100644
--- a/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
+++ b/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
@@ -3,7 +3,7 @@ from aiohttp import web
async def test_taint(request: web.Request): # $ requestHandler
ensure_tainted(
- request, # $ MISSING: tainted
+ request, # $ tainted
# yarl.URL instances
# https://yarl.readthedocs.io/en/stable/api.html#yarl.URL
From d953ea47d4718d55300c273f409eb7b2168d4652 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 26 May 2021 15:53:33 +0200
Subject: [PATCH 109/272] Python: Basic handling of tainted attributes in
aiohttp
---
.../src/semmle/python/frameworks/Aiohttp.qll | 29 +++++++-
.../frameworks/aiohttp/taint_test.py | 74 +++++++++----------
2 files changed, 65 insertions(+), 38 deletions(-)
diff --git a/python/ql/src/semmle/python/frameworks/Aiohttp.qll b/python/ql/src/semmle/python/frameworks/Aiohttp.qll
index e7131e2c409..7e2c9dc8424 100644
--- a/python/ql/src/semmle/python/frameworks/Aiohttp.qll
+++ b/python/ql/src/semmle/python/frameworks/Aiohttp.qll
@@ -197,10 +197,37 @@ module AiohttpWebModel {
// ```
this.getParameter() =
max(Parameter param, int i | param = requestHandler.getArg(i) | param order by i)
-
)
}
override string getSourceType() { result = "aiohttp.web.Request" }
}
+
+ /**
+ * Taint propagation for `aiohttp.web.Request`.
+ *
+ * See https://docs.aiohttp.org/en/stable/web_reference.html#request-and-base-request
+ */
+ private class AiohttpRequestAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
+ override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
+ // Methods
+ exists(string method_name | method_name in ["TODO"] |
+ // Method access (obj -> obj.meth)
+ none()
+ or
+ // Method call (obj.meth -> obj.meth())
+ none()
+ )
+ or
+ // Attributes
+ nodeFrom = Request::instance() and
+ nodeTo.(DataFlow::AttrRead).getObject() = nodeFrom and
+ nodeTo.(DataFlow::AttrRead).getAttributeName() in [
+ "url", "rel_url", "forwarded", "host", "remote", "path", "path_qs", "raw_path", "query",
+ "headers", "transport", "cookies", "content", "_payload", "body_exists", "has_body",
+ "content_type", "charset", "http_range", "if_modified_since", "if_unmodified_since",
+ "if_range"
+ ]
+ }
+ }
}
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py b/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
index d996d07ead2..39c814564ce 100644
--- a/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
+++ b/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
@@ -8,76 +8,76 @@ async def test_taint(request: web.Request): # $ requestHandler
# yarl.URL instances
# https://yarl.readthedocs.io/en/stable/api.html#yarl.URL
# see below
- request.url, # $ MISSING: tainted
- request.rel_url, # $ MISSING: tainted
+ request.url, # $ tainted
+ request.rel_url, # $ tainted
- request.forwarded, # $ MISSING: tainted
+ request.forwarded, # $ tainted
- request.host, # $ MISSING: tainted
- request.remote, # $ MISSING: tainted
- request.path, # $ MISSING: tainted
- request.path_qs, # $ MISSING: tainted
- request.raw_path, # $ MISSING: tainted
+ request.host, # $ tainted
+ request.remote, # $ tainted
+ request.path, # $ tainted
+ request.path_qs, # $ tainted
+ request.raw_path, # $ tainted
# multidict.MultiDictProxy[str]
# see https://multidict.readthedocs.io/en/stable/multidict.html#multidict.MultiDictProxy
# TODO: Should have a better way to capture that we in fact _do_ model this as a
# an instance of the right class, and have the actual taint_test for that in a
# different file!
- request.query, # $ MISSING: tainted
- request.query["key"], # $ MISSING: tainted
- request.query.get("key"), # $ MISSING: tainted
+ request.query, # $ tainted
+ request.query["key"], # $ tainted
+ request.query.get("key"), # $ tainted
request.query.getone("key"), # $ MISSING: tainted
request.query.getall("key"), # $ MISSING: tainted
request.query.keys(), # $ MISSING: tainted
- request.query.values(), # $ MISSING: tainted
- request.query.items(), # $ MISSING: tainted
- request.query.copy(), # $ MISSING: tainted
- list(request.query), # $ MISSING: tainted
- iter(request.query), # $ MISSING: tainted
+ request.query.values(), # $ tainted
+ request.query.items(), # $ tainted
+ request.query.copy(), # $ tainted
+ list(request.query), # $ tainted
+ iter(request.query), # $ tainted
# multidict.CIMultiDictProxy[str]
# see https://multidict.readthedocs.io/en/stable/multidict.html#multidict.CIMultiDictProxy
# TODO: Should have a better way to capture that we in fact _do_ model this as a
# an instance of the right class, and have the actual taint_test for that in a
# different file!
- request.headers, # $ MISSING: tainted
- request.query.getone("key"), # $ MISSING: tainted
+ request.headers, # $ tainted
+ request.headers.getone("key"), # $ MISSING: tainted
# https://docs.python.org/3/library/asyncio-protocol.html#asyncio-transport
# TODO
- request.transport, # $ MISSING: tainted
+ request.transport, # $ tainted
request.transport.get_extra_info("key"), # $ MISSING: tainted
# dict-like (readonly)
- request.cookies, # $ MISSING: tainted
- request.cookies["key"], # $ MISSING: tainted
- request.cookies.get("key"), # $ MISSING: tainted
+ request.cookies, # $ tainted
+ request.cookies["key"], # $ tainted
+ request.cookies.get("key"), # $ tainted
request.cookies.keys(), # $ MISSING: tainted
- request.cookies.values(), # $ MISSING: tainted
- request.cookies.items(), # $ MISSING: tainted
- list(request.cookies), # $ MISSING: tainted
- iter(request.cookies), # $ MISSING: tainted
+ request.cookies.values(), # $ tainted
+ request.cookies.items(), # $ tainted
+ list(request.cookies), # $ tainted
+ iter(request.cookies), # $ tainted
# aiohttp.StreamReader
# see https://docs.aiohttp.org/en/stable/streams.html#aiohttp.StreamReader
# TODO
- request.content, # $ MISSING: tainted
- request._payload, # $ MISSING: tainted
+ request.content, # $ tainted
+ request._payload, # $ tainted
- request.body_exists, # $ MISSING: tainted
- request.has_body, # $ MISSING: tainted
+ request.body_exists, # $ tainted
+ request.has_body, # $ tainted
- request.content_type, # $ MISSING: tainted
- request.charset, # $ MISSING: tainted
+ request.content_type, # $ tainted
+ request.charset, # $ tainted
- request.http_range, # $ MISSING: tainted
+ request.http_range, # $ tainted
# Optional[datetime]
- request.if_modified_since, # $ MISSING: tainted
- request.if_unmodified_since, # $ MISSING: tainted
- request.if_range, # $ MISSING: tainted
+ request.if_modified_since, # $ tainted
+ request.if_unmodified_since, # $ tainted
+ request.if_range, # $ tainted
request.clone(scheme="https"), # $ MISSING: tainted
@@ -182,7 +182,7 @@ async def test_taint(request: web.Request): # $ requestHandler
request.url.with_fragment("foo"), # $ MISSING: tainted
request.url.with_name("foo"), # $ MISSING: tainted
- request.url.join(yarl.URL("wat.html")), # $ MISSING: tainted
+ request.url.join(yarl.URL("wat.html")), # $ tainted
request.url.human_repr(), # $ MISSING: tainted
)
From 597a9dfc80051c3652132a2ecf59f9808ded98be Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 26 May 2021 15:57:02 +0200
Subject: [PATCH 110/272] Python: Don't consider has_body tainted
Although it technically is, I think it belong in the section of things
that are unlikely to be exploitable
---
python/ql/src/semmle/python/frameworks/Aiohttp.qll | 5 ++---
.../ql/test/library-tests/frameworks/aiohttp/taint_test.py | 6 +++---
2 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/python/ql/src/semmle/python/frameworks/Aiohttp.qll b/python/ql/src/semmle/python/frameworks/Aiohttp.qll
index 7e2c9dc8424..1a2a8e64bf5 100644
--- a/python/ql/src/semmle/python/frameworks/Aiohttp.qll
+++ b/python/ql/src/semmle/python/frameworks/Aiohttp.qll
@@ -224,9 +224,8 @@ module AiohttpWebModel {
nodeTo.(DataFlow::AttrRead).getObject() = nodeFrom and
nodeTo.(DataFlow::AttrRead).getAttributeName() in [
"url", "rel_url", "forwarded", "host", "remote", "path", "path_qs", "raw_path", "query",
- "headers", "transport", "cookies", "content", "_payload", "body_exists", "has_body",
- "content_type", "charset", "http_range", "if_modified_since", "if_unmodified_since",
- "if_range"
+ "headers", "transport", "cookies", "content", "_payload", "content_type", "charset",
+ "http_range", "if_modified_since", "if_unmodified_since", "if_range"
]
}
}
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py b/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
index 39c814564ce..a3d0e30fb42 100644
--- a/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
+++ b/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
@@ -66,9 +66,6 @@ async def test_taint(request: web.Request): # $ requestHandler
request.content, # $ tainted
request._payload, # $ tainted
- request.body_exists, # $ tainted
- request.has_body, # $ tainted
-
request.content_type, # $ tainted
request.charset, # $ tainted
@@ -116,6 +113,9 @@ async def test_taint(request: web.Request): # $ requestHandler
request.keep_alive,
request.content_length,
+ request.body_exists,
+ request.has_body,
+ request.can_read_body,
)
ensure_not_tainted(
From 63c7fa0c2ca12453c1a1fea16bd85c2ec55a23ea Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 26 May 2021 16:05:36 +0200
Subject: [PATCH 111/272] Python: aiohttp match_info should be tainted
Whoops
---
python/ql/src/semmle/python/frameworks/Aiohttp.qll | 2 +-
.../ql/test/library-tests/frameworks/aiohttp/taint_test.py | 6 +++++-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/python/ql/src/semmle/python/frameworks/Aiohttp.qll b/python/ql/src/semmle/python/frameworks/Aiohttp.qll
index 1a2a8e64bf5..6455bf79c3c 100644
--- a/python/ql/src/semmle/python/frameworks/Aiohttp.qll
+++ b/python/ql/src/semmle/python/frameworks/Aiohttp.qll
@@ -225,7 +225,7 @@ module AiohttpWebModel {
nodeTo.(DataFlow::AttrRead).getAttributeName() in [
"url", "rel_url", "forwarded", "host", "remote", "path", "path_qs", "raw_path", "query",
"headers", "transport", "cookies", "content", "_payload", "content_type", "charset",
- "http_range", "if_modified_since", "if_unmodified_since", "if_range"
+ "http_range", "if_modified_since", "if_unmodified_since", "if_range", "match_info"
]
}
}
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py b/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
index a3d0e30fb42..33cee33c3a5 100644
--- a/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
+++ b/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
@@ -19,6 +19,11 @@ async def test_taint(request: web.Request): # $ requestHandler
request.path_qs, # $ tainted
request.raw_path, # $ tainted
+ # dict-like for captured parts of the URL
+ request.match_info, # $ tainted
+ request.match_info["key"], # $ tainted
+ request.match_info.get("key"), # $ tainted
+
# multidict.MultiDictProxy[str]
# see https://multidict.readthedocs.io/en/stable/multidict.html#multidict.MultiDictProxy
# TODO: Should have a better way to capture that we in fact _do_ model this as a
@@ -121,7 +126,6 @@ async def test_taint(request: web.Request): # $ requestHandler
ensure_not_tainted(
request.loop,
- request.match_info,
request.app,
request.config_dict,
)
From dd131e6bf79c6458d1eece2c9e27d2abe1189edb Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Thu, 27 May 2021 10:28:07 +0200
Subject: [PATCH 112/272] Python: Add taint-step for methods on
aiohttp.web.Request
---
.../src/semmle/python/frameworks/Aiohttp.qll | 21 ++++++++++++++-----
.../frameworks/aiohttp/taint_test.py | 14 ++++++-------
2 files changed, 23 insertions(+), 12 deletions(-)
diff --git a/python/ql/src/semmle/python/frameworks/Aiohttp.qll b/python/ql/src/semmle/python/frameworks/Aiohttp.qll
index 6455bf79c3c..6f36f31a5b9 100644
--- a/python/ql/src/semmle/python/frameworks/Aiohttp.qll
+++ b/python/ql/src/semmle/python/frameworks/Aiohttp.qll
@@ -211,12 +211,23 @@ module AiohttpWebModel {
private class AiohttpRequestAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// Methods
- exists(string method_name | method_name in ["TODO"] |
- // Method access (obj -> obj.meth)
- none()
+ //
+ // TODO: When we have tools that make it easy, model these properly to handle
+ // `meth = obj.meth; meth()`. Until then, we'll use this more syntactic approach
+ // (since it allows us to at least capture the most common cases).
+ nodeFrom = Request::instance() and
+ exists(DataFlow::AttrRead attr | attr.getObject() = nodeFrom |
+ // normal methods
+ attr.getAttributeName() in ["clone", "get_extra_info"] and
+ nodeTo.(DataFlow::CallCfgNode).getFunction() = attr
or
- // Method call (obj.meth -> obj.meth())
- none()
+ // async methods
+ exists(Await await, DataFlow::CallCfgNode call |
+ attr.getAttributeName() in ["read", "text", "json", "multipart", "post"] and
+ call.getFunction() = attr and
+ await.getValue() = call.asExpr() and
+ nodeTo.asExpr() = await
+ )
)
or
// Attributes
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py b/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
index 33cee33c3a5..e16a2d79d66 100644
--- a/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
+++ b/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
@@ -81,25 +81,25 @@ async def test_taint(request: web.Request): # $ requestHandler
request.if_unmodified_since, # $ tainted
request.if_range, # $ tainted
- request.clone(scheme="https"), # $ MISSING: tainted
+ request.clone(scheme="https"), # $ tainted
# TODO: like request.transport.get_extra_info
- request.get_extra_info("key"), # $ MISSING: tainted
+ request.get_extra_info("key"), # $ tainted
# bytes
- await request.read(), # $ MISSING: tainted
+ await request.read(), # $ tainted
# str
- await request.text(), # $ MISSING: tainted
+ await request.text(), # $ tainted
# obj
- await request.json(), # $ MISSING: tainted
+ await request.json(), # $ tainted
# aiohttp.multipart.MultipartReader
- await request.multipart(), # $ MISSING: tainted
+ await request.multipart(), # $ tainted
# multidict.MultiDictProxy[str]
- await request.post(), # $ MISSING: tainted
+ await request.post(), # $ tainted
(await request.post()).getone("key"), # $ MISSING: tainted
)
From e76f02b016cadd7f2a5852d665b91b37a686b40a Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Thu, 27 May 2021 10:29:39 +0200
Subject: [PATCH 113/272] Python: Minor refactor to use LocalSourceNode
This just more correctly reflects the reality, since the type-tracking
predicate just below only holds for LocalSourceNode anyway.
---
python/ql/src/semmle/python/frameworks/Aiohttp.qll | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/python/ql/src/semmle/python/frameworks/Aiohttp.qll b/python/ql/src/semmle/python/frameworks/Aiohttp.qll
index 6f36f31a5b9..074c64dae8f 100644
--- a/python/ql/src/semmle/python/frameworks/Aiohttp.qll
+++ b/python/ql/src/semmle/python/frameworks/Aiohttp.qll
@@ -159,7 +159,7 @@ module AiohttpWebModel {
* Use `Request::instance()` predicate to get
* references to instances of `aiohttp.web.Request`.
*/
- abstract class InstanceSource extends DataFlow::Node { }
+ abstract class InstanceSource extends DataFlow::LocalSourceNode { }
/** Gets a reference to an instance of `aiohttp.web.Request`. */
private DataFlow::LocalSourceNode instance(DataFlow::TypeTracker t) {
@@ -240,4 +240,6 @@ module AiohttpWebModel {
]
}
}
+
+
}
From 72e6a1489c047d9a169695964923cd2f952cdfa2 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Thu, 27 May 2021 10:49:50 +0200
Subject: [PATCH 114/272] Python: Add taint-steps for MultiDictProxy
---
docs/codeql/support/reusables/frameworks.rst | 3 +-
python/ql/src/semmle/python/Frameworks.qll | 1 +
.../src/semmle/python/frameworks/Aiohttp.qll | 8 ++-
.../semmle/python/frameworks/Multidict.qll | 72 +++++++++++++++++++
.../frameworks/aiohttp/taint_test.py | 6 +-
5 files changed, 85 insertions(+), 5 deletions(-)
create mode 100644 python/ql/src/semmle/python/frameworks/Multidict.qll
diff --git a/docs/codeql/support/reusables/frameworks.rst b/docs/codeql/support/reusables/frameworks.rst
index 41680cffe0e..ac93993ec7d 100644
--- a/docs/codeql/support/reusables/frameworks.rst
+++ b/docs/codeql/support/reusables/frameworks.rst
@@ -161,8 +161,9 @@ Python built-in support
simplejson, Serialization
ujson, Serialization
fabric, Utility library
- invoke, Utility library
idna, Utility library
+ invoke, Utility library
+ multidict, Utility library
mysql-connector-python, Database
MySQLdb, Database
psycopg2, Database
diff --git a/python/ql/src/semmle/python/Frameworks.qll b/python/ql/src/semmle/python/Frameworks.qll
index 172f3d11bfb..065ec5d69f9 100644
--- a/python/ql/src/semmle/python/Frameworks.qll
+++ b/python/ql/src/semmle/python/Frameworks.qll
@@ -13,6 +13,7 @@ private import semmle.python.frameworks.Fabric
private import semmle.python.frameworks.Flask
private import semmle.python.frameworks.Idna
private import semmle.python.frameworks.Invoke
+private import semmle.python.frameworks.Multidict
private import semmle.python.frameworks.MysqlConnectorPython
private import semmle.python.frameworks.MySQLdb
private import semmle.python.frameworks.Psycopg2
diff --git a/python/ql/src/semmle/python/frameworks/Aiohttp.qll b/python/ql/src/semmle/python/frameworks/Aiohttp.qll
index 074c64dae8f..0f2d3d5d18c 100644
--- a/python/ql/src/semmle/python/frameworks/Aiohttp.qll
+++ b/python/ql/src/semmle/python/frameworks/Aiohttp.qll
@@ -10,6 +10,7 @@ private import semmle.python.dataflow.new.TaintTracking
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
private import semmle.python.frameworks.internal.PoorMansFunctionResolution
+private import semmle.python.frameworks.Multidict
/**
* INTERNAL: Do not use.
@@ -241,5 +242,10 @@ module AiohttpWebModel {
}
}
-
+ class AiohttpRequestMultiDictProxyInstances extends Multidict::MultiDictProxy::InstanceSource {
+ AiohttpRequestMultiDictProxyInstances() {
+ this.(DataFlow::AttrRead).getObject() = Request::instance() and
+ this.(DataFlow::AttrRead).getAttributeName() in ["query", "headers"]
+ }
+ }
}
diff --git a/python/ql/src/semmle/python/frameworks/Multidict.qll b/python/ql/src/semmle/python/frameworks/Multidict.qll
new file mode 100644
index 00000000000..7a2c2bf0751
--- /dev/null
+++ b/python/ql/src/semmle/python/frameworks/Multidict.qll
@@ -0,0 +1,72 @@
+/**
+ * Provides classes modeling security-relevant aspects of the `multidict` PyPI package.
+ * See https://multidict.readthedocs.io/en/stable/.
+ */
+
+private import python
+private import semmle.python.dataflow.new.DataFlow
+private import semmle.python.dataflow.new.TaintTracking
+private import semmle.python.Concepts
+private import semmle.python.ApiGraphs
+
+/**
+ * INTERNAL: Do not use.
+ *
+ * Provides models for the `multidict` PyPI package.
+ * See https://multidict.readthedocs.io/en/stable/.
+ */
+module Multidict {
+ /**
+ * Provides models for a `MultiDictProxy` class:
+ * - `multidict.MultiDictProxy`
+ * - `multidict.CIMultiDictProxy`
+ *
+ * See https://multidict.readthedocs.io/en/stable/multidict.html#multidictproxy
+ */
+ module MultiDictProxy {
+ /**
+ * A source of instances of `multidict.MultiDictProxy`, extend this class to model
+ * new instances.
+ *
+ * This can include instantiations of the class, return values from function
+ * calls, or a special parameter that will be set when functions are called by an external
+ * library.
+ *
+ * Use `MultiDictProxy::instance()` predicate to get
+ * references to instances of `multidict.MultiDictProxy`.
+ */
+ abstract class InstanceSource extends DataFlow::LocalSourceNode { }
+
+ /** Gets a reference to an instance of `multidict.MultiDictProxy`. */
+ private DataFlow::LocalSourceNode instance(DataFlow::TypeTracker t) {
+ t.start() and
+ result instanceof InstanceSource
+ or
+ exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
+ }
+
+ /** Gets a reference to an instance of `multidict.MultiDictProxy`. */
+ DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
+
+ /**
+ * Taint propagation for `multidict.MultiDictProxy`.
+ *
+ * See https://multidict.readthedocs.io/en/stable/multidict.html#multidictproxy
+ */
+ class MultiDictProxyAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
+ override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
+ // Methods
+ //
+ // TODO: When we have tools that make it easy, model these properly to handle
+ // `meth = obj.meth; meth()`. Until then, we'll use this more syntactic approach
+ // (since it allows us to at least capture the most common cases).
+ nodeFrom = instance() and
+ exists(DataFlow::AttrRead attr | attr.getObject() = nodeFrom |
+ // methods (non-async)
+ attr.getAttributeName() in ["getone", "getall"] and
+ nodeTo.(DataFlow::CallCfgNode).getFunction() = attr
+ )
+ }
+ }
+ }
+}
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py b/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
index e16a2d79d66..eab09f17e74 100644
--- a/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
+++ b/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
@@ -32,8 +32,8 @@ async def test_taint(request: web.Request): # $ requestHandler
request.query, # $ tainted
request.query["key"], # $ tainted
request.query.get("key"), # $ tainted
- request.query.getone("key"), # $ MISSING: tainted
- request.query.getall("key"), # $ MISSING: tainted
+ request.query.getone("key"), # $ tainted
+ request.query.getall("key"), # $ tainted
request.query.keys(), # $ MISSING: tainted
request.query.values(), # $ tainted
request.query.items(), # $ tainted
@@ -47,7 +47,7 @@ async def test_taint(request: web.Request): # $ requestHandler
# an instance of the right class, and have the actual taint_test for that in a
# different file!
request.headers, # $ tainted
- request.headers.getone("key"), # $ MISSING: tainted
+ request.headers.getone("key"), # $ tainted
# https://docs.python.org/3/library/asyncio-protocol.html#asyncio-transport
# TODO
From fb21bc04fa5c5d6996d387c99e1e7f3d22891252 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Thu, 27 May 2021 20:23:36 +0200
Subject: [PATCH 115/272] Python: Add taint-steps for `yarl.URL`
---
docs/codeql/support/reusables/frameworks.rst | 1 +
python/ql/src/semmle/python/Frameworks.qll | 1 +
.../src/semmle/python/frameworks/Aiohttp.qll | 8 ++
.../ql/src/semmle/python/frameworks/Yarl.qll | 110 ++++++++++++++++++
.../frameworks/aiohttp/taint_test.py | 75 ++++++------
5 files changed, 158 insertions(+), 37 deletions(-)
create mode 100644 python/ql/src/semmle/python/frameworks/Yarl.qll
diff --git a/docs/codeql/support/reusables/frameworks.rst b/docs/codeql/support/reusables/frameworks.rst
index ac93993ec7d..d39b85931ce 100644
--- a/docs/codeql/support/reusables/frameworks.rst
+++ b/docs/codeql/support/reusables/frameworks.rst
@@ -164,6 +164,7 @@ Python built-in support
idna, Utility library
invoke, Utility library
multidict, Utility library
+ yarl, Utility library
mysql-connector-python, Database
MySQLdb, Database
psycopg2, Database
diff --git a/python/ql/src/semmle/python/Frameworks.qll b/python/ql/src/semmle/python/Frameworks.qll
index 065ec5d69f9..523f844954b 100644
--- a/python/ql/src/semmle/python/Frameworks.qll
+++ b/python/ql/src/semmle/python/Frameworks.qll
@@ -23,3 +23,4 @@ private import semmle.python.frameworks.Stdlib
private import semmle.python.frameworks.Tornado
private import semmle.python.frameworks.Ujson
private import semmle.python.frameworks.Yaml
+private import semmle.python.frameworks.Yarl
diff --git a/python/ql/src/semmle/python/frameworks/Aiohttp.qll b/python/ql/src/semmle/python/frameworks/Aiohttp.qll
index 0f2d3d5d18c..c6616b93712 100644
--- a/python/ql/src/semmle/python/frameworks/Aiohttp.qll
+++ b/python/ql/src/semmle/python/frameworks/Aiohttp.qll
@@ -11,6 +11,7 @@ private import semmle.python.Concepts
private import semmle.python.ApiGraphs
private import semmle.python.frameworks.internal.PoorMansFunctionResolution
private import semmle.python.frameworks.Multidict
+private import semmle.python.frameworks.Yarl
/**
* INTERNAL: Do not use.
@@ -248,4 +249,11 @@ module AiohttpWebModel {
this.(DataFlow::AttrRead).getAttributeName() in ["query", "headers"]
}
}
+
+ class AiohttpRequestYarlUrlInstances extends Yarl::Url::InstanceSource {
+ AiohttpRequestYarlUrlInstances() {
+ this.(DataFlow::AttrRead).getObject() = Request::instance() and
+ this.(DataFlow::AttrRead).getAttributeName() in ["url", "rel_url"]
+ }
+ }
}
diff --git a/python/ql/src/semmle/python/frameworks/Yarl.qll b/python/ql/src/semmle/python/frameworks/Yarl.qll
new file mode 100644
index 00000000000..a998d8e0ca1
--- /dev/null
+++ b/python/ql/src/semmle/python/frameworks/Yarl.qll
@@ -0,0 +1,110 @@
+/**
+ * Provides classes modeling security-relevant aspects of the `yarl` PyPI package.
+ * See https://yarl.readthedocs.io/en/stable/.
+ */
+
+private import python
+private import semmle.python.dataflow.new.DataFlow
+private import semmle.python.dataflow.new.TaintTracking
+private import semmle.python.Concepts
+private import semmle.python.ApiGraphs
+private import semmle.python.frameworks.Multidict
+
+/**
+ * INTERNAL: Do not use.
+ *
+ * Provides models for the `yarl` PyPI package.
+ * See https://multidict.readthedocs.io/en/stable/.
+ */
+module Yarl {
+ /**
+ * Provides models for a the `yarl.URL` class:
+ *
+ * See https://yarl.readthedocs.io/en/stable/api.html#yarl.URL
+ */
+ module Url {
+ /**
+ * A source of instances of `yarl.URL`, extend this class to model new instances.
+ *
+ * This can include instantiations of the class, return values from function
+ * calls, or a special parameter that will be set when functions are called by an external
+ * library.
+ *
+ * Use `Url::instance()` predicate to get references to instances of `yarl.URL`.
+ */
+ abstract class InstanceSource extends DataFlow::LocalSourceNode { }
+
+ /** Gets a reference to an instance of `yarl.URL`. */
+ private DataFlow::LocalSourceNode instance(DataFlow::TypeTracker t) {
+ t.start() and
+ result instanceof InstanceSource
+ or
+ exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
+ }
+
+ /** Gets a reference to an instance of `yarl.URL`. */
+ DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
+
+ /**
+ * Taint propagation for `yarl.URL`.
+ *
+ * See https://yarl.readthedocs.io/en/stable/api.html#yarl.URL
+ */
+ class YarlUrlAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
+ override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
+ // Methods
+ //
+ // TODO: When we have tools that make it easy, model these properly to handle
+ // `meth = obj.meth; meth()`. Until then, we'll use this more syntactic approach
+ // (since it allows us to at least capture the most common cases).
+ exists(DataFlow::AttrRead attr |
+ // methods (that replaces part of URL, taken as only arguments)
+ attr.getAttributeName() in [
+ "with_scheme", "with_user", "with_password", "with_host", "with_port", "with_path",
+ "with_query", "with_query", "update_query", "update_query", "with_fragment",
+ "with_name",
+ // join is a bit different, but is still correct to add here :+1:
+ "join"
+ ] and
+ (
+ // obj -> obj.meth()
+ nodeFrom = instance() and
+ attr.getObject() = nodeFrom and
+ nodeTo.(DataFlow::CallCfgNode).getFunction() = attr
+ or
+ // argument of obj.meth() -> obj.meth()
+ attr.getObject() = instance() and
+ nodeTo.(DataFlow::CallCfgNode).getFunction() = attr and
+ nodeFrom in [
+ nodeTo.(DataFlow::CallCfgNode).getArg(_),
+ nodeTo.(DataFlow::CallCfgNode).getArgByName(_)
+ ]
+ )
+ or
+ // other methods
+ nodeFrom = instance() and
+ attr.getObject() = nodeFrom and
+ attr.getAttributeName() in ["human_repr"] and
+ nodeTo.(DataFlow::CallCfgNode).getFunction() = attr
+ )
+ or
+ // Attributes
+ nodeFrom = instance() and
+ nodeTo.(DataFlow::AttrRead).getObject() = nodeFrom and
+ nodeTo.(DataFlow::AttrRead).getAttributeName() in [
+ "user", "raw_user", "password", "raw_password", "host", "raw_host", "port",
+ "explicit_port", "authority", "raw_authority", "path", "raw_path", "path_qs",
+ "raw_path_qs", "query_string", "raw_query_string", "fragment", "raw_fragment", "parts",
+ "raw_parts", "name", "raw_name", "query"
+ ]
+ }
+ }
+
+ class YarlUrlMultiDictProxyInstance extends Multidict::MultiDictProxy::InstanceSource {
+ YarlUrlMultiDictProxyInstance() {
+ this.(DataFlow::AttrRead).getObject() = Yarl::Url::instance() and
+ this.(DataFlow::AttrRead).getAttributeName() = "query"
+ }
+ }
+ }
+}
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py b/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
index eab09f17e74..30e42cc3f3b 100644
--- a/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
+++ b/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
@@ -136,59 +136,60 @@ async def test_taint(request: web.Request): # $ requestHandler
import yarl
ensure_tainted(
- request.url.user, # $ MISSING: tainted
- request.url.raw_user, # $ MISSING: tainted
+ # see https://yarl.readthedocs.io/en/stable/api.html#yarl.URL
+ request.url.user, # $ tainted
+ request.url.raw_user, # $ tainted
- request.url.password, # $ MISSING: tainted
- request.url.raw_password, # $ MISSING: tainted
+ request.url.password, # $ tainted
+ request.url.raw_password, # $ tainted
- request.url.host, # $ MISSING: tainted
- request.url.raw_host, # $ MISSING: tainted
+ request.url.host, # $ tainted
+ request.url.raw_host, # $ tainted
- request.url.port, # $ MISSING: tainted
- request.url.explicit_port, # $ MISSING: tainted
+ request.url.port, # $ tainted
+ request.url.explicit_port, # $ tainted
- request.url.authority, # $ MISSING: tainted
- request.url.raw_authority, # $ MISSING: tainted
+ request.url.authority, # $ tainted
+ request.url.raw_authority, # $ tainted
- request.url.path, # $ MISSING: tainted
- request.url.raw_path, # $ MISSING: tainted
+ request.url.path, # $ tainted
+ request.url.raw_path, # $ tainted
- request.url.path_qs, # $ MISSING: tainted
- request.url.raw_path_qs, # $ MISSING: tainted
+ request.url.path_qs, # $ tainted
+ request.url.raw_path_qs, # $ tainted
- request.url.query_string, # $ MISSING: tainted
- request.url.raw_query_string, # $ MISSING: tainted
+ request.url.query_string, # $ tainted
+ request.url.raw_query_string, # $ tainted
- request.url.fragment, # $ MISSING: tainted
- request.url.raw_fragment, # $ MISSING: tainted
+ request.url.fragment, # $ tainted
+ request.url.raw_fragment, # $ tainted
- request.url.parts, # $ MISSING: tainted
- request.url.raw_parts, # $ MISSING: tainted
+ request.url.parts, # $ tainted
+ request.url.raw_parts, # $ tainted
- request.url.name, # $ MISSING: tainted
- request.url.raw_name, # $ MISSING: tainted
+ request.url.name, # $ tainted
+ request.url.raw_name, # $ tainted
# multidict.MultiDictProxy[str]
- request.url.query, # $ MISSING: tainted
- request.url.query.getone("key"), # $ MISSING: tainted
+ request.url.query, # $ tainted
+ request.url.query.getone("key"), # $ tainted
- request.url.with_scheme("foo"), # $ MISSING: tainted
- request.url.with_user("foo"), # $ MISSING: tainted
- request.url.with_password("foo"), # $ MISSING: tainted
- request.url.with_host("foo"), # $ MISSING: tainted
- request.url.with_port("foo"), # $ MISSING: tainted
- request.url.with_path("foo"), # $ MISSING: tainted
- request.url.with_query({"foo": 42}), # $ MISSING: tainted
- request.url.with_query(foo=42), # $ MISSING: tainted
- request.url.update_query({"foo": 42}), # $ MISSING: tainted
- request.url.update_query(foo=42), # $ MISSING: tainted
- request.url.with_fragment("foo"), # $ MISSING: tainted
- request.url.with_name("foo"), # $ MISSING: tainted
+ request.url.with_scheme("foo"), # $ tainted
+ request.url.with_user("foo"), # $ tainted
+ request.url.with_password("foo"), # $ tainted
+ request.url.with_host("foo"), # $ tainted
+ request.url.with_port("foo"), # $ tainted
+ request.url.with_path("foo"), # $ tainted
+ request.url.with_query({"foo": 42}), # $ tainted
+ request.url.with_query(foo=42), # $ tainted
+ request.url.update_query({"foo": 42}), # $ tainted
+ request.url.update_query(foo=42), # $ tainted
+ request.url.with_fragment("foo"), # $ tainted
+ request.url.with_name("foo"), # $ tainted
request.url.join(yarl.URL("wat.html")), # $ tainted
- request.url.human_repr(), # $ MISSING: tainted
+ request.url.human_repr(), # $ tainted
)
From 1aa222d7ccfafb8c7caaba18c00875325ffaf0db Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Tue, 1 Jun 2021 12:59:37 +0200
Subject: [PATCH 116/272] Python: Add taint-test for class-based view
---
.../test/library-tests/frameworks/aiohttp/taint_test.py | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py b/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
index 30e42cc3f3b..16d4fc27412 100644
--- a/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
+++ b/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
@@ -193,8 +193,16 @@ async def test_taint(request: web.Request): # $ requestHandler
)
+class TaintTestClass(web.View):
+ def get(self):
+ ensure_tainted(
+ self.request, # $ MISSING: tainted
+ )
+
+
app = web.Application()
app.router.add_get(r"/test_taint/{name}/{number:\d+}", test_taint) # $ routeSetup="/test_taint/{name}/{number:\d+}"
+app.router.add_view(r"/test_taint_class", TaintTestClass) # $ routeSetup="/test_taint_class"
if __name__ == "__main__":
From 8c039d56881ea381d65e018faadb71b8b5d4b21f Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Tue, 1 Jun 2021 13:13:04 +0200
Subject: [PATCH 117/272] Python: Add more aiohttp view routing tests
---
.../frameworks/aiohttp/routing_test.py | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/routing_test.py b/python/ql/test/library-tests/frameworks/aiohttp/routing_test.py
index 880a7f8cb87..ffd339a4d1a 100644
--- a/python/ql/test/library-tests/frameworks/aiohttp/routing_test.py
+++ b/python/ql/test/library-tests/frameworks/aiohttp/routing_test.py
@@ -109,6 +109,19 @@ if True:
app.router.add_view("/MyWebView3", MyWebView3) # $ MISSING: routeSetup
+ # no route-setup
+ class MyWebViewNoRoute(web.View):
+ async def get(self): # $ MISSING: requestHandler
+ return web.Response(text="MyWebViewNoRoute.get")
+
+ if len(__name__) < 0: # avoid running, but fool analysis to not consider dead code
+ # no explicit-view subclass (but route-setup)
+ class MyWebViewNoSubclassButRoute(somelib.someclass):
+ async def get(self): # $ MISSING: requestHandler
+ return web.Response(text="MyWebViewNoSubclassButRoute.get")
+
+ app.router.add_view("/MyWebViewNoSubclassButRoute", MyWebViewNoSubclassButRoute) # $ MISSING: routeSetup
+
## =================== ##
## "Routed parameters" ##
## =================== ##
From c4b618dcf5d3b54a85208ffba9ba5c0a7ece0879 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Tue, 1 Jun 2021 17:21:41 +0200
Subject: [PATCH 118/272] Python: Model view-classes in aiohttp.web
No taint modeling of them yet though
---
.../src/semmle/python/frameworks/Aiohttp.qll | 150 +++++++++++++++++-
.../frameworks/aiohttp/routing_test.py | 18 +--
.../frameworks/aiohttp/taint_test.py | 2 +-
3 files changed, 158 insertions(+), 12 deletions(-)
diff --git a/python/ql/src/semmle/python/frameworks/Aiohttp.qll b/python/ql/src/semmle/python/frameworks/Aiohttp.qll
index c6616b93712..8ea5121d25c 100644
--- a/python/ql/src/semmle/python/frameworks/Aiohttp.qll
+++ b/python/ql/src/semmle/python/frameworks/Aiohttp.qll
@@ -20,6 +20,19 @@ private import semmle.python.frameworks.Yarl
* See https://docs.aiohttp.org/en/stable/web.html
*/
module AiohttpWebModel {
+ /**
+ * Provides models for the `aiohttp.web.View` class and subclasses.
+ *
+ * See https://docs.aiohttp.org/en/stable/web_reference.html#view.
+ */
+ module View {
+ /** Gets a reference to the `flask.views.View` class or any subclass. */
+ API::Node subclassRef() {
+ result = API::moduleImport("aiohttp").getMember("web").getMember("View").getASubclass*()
+ }
+ }
+
+ // -- route modeling --
/** Gets a reference to a `aiohttp.web.Application` instance. */
API::Node applicationInstance() {
// Not sure whether you're allowed to add routes _after_ starting the app, for
@@ -36,7 +49,6 @@ module AiohttpWebModel {
result = applicationInstance().getMember("router")
}
- // -- route modeling --
/** A route setup in `aiohttp.web` */
abstract class AiohttpRouteSetup extends HTTP::Server::RouteSetup::Range {
override Parameter getARoutedParameter() { none() }
@@ -54,6 +66,50 @@ module AiohttpWebModel {
}
}
+ /**
+ * Gets a reference to a class, that has been backtracked from the view-class handler
+ * argument `origin` (to a route-setup for view-classes).
+ */
+ private DataFlow::LocalSourceNode viewClassBackTracker(
+ DataFlow::TypeBackTracker t, DataFlow::Node origin
+ ) {
+ t.start() and
+ origin = any(AiohttpViewRouteSetup rs).getViewClassArg() and
+ result = origin.getALocalSource()
+ or
+ exists(DataFlow::TypeBackTracker t2 |
+ result = viewClassBackTracker(t2, origin).backtrack(t2, t)
+ )
+ }
+
+ /**
+ * Gets a reference to a class, that has been backtracked from the view-class handler
+ * argument `origin` (to a route-setup for view-classes).
+ */
+ DataFlow::LocalSourceNode viewClassBackTracker(DataFlow::Node origin) {
+ result = viewClassBackTracker(DataFlow::TypeBackTracker::end(), origin)
+ }
+
+ Class getBackTrackedViewClass(DataFlow::Node origin) {
+ result.getParent() = viewClassBackTracker(origin).asExpr()
+ }
+
+ /** An aiohttp route setup that uses view-classes as request handlers. */
+ abstract class AiohttpViewRouteSetup extends AiohttpRouteSetup {
+ /** Gets the argument specifying the view-class handler. */
+ abstract DataFlow::Node getViewClassArg();
+
+ /** Gets the view-class that is referenced in the view-class handler argument. */
+ Class getViewClass() { result = getBackTrackedViewClass(this.getViewClassArg()) }
+
+ override Function getARequestHandler() {
+ exists(AiohttpViewClass cls |
+ cls = this.getViewClass() and
+ result = cls.getARequestHandler()
+ )
+ }
+ }
+
/**
* A route-setup from `add_route` or any of `add_get`, `add_post`, etc. on an
* `aiohttp.web.UrlDispatcher`.
@@ -142,6 +198,91 @@ module AiohttpWebModel {
override Function getARequestHandler() { result.getADecorator() = this.asExpr() }
}
+ /**
+ * A view-class route-setup from either:
+ * - `add_view` method on a `aiohttp.web.UrlDispatcher`
+ * - `view` function from `aiohttp.web`
+ */
+ class AiohttpViewRouteSetupFromFunction extends AiohttpViewRouteSetup, DataFlow::CallCfgNode {
+ AiohttpViewRouteSetupFromFunction() {
+ this = urlDispathcerInstance().getMember("add_view").getACall()
+ or
+ this = API::moduleImport("aiohttp").getMember("web").getMember("view").getACall()
+ or
+ this =
+ API::moduleImport("aiohttp")
+ .getMember("web")
+ .getMember("RouteTableDef")
+ .getReturn()
+ .getMember("view")
+ .getACall()
+ }
+
+ override DataFlow::Node getUrlPatternArg() {
+ result in [this.getArg(0), this.getArgByName("path")]
+ }
+
+ override DataFlow::Node getViewClassArg() {
+ result in [this.getArg(1), this.getArgByName("handler")]
+ }
+ }
+
+ /**
+ * A view-class route-setup from the `view` decorator from a `aiohttp.web.RouteTableDef`.
+ */
+ class AiohttpViewRouteSetupFromDecorator extends AiohttpViewRouteSetup, DataFlow::CallCfgNode {
+ AiohttpViewRouteSetupFromDecorator() {
+ this =
+ API::moduleImport("aiohttp")
+ .getMember("web")
+ .getMember("RouteTableDef")
+ .getReturn()
+ .getMember("view")
+ .getACall()
+ }
+
+ override DataFlow::Node getUrlPatternArg() {
+ result in [this.getArg(0), this.getArgByName("path")]
+ }
+
+ override DataFlow::Node getViewClassArg() { none() }
+
+ override Class getViewClass() { result.getADecorator() = this.asExpr() }
+ }
+
+ /** A class that we consider a aiohttp.web View class. */
+ abstract class AiohttpViewClass extends Class {
+ /** Gets a function that could handle incoming requests, if any. */
+ Function getARequestHandler() {
+ // TODO: This doesn't handle attribute assignment. Should be OK, but analysis is not as complete as with
+ // points-to and `.lookup`, which would handle `post = my_post_handler` inside class def
+ result = this.getAMethod() and
+ result.getName() = HTTP::httpVerbLower()
+ }
+ }
+
+ /** A class that has a super-type which is a aiohttp.web View class. */
+ class AiohttpViewClassFromSuperClass extends AiohttpViewClass {
+ AiohttpViewClassFromSuperClass() { this.getABase() = View::subclassRef().getAUse().asExpr() }
+ }
+
+ /** A class that is used in a route-setup, therefore being considered a aiohttp.web View class. */
+ class AiohttpViewClassFromRouteSetup extends AiohttpViewClass {
+ AiohttpViewClassFromRouteSetup() { this = any(AiohttpViewRouteSetup rs).getViewClass() }
+ }
+
+ /** A request handler defined in an `aiohttp.web` view class, that has no known route. */
+ private class AiohttpViewClassRequestHandlerWithoutKnownRoute extends HTTP::Server::RequestHandler::Range {
+ AiohttpViewClassRequestHandlerWithoutKnownRoute() {
+ exists(AiohttpViewClass vc | vc.getARequestHandler() = this) and
+ not exists(AiohttpRouteSetup setup | setup.getARequestHandler() = this)
+ }
+
+ override Parameter getARoutedParameter() { none() }
+
+ override string getFramework() { result = "aiohttp.web" }
+ }
+
// ---------------------------------------------------------------------------
// aiohttp.web.Request taint modeling
// ---------------------------------------------------------------------------
@@ -183,7 +324,7 @@ module AiohttpWebModel {
DataFlow::ParameterNode {
AiohttpRequestHandlerRequestParam() {
exists(Function requestHandler |
- requestHandler = any(AiohttpRouteSetup setup).getARequestHandler() and
+ requestHandler = any(AiohttpCoroutineRouteSetup setup).getARequestHandler() and
// We select the _last_ parameter for the request since that is what they do in
// `aiohttp-jinja2`.
// https://github.com/aio-libs/aiohttp-jinja2/blob/7fb4daf2c3003921d34031d38c2311ee0e02c18b/aiohttp_jinja2/__init__.py#L235
@@ -200,6 +341,11 @@ module AiohttpWebModel {
this.getParameter() =
max(Parameter param, int i | param = requestHandler.getArg(i) | param order by i)
)
+ or
+ exists(AiohttpViewClass vc |
+ // TODO
+ none()
+ )
}
override string getSourceType() { result = "aiohttp.web.Request" }
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/routing_test.py b/python/ql/test/library-tests/frameworks/aiohttp/routing_test.py
index ffd339a4d1a..934d7673718 100644
--- a/python/ql/test/library-tests/frameworks/aiohttp/routing_test.py
+++ b/python/ql/test/library-tests/frameworks/aiohttp/routing_test.py
@@ -83,20 +83,20 @@ if True:
# `app.add_routes` with list
class MyWebView1(web.View):
- async def get(self): # $ MISSING: requestHandler
+ async def get(self): # $ requestHandler
return web.Response(text="MyWebView1.get")
app.add_routes([
- web.view("/MyWebView1", MyWebView1) # $ MISSING: routeSetup
+ web.view("/MyWebView1", MyWebView1) # $ routeSetup="/MyWebView1"
])
# using decorator
routes = web.RouteTableDef()
- @routes.view("/MyWebView2") # $ MISSING: routeSetup
+ @routes.view("/MyWebView2") # $ routeSetup="/MyWebView2"
class MyWebView2(web.View):
- async def get(self): # $ MISSING: requestHandler
+ async def get(self): # $ requestHandler
return web.Response(text="MyWebView2.get")
app.add_routes(routes)
@@ -104,23 +104,23 @@ if True:
# `app.router.add_view`
class MyWebView3(web.View):
- async def get(self): # $ MISSING: requestHandler
+ async def get(self): # $ requestHandler
return web.Response(text="MyWebView3.get")
- app.router.add_view("/MyWebView3", MyWebView3) # $ MISSING: routeSetup
+ app.router.add_view("/MyWebView3", MyWebView3) # $ routeSetup="/MyWebView3"
# no route-setup
class MyWebViewNoRoute(web.View):
- async def get(self): # $ MISSING: requestHandler
+ async def get(self): # $ requestHandler
return web.Response(text="MyWebViewNoRoute.get")
if len(__name__) < 0: # avoid running, but fool analysis to not consider dead code
# no explicit-view subclass (but route-setup)
class MyWebViewNoSubclassButRoute(somelib.someclass):
- async def get(self): # $ MISSING: requestHandler
+ async def get(self): # $ requestHandler
return web.Response(text="MyWebViewNoSubclassButRoute.get")
- app.router.add_view("/MyWebViewNoSubclassButRoute", MyWebViewNoSubclassButRoute) # $ MISSING: routeSetup
+ app.router.add_view("/MyWebViewNoSubclassButRoute", MyWebViewNoSubclassButRoute) # $ routeSetup="/MyWebViewNoSubclassButRoute"
## =================== ##
## "Routed parameters" ##
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py b/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
index 16d4fc27412..28ff33b15a6 100644
--- a/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
+++ b/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
@@ -194,7 +194,7 @@ async def test_taint(request: web.Request): # $ requestHandler
class TaintTestClass(web.View):
- def get(self):
+ def get(self): # $ requestHandler
ensure_tainted(
self.request, # $ MISSING: tainted
)
From c69b85766261e64b47707f76ae1bf0dd0e3f6168 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Tue, 1 Jun 2021 17:32:56 +0200
Subject: [PATCH 119/272] Python: Add self.request as RemoteFlowSource for
aiohttp View
Just like we do for Django in
https://github.com/github/codeql/blob/7393443f8ca0b51a55f2aa5cca9595ab35fb92dc/python/ql/src/semmle/python/frameworks/Django.qll#L1786-L1804
---
.../src/semmle/python/frameworks/Aiohttp.qll | 20 +++++++---
.../src/semmle/python/frameworks/Django.qll | 25 +-----------
.../frameworks/internal/SelfRefMixin.qll | 38 +++++++++++++++++++
.../frameworks/aiohttp/taint_test.py | 3 +-
4 files changed, 55 insertions(+), 31 deletions(-)
create mode 100644 python/ql/src/semmle/python/frameworks/internal/SelfRefMixin.qll
diff --git a/python/ql/src/semmle/python/frameworks/Aiohttp.qll b/python/ql/src/semmle/python/frameworks/Aiohttp.qll
index 8ea5121d25c..3a8989e2c4c 100644
--- a/python/ql/src/semmle/python/frameworks/Aiohttp.qll
+++ b/python/ql/src/semmle/python/frameworks/Aiohttp.qll
@@ -10,6 +10,7 @@ private import semmle.python.dataflow.new.TaintTracking
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
private import semmle.python.frameworks.internal.PoorMansFunctionResolution
+private import semmle.python.frameworks.internal.SelfRefMixin
private import semmle.python.frameworks.Multidict
private import semmle.python.frameworks.Yarl
@@ -251,7 +252,7 @@ module AiohttpWebModel {
}
/** A class that we consider a aiohttp.web View class. */
- abstract class AiohttpViewClass extends Class {
+ abstract class AiohttpViewClass extends Class, SelfRefMixin {
/** Gets a function that could handle incoming requests, if any. */
Function getARequestHandler() {
// TODO: This doesn't handle attribute assignment. Should be OK, but analysis is not as complete as with
@@ -341,16 +342,23 @@ module AiohttpWebModel {
this.getParameter() =
max(Parameter param, int i | param = requestHandler.getArg(i) | param order by i)
)
- or
- exists(AiohttpViewClass vc |
- // TODO
- none()
- )
}
override string getSourceType() { result = "aiohttp.web.Request" }
}
+ class AiohttpViewClassRequestAttributeRead extends Request::InstanceSource,
+ RemoteFlowSource::Range, DataFlow::Node {
+ AiohttpViewClassRequestAttributeRead() {
+ this.(DataFlow::AttrRead).getObject() = any(AiohttpViewClass vc).getASelfRef() and
+ this.(DataFlow::AttrRead).getAttributeName() = "request"
+ }
+
+ override string getSourceType() {
+ result = "aiohttp.web.Request from self.request in View class"
+ }
+ }
+
/**
* Taint propagation for `aiohttp.web.Request`.
*
diff --git a/python/ql/src/semmle/python/frameworks/Django.qll b/python/ql/src/semmle/python/frameworks/Django.qll
index f1ae74bc8b5..fbe967d629f 100644
--- a/python/ql/src/semmle/python/frameworks/Django.qll
+++ b/python/ql/src/semmle/python/frameworks/Django.qll
@@ -12,6 +12,7 @@ private import semmle.python.ApiGraphs
private import semmle.python.frameworks.PEP249
private import semmle.python.regex
private import semmle.python.frameworks.internal.PoorMansFunctionResolution
+private import semmle.python.frameworks.internal.SelfRefMixin
/**
* Provides models for the `django` PyPI package.
@@ -1388,31 +1389,7 @@ private module PrivateDjango {
// Helpers
// ---------------------------------------------------------------------------
- /** Adds the `getASelfRef` member predicate when modeling a class. */
- abstract private class SelfRefMixin extends Class {
- /**
- * Gets a reference to instances of this class, originating from a self parameter of
- * a method defined on this class.
- *
- * Note: TODO: This doesn't take MRO into account
- * Note: TODO: This doesn't take staticmethod/classmethod into account
- */
- private DataFlow::LocalSourceNode getASelfRef(DataFlow::TypeTracker t) {
- t.start() and
- result.(DataFlow::ParameterNode).getParameter() = this.getAMethod().getArg(0)
- or
- exists(DataFlow::TypeTracker t2 | result = this.getASelfRef(t2).track(t2, t))
- }
- /**
- * Gets a reference to instances of this class, originating from a self parameter of
- * a method defined on this class.
- *
- * Note: TODO: This doesn't take MRO into account
- * Note: TODO: This doesn't take staticmethod/classmethod into account
- */
- DataFlow::Node getASelfRef() { this.getASelfRef(DataFlow::TypeTracker::end()).flowsTo(result) }
- }
// ---------------------------------------------------------------------------
// Form and form field modeling
diff --git a/python/ql/src/semmle/python/frameworks/internal/SelfRefMixin.qll b/python/ql/src/semmle/python/frameworks/internal/SelfRefMixin.qll
new file mode 100644
index 00000000000..d43d6b54bfb
--- /dev/null
+++ b/python/ql/src/semmle/python/frameworks/internal/SelfRefMixin.qll
@@ -0,0 +1,38 @@
+/**
+ * INTERNAL: Do not use.
+ *
+ * Provides the `SelfRefMixin` class.
+ */
+
+private import python
+private import semmle.python.dataflow.new.DataFlow
+
+/**
+ * INTERNAL: Do not use.
+ *
+ * Adds the `getASelfRef` member predicate when modeling a class.
+ */
+abstract class SelfRefMixin extends Class {
+ /**
+ * Gets a reference to instances of this class, originating from a self parameter of
+ * a method defined on this class.
+ *
+ * Note: TODO: This doesn't take MRO into account
+ * Note: TODO: This doesn't take staticmethod/classmethod into account
+ */
+ private DataFlow::LocalSourceNode getASelfRef(DataFlow::TypeTracker t) {
+ t.start() and
+ result.(DataFlow::ParameterNode).getParameter() = this.getAMethod().getArg(0)
+ or
+ exists(DataFlow::TypeTracker t2 | result = this.getASelfRef(t2).track(t2, t))
+ }
+
+ /**
+ * Gets a reference to instances of this class, originating from a self parameter of
+ * a method defined on this class.
+ *
+ * Note: TODO: This doesn't take MRO into account
+ * Note: TODO: This doesn't take staticmethod/classmethod into account
+ */
+ DataFlow::Node getASelfRef() { this.getASelfRef(DataFlow::TypeTracker::end()).flowsTo(result) }
+}
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py b/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
index 28ff33b15a6..dcf21f92687 100644
--- a/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
+++ b/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
@@ -196,7 +196,8 @@ async def test_taint(request: web.Request): # $ requestHandler
class TaintTestClass(web.View):
def get(self): # $ requestHandler
ensure_tainted(
- self.request, # $ MISSING: tainted
+ self.request, # $ tainted
+ self.request.url # $ tainted
)
From 919a0b6b84489488c1dcb1de41e0097f01ccddca Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Tue, 1 Jun 2021 17:51:19 +0200
Subject: [PATCH 120/272] Python: aiohttp route setup is more complicated than
expected
---
.../frameworks/aiohttp/routing_test.py | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/routing_test.py b/python/ql/test/library-tests/frameworks/aiohttp/routing_test.py
index 934d7673718..ad1883b5828 100644
--- a/python/ql/test/library-tests/frameworks/aiohttp/routing_test.py
+++ b/python/ql/test/library-tests/frameworks/aiohttp/routing_test.py
@@ -122,6 +122,23 @@ if True:
app.router.add_view("/MyWebViewNoSubclassButRoute", MyWebViewNoSubclassButRoute) # $ routeSetup="/MyWebViewNoSubclassButRoute"
+
+# Apparently there is no enforcement that `add_view` is only for views, and vice-versa
+# for `add_get` only being for async functions.
+if True:
+ async def no_rules(request): # $ MISSING: requestHandler
+ return web.Response(text="no_rules")
+
+ app.router.add_view("/no_rules", no_rules) # $ routeSetup="/no_rules"
+
+
+ class NoRulesView(web.View):
+ async def get(self): # $ requestHandler
+ return web.Response(text="NoRulesView.get")
+
+ app.router.add_get("/NoRulesView", NoRulesView) # $ routeSetup="/NoRulesView"
+
+
## =================== ##
## "Routed parameters" ##
## =================== ##
From 5d4140d3e2d26a81db5bdd859d97ca7b752425a1 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Tue, 1 Jun 2021 18:40:20 +0200
Subject: [PATCH 121/272] Python: Handle more complicated route-setup in
aiohttp
Since we want to be able to easy select request-handlers that are not
set up as part of a view-class, we need to easily be able to identify
those. To handle cases like the one below, we _can't_ just define these
to be all the async functions that are not methods on a class :(
```py
# see https://docs.aiohttp.org/en/stable/web_quickstart.html#organizing-handlers-in-classes
class MyCustomHandlerClass:
async def foo_handler(self, request): # $ MISSING: requestHandler
return web.Response(text="MyCustomHandlerClass.foo")
my_custom_handler = MyCustomHandlerClass()
app.router.add_get("/MyCustomHandlerClass/foo", my_custom_handler.foo_handler) # $ routeSetup="/MyCustomHandlerClass/foo"
```
So it seemed easiest to narrow down the route-setups, but that means we
want both refinement and extensibility... so `::Range` pattern to the
rescue :tada:
The important piece of code that still works after this commit, but
which hasn't been changed, is the one below:
```codeql
/**
* A parameter that will receive a `aiohttp.web.Request` instance when a request
* handler is invoked.
*/
class AiohttpRequestHandlerRequestParam extends Request::InstanceSource, RemoteFlowSource::Range,
DataFlow::ParameterNode {
AiohttpRequestHandlerRequestParam() {
exists(Function requestHandler |
requestHandler = any(AiohttpCoroutineRouteSetup setup).getARequestHandler() and
```
---
.../src/semmle/python/frameworks/Aiohttp.qll | 259 ++++++++----------
.../frameworks/aiohttp/routing_test.py | 2 +-
2 files changed, 121 insertions(+), 140 deletions(-)
diff --git a/python/ql/src/semmle/python/frameworks/Aiohttp.qll b/python/ql/src/semmle/python/frameworks/Aiohttp.qll
index 3a8989e2c4c..f85071adaa2 100644
--- a/python/ql/src/semmle/python/frameworks/Aiohttp.qll
+++ b/python/ql/src/semmle/python/frameworks/Aiohttp.qll
@@ -50,110 +50,130 @@ module AiohttpWebModel {
result = applicationInstance().getMember("router")
}
- /** A route setup in `aiohttp.web` */
- abstract class AiohttpRouteSetup extends HTTP::Server::RouteSetup::Range {
+ /**
+ * A route setup in `aiohttp.web`. Since all route-setups can technically use either
+ * coroutines or view-classes as the handler argument (although that's not how you're
+ * **supposed** to do things), we also need to handle this.
+ *
+ * Extend this class to refine existing API models. If you want to model new APIs,
+ * extend `AiohttpRouteSetup::Range` instead.
+ */
+ class AiohttpRouteSetup extends HTTP::Server::RouteSetup::Range {
+ AiohttpRouteSetup::Range range;
+
+ AiohttpRouteSetup() { this = range }
+
override Parameter getARoutedParameter() { none() }
override string getFramework() { result = "aiohttp.web" }
+
+ /** Gets the argument specifying the handler (either a coroutine or a view-class). */
+ DataFlow::Node getHandlerArg() { result = range.getHandlerArg() }
+
+ override DataFlow::Node getUrlPatternArg() { result = range.getUrlPatternArg() }
+
+ /** Gets the view-class that is referenced in the view-class handler argument, if any. */
+ Class getViewClass() { result = range.getViewClass() }
+
+ override Function getARequestHandler() { result = range.getARequestHandler() }
+ }
+
+ /** Provides a class for modeling new aiohttp.web route setups. */
+ private module AiohttpRouteSetup {
+ /**
+ * A route setup in `aiohttp.web`. Since all route-setups can technically use either
+ * coroutines or view-classes as the handler argument (although that's not how you're
+ * **supposed** to do things), we also need to handle this.
+ *
+ * Extend this class to model new APIs. If you want to refine existing API models,
+ * extend `AiohttpRouteSetup` instead.
+ */
+ abstract class Range extends DataFlow::Node {
+ /** Gets the argument used to set the URL pattern. */
+ abstract DataFlow::Node getUrlPatternArg();
+
+ /** Gets the argument specifying the handler (either a coroutine or a view-class). */
+ abstract DataFlow::Node getHandlerArg();
+
+ /** Gets the view-class that is referenced in the view-class handler argument, if any. */
+ Class getViewClass() { result = getBackTrackedViewClass(this.getHandlerArg()) }
+
+ /**
+ * Gets a function that will handle incoming requests for this route, if any.
+ *
+ * NOTE: This will be modified in the near future to have a `RequestHandler` result, instead of a `Function`.
+ */
+ Function getARequestHandler() {
+ this.getHandlerArg() = poorMansFunctionTracker(result)
+ or
+ exists(AiohttpViewClass cls |
+ cls = this.getViewClass() and
+ result = cls.getARequestHandler()
+ )
+ }
+ }
+
+ /**
+ * Gets a reference to a class, that has been backtracked from the view-class handler
+ * argument `origin` (to a route-setup for view-classes).
+ */
+ private DataFlow::LocalSourceNode viewClassBackTracker(
+ DataFlow::TypeBackTracker t, DataFlow::Node origin
+ ) {
+ t.start() and
+ origin = any(Range rs).getHandlerArg() and
+ result = origin.getALocalSource()
+ or
+ exists(DataFlow::TypeBackTracker t2 |
+ result = viewClassBackTracker(t2, origin).backtrack(t2, t)
+ )
+ }
+
+ /**
+ * Gets a reference to a class, that has been backtracked from the view-class handler
+ * argument `origin` (to a route-setup for view-classes).
+ */
+ DataFlow::LocalSourceNode viewClassBackTracker(DataFlow::Node origin) {
+ result = viewClassBackTracker(DataFlow::TypeBackTracker::end(), origin)
+ }
+
+ Class getBackTrackedViewClass(DataFlow::Node origin) {
+ result.getParent() = viewClassBackTracker(origin).asExpr()
+ }
}
/** An aiohttp route setup that uses coroutines (async function) as request handler. */
- abstract class AiohttpCoroutineRouteSetup extends AiohttpRouteSetup {
- /** Gets the argument specifying the request handler (which is a coroutine/async function) */
- abstract DataFlow::Node getRequestHandlerArg();
-
- override Function getARequestHandler() {
- this.getRequestHandlerArg() = poorMansFunctionTracker(result)
- }
- }
-
- /**
- * Gets a reference to a class, that has been backtracked from the view-class handler
- * argument `origin` (to a route-setup for view-classes).
- */
- private DataFlow::LocalSourceNode viewClassBackTracker(
- DataFlow::TypeBackTracker t, DataFlow::Node origin
- ) {
- t.start() and
- origin = any(AiohttpViewRouteSetup rs).getViewClassArg() and
- result = origin.getALocalSource()
- or
- exists(DataFlow::TypeBackTracker t2 |
- result = viewClassBackTracker(t2, origin).backtrack(t2, t)
- )
- }
-
- /**
- * Gets a reference to a class, that has been backtracked from the view-class handler
- * argument `origin` (to a route-setup for view-classes).
- */
- DataFlow::LocalSourceNode viewClassBackTracker(DataFlow::Node origin) {
- result = viewClassBackTracker(DataFlow::TypeBackTracker::end(), origin)
- }
-
- Class getBackTrackedViewClass(DataFlow::Node origin) {
- result.getParent() = viewClassBackTracker(origin).asExpr()
+ class AiohttpCoroutineRouteSetup extends AiohttpRouteSetup {
+ AiohttpCoroutineRouteSetup() { this.getHandlerArg() = poorMansFunctionTracker(_) }
}
/** An aiohttp route setup that uses view-classes as request handlers. */
- abstract class AiohttpViewRouteSetup extends AiohttpRouteSetup {
- /** Gets the argument specifying the view-class handler. */
- abstract DataFlow::Node getViewClassArg();
-
- /** Gets the view-class that is referenced in the view-class handler argument. */
- Class getViewClass() { result = getBackTrackedViewClass(this.getViewClassArg()) }
-
- override Function getARequestHandler() {
- exists(AiohttpViewClass cls |
- cls = this.getViewClass() and
- result = cls.getARequestHandler()
- )
- }
+ class AiohttpViewRouteSetup extends AiohttpRouteSetup {
+ AiohttpViewRouteSetup() { exists(this.getViewClass()) }
}
/**
- * A route-setup from `add_route` or any of `add_get`, `add_post`, etc. on an
- * `aiohttp.web.UrlDispatcher`.
+ * A route-setup from
+ * - `add_route`, `add_view`, `add_get`, `add_post`, , etc. on an `aiohttp.web.UrlDispatcher`.
+ * - `route`, `view`, `get`, `post`, etc. functions from `aiohttp.web`.
*/
- class AiohttpUrlDispatcherAddRouteCall extends AiohttpCoroutineRouteSetup, DataFlow::CallCfgNode {
+ class AiohttpAddRouteCall extends AiohttpRouteSetup::Range, DataFlow::CallCfgNode {
/** At what index route arguments starts, so we can handle "route" version together with get/post/... */
int routeArgsStart;
- AiohttpUrlDispatcherAddRouteCall() {
- this = urlDispathcerInstance().getMember("add_" + HTTP::httpVerbLower()).getACall() and
- routeArgsStart = 0
- or
- this = urlDispathcerInstance().getMember("add_route").getACall() and
- routeArgsStart = 1
- }
-
- override DataFlow::Node getUrlPatternArg() {
- result in [this.getArg(routeArgsStart + 0), this.getArgByName("path")]
- }
-
- override DataFlow::Node getRequestHandlerArg() {
- result in [this.getArg(routeArgsStart + 1), this.getArgByName("handler")]
- }
- }
-
- /**
- * A route-setup from using `route` or any of `get`, `post`, etc. functions from `aiohttp.web`.
- *
- * Note that technically, this does not set up a route in itself (since it needs to be added to an application first).
- * However, modeling this way makes it easier, and we don't expect it to lead to many problems.
- */
- class AiohttpWebRouteCall extends AiohttpCoroutineRouteSetup, DataFlow::CallCfgNode {
- /** At what index route arguments starts, so we can handle "route" version together with get/post/... */
- int routeArgsStart;
-
- AiohttpWebRouteCall() {
+ AiohttpAddRouteCall() {
exists(string funcName |
funcName = HTTP::httpVerbLower() and
routeArgsStart = 0
or
+ funcName = "view" and
+ routeArgsStart = 0
+ or
funcName = "route" and
routeArgsStart = 1
|
+ this = urlDispathcerInstance().getMember("add_" + funcName).getACall()
+ or
this = API::moduleImport("aiohttp").getMember("web").getMember(funcName).getACall()
)
}
@@ -162,21 +182,24 @@ module AiohttpWebModel {
result in [this.getArg(routeArgsStart + 0), this.getArgByName("path")]
}
- override DataFlow::Node getRequestHandlerArg() {
+ override DataFlow::Node getHandlerArg() {
result in [this.getArg(routeArgsStart + 1), this.getArgByName("handler")]
}
}
- /** A route-setup from using a `route` or any of `get`, `post`, etc. decorators from a `aiohttp.web.RouteTableDef`. */
- class AiohttpRouteTableDefRouteCall extends AiohttpCoroutineRouteSetup, DataFlow::CallCfgNode {
+ /** A route-setup using a decorator, such as `route`, `view`, `get`, `post`, etc. on a `aiohttp.web.RouteTableDef`. */
+ class AiohttpDecoratorRouteSetup extends AiohttpRouteSetup::Range, DataFlow::CallCfgNode {
/** At what index route arguments starts, so we can handle "route" version together with get/post/... */
int routeArgsStart;
- AiohttpRouteTableDefRouteCall() {
+ AiohttpDecoratorRouteSetup() {
exists(string decoratorName |
decoratorName = HTTP::httpVerbLower() and
routeArgsStart = 0
or
+ decoratorName = "view" and
+ routeArgsStart = 0
+ or
decoratorName = "route" and
routeArgsStart = 1
|
@@ -194,61 +217,19 @@ module AiohttpWebModel {
result in [this.getArg(routeArgsStart + 0), this.getArgByName("path")]
}
- override DataFlow::Node getRequestHandlerArg() { none() }
-
- override Function getARequestHandler() { result.getADecorator() = this.asExpr() }
- }
-
- /**
- * A view-class route-setup from either:
- * - `add_view` method on a `aiohttp.web.UrlDispatcher`
- * - `view` function from `aiohttp.web`
- */
- class AiohttpViewRouteSetupFromFunction extends AiohttpViewRouteSetup, DataFlow::CallCfgNode {
- AiohttpViewRouteSetupFromFunction() {
- this = urlDispathcerInstance().getMember("add_view").getACall()
- or
- this = API::moduleImport("aiohttp").getMember("web").getMember("view").getACall()
- or
- this =
- API::moduleImport("aiohttp")
- .getMember("web")
- .getMember("RouteTableDef")
- .getReturn()
- .getMember("view")
- .getACall()
- }
-
- override DataFlow::Node getUrlPatternArg() {
- result in [this.getArg(0), this.getArgByName("path")]
- }
-
- override DataFlow::Node getViewClassArg() {
- result in [this.getArg(1), this.getArgByName("handler")]
- }
- }
-
- /**
- * A view-class route-setup from the `view` decorator from a `aiohttp.web.RouteTableDef`.
- */
- class AiohttpViewRouteSetupFromDecorator extends AiohttpViewRouteSetup, DataFlow::CallCfgNode {
- AiohttpViewRouteSetupFromDecorator() {
- this =
- API::moduleImport("aiohttp")
- .getMember("web")
- .getMember("RouteTableDef")
- .getReturn()
- .getMember("view")
- .getACall()
- }
-
- override DataFlow::Node getUrlPatternArg() {
- result in [this.getArg(0), this.getArgByName("path")]
- }
-
- override DataFlow::Node getViewClassArg() { none() }
+ override DataFlow::Node getHandlerArg() { none() }
override Class getViewClass() { result.getADecorator() = this.asExpr() }
+
+ override Function getARequestHandler() {
+ // we're decorating a class
+ exists(this.getViewClass()) and
+ result = super.getARequestHandler()
+ or
+ // we're decorating a function
+ not exists(this.getViewClass()) and
+ result.getADecorator() = this.asExpr()
+ }
}
/** A class that we consider a aiohttp.web View class. */
@@ -269,7 +250,7 @@ module AiohttpWebModel {
/** A class that is used in a route-setup, therefore being considered a aiohttp.web View class. */
class AiohttpViewClassFromRouteSetup extends AiohttpViewClass {
- AiohttpViewClassFromRouteSetup() { this = any(AiohttpViewRouteSetup rs).getViewClass() }
+ AiohttpViewClassFromRouteSetup() { this = any(AiohttpRouteSetup rs).getViewClass() }
}
/** A request handler defined in an `aiohttp.web` view class, that has no known route. */
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/routing_test.py b/python/ql/test/library-tests/frameworks/aiohttp/routing_test.py
index ad1883b5828..eac030ec02a 100644
--- a/python/ql/test/library-tests/frameworks/aiohttp/routing_test.py
+++ b/python/ql/test/library-tests/frameworks/aiohttp/routing_test.py
@@ -126,7 +126,7 @@ if True:
# Apparently there is no enforcement that `add_view` is only for views, and vice-versa
# for `add_get` only being for async functions.
if True:
- async def no_rules(request): # $ MISSING: requestHandler
+ async def no_rules(request): # $ requestHandler
return web.Response(text="no_rules")
app.router.add_view("/no_rules", no_rules) # $ routeSetup="/no_rules"
From 735df4597f7563528062cf5eb6579ad12680a2a5 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 2 Jun 2021 10:58:04 +0200
Subject: [PATCH 122/272] Python: Aiohttp add response tests
---
.../frameworks/aiohttp/response_test.py | 72 +++++++++++++++++++
1 file changed, 72 insertions(+)
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/response_test.py b/python/ql/test/library-tests/frameworks/aiohttp/response_test.py
index e69de29bb2d..e58d0c6c189 100644
--- a/python/ql/test/library-tests/frameworks/aiohttp/response_test.py
+++ b/python/ql/test/library-tests/frameworks/aiohttp/response_test.py
@@ -0,0 +1,72 @@
+from aiohttp import web
+
+
+routes = web.RouteTableDef()
+
+
+@routes.get("/raw_text") # $ routeSetup="/raw_text"
+async def raw_text(request): # $ requestHandler
+ return web.Response(text="foo") # $ MISSING: HttpResponse
+
+
+@routes.get("/raw_body") # $ routeSetup="/raw_body"
+async def raw_body(request): # $ requestHandler
+ return web.Response(body=b"foo") # $ MISSING: HttpResponse
+
+
+@routes.get("/html_text") # $ routeSetup="/html_text"
+async def html_text(request): # $ requestHandler
+ return web.Response(text="foo", content_type="text/html") # $ MISSING: HttpResponse
+
+
+@routes.get("/html_body") # $ routeSetup="/html_body"
+async def html_body(request): # $ requestHandler
+ return web.Response(body=b"foo", content_type="text/html") # $ MISSING: HttpResponse
+
+
+@routes.get("/html_body_set_later") # $ routeSetup="/html_body_set_later"
+async def html_body_set_later(request): # $ requestHandler
+ resp = web.Response(body=b"foo") # $ MISSING: HttpResponse
+ resp.content_type = "text/html"
+ return resp
+
+# Each HTTP status code has an exception
+# see https://docs.aiohttp.org/en/stable/web_quickstart.html#exceptions
+
+@routes.get("/through_200_exception") # $ routeSetup="/through_200_exception"
+async def through_200_exception(request): # $ requestHandler
+ raise web.HTTPOk(text="foo") # $ MISSING: HttpResponse
+
+
+@routes.get("/through_200_exception_html") # $ routeSetup="/through_200_exception_html"
+async def through_200_exception(request): # $ requestHandler
+ exception = web.HTTPOk(text="foo") # $ MISSING: HttpResponse
+ exception.content_type = "text/html"
+ raise exception
+
+
+@routes.get("/through_404_exception") # $ routeSetup="/through_404_exception"
+async def through_404_exception(request): # $ requestHandler
+ raise web.HTTPNotFound(text="foo") # $ MISSING: HttpResponse
+
+
+@routes.get("/redirect_301") # $ routeSetup="/redirect_301"
+async def redirect_301(request): # $ requestHandler
+ if not "kwarg" in request.url.query:
+ raise web.HTTPMovedPermanently("/login") # $ MISSING: HttpResponse HttpRedirectResponse
+ else:
+ raise web.HTTPMovedPermanently(location="/logout") # $ MISSING: HttpResponse HttpRedirectResponse
+
+
+@routes.get("/redirect_302") # $ routeSetup="/redirect_302"
+async def redirect_302(request): # $ requestHandler
+ if not "kwarg" in request.url.query:
+ raise web.HTTPFound("/login") # $ MISSING: HttpResponse HttpRedirectResponse
+ else:
+ raise web.HTTPFound(location="/logout") # $ MISSING: HttpResponse HttpRedirectResponse
+
+
+if __name__ == "__main__":
+ app = web.Application()
+ app.add_routes(routes)
+ web.run_app(app)
From 2dbbf52903bd32439c337f7e31c52313aa6f49ff Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 2 Jun 2021 18:08:13 +0200
Subject: [PATCH 123/272] Python: Model HTTP responses in aiohttp.web
---
.../src/semmle/python/frameworks/Aiohttp.qll | 85 +++++++++++++++++++
.../frameworks/aiohttp/response_test.py | 28 +++---
.../frameworks/aiohttp/routing_test.py | 40 ++++-----
3 files changed, 119 insertions(+), 34 deletions(-)
diff --git a/python/ql/src/semmle/python/frameworks/Aiohttp.qll b/python/ql/src/semmle/python/frameworks/Aiohttp.qll
index f85071adaa2..24f141807ba 100644
--- a/python/ql/src/semmle/python/frameworks/Aiohttp.qll
+++ b/python/ql/src/semmle/python/frameworks/Aiohttp.qll
@@ -391,4 +391,89 @@ module AiohttpWebModel {
this.(DataFlow::AttrRead).getAttributeName() in ["url", "rel_url"]
}
}
+
+ // ---------------------------------------------------------------------------
+ // aiohttp.web Response modeling
+ // ---------------------------------------------------------------------------
+ /**
+ * An instantiation of `aiohttp.web.Response`.
+ *
+ * Note that `aiohttp.web.HTTPException` (and it's subclasses) is a subclass of `aiohttp.web.Response`.
+ *
+ * See
+ * - https://docs.aiohttp.org/en/stable/web_reference.html#aiohttp.web.Response
+ * - https://docs.aiohttp.org/en/stable/web_quickstart.html#aiohttp-web-exceptions
+ */
+ class AiohttpWebResponseInstantiation extends HTTP::Server::HttpResponse::Range,
+ DataFlow::CallCfgNode {
+ AiohttpWebResponseInstantiation() {
+ this = API::moduleImport("aiohttp").getMember("web").getMember("Response").getACall()
+ or
+ exists(string httpExceptionClassName |
+ httpExceptionClassName in [
+ "HTTPException", "HTTPSuccessful", "HTTPOk", "HTTPCreated", "HTTPAccepted",
+ "HTTPNonAuthoritativeInformation", "HTTPNoContent", "HTTPResetContent",
+ "HTTPPartialContent", "HTTPRedirection", "HTTPMultipleChoices", "HTTPMovedPermanently",
+ "HTTPFound", "HTTPSeeOther", "HTTPNotModified", "HTTPUseProxy", "HTTPTemporaryRedirect",
+ "HTTPPermanentRedirect", "HTTPError", "HTTPClientError", "HTTPBadRequest",
+ "HTTPUnauthorized", "HTTPPaymentRequired", "HTTPForbidden", "HTTPNotFound",
+ "HTTPMethodNotAllowed", "HTTPNotAcceptable", "HTTPProxyAuthenticationRequired",
+ "HTTPRequestTimeout", "HTTPConflict", "HTTPGone", "HTTPLengthRequired",
+ "HTTPPreconditionFailed", "HTTPRequestEntityTooLarge", "HTTPRequestURITooLong",
+ "HTTPUnsupportedMediaType", "HTTPRequestRangeNotSatisfiable", "HTTPExpectationFailed",
+ "HTTPMisdirectedRequest", "HTTPUnprocessableEntity", "HTTPFailedDependency",
+ "HTTPUpgradeRequired", "HTTPPreconditionRequired", "HTTPTooManyRequests",
+ "HTTPRequestHeaderFieldsTooLarge", "HTTPUnavailableForLegalReasons", "HTTPServerError",
+ "HTTPInternalServerError", "HTTPNotImplemented", "HTTPBadGateway",
+ "HTTPServiceUnavailable", "HTTPGatewayTimeout", "HTTPVersionNotSupported",
+ "HTTPVariantAlsoNegotiates", "HTTPInsufficientStorage", "HTTPNotExtended",
+ "HTTPNetworkAuthenticationRequired"
+ ] and
+ this =
+ API::moduleImport("aiohttp").getMember("web").getMember(httpExceptionClassName).getACall()
+ )
+ }
+
+ override DataFlow::Node getBody() {
+ result in [this.getArgByName("text"), this.getArgByName("body")]
+ }
+
+ override DataFlow::Node getMimetypeOrContentTypeArg() {
+ result = this.getArgByName("content_type")
+ }
+
+ override string getMimetypeDefault() {
+ exists(this.getArgByName("text")) and
+ result = "text/plain"
+ or
+ not exists(this.getArgByName("text")) and
+ result = "application/octet-stream"
+ }
+ }
+
+ /**
+ * An instantiation of aiohttp.web HTTP redirect exception.
+ *
+ * See the part about redirects at https://docs.aiohttp.org/en/stable/web_quickstart.html#aiohttp-web-exceptions
+ */
+ class AiohttpRedirectExceptionInstantiation extends AiohttpWebResponseInstantiation,
+ HTTP::Server::HttpRedirectResponse::Range {
+ AiohttpRedirectExceptionInstantiation() {
+ exists(string httpRedirectExceptionClassName |
+ httpRedirectExceptionClassName in [
+ "HTTPMultipleChoices", "HTTPMovedPermanently", "HTTPFound", "HTTPSeeOther",
+ "HTTPNotModified", "HTTPUseProxy", "HTTPTemporaryRedirect", "HTTPPermanentRedirect"
+ ] and
+ this =
+ API::moduleImport("aiohttp")
+ .getMember("web")
+ .getMember(httpRedirectExceptionClassName)
+ .getACall()
+ )
+ }
+
+ override DataFlow::Node getRedirectLocation() {
+ result in [this.getArg(0), this.getArgByName("location")]
+ }
+ }
}
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/response_test.py b/python/ql/test/library-tests/frameworks/aiohttp/response_test.py
index e58d0c6c189..1988f443560 100644
--- a/python/ql/test/library-tests/frameworks/aiohttp/response_test.py
+++ b/python/ql/test/library-tests/frameworks/aiohttp/response_test.py
@@ -6,28 +6,28 @@ routes = web.RouteTableDef()
@routes.get("/raw_text") # $ routeSetup="/raw_text"
async def raw_text(request): # $ requestHandler
- return web.Response(text="foo") # $ MISSING: HttpResponse
+ return web.Response(text="foo") # $ HttpResponse mimetype=text/plain responseBody="foo"
@routes.get("/raw_body") # $ routeSetup="/raw_body"
async def raw_body(request): # $ requestHandler
- return web.Response(body=b"foo") # $ MISSING: HttpResponse
+ return web.Response(body=b"foo") # $ HttpResponse mimetype=application/octet-stream responseBody=b"foo"
@routes.get("/html_text") # $ routeSetup="/html_text"
async def html_text(request): # $ requestHandler
- return web.Response(text="foo", content_type="text/html") # $ MISSING: HttpResponse
+ return web.Response(text="foo", content_type="text/html") # $ HttpResponse mimetype=text/html responseBody="foo"
@routes.get("/html_body") # $ routeSetup="/html_body"
async def html_body(request): # $ requestHandler
- return web.Response(body=b"foo", content_type="text/html") # $ MISSING: HttpResponse
+ return web.Response(body=b"foo", content_type="text/html") # $ HttpResponse mimetype=text/html responseBody=b"foo"
@routes.get("/html_body_set_later") # $ routeSetup="/html_body_set_later"
async def html_body_set_later(request): # $ requestHandler
- resp = web.Response(body=b"foo") # $ MISSING: HttpResponse
- resp.content_type = "text/html"
+ resp = web.Response(body=b"foo") # $ HttpResponse mimetype=application/octet-stream responseBody=b"foo"
+ resp.content_type = "text/html" # $ MISSING: mimetype=text/html
return resp
# Each HTTP status code has an exception
@@ -35,35 +35,35 @@ async def html_body_set_later(request): # $ requestHandler
@routes.get("/through_200_exception") # $ routeSetup="/through_200_exception"
async def through_200_exception(request): # $ requestHandler
- raise web.HTTPOk(text="foo") # $ MISSING: HttpResponse
+ raise web.HTTPOk(text="foo") # $ HttpResponse mimetype=text/plain responseBody="foo"
@routes.get("/through_200_exception_html") # $ routeSetup="/through_200_exception_html"
async def through_200_exception(request): # $ requestHandler
- exception = web.HTTPOk(text="foo") # $ MISSING: HttpResponse
- exception.content_type = "text/html"
+ exception = web.HTTPOk(text="foo") # $ HttpResponse mimetype=text/plain responseBody="foo"
+ exception.content_type = "text/html" # $ MISSING: mimetype=text/html
raise exception
@routes.get("/through_404_exception") # $ routeSetup="/through_404_exception"
async def through_404_exception(request): # $ requestHandler
- raise web.HTTPNotFound(text="foo") # $ MISSING: HttpResponse
+ raise web.HTTPNotFound(text="foo") # $ HttpResponse mimetype=text/plain responseBody="foo"
@routes.get("/redirect_301") # $ routeSetup="/redirect_301"
async def redirect_301(request): # $ requestHandler
if not "kwarg" in request.url.query:
- raise web.HTTPMovedPermanently("/login") # $ MISSING: HttpResponse HttpRedirectResponse
+ raise web.HTTPMovedPermanently("/login") # $ HttpResponse HttpRedirectResponse mimetype=application/octet-stream redirectLocation="/login"
else:
- raise web.HTTPMovedPermanently(location="/logout") # $ MISSING: HttpResponse HttpRedirectResponse
+ raise web.HTTPMovedPermanently(location="/logout") # $ HttpResponse HttpRedirectResponse mimetype=application/octet-stream redirectLocation="/logout"
@routes.get("/redirect_302") # $ routeSetup="/redirect_302"
async def redirect_302(request): # $ requestHandler
if not "kwarg" in request.url.query:
- raise web.HTTPFound("/login") # $ MISSING: HttpResponse HttpRedirectResponse
+ raise web.HTTPFound("/login") # $ HttpResponse HttpRedirectResponse mimetype=application/octet-stream redirectLocation="/login"
else:
- raise web.HTTPFound(location="/logout") # $ MISSING: HttpResponse HttpRedirectResponse
+ raise web.HTTPFound(location="/logout") # $ HttpResponse HttpRedirectResponse mimetype=application/octet-stream redirectLocation="/logout"
if __name__ == "__main__":
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/routing_test.py b/python/ql/test/library-tests/frameworks/aiohttp/routing_test.py
index eac030ec02a..23bd9c93a3c 100644
--- a/python/ql/test/library-tests/frameworks/aiohttp/routing_test.py
+++ b/python/ql/test/library-tests/frameworks/aiohttp/routing_test.py
@@ -16,13 +16,13 @@ if True:
# `app.add_routes` with list
async def foo(request): # $ requestHandler
- return web.Response(text="foo")
+ return web.Response(text="foo") # $ HttpResponse
async def foo2(request): # $ requestHandler
- return web.Response(text="foo2")
+ return web.Response(text="foo2") # $ HttpResponse
async def foo3(request): # $ requestHandler
- return web.Response(text="foo3")
+ return web.Response(text="foo3") # $ HttpResponse
app.add_routes([
web.get("/foo", foo), # $ routeSetup="/foo"
@@ -36,32 +36,32 @@ if True:
@routes.get("/bar") # $ routeSetup="/bar"
async def bar(request): # $ requestHandler
- return web.Response(text="bar")
+ return web.Response(text="bar") # $ HttpResponse
@routes.route("*", "/bar2") # $ routeSetup="/bar2"
async def bar2(request): # $ requestHandler
- return web.Response(text="bar2")
+ return web.Response(text="bar2") # $ HttpResponse
@routes.get(path="/bar3") # $ routeSetup="/bar3"
async def bar3(request): # $ requestHandler
- return web.Response(text="bar3")
+ return web.Response(text="bar3") # $ HttpResponse
app.add_routes(routes)
# `app.router.add_get` / `app.router.add_route`
async def baz(request): # $ requestHandler
- return web.Response(text="baz")
+ return web.Response(text="baz") # $ HttpResponse
app.router.add_get("/baz", baz) # $ routeSetup="/baz"
async def baz2(request): # $ requestHandler
- return web.Response(text="baz2")
+ return web.Response(text="baz2") # $ HttpResponse
app.router.add_route("*", "/baz2", baz2) # $ routeSetup="/baz2"
async def baz3(request): # $ requestHandler
- return web.Response(text="baz3")
+ return web.Response(text="baz3") # $ HttpResponse
app.router.add_get(path="/baz3", handler=baz3) # $ routeSetup="/baz3"
@@ -73,7 +73,7 @@ if True:
class MyCustomHandlerClass:
async def foo_handler(self, request): # $ MISSING: requestHandler
- return web.Response(text="MyCustomHandlerClass.foo")
+ return web.Response(text="MyCustomHandlerClass.foo") # $ HttpResponse
my_custom_handler = MyCustomHandlerClass()
app.router.add_get("/MyCustomHandlerClass/foo", my_custom_handler.foo_handler) # $ routeSetup="/MyCustomHandlerClass/foo"
@@ -84,7 +84,7 @@ if True:
# `app.add_routes` with list
class MyWebView1(web.View):
async def get(self): # $ requestHandler
- return web.Response(text="MyWebView1.get")
+ return web.Response(text="MyWebView1.get") # $ HttpResponse
app.add_routes([
web.view("/MyWebView1", MyWebView1) # $ routeSetup="/MyWebView1"
@@ -97,7 +97,7 @@ if True:
@routes.view("/MyWebView2") # $ routeSetup="/MyWebView2"
class MyWebView2(web.View):
async def get(self): # $ requestHandler
- return web.Response(text="MyWebView2.get")
+ return web.Response(text="MyWebView2.get") # $ HttpResponse
app.add_routes(routes)
@@ -105,20 +105,20 @@ if True:
# `app.router.add_view`
class MyWebView3(web.View):
async def get(self): # $ requestHandler
- return web.Response(text="MyWebView3.get")
+ return web.Response(text="MyWebView3.get") # $ HttpResponse
app.router.add_view("/MyWebView3", MyWebView3) # $ routeSetup="/MyWebView3"
# no route-setup
class MyWebViewNoRoute(web.View):
async def get(self): # $ requestHandler
- return web.Response(text="MyWebViewNoRoute.get")
+ return web.Response(text="MyWebViewNoRoute.get") # $ HttpResponse
if len(__name__) < 0: # avoid running, but fool analysis to not consider dead code
# no explicit-view subclass (but route-setup)
class MyWebViewNoSubclassButRoute(somelib.someclass):
async def get(self): # $ requestHandler
- return web.Response(text="MyWebViewNoSubclassButRoute.get")
+ return web.Response(text="MyWebViewNoSubclassButRoute.get") # $ HttpResponse
app.router.add_view("/MyWebViewNoSubclassButRoute", MyWebViewNoSubclassButRoute) # $ routeSetup="/MyWebViewNoSubclassButRoute"
@@ -127,14 +127,14 @@ if True:
# for `add_get` only being for async functions.
if True:
async def no_rules(request): # $ requestHandler
- return web.Response(text="no_rules")
+ return web.Response(text="no_rules") # $ HttpResponse
app.router.add_view("/no_rules", no_rules) # $ routeSetup="/no_rules"
class NoRulesView(web.View):
async def get(self): # $ requestHandler
- return web.Response(text="NoRulesView.get")
+ return web.Response(text="NoRulesView.get") # $ HttpResponse
app.router.add_get("/NoRulesView", NoRulesView) # $ routeSetup="/NoRulesView"
@@ -149,7 +149,7 @@ if True:
async def matching(request: web.Request): # $ requestHandler
name = request.match_info['name']
number = request.match_info['number']
- return web.Response(text="matching name={} number={}".format(name, number))
+ return web.Response(text="matching name={} number={}".format(name, number)) # $ HttpResponse
app.router.add_get(r"/matching/{name}/{number:\d+}", matching) # $ routeSetup="/matching/{name}/{number:\d+}"
@@ -161,7 +161,7 @@ if True:
subapp = web.Application()
async def subapp_handler(request): # $ requestHandler
- return web.Response(text="subapp_handler")
+ return web.Response(text="subapp_handler") # $ HttpResponse
subapp.router.add_get("/subapp_handler", subapp_handler) # $ routeSetup="/subapp_handler"
@@ -177,7 +177,7 @@ if True:
if True:
async def manual_dispatcher_instance(request): # $ requestHandler
- return web.Response(text="manual_dispatcher_instance")
+ return web.Response(text="manual_dispatcher_instance") # $ HttpResponse
url_dispatcher = web.UrlDispatcher()
url_dispatcher.add_get("/manual_dispatcher_instance", manual_dispatcher_instance) # $ routeSetup="/manual_dispatcher_instance"
From 3c47e583d82911967103f13148f6ece78ded09bb Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Thu, 3 Jun 2021 10:54:36 +0200
Subject: [PATCH 124/272] Python: Add test for missing data-flow step in
aiohttp.web
---
.../frameworks/aiohttp/app_conf_test.py | 48 +++++++++++++++++++
1 file changed, 48 insertions(+)
create mode 100644 python/ql/test/library-tests/frameworks/aiohttp/app_conf_test.py
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/app_conf_test.py b/python/ql/test/library-tests/frameworks/aiohttp/app_conf_test.py
new file mode 100644
index 00000000000..ce15e2110ab
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/aiohttp/app_conf_test.py
@@ -0,0 +1,48 @@
+"""
+This file is a test of an extra data-flow step that we want to have for
+aiohttp.web.Application
+
+We don't really have an established way to test extra data-flow steps in external
+libraries right now, so for now I've just used our normal taint-flow testing ¯\_(ツ)_/¯
+
+see https://docs.aiohttp.org/en/stable/web_advanced.html#application-s-config
+"""
+
+from aiohttp import web
+
+# to make code runable
+TAINTED_STRING = "TAINTED_STRING"
+def ensure_tainted(*args, **kwargs):
+ pass
+
+ensure_tainted(
+ TAINTED_STRING # $ tainted
+)
+
+
+async def example(request: web.Request): # $ requestHandler
+ return web.Response(text=f'example {request.app["foo"]=}') # $ HttpResponse
+
+
+async def also_works(request: web.Request): # $ requestHandler
+ return web.Response(text=f'also_works {request.config_dict["foo"]=}') # $ HttpResponse
+
+
+async def taint_test(request: web.Request): # $ requestHandler
+ ensure_tainted(
+ request.app["ts"], # $ MISSING: tainted
+ request.config_dict["ts"], # $ MISSING: tainted
+ )
+ return web.Response(text="ok") # $ HttpResponse
+
+
+app = web.Application()
+app.router.add_get("", example) # $ routeSetup=""
+app.router.add_get("/also-works", also_works) # $ routeSetup="/also-works"
+app.router.add_get("/taint-test", taint_test) # $ routeSetup="/taint-test"
+app["foo"] = 42
+app["ts"] = TAINTED_STRING
+
+
+if __name__ == "__main__":
+ web.run_app(app)
From 607dcd4a277776fcf91405b13b0cf24dbeaf2735 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Thu, 3 Jun 2021 11:05:13 +0200
Subject: [PATCH 125/272] Don't use CSV models for private flow configs
---
.../code/java/security/JexlInjection.qll | 64 ++++++++++---------
1 file changed, 34 insertions(+), 30 deletions(-)
diff --git a/java/ql/src/semmle/code/java/security/JexlInjection.qll b/java/ql/src/semmle/code/java/security/JexlInjection.qll
index 8e8923a746b..2328a4d4cbb 100644
--- a/java/ql/src/semmle/code/java/security/JexlInjection.qll
+++ b/java/ql/src/semmle/code/java/security/JexlInjection.qll
@@ -131,43 +131,40 @@ private predicate isSafeEngine(Expr expr) {
private class SandboxedJexlFlowConfig extends DataFlow2::Configuration {
SandboxedJexlFlowConfig() { this = "JexlInjection::SandboxedJexlFlowConfig" }
- override predicate isSource(DataFlow::Node node) { sourceNode(node, "sandboxed-jexl") }
+ override predicate isSource(DataFlow::Node node) { node instanceof SandboxedJexlSource }
- override predicate isSink(DataFlow::Node node) { sinkNode(node, "sandboxed-jexl") }
+ override predicate isSink(DataFlow::Node node) {
+ exists(MethodAccess ma, Method m |
+ m instanceof CreateJexlScriptMethod or
+ m instanceof CreateJexlExpressionMethod or
+ m instanceof CreateJexlTemplateMethod
+ |
+ ma.getMethod() = m and ma.getQualifier() = node.asExpr()
+ )
+ }
override predicate isAdditionalFlowStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
createJexlEngineStep(fromNode, toNode)
}
}
-private class SandoboxedJexlSourceModel extends SourceModelCsv {
- override predicate row(string row) {
- row =
- [
- // JEXL2
- "org.apache.commons.jexl2;JexlEngine;false;JexlEngine;(Uberspect,JexlArithmetic,Map,Log);;ReturnValue;sandboxed-jexl",
- // JEXL3
- "org.apache.commons.jexl3;JexlBuilder;false;uberspect;(JexlUberspect);;ReturnValue;sandboxed-jexl",
- "org.apache.commons.jexl3;JexlBuilder;false;sandbox;(JexlSandbox);;ReturnValue;sandboxed-jexl"
- ]
- }
-}
-
-private class SandoboxedJexlSinkModel extends SinkModelCsv {
- override predicate row(string row) {
- row =
- [
- // JEXL2
- "org.apache.commons.jexl2;JexlEngine;false;createScript;;;Argument[-1];sandboxed-jexl",
- "org.apache.commons.jexl2;JexlEngine;false;createExpression;;;Argument[-1];sandboxed-jexl",
- "org.apache.commons.jexl2;UnifiedJEXL;false;parse;;;Argument[-1];sandboxed-jexl",
- "org.apache.commons.jexl2;UnifiedJEXL;false;createTemplate;;;Argument[-1];sandboxed-jexl",
- // JEXL3
- "org.apache.commons.jexl3;JexlEngine;false;createScript;;;Argument[-1];sandboxed-jexl",
- "org.apache.commons.jexl3;JexlEngine;false;createExpression;;;Argument[-1];sandboxed-jexl",
- "org.apache.commons.jexl3;JxltEngine;false;createExpression;;;Argument[-1];sandboxed-jexl",
- "org.apache.commons.jexl3;JxltEngine;false;createTemplate;;;Argument[-1];sandboxed-jexl"
- ]
+/**
+ * Defines a data flow source for JEXL engines configured with a sandbox.
+ */
+private class SandboxedJexlSource extends DataFlow::ExprNode {
+ SandboxedJexlSource() {
+ exists(MethodAccess ma, Method m | m = ma.getMethod() |
+ m.getDeclaringType() instanceof JexlBuilder and
+ m.hasName(["uberspect", "sandbox"]) and
+ m.getReturnType() instanceof JexlBuilder and
+ this.asExpr() = [ma, ma.getQualifier()]
+ )
+ or
+ exists(ConstructorCall cc |
+ cc.getConstructedType() instanceof JexlEngine and
+ cc.getArgument(0).getType() instanceof JexlUberspect and
+ cc = this.asExpr()
+ )
}
}
@@ -238,6 +235,13 @@ private class UnifiedJexl extends JexlRefType {
UnifiedJexl() { hasName("UnifiedJEXL") }
}
+private class JexlUberspect extends Interface {
+ JexlUberspect() {
+ hasQualifiedName("org.apache.commons.jexl2.introspection", "Uberspect") or
+ hasQualifiedName("org.apache.commons.jexl3.introspection", "JexlUberspect")
+ }
+}
+
private class Reader extends RefType {
Reader() { hasQualifiedName("java.io", "Reader") }
}
From d044b155338fa184f3069a5e943e6273776f96f5 Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Thu, 3 Jun 2021 10:54:45 +0200
Subject: [PATCH 126/272] C#: Add colliding method ID tests
---
.../library-tests/methods/DB-CHECK.expected | 130 +++
.../methods/ExtensionMethods.expected | 24 +-
.../library-tests/methods/Methods1.expected | 2 +-
.../library-tests/methods/Methods2.expected | 2 +-
.../library-tests/methods/Methods3.expected | 2 +-
.../library-tests/methods/Methods4.expected | 2 +-
.../library-tests/methods/Methods5.expected | 52 +-
.../methods/Parameters1.expected | 4 +-
.../methods/Parameters2.expected | 8 +-
.../methods/Parameters3.expected | 4 +-
.../methods/Parameters5.expected | 14 +-
.../methods/Parameters6.expected | 4 +-
.../methods/Parameters7.expected | 2 +-
.../methods/Parameters8.expected | 102 +-
.../methods/Parameters9.expected | 14 +-
.../library-tests/methods/PrintAst.expected | 931 ++++++++++--------
.../ql/test/library-tests/methods/methods.cs | 28 +
17 files changed, 798 insertions(+), 527 deletions(-)
create mode 100644 csharp/ql/test/library-tests/methods/DB-CHECK.expected
diff --git a/csharp/ql/test/library-tests/methods/DB-CHECK.expected b/csharp/ql/test/library-tests/methods/DB-CHECK.expected
new file mode 100644
index 00000000000..4a99289a434
--- /dev/null
+++ b/csharp/ql/test/library-tests/methods/DB-CHECK.expected
@@ -0,0 +1,130 @@
+[INVALID_KEY] predicate methods(@method id, string name, @type declaring_type_id, @type_or_ref type_id, @method unbound_id): The key set {id} does not functionally determine all fields.
+Here is a pair of tuples that agree on the key set but differ at index 4:
+Tuple 1 in row 28: (828,"M",827,1087,813)
+Tuple 2 in row 29: (828,"M",827,1087,1135)
+ Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
+ Relevant element: Full ID for 827: @"(294)<(366)>;type". The ID may expand to @"{@"{@"{@";namespace"}.Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"{@";namespace"}.System;namespace"}.Int32;type"}>;type"
+ Relevant element: Full ID for 1087: @"(365);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"};typeRef"
+ Relevant element: Full ID for 813: @"(365) (294).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@";namespace"}.Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
+ Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
+ Relevant element: Full ID for 827: @"(294)<(366)>;type". The ID may expand to @"{@"{@"{@";namespace"}.Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"{@";namespace"}.System;namespace"}.Int32;type"}>;type"
+ Relevant element: Full ID for 1087: @"(365);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"};typeRef"
+ Relevant element: Full ID for 1135: @"(365) (294).M((296) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@";namespace"}.Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}_0;typeparameter"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
+[INVALID_KEY] predicate constructors(@constructor id, string name, @type declaring_type_id, @constructor unbound_id): The key set {id} does not functionally determine all fields.
+Here is a pair of tuples that agree on the key set but differ at index 3:
+Tuple 1 in row 14: (897,"Nested",830,332)
+Tuple 2 in row 15: (897,"Nested",830,1143)
+ Relevant element: Full ID for 897: @"(830)((366) p1);constructor". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.Nested;type"}({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1);constructor"
+ Relevant element: Full ID for 830: @"(827).Nested;type". The ID may expand to @"{@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.Nested;type"
+ Relevant element: Full ID for 332: @"(297)((296) p1);constructor". The ID may expand to @"{@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.Nested;type"}({@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}_0;typeparameter"} p1);constructor"
+ Relevant element: Full ID for 897: @"(830)((366) p1);constructor". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.Nested;type"}({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1);constructor"
+ Relevant element: Full ID for 830: @"(827).Nested;type". The ID may expand to @"{@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.Nested;type"
+ Relevant element: Full ID for 1143: @"(297)((366) p1);constructor". The ID may expand to @"{@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.Nested;type"}({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1);constructor"
+[INVALID_KEY_SET] predicate params(@parameter id, string name, @type_or_ref type_id, int index, int mode, @parameterizable parent_id, @parameter unbound_id): The key set {name, parent_id} does not functionally determine all fields.
+Here is a pair of tuples that agree on the key set but differ at index 6:
+Tuple 1 in row 34: (1005,"p1",1083,0,0,828,1138)
+Tuple 2 in row 35: (1005,"p1",1083,0,0,828,1348)
+ Relevant element: Full ID for 1005: @"(828)_0;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_0;parameter"
+ Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
+ Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
+ Relevant element: Full ID for 1138: @"(1135)_0;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"(130).TestCollidingMethods`1;type"}_0;typeparameter"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_0;parameter"
+ Relevant element: Full ID for 1005: @"(828)_0;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_0;parameter"
+ Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
+ Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
+ Relevant element: Full ID for 1348: @"(813)_0;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_0;parameter"
+[INVALID_KEY_SET] predicate params(@parameter id, string name, @type_or_ref type_id, int index, int mode, @parameterizable parent_id, @parameter unbound_id): The key set {name, parent_id} does not functionally determine all fields.
+Here is a pair of tuples that agree on the key set but differ at index 6:
+Tuple 1 in row 42: (1031,"p1",1083,0,0,897,335)
+Tuple 2 in row 43: (1031,"p1",1083,0,0,897,1146)
+ Relevant element: Full ID for 1031: @"(897)_0;parameter". The ID may expand to @"{@"{@"{@"(294)<(366)>;type"}.Nested;type"}({@"{@"(111).System;namespace"}.Int32;type"} p1);constructor"}_0;parameter"
+ Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
+ Relevant element: Full ID for 897: @"(830)((366) p1);constructor". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.Nested;type"}({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1);constructor"
+ Relevant element: Full ID for 335: @"(332)_0;parameter". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}.Nested;type"}({@"{@"(130).TestCollidingMethods`1;type"}_0;typeparameter"} p1);constructor"}_0;parameter"
+ Relevant element: Full ID for 1031: @"(897)_0;parameter". The ID may expand to @"{@"{@"{@"(294)<(366)>;type"}.Nested;type"}({@"{@"(111).System;namespace"}.Int32;type"} p1);constructor"}_0;parameter"
+ Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
+ Relevant element: Full ID for 897: @"(830)((366) p1);constructor". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.Nested;type"}({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1);constructor"
+ Relevant element: Full ID for 1146: @"(1143)_0;parameter". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}.Nested;type"}({@"{@"(111).System;namespace"}.Int32;type"} p1);constructor"}_0;parameter"
+[INVALID_KEY_SET] predicate params(@parameter id, string name, @type_or_ref type_id, int index, int mode, @parameterizable parent_id, @parameter unbound_id): The key set {name, parent_id} does not functionally determine all fields.
+Here is a pair of tuples that agree on the key set but differ at index 6:
+Tuple 1 in row 36: (1006,"p2",1083,1,0,828,1140)
+Tuple 2 in row 37: (1006,"p2",1083,1,0,828,1350)
+ Relevant element: Full ID for 1006: @"(828)_1;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_1;parameter"
+ Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
+ Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
+ Relevant element: Full ID for 1140: @"(1135)_1;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"(130).TestCollidingMethods`1;type"}_0;typeparameter"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_1;parameter"
+ Relevant element: Full ID for 1006: @"(828)_1;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_1;parameter"
+ Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
+ Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
+ Relevant element: Full ID for 1350: @"(813)_1;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_1;parameter"
+[INVALID_KEY_SET] predicate params(@parameter id, string name, @type_or_ref type_id, int index, int mode, @parameterizable parent_id, @parameter unbound_id): The key set {index, parent_id} does not functionally determine all fields.
+Here is a pair of tuples that agree on the key set but differ at index 6:
+Tuple 1 in row 34: (1005,"p1",1083,0,0,828,1138)
+Tuple 2 in row 35: (1005,"p1",1083,0,0,828,1348)
+ Relevant element: Full ID for 1005: @"(828)_0;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_0;parameter"
+ Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
+ Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
+ Relevant element: Full ID for 1138: @"(1135)_0;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"(130).TestCollidingMethods`1;type"}_0;typeparameter"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_0;parameter"
+ Relevant element: Full ID for 1005: @"(828)_0;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_0;parameter"
+ Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
+ Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
+ Relevant element: Full ID for 1348: @"(813)_0;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_0;parameter"
+[INVALID_KEY_SET] predicate params(@parameter id, string name, @type_or_ref type_id, int index, int mode, @parameterizable parent_id, @parameter unbound_id): The key set {index, parent_id} does not functionally determine all fields.
+Here is a pair of tuples that agree on the key set but differ at index 6:
+Tuple 1 in row 42: (1031,"p1",1083,0,0,897,335)
+Tuple 2 in row 43: (1031,"p1",1083,0,0,897,1146)
+ Relevant element: Full ID for 1031: @"(897)_0;parameter". The ID may expand to @"{@"{@"{@"(294)<(366)>;type"}.Nested;type"}({@"{@"(111).System;namespace"}.Int32;type"} p1);constructor"}_0;parameter"
+ Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
+ Relevant element: Full ID for 897: @"(830)((366) p1);constructor". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.Nested;type"}({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1);constructor"
+ Relevant element: Full ID for 335: @"(332)_0;parameter". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}.Nested;type"}({@"{@"(130).TestCollidingMethods`1;type"}_0;typeparameter"} p1);constructor"}_0;parameter"
+ Relevant element: Full ID for 1031: @"(897)_0;parameter". The ID may expand to @"{@"{@"{@"(294)<(366)>;type"}.Nested;type"}({@"{@"(111).System;namespace"}.Int32;type"} p1);constructor"}_0;parameter"
+ Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
+ Relevant element: Full ID for 897: @"(830)((366) p1);constructor". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.Nested;type"}({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1);constructor"
+ Relevant element: Full ID for 1146: @"(1143)_0;parameter". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}.Nested;type"}({@"{@"(111).System;namespace"}.Int32;type"} p1);constructor"}_0;parameter"
+[INVALID_KEY_SET] predicate params(@parameter id, string name, @type_or_ref type_id, int index, int mode, @parameterizable parent_id, @parameter unbound_id): The key set {index, parent_id} does not functionally determine all fields.
+Here is a pair of tuples that agree on the key set but differ at index 6:
+Tuple 1 in row 36: (1006,"p2",1083,1,0,828,1140)
+Tuple 2 in row 37: (1006,"p2",1083,1,0,828,1350)
+ Relevant element: Full ID for 1006: @"(828)_1;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_1;parameter"
+ Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
+ Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
+ Relevant element: Full ID for 1140: @"(1135)_1;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"(130).TestCollidingMethods`1;type"}_0;typeparameter"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_1;parameter"
+ Relevant element: Full ID for 1006: @"(828)_1;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_1;parameter"
+ Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
+ Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
+ Relevant element: Full ID for 1350: @"(813)_1;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_1;parameter"
+[INVALID_KEY] predicate params(@parameter id, string name, @type_or_ref type_id, int index, int mode, @parameterizable parent_id, @parameter unbound_id): The key set {id} does not functionally determine all fields.
+Here is a pair of tuples that agree on the key set but differ at index 6:
+Tuple 1 in row 34: (1005,"p1",1083,0,0,828,1138)
+Tuple 2 in row 35: (1005,"p1",1083,0,0,828,1348)
+ Relevant element: Full ID for 1005: @"(828)_0;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_0;parameter"
+ Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
+ Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
+ Relevant element: Full ID for 1138: @"(1135)_0;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"(130).TestCollidingMethods`1;type"}_0;typeparameter"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_0;parameter"
+ Relevant element: Full ID for 1005: @"(828)_0;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_0;parameter"
+ Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
+ Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
+ Relevant element: Full ID for 1348: @"(813)_0;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_0;parameter"
+[INVALID_KEY] predicate params(@parameter id, string name, @type_or_ref type_id, int index, int mode, @parameterizable parent_id, @parameter unbound_id): The key set {id} does not functionally determine all fields.
+Here is a pair of tuples that agree on the key set but differ at index 6:
+Tuple 1 in row 36: (1006,"p2",1083,1,0,828,1140)
+Tuple 2 in row 37: (1006,"p2",1083,1,0,828,1350)
+ Relevant element: Full ID for 1006: @"(828)_1;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_1;parameter"
+ Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
+ Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
+ Relevant element: Full ID for 1140: @"(1135)_1;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"(130).TestCollidingMethods`1;type"}_0;typeparameter"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_1;parameter"
+ Relevant element: Full ID for 1006: @"(828)_1;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_1;parameter"
+ Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
+ Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
+ Relevant element: Full ID for 1350: @"(813)_1;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_1;parameter"
+[INVALID_KEY] predicate params(@parameter id, string name, @type_or_ref type_id, int index, int mode, @parameterizable parent_id, @parameter unbound_id): The key set {id} does not functionally determine all fields.
+Here is a pair of tuples that agree on the key set but differ at index 6:
+Tuple 1 in row 42: (1031,"p1",1083,0,0,897,335)
+Tuple 2 in row 43: (1031,"p1",1083,0,0,897,1146)
+ Relevant element: Full ID for 1031: @"(897)_0;parameter". The ID may expand to @"{@"{@"{@"(294)<(366)>;type"}.Nested;type"}({@"{@"(111).System;namespace"}.Int32;type"} p1);constructor"}_0;parameter"
+ Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
+ Relevant element: Full ID for 897: @"(830)((366) p1);constructor". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.Nested;type"}({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1);constructor"
+ Relevant element: Full ID for 335: @"(332)_0;parameter". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}.Nested;type"}({@"{@"(130).TestCollidingMethods`1;type"}_0;typeparameter"} p1);constructor"}_0;parameter"
+ Relevant element: Full ID for 1031: @"(897)_0;parameter". The ID may expand to @"{@"{@"{@"(294)<(366)>;type"}.Nested;type"}({@"{@"(111).System;namespace"}.Int32;type"} p1);constructor"}_0;parameter"
+ Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
+ Relevant element: Full ID for 897: @"(830)((366) p1);constructor". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.Nested;type"}({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1);constructor"
+ Relevant element: Full ID for 1146: @"(1143)_0;parameter". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}.Nested;type"}({@"{@"(111).System;namespace"}.Int32;type"} p1);constructor"}_0;parameter"
diff --git a/csharp/ql/test/library-tests/methods/ExtensionMethods.expected b/csharp/ql/test/library-tests/methods/ExtensionMethods.expected
index d7593f8f223..795014ea078 100644
--- a/csharp/ql/test/library-tests/methods/ExtensionMethods.expected
+++ b/csharp/ql/test/library-tests/methods/ExtensionMethods.expected
@@ -1,12 +1,12 @@
-| methods.cs:118:44:118:55 | call to method ToInt32 | 0 | methods.cs:118:52:118:54 | "0" |
-| methods.cs:127:34:127:52 | call to method Slice | 0 | methods.cs:127:34:127:40 | access to local variable strings |
-| methods.cs:127:34:127:52 | call to method Slice | 1 | methods.cs:127:48:127:48 | 1 |
-| methods.cs:127:34:127:52 | call to method Slice | 2 | methods.cs:127:51:127:51 | 2 |
-| methods.cs:129:42:129:52 | call to method ToInt32 | 0 | methods.cs:129:42:129:42 | access to local variable s |
-| methods.cs:132:13:132:34 | call to method ToInt32 | 0 | methods.cs:132:32:132:33 | "" |
-| methods.cs:132:13:132:34 | call to method ToInt32 | -1 | methods.cs:132:13:132:22 | access to type Extensions |
-| methods.cs:134:13:134:49 | call to method ToBool | 0 | methods.cs:134:31:134:36 | "true" |
-| methods.cs:134:13:134:49 | call to method ToBool | 1 | methods.cs:134:39:134:48 | delegate creation of type Func |
-| methods.cs:134:13:134:49 | call to method ToBool | -1 | methods.cs:134:13:134:22 | access to type Extensions |
-| methods.cs:180:20:180:39 | call to method SkipTwo | 0 | methods.cs:180:20:180:23 | access to parameter list |
-| methods.cs:180:20:180:39 | call to method SkipTwo | 1 | methods.cs:180:38:180:38 | access to parameter i |
+| methods.cs:119:44:119:55 | call to method ToInt32 | 0 | methods.cs:119:52:119:54 | "0" |
+| methods.cs:128:34:128:52 | call to method Slice | 0 | methods.cs:128:34:128:40 | access to local variable strings |
+| methods.cs:128:34:128:52 | call to method Slice | 1 | methods.cs:128:48:128:48 | 1 |
+| methods.cs:128:34:128:52 | call to method Slice | 2 | methods.cs:128:51:128:51 | 2 |
+| methods.cs:130:42:130:52 | call to method ToInt32 | 0 | methods.cs:130:42:130:42 | access to local variable s |
+| methods.cs:133:13:133:34 | call to method ToInt32 | 0 | methods.cs:133:32:133:33 | "" |
+| methods.cs:133:13:133:34 | call to method ToInt32 | -1 | methods.cs:133:13:133:22 | access to type Extensions |
+| methods.cs:135:13:135:49 | call to method ToBool | 0 | methods.cs:135:31:135:36 | "true" |
+| methods.cs:135:13:135:49 | call to method ToBool | 1 | methods.cs:135:39:135:48 | delegate creation of type Func |
+| methods.cs:135:13:135:49 | call to method ToBool | -1 | methods.cs:135:13:135:22 | access to type Extensions |
+| methods.cs:181:20:181:39 | call to method SkipTwo | 0 | methods.cs:181:20:181:23 | access to parameter list |
+| methods.cs:181:20:181:39 | call to method SkipTwo | 1 | methods.cs:181:38:181:38 | access to parameter i |
diff --git a/csharp/ql/test/library-tests/methods/Methods1.expected b/csharp/ql/test/library-tests/methods/Methods1.expected
index be14a801185..054f358330f 100644
--- a/csharp/ql/test/library-tests/methods/Methods1.expected
+++ b/csharp/ql/test/library-tests/methods/Methods1.expected
@@ -1 +1 @@
-| methods.cs:9:21:9:24 | Swap |
+| methods.cs:10:21:10:24 | Swap |
diff --git a/csharp/ql/test/library-tests/methods/Methods2.expected b/csharp/ql/test/library-tests/methods/Methods2.expected
index b9fad0aa394..d0dc1559fa6 100644
--- a/csharp/ql/test/library-tests/methods/Methods2.expected
+++ b/csharp/ql/test/library-tests/methods/Methods2.expected
@@ -1 +1 @@
-| methods.cs:28:28:28:33 | Divide |
+| methods.cs:29:28:29:33 | Divide |
diff --git a/csharp/ql/test/library-tests/methods/Methods3.expected b/csharp/ql/test/library-tests/methods/Methods3.expected
index 8c726a4b6ff..1fb5f1dd0ba 100644
--- a/csharp/ql/test/library-tests/methods/Methods3.expected
+++ b/csharp/ql/test/library-tests/methods/Methods3.expected
@@ -1 +1 @@
-| methods.cs:49:11:49:25 | TestOverloading |
+| methods.cs:50:11:50:25 | TestOverloading |
diff --git a/csharp/ql/test/library-tests/methods/Methods4.expected b/csharp/ql/test/library-tests/methods/Methods4.expected
index f709606a238..6eaa7230538 100644
--- a/csharp/ql/test/library-tests/methods/Methods4.expected
+++ b/csharp/ql/test/library-tests/methods/Methods4.expected
@@ -1 +1 @@
-| methods.cs:49:11:49:25 | TestOverloading | methods.cs:57:21:57:21 | F | Object |
+| methods.cs:50:11:50:25 | TestOverloading | methods.cs:58:21:58:21 | F | Object |
diff --git a/csharp/ql/test/library-tests/methods/Methods5.expected b/csharp/ql/test/library-tests/methods/Methods5.expected
index 4f7e31d5bee..03638800b01 100644
--- a/csharp/ql/test/library-tests/methods/Methods5.expected
+++ b/csharp/ql/test/library-tests/methods/Methods5.expected
@@ -1,22 +1,30 @@
-| methods.cs:16:14:16:17 | Main | methods.cs:9:21:9:24 | Swap | methods.cs:6:18:6:24 | TestRef |
-| methods.cs:16:14:16:17 | Main | methods.cs:46:28:46:36 | WriteLine | methods.cs:6:18:6:24 | TestRef |
-| methods.cs:34:14:34:17 | Main | methods.cs:28:28:28:33 | Divide | methods.cs:25:18:25:24 | TestOut |
-| methods.cs:34:14:34:17 | Main | methods.cs:46:28:46:36 | WriteLine | methods.cs:25:18:25:24 | TestOut |
-| methods.cs:52:21:52:21 | F | methods.cs:46:28:46:36 | WriteLine | methods.cs:49:11:49:25 | TestOverloading |
-| methods.cs:57:21:57:21 | F | methods.cs:46:28:46:36 | WriteLine | methods.cs:49:11:49:25 | TestOverloading |
-| methods.cs:62:21:62:21 | F | methods.cs:46:28:46:36 | WriteLine | methods.cs:49:11:49:25 | TestOverloading |
-| methods.cs:67:21:67:21 | F | methods.cs:46:28:46:36 | WriteLine | methods.cs:49:11:49:25 | TestOverloading |
-| methods.cs:72:21:72:24 | F | methods.cs:46:28:46:36 | WriteLine | methods.cs:49:11:49:25 | TestOverloading |
-| methods.cs:77:21:77:21 | F | methods.cs:46:28:46:36 | WriteLine | methods.cs:49:11:49:25 | TestOverloading |
-| methods.cs:82:14:82:17 | Main | methods.cs:52:21:52:21 | F | methods.cs:49:11:49:25 | TestOverloading |
-| methods.cs:82:14:82:17 | Main | methods.cs:57:21:57:21 | F | methods.cs:49:11:49:25 | TestOverloading |
-| methods.cs:82:14:82:17 | Main | methods.cs:62:21:62:21 | F | methods.cs:49:11:49:25 | TestOverloading |
-| methods.cs:82:14:82:17 | Main | methods.cs:67:21:67:21 | F | methods.cs:49:11:49:25 | TestOverloading |
-| methods.cs:82:14:82:17 | Main | methods.cs:72:21:72:24 | F | methods.cs:49:11:49:25 | TestOverloading |
-| methods.cs:82:14:82:17 | Main | methods.cs:72:21:72:24 | F | methods.cs:49:11:49:25 | TestOverloading |
-| methods.cs:82:14:82:17 | Main | methods.cs:77:21:77:21 | F | methods.cs:49:11:49:25 | TestOverloading |
-| methods.cs:118:27:118:37 | CallToInt32 | methods.cs:99:27:99:33 | ToInt32 | methods.cs:96:25:96:34 | Extensions |
-| methods.cs:124:21:124:24 | Main | methods.cs:99:27:99:33 | ToInt32 | methods.cs:121:18:121:31 | TestExtensions |
-| methods.cs:124:21:124:24 | Main | methods.cs:104:28:104:33 | ToBool | methods.cs:121:18:121:31 | TestExtensions |
-| methods.cs:124:21:124:24 | Main | methods.cs:109:27:109:34 | Slice | methods.cs:121:18:121:31 | TestExtensions |
-| methods.cs:178:67:178:76 | SkipTwoInt | methods.cs:173:65:173:74 | SkipTwo | methods.cs:166:18:166:47 | TestDefaultExtensionParameters |
+| methods.cs:17:14:17:17 | Main | methods.cs:10:21:10:24 | Swap | methods.cs:7:18:7:24 | TestRef |
+| methods.cs:17:14:17:17 | Main | methods.cs:47:28:47:36 | WriteLine | methods.cs:7:18:7:24 | TestRef |
+| methods.cs:35:14:35:17 | Main | methods.cs:29:28:29:33 | Divide | methods.cs:26:18:26:24 | TestOut |
+| methods.cs:35:14:35:17 | Main | methods.cs:47:28:47:36 | WriteLine | methods.cs:26:18:26:24 | TestOut |
+| methods.cs:53:21:53:21 | F | methods.cs:47:28:47:36 | WriteLine | methods.cs:50:11:50:25 | TestOverloading |
+| methods.cs:58:21:58:21 | F | methods.cs:47:28:47:36 | WriteLine | methods.cs:50:11:50:25 | TestOverloading |
+| methods.cs:63:21:63:21 | F | methods.cs:47:28:47:36 | WriteLine | methods.cs:50:11:50:25 | TestOverloading |
+| methods.cs:68:21:68:21 | F | methods.cs:47:28:47:36 | WriteLine | methods.cs:50:11:50:25 | TestOverloading |
+| methods.cs:73:21:73:24 | F | methods.cs:47:28:47:36 | WriteLine | methods.cs:50:11:50:25 | TestOverloading |
+| methods.cs:78:21:78:21 | F | methods.cs:47:28:47:36 | WriteLine | methods.cs:50:11:50:25 | TestOverloading |
+| methods.cs:83:14:83:17 | Main | methods.cs:53:21:53:21 | F | methods.cs:50:11:50:25 | TestOverloading |
+| methods.cs:83:14:83:17 | Main | methods.cs:58:21:58:21 | F | methods.cs:50:11:50:25 | TestOverloading |
+| methods.cs:83:14:83:17 | Main | methods.cs:63:21:63:21 | F | methods.cs:50:11:50:25 | TestOverloading |
+| methods.cs:83:14:83:17 | Main | methods.cs:68:21:68:21 | F | methods.cs:50:11:50:25 | TestOverloading |
+| methods.cs:83:14:83:17 | Main | methods.cs:73:21:73:24 | F | methods.cs:50:11:50:25 | TestOverloading |
+| methods.cs:83:14:83:17 | Main | methods.cs:73:21:73:24 | F | methods.cs:50:11:50:25 | TestOverloading |
+| methods.cs:83:14:83:17 | Main | methods.cs:78:21:78:21 | F | methods.cs:50:11:50:25 | TestOverloading |
+| methods.cs:119:27:119:37 | CallToInt32 | methods.cs:100:27:100:33 | ToInt32 | methods.cs:97:25:97:34 | Extensions |
+| methods.cs:125:21:125:24 | Main | methods.cs:100:27:100:33 | ToInt32 | methods.cs:122:18:122:31 | TestExtensions |
+| methods.cs:125:21:125:24 | Main | methods.cs:105:28:105:33 | ToBool | methods.cs:122:18:122:31 | TestExtensions |
+| methods.cs:125:21:125:24 | Main | methods.cs:110:27:110:34 | Slice | methods.cs:122:18:122:31 | TestExtensions |
+| methods.cs:179:67:179:76 | SkipTwoInt | methods.cs:174:65:174:74 | SkipTwo | methods.cs:167:18:167:47 | TestDefaultExtensionParameters |
+| methods.cs:190:21:190:25 | Calls | methods.cs:187:21:187:21 | M | methods.cs:185:18:185:40 | TestCollidingMethods<> |
+| methods.cs:190:21:190:25 | Calls | methods.cs:187:21:187:21 | M | methods.cs:185:18:185:40 | TestCollidingMethods<> |
+| methods.cs:190:21:190:25 | Calls | methods.cs:188:21:188:21 | M | methods.cs:185:18:185:40 | TestCollidingMethods<> |
+| methods.cs:190:21:190:25 | Calls | methods.cs:188:21:188:21 | M | methods.cs:185:18:185:40 | TestCollidingMethods<> |
+| methods.cs:203:20:203:25 | Nested | methods.cs:202:20:202:25 | Nested | methods.cs:200:22:200:27 | Nested |
+| methods.cs:203:20:203:25 | Nested | methods.cs:202:20:202:25 | Nested | methods.cs:200:22:200:27 | Nested |
+| methods.cs:203:20:203:25 | Nested | methods.cs:203:20:203:25 | Nested | methods.cs:200:22:200:27 | Nested |
+| methods.cs:203:20:203:25 | Nested | methods.cs:203:20:203:25 | Nested | methods.cs:200:22:200:27 | Nested |
diff --git a/csharp/ql/test/library-tests/methods/Parameters1.expected b/csharp/ql/test/library-tests/methods/Parameters1.expected
index 9d9948f38dd..e4e643616f6 100644
--- a/csharp/ql/test/library-tests/methods/Parameters1.expected
+++ b/csharp/ql/test/library-tests/methods/Parameters1.expected
@@ -1,2 +1,2 @@
-| methods.cs:9:21:9:24 | Swap | methods.cs:9:34:9:34 | x |
-| methods.cs:9:21:9:24 | Swap | methods.cs:9:45:9:45 | y |
+| methods.cs:10:21:10:24 | Swap | methods.cs:10:34:10:34 | x |
+| methods.cs:10:21:10:24 | Swap | methods.cs:10:45:10:45 | y |
diff --git a/csharp/ql/test/library-tests/methods/Parameters2.expected b/csharp/ql/test/library-tests/methods/Parameters2.expected
index 645af10244f..30a7ca05274 100644
--- a/csharp/ql/test/library-tests/methods/Parameters2.expected
+++ b/csharp/ql/test/library-tests/methods/Parameters2.expected
@@ -1,4 +1,4 @@
-| methods.cs:28:28:28:33 | Divide | methods.cs:28:39:28:39 | x |
-| methods.cs:28:28:28:33 | Divide | methods.cs:28:46:28:46 | y |
-| methods.cs:28:28:28:33 | Divide | methods.cs:28:57:28:62 | result |
-| methods.cs:28:28:28:33 | Divide | methods.cs:28:73:28:81 | remainder |
+| methods.cs:29:28:29:33 | Divide | methods.cs:29:39:29:39 | x |
+| methods.cs:29:28:29:33 | Divide | methods.cs:29:46:29:46 | y |
+| methods.cs:29:28:29:33 | Divide | methods.cs:29:57:29:62 | result |
+| methods.cs:29:28:29:33 | Divide | methods.cs:29:73:29:81 | remainder |
diff --git a/csharp/ql/test/library-tests/methods/Parameters3.expected b/csharp/ql/test/library-tests/methods/Parameters3.expected
index 68936353065..ae1c5c02e80 100644
--- a/csharp/ql/test/library-tests/methods/Parameters3.expected
+++ b/csharp/ql/test/library-tests/methods/Parameters3.expected
@@ -1,2 +1,2 @@
-| methods.cs:45:28:45:32 | Write | Object[] |
-| methods.cs:45:28:45:32 | Write | String |
+| methods.cs:46:28:46:32 | Write | Object[] |
+| methods.cs:46:28:46:32 | Write | String |
diff --git a/csharp/ql/test/library-tests/methods/Parameters5.expected b/csharp/ql/test/library-tests/methods/Parameters5.expected
index 823329fe908..1fd039430b1 100644
--- a/csharp/ql/test/library-tests/methods/Parameters5.expected
+++ b/csharp/ql/test/library-tests/methods/Parameters5.expected
@@ -1,7 +1,7 @@
-| methods.cs:145:40:145:40 | c | methods.cs:145:44:145:44 | 1 | 1 |
-| methods.cs:145:51:145:51 | d | methods.cs:145:55:145:55 | 2 | 2 |
-| methods.cs:145:65:145:65 | e | methods.cs:145:69:145:77 | ... + ... | ab |
-| methods.cs:153:45:153:45 | x | methods.cs:153:49:153:53 | "abc" | abc |
-| methods.cs:153:63:153:63 | y | methods.cs:153:67:153:78 | object creation of type Double | 0 |
-| methods.cs:159:36:159:36 | y | methods.cs:159:40:159:40 | 0 | 0 |
-| methods.cs:159:36:159:36 | y | methods.cs:159:40:159:40 | 0 | 0 |
+| methods.cs:146:40:146:40 | c | methods.cs:146:44:146:44 | 1 | 1 |
+| methods.cs:146:51:146:51 | d | methods.cs:146:55:146:55 | 2 | 2 |
+| methods.cs:146:65:146:65 | e | methods.cs:146:69:146:77 | ... + ... | ab |
+| methods.cs:154:45:154:45 | x | methods.cs:154:49:154:53 | "abc" | abc |
+| methods.cs:154:63:154:63 | y | methods.cs:154:67:154:78 | object creation of type Double | 0 |
+| methods.cs:160:36:160:36 | y | methods.cs:160:40:160:40 | 0 | 0 |
+| methods.cs:160:36:160:36 | y | methods.cs:160:40:160:40 | 0 | 0 |
diff --git a/csharp/ql/test/library-tests/methods/Parameters6.expected b/csharp/ql/test/library-tests/methods/Parameters6.expected
index 25c2a9e1e9f..c431492acd1 100644
--- a/csharp/ql/test/library-tests/methods/Parameters6.expected
+++ b/csharp/ql/test/library-tests/methods/Parameters6.expected
@@ -1,2 +1,2 @@
-| methods.cs:157:40:157:40 | b | methods.cs:157:44:157:45 | 12 | 12 |
-| methods.cs:157:55:157:55 | c | methods.cs:157:59:157:70 | object creation of type Double | 0 |
+| methods.cs:158:40:158:40 | b | methods.cs:158:44:158:45 | 12 | 12 |
+| methods.cs:158:55:158:55 | c | methods.cs:158:59:158:70 | object creation of type Double | 0 |
diff --git a/csharp/ql/test/library-tests/methods/Parameters7.expected b/csharp/ql/test/library-tests/methods/Parameters7.expected
index 372d22c8dfd..bbc0f36bae0 100644
--- a/csharp/ql/test/library-tests/methods/Parameters7.expected
+++ b/csharp/ql/test/library-tests/methods/Parameters7.expected
@@ -1 +1 @@
-| methods.cs:162:13:162:15 | value |
+| methods.cs:163:13:163:15 | value |
diff --git a/csharp/ql/test/library-tests/methods/Parameters8.expected b/csharp/ql/test/library-tests/methods/Parameters8.expected
index d6bebaf61cc..6efd343a538 100644
--- a/csharp/ql/test/library-tests/methods/Parameters8.expected
+++ b/csharp/ql/test/library-tests/methods/Parameters8.expected
@@ -1,43 +1,59 @@
-| methods.cs:9:21:9:24 | Swap | methods.cs:9:34:9:34 | x |
-| methods.cs:9:21:9:24 | Swap | methods.cs:9:45:9:45 | y |
-| methods.cs:28:28:28:33 | Divide | methods.cs:28:39:28:39 | x |
-| methods.cs:28:28:28:33 | Divide | methods.cs:28:46:28:46 | y |
-| methods.cs:28:28:28:33 | Divide | methods.cs:28:57:28:62 | result |
-| methods.cs:28:28:28:33 | Divide | methods.cs:28:73:28:81 | remainder |
-| methods.cs:45:28:45:32 | Write | methods.cs:45:41:45:43 | fmt |
-| methods.cs:45:28:45:32 | Write | methods.cs:45:62:45:65 | args |
-| methods.cs:46:28:46:36 | WriteLine | methods.cs:46:45:46:47 | fmt |
-| methods.cs:46:28:46:36 | WriteLine | methods.cs:46:66:46:69 | args |
-| methods.cs:57:21:57:21 | F | methods.cs:57:30:57:30 | x |
-| methods.cs:62:21:62:21 | F | methods.cs:62:27:62:27 | x |
-| methods.cs:67:21:67:21 | F | methods.cs:67:30:67:30 | x |
-| methods.cs:72:21:72:24 | F | methods.cs:72:28:72:28 | x |
-| methods.cs:72:21:72:24 | F | methods.cs:72:28:72:28 | x |
-| methods.cs:72:21:72:24 | F | methods.cs:72:28:72:28 | x |
-| methods.cs:77:21:77:21 | F | methods.cs:77:30:77:30 | x |
-| methods.cs:77:21:77:21 | F | methods.cs:77:40:77:40 | y |
-| methods.cs:99:27:99:33 | ToInt32 | methods.cs:99:35:99:47 | s |
-| methods.cs:99:27:99:33 | ToInt32 | methods.cs:99:47:99:47 | s |
-| methods.cs:104:28:104:33 | ToBool | methods.cs:104:47:104:47 | s |
-| methods.cs:104:28:104:33 | ToBool | methods.cs:104:69:104:69 | f |
-| methods.cs:109:27:109:34 | Slice | methods.cs:109:36:109:50 | source |
-| methods.cs:109:27:109:34 | Slice | methods.cs:109:45:109:50 | source |
-| methods.cs:109:27:109:34 | Slice | methods.cs:109:57:109:61 | index |
-| methods.cs:109:27:109:34 | Slice | methods.cs:109:57:109:61 | index |
-| methods.cs:109:27:109:34 | Slice | methods.cs:109:68:109:72 | count |
-| methods.cs:109:27:109:34 | Slice | methods.cs:109:68:109:72 | count |
-| methods.cs:141:14:141:20 | Method1 | methods.cs:141:26:141:26 | x |
-| methods.cs:141:14:141:20 | Method1 | methods.cs:141:33:141:33 | y |
-| methods.cs:145:14:145:20 | Method2 | methods.cs:145:26:145:26 | a |
-| methods.cs:145:14:145:20 | Method2 | methods.cs:145:33:145:33 | b |
-| methods.cs:145:14:145:20 | Method2 | methods.cs:145:40:145:40 | c |
-| methods.cs:145:14:145:20 | Method2 | methods.cs:145:51:145:51 | d |
-| methods.cs:145:14:145:20 | Method2 | methods.cs:145:65:145:65 | e |
-| methods.cs:168:27:168:30 | Plus | methods.cs:168:41:168:44 | left |
-| methods.cs:168:27:168:30 | Plus | methods.cs:168:51:168:55 | right |
-| methods.cs:173:65:173:74 | SkipTwo | methods.cs:173:76:173:126 | list |
-| methods.cs:173:65:173:74 | SkipTwo | methods.cs:173:123:173:126 | list |
-| methods.cs:173:65:173:74 | SkipTwo | methods.cs:173:133:173:133 | i |
-| methods.cs:173:65:173:74 | SkipTwo | methods.cs:173:133:173:133 | i |
-| methods.cs:178:67:178:76 | SkipTwoInt | methods.cs:178:127:178:130 | list |
-| methods.cs:178:67:178:76 | SkipTwoInt | methods.cs:178:137:178:137 | i |
+| methods.cs:10:21:10:24 | Swap | methods.cs:10:34:10:34 | x |
+| methods.cs:10:21:10:24 | Swap | methods.cs:10:45:10:45 | y |
+| methods.cs:29:28:29:33 | Divide | methods.cs:29:39:29:39 | x |
+| methods.cs:29:28:29:33 | Divide | methods.cs:29:46:29:46 | y |
+| methods.cs:29:28:29:33 | Divide | methods.cs:29:57:29:62 | result |
+| methods.cs:29:28:29:33 | Divide | methods.cs:29:73:29:81 | remainder |
+| methods.cs:46:28:46:32 | Write | methods.cs:46:41:46:43 | fmt |
+| methods.cs:46:28:46:32 | Write | methods.cs:46:62:46:65 | args |
+| methods.cs:47:28:47:36 | WriteLine | methods.cs:47:45:47:47 | fmt |
+| methods.cs:47:28:47:36 | WriteLine | methods.cs:47:66:47:69 | args |
+| methods.cs:58:21:58:21 | F | methods.cs:58:30:58:30 | x |
+| methods.cs:63:21:63:21 | F | methods.cs:63:27:63:27 | x |
+| methods.cs:68:21:68:21 | F | methods.cs:68:30:68:30 | x |
+| methods.cs:73:21:73:24 | F | methods.cs:73:28:73:28 | x |
+| methods.cs:73:21:73:24 | F | methods.cs:73:28:73:28 | x |
+| methods.cs:73:21:73:24 | F | methods.cs:73:28:73:28 | x |
+| methods.cs:78:21:78:21 | F | methods.cs:78:30:78:30 | x |
+| methods.cs:78:21:78:21 | F | methods.cs:78:40:78:40 | y |
+| methods.cs:100:27:100:33 | ToInt32 | methods.cs:100:35:100:47 | s |
+| methods.cs:100:27:100:33 | ToInt32 | methods.cs:100:47:100:47 | s |
+| methods.cs:105:28:105:33 | ToBool | methods.cs:105:47:105:47 | s |
+| methods.cs:105:28:105:33 | ToBool | methods.cs:105:69:105:69 | f |
+| methods.cs:110:27:110:34 | Slice | methods.cs:110:36:110:50 | source |
+| methods.cs:110:27:110:34 | Slice | methods.cs:110:45:110:50 | source |
+| methods.cs:110:27:110:34 | Slice | methods.cs:110:57:110:61 | index |
+| methods.cs:110:27:110:34 | Slice | methods.cs:110:57:110:61 | index |
+| methods.cs:110:27:110:34 | Slice | methods.cs:110:68:110:72 | count |
+| methods.cs:110:27:110:34 | Slice | methods.cs:110:68:110:72 | count |
+| methods.cs:142:14:142:20 | Method1 | methods.cs:142:26:142:26 | x |
+| methods.cs:142:14:142:20 | Method1 | methods.cs:142:33:142:33 | y |
+| methods.cs:146:14:146:20 | Method2 | methods.cs:146:26:146:26 | a |
+| methods.cs:146:14:146:20 | Method2 | methods.cs:146:33:146:33 | b |
+| methods.cs:146:14:146:20 | Method2 | methods.cs:146:40:146:40 | c |
+| methods.cs:146:14:146:20 | Method2 | methods.cs:146:51:146:51 | d |
+| methods.cs:146:14:146:20 | Method2 | methods.cs:146:65:146:65 | e |
+| methods.cs:169:27:169:30 | Plus | methods.cs:169:41:169:44 | left |
+| methods.cs:169:27:169:30 | Plus | methods.cs:169:51:169:55 | right |
+| methods.cs:174:65:174:74 | SkipTwo | methods.cs:174:76:174:126 | list |
+| methods.cs:174:65:174:74 | SkipTwo | methods.cs:174:123:174:126 | list |
+| methods.cs:174:65:174:74 | SkipTwo | methods.cs:174:133:174:133 | i |
+| methods.cs:174:65:174:74 | SkipTwo | methods.cs:174:133:174:133 | i |
+| methods.cs:179:67:179:76 | SkipTwoInt | methods.cs:179:127:179:130 | list |
+| methods.cs:179:67:179:76 | SkipTwoInt | methods.cs:179:137:179:137 | i |
+| methods.cs:187:21:187:21 | M | methods.cs:187:25:187:26 | p1 |
+| methods.cs:187:21:187:21 | M | methods.cs:187:25:187:26 | p1 |
+| methods.cs:187:21:187:21 | M | methods.cs:187:25:187:26 | p1 |
+| methods.cs:187:21:187:21 | M | methods.cs:187:33:187:34 | p2 |
+| methods.cs:187:21:187:21 | M | methods.cs:187:33:187:34 | p2 |
+| methods.cs:187:21:187:21 | M | methods.cs:187:33:187:34 | p2 |
+| methods.cs:187:21:187:21 | M | methods.cs:188:27:188:28 | p1 |
+| methods.cs:187:21:187:21 | M | methods.cs:188:35:188:36 | p2 |
+| methods.cs:188:21:188:21 | M | methods.cs:187:25:187:26 | p1 |
+| methods.cs:188:21:188:21 | M | methods.cs:187:33:187:34 | p2 |
+| methods.cs:188:21:188:21 | M | methods.cs:188:27:188:28 | p1 |
+| methods.cs:188:21:188:21 | M | methods.cs:188:27:188:28 | p1 |
+| methods.cs:188:21:188:21 | M | methods.cs:188:27:188:28 | p1 |
+| methods.cs:188:21:188:21 | M | methods.cs:188:35:188:36 | p2 |
+| methods.cs:188:21:188:21 | M | methods.cs:188:35:188:36 | p2 |
+| methods.cs:188:21:188:21 | M | methods.cs:188:35:188:36 | p2 |
diff --git a/csharp/ql/test/library-tests/methods/Parameters9.expected b/csharp/ql/test/library-tests/methods/Parameters9.expected
index 2af714b9abc..ee0a6ef0e53 100644
--- a/csharp/ql/test/library-tests/methods/Parameters9.expected
+++ b/csharp/ql/test/library-tests/methods/Parameters9.expected
@@ -1,7 +1,7 @@
-| methods.cs:145:40:145:40 | c | methods.cs:145:44:145:44 | 1 | Method2(int, int, int, int, string) |
-| methods.cs:145:51:145:51 | d | methods.cs:145:55:145:55 | 2 | Method2(int, int, int, int, string) |
-| methods.cs:145:65:145:65 | e | methods.cs:145:69:145:77 | ... + ... | Method2(int, int, int, int, string) |
-| methods.cs:168:51:168:55 | right | methods.cs:168:59:168:59 | 0 | Plus(int, int) |
-| methods.cs:173:133:173:133 | i | methods.cs:173:137:173:137 | 1 | SkipTwo(IEnumerable, int) |
-| methods.cs:173:133:173:133 | i | methods.cs:173:137:173:137 | 1 | SkipTwo(IEnumerable, int) |
-| methods.cs:178:137:178:137 | i | methods.cs:178:141:178:141 | 1 | SkipTwoInt(IEnumerable, int) |
+| methods.cs:146:40:146:40 | c | methods.cs:146:44:146:44 | 1 | Method2(int, int, int, int, string) |
+| methods.cs:146:51:146:51 | d | methods.cs:146:55:146:55 | 2 | Method2(int, int, int, int, string) |
+| methods.cs:146:65:146:65 | e | methods.cs:146:69:146:77 | ... + ... | Method2(int, int, int, int, string) |
+| methods.cs:169:51:169:55 | right | methods.cs:169:59:169:59 | 0 | Plus(int, int) |
+| methods.cs:174:133:174:133 | i | methods.cs:174:137:174:137 | 1 | SkipTwo(IEnumerable, int) |
+| methods.cs:174:133:174:133 | i | methods.cs:174:137:174:137 | 1 | SkipTwo(IEnumerable, int) |
+| methods.cs:179:137:179:137 | i | methods.cs:179:141:179:141 | 1 | SkipTwoInt(IEnumerable, int) |
diff --git a/csharp/ql/test/library-tests/methods/PrintAst.expected b/csharp/ql/test/library-tests/methods/PrintAst.expected
index a41e0362094..a2220c63ea0 100644
--- a/csharp/ql/test/library-tests/methods/PrintAst.expected
+++ b/csharp/ql/test/library-tests/methods/PrintAst.expected
@@ -1,53 +1,44 @@
methods.cs:
-# 3| [NamespaceDeclaration] namespace ... { ... }
-# 6| 1: [Class] TestRef
-# 9| 5: [Method] Swap
-# 9| -1: [TypeMention] Void
+# 4| [NamespaceDeclaration] namespace ... { ... }
+# 7| 1: [Class] TestRef
+# 10| 5: [Method] Swap
+# 10| -1: [TypeMention] Void
#-----| 2: (Parameters)
-# 9| 0: [Parameter] x
-# 9| -1: [TypeMention] int
-# 9| 1: [Parameter] y
-# 9| -1: [TypeMention] int
-# 10| 4: [BlockStmt] {...}
-# 11| 0: [LocalVariableDeclStmt] ... ...;
-# 11| 0: [LocalVariableDeclAndInitExpr] Int32 temp = ...
-# 11| -1: [TypeMention] int
-# 11| 0: [LocalVariableAccess] access to local variable temp
-# 11| 1: [ParameterAccess] access to parameter x
-# 12| 1: [ExprStmt] ...;
-# 12| 0: [AssignExpr] ... = ...
-# 12| 0: [ParameterAccess] access to parameter x
-# 12| 1: [ParameterAccess] access to parameter y
-# 13| 2: [ExprStmt] ...;
+# 10| 0: [Parameter] x
+# 10| -1: [TypeMention] int
+# 10| 1: [Parameter] y
+# 10| -1: [TypeMention] int
+# 11| 4: [BlockStmt] {...}
+# 12| 0: [LocalVariableDeclStmt] ... ...;
+# 12| 0: [LocalVariableDeclAndInitExpr] Int32 temp = ...
+# 12| -1: [TypeMention] int
+# 12| 0: [LocalVariableAccess] access to local variable temp
+# 12| 1: [ParameterAccess] access to parameter x
+# 13| 1: [ExprStmt] ...;
# 13| 0: [AssignExpr] ... = ...
-# 13| 0: [ParameterAccess] access to parameter y
-# 13| 1: [LocalVariableAccess] access to local variable temp
-# 16| 6: [Method] Main
-# 16| -1: [TypeMention] Void
-# 17| 4: [BlockStmt] {...}
-# 18| 0: [LocalVariableDeclStmt] ... ...;
-# 18| 0: [LocalVariableDeclAndInitExpr] Int32 i = ...
-# 18| -1: [TypeMention] int
-# 18| 0: [LocalVariableAccess] access to local variable i
-# 18| 1: [IntLiteral] 1
-# 18| 1: [LocalVariableDeclAndInitExpr] Int32 j = ...
-# 18| -1: [TypeMention] int
-# 18| 0: [LocalVariableAccess] access to local variable j
-# 18| 1: [IntLiteral] 2
-# 19| 1: [ExprStmt] ...;
-# 19| 0: [MethodCall] call to method Swap
+# 13| 0: [ParameterAccess] access to parameter x
+# 13| 1: [ParameterAccess] access to parameter y
+# 14| 2: [ExprStmt] ...;
+# 14| 0: [AssignExpr] ... = ...
+# 14| 0: [ParameterAccess] access to parameter y
+# 14| 1: [LocalVariableAccess] access to local variable temp
+# 17| 6: [Method] Main
+# 17| -1: [TypeMention] Void
+# 18| 4: [BlockStmt] {...}
+# 19| 0: [LocalVariableDeclStmt] ... ...;
+# 19| 0: [LocalVariableDeclAndInitExpr] Int32 i = ...
+# 19| -1: [TypeMention] int
# 19| 0: [LocalVariableAccess] access to local variable i
-# 19| 1: [LocalVariableAccess] access to local variable j
-# 20| 2: [ExprStmt] ...;
-# 20| 0: [MethodCall] call to method WriteLine
-# 20| -1: [TypeAccess] access to type Console
-# 20| 0: [TypeMention] Console
-# 20| 0: [StringLiteral] "{0} {1}"
-# 20| 1: [CastExpr] (...) ...
-# 20| 1: [LocalVariableAccess] access to local variable i
-# 20| 2: [CastExpr] (...) ...
-# 20| 1: [LocalVariableAccess] access to local variable j
-# 21| 3: [ExprStmt] ...;
+# 19| 1: [IntLiteral] 1
+# 19| 1: [LocalVariableDeclAndInitExpr] Int32 j = ...
+# 19| -1: [TypeMention] int
+# 19| 0: [LocalVariableAccess] access to local variable j
+# 19| 1: [IntLiteral] 2
+# 20| 1: [ExprStmt] ...;
+# 20| 0: [MethodCall] call to method Swap
+# 20| 0: [LocalVariableAccess] access to local variable i
+# 20| 1: [LocalVariableAccess] access to local variable j
+# 21| 2: [ExprStmt] ...;
# 21| 0: [MethodCall] call to method WriteLine
# 21| -1: [TypeAccess] access to type Console
# 21| 0: [TypeMention] Console
@@ -56,65 +47,65 @@ methods.cs:
# 21| 1: [LocalVariableAccess] access to local variable i
# 21| 2: [CastExpr] (...) ...
# 21| 1: [LocalVariableAccess] access to local variable j
-# 25| 2: [Class] TestOut
-# 28| 5: [Method] Divide
-# 28| -1: [TypeMention] Void
+# 22| 3: [ExprStmt] ...;
+# 22| 0: [MethodCall] call to method WriteLine
+# 22| -1: [TypeAccess] access to type Console
+# 22| 0: [TypeMention] Console
+# 22| 0: [StringLiteral] "{0} {1}"
+# 22| 1: [CastExpr] (...) ...
+# 22| 1: [LocalVariableAccess] access to local variable i
+# 22| 2: [CastExpr] (...) ...
+# 22| 1: [LocalVariableAccess] access to local variable j
+# 26| 2: [Class] TestOut
+# 29| 5: [Method] Divide
+# 29| -1: [TypeMention] Void
#-----| 2: (Parameters)
-# 28| 0: [Parameter] x
-# 28| -1: [TypeMention] int
-# 28| 1: [Parameter] y
-# 28| -1: [TypeMention] int
-# 28| 2: [Parameter] result
-# 28| -1: [TypeMention] int
-# 28| 3: [Parameter] remainder
-# 28| -1: [TypeMention] int
-# 29| 4: [BlockStmt] {...}
-# 30| 0: [ExprStmt] ...;
-# 30| 0: [AssignExpr] ... = ...
-# 30| 0: [ParameterAccess] access to parameter result
-# 30| 1: [DivExpr] ... / ...
-# 30| 0: [ParameterAccess] access to parameter x
-# 30| 1: [ParameterAccess] access to parameter y
-# 31| 1: [ExprStmt] ...;
+# 29| 0: [Parameter] x
+# 29| -1: [TypeMention] int
+# 29| 1: [Parameter] y
+# 29| -1: [TypeMention] int
+# 29| 2: [Parameter] result
+# 29| -1: [TypeMention] int
+# 29| 3: [Parameter] remainder
+# 29| -1: [TypeMention] int
+# 30| 4: [BlockStmt] {...}
+# 31| 0: [ExprStmt] ...;
# 31| 0: [AssignExpr] ... = ...
-# 31| 0: [ParameterAccess] access to parameter remainder
-# 31| 1: [RemExpr] ... % ...
+# 31| 0: [ParameterAccess] access to parameter result
+# 31| 1: [DivExpr] ... / ...
# 31| 0: [ParameterAccess] access to parameter x
# 31| 1: [ParameterAccess] access to parameter y
-# 34| 6: [Method] Main
-# 34| -1: [TypeMention] Void
-# 35| 4: [BlockStmt] {...}
-# 36| 0: [LocalVariableDeclStmt] ... ...;
-# 36| 0: [LocalVariableDeclExpr] Int32 res
-# 36| 0: [TypeMention] int
-# 36| 1: [LocalVariableDeclExpr] Int32 rem
-# 36| 0: [TypeMention] int
-# 37| 1: [ExprStmt] ...;
-# 37| 0: [MethodCall] call to method Divide
-# 37| 0: [IntLiteral] 10
-# 37| 1: [IntLiteral] 3
-# 37| 2: [LocalVariableAccess] access to local variable res
-# 37| 3: [LocalVariableAccess] access to local variable rem
-# 38| 2: [ExprStmt] ...;
-# 38| 0: [MethodCall] call to method WriteLine
-# 38| -1: [TypeAccess] access to type Console
-# 38| 0: [TypeMention] Console
-# 38| 0: [StringLiteral] "{0} {1}"
-# 38| 1: [CastExpr] (...) ...
-# 38| 1: [LocalVariableAccess] access to local variable res
-# 38| 2: [CastExpr] (...) ...
-# 38| 1: [LocalVariableAccess] access to local variable rem
-# 42| 3: [Class] Console
-# 45| 5: [Method] Write
-# 45| -1: [TypeMention] Void
-#-----| 2: (Parameters)
-# 45| 0: [Parameter] fmt
-# 45| -1: [TypeMention] string
-# 45| 1: [Parameter] args
-# 45| -1: [TypeMention] Object[]
-# 45| 1: [TypeMention] object
-# 45| 4: [BlockStmt] {...}
-# 46| 6: [Method] WriteLine
+# 32| 1: [ExprStmt] ...;
+# 32| 0: [AssignExpr] ... = ...
+# 32| 0: [ParameterAccess] access to parameter remainder
+# 32| 1: [RemExpr] ... % ...
+# 32| 0: [ParameterAccess] access to parameter x
+# 32| 1: [ParameterAccess] access to parameter y
+# 35| 6: [Method] Main
+# 35| -1: [TypeMention] Void
+# 36| 4: [BlockStmt] {...}
+# 37| 0: [LocalVariableDeclStmt] ... ...;
+# 37| 0: [LocalVariableDeclExpr] Int32 res
+# 37| 0: [TypeMention] int
+# 37| 1: [LocalVariableDeclExpr] Int32 rem
+# 37| 0: [TypeMention] int
+# 38| 1: [ExprStmt] ...;
+# 38| 0: [MethodCall] call to method Divide
+# 38| 0: [IntLiteral] 10
+# 38| 1: [IntLiteral] 3
+# 38| 2: [LocalVariableAccess] access to local variable res
+# 38| 3: [LocalVariableAccess] access to local variable rem
+# 39| 2: [ExprStmt] ...;
+# 39| 0: [MethodCall] call to method WriteLine
+# 39| -1: [TypeAccess] access to type Console
+# 39| 0: [TypeMention] Console
+# 39| 0: [StringLiteral] "{0} {1}"
+# 39| 1: [CastExpr] (...) ...
+# 39| 1: [LocalVariableAccess] access to local variable res
+# 39| 2: [CastExpr] (...) ...
+# 39| 1: [LocalVariableAccess] access to local variable rem
+# 43| 3: [Class] Console
+# 46| 5: [Method] Write
# 46| -1: [TypeMention] Void
#-----| 2: (Parameters)
# 46| 0: [Parameter] fmt
@@ -123,354 +114,452 @@ methods.cs:
# 46| -1: [TypeMention] Object[]
# 46| 1: [TypeMention] object
# 46| 4: [BlockStmt] {...}
-# 49| 4: [Class] TestOverloading
-# 52| 5: [Method] F
-# 52| -1: [TypeMention] Void
-# 53| 4: [BlockStmt] {...}
-# 54| 0: [ExprStmt] ...;
-# 54| 0: [MethodCall] call to method WriteLine
-# 54| -1: [TypeAccess] access to type Console
-# 54| 0: [TypeMention] Console
-# 54| 0: [StringLiteral] "F()"
-# 57| 6: [Method] F
-# 57| -1: [TypeMention] Void
+# 47| 6: [Method] WriteLine
+# 47| -1: [TypeMention] Void
#-----| 2: (Parameters)
-# 57| 0: [Parameter] x
-# 57| -1: [TypeMention] object
-# 58| 4: [BlockStmt] {...}
-# 59| 0: [ExprStmt] ...;
-# 59| 0: [MethodCall] call to method WriteLine
-# 59| -1: [TypeAccess] access to type Console
-# 59| 0: [TypeMention] Console
-# 59| 0: [StringLiteral] "F(object)"
-# 62| 7: [Method] F
-# 62| -1: [TypeMention] Void
+# 47| 0: [Parameter] fmt
+# 47| -1: [TypeMention] string
+# 47| 1: [Parameter] args
+# 47| -1: [TypeMention] Object[]
+# 47| 1: [TypeMention] object
+# 47| 4: [BlockStmt] {...}
+# 50| 4: [Class] TestOverloading
+# 53| 5: [Method] F
+# 53| -1: [TypeMention] Void
+# 54| 4: [BlockStmt] {...}
+# 55| 0: [ExprStmt] ...;
+# 55| 0: [MethodCall] call to method WriteLine
+# 55| -1: [TypeAccess] access to type Console
+# 55| 0: [TypeMention] Console
+# 55| 0: [StringLiteral] "F()"
+# 58| 6: [Method] F
+# 58| -1: [TypeMention] Void
#-----| 2: (Parameters)
-# 62| 0: [Parameter] x
-# 62| -1: [TypeMention] int
-# 63| 4: [BlockStmt] {...}
-# 64| 0: [ExprStmt] ...;
-# 64| 0: [MethodCall] call to method WriteLine
-# 64| -1: [TypeAccess] access to type Console
-# 64| 0: [TypeMention] Console
-# 64| 0: [StringLiteral] "F(int)"
-# 67| 8: [Method] F
-# 67| -1: [TypeMention] Void
+# 58| 0: [Parameter] x
+# 58| -1: [TypeMention] object
+# 59| 4: [BlockStmt] {...}
+# 60| 0: [ExprStmt] ...;
+# 60| 0: [MethodCall] call to method WriteLine
+# 60| -1: [TypeAccess] access to type Console
+# 60| 0: [TypeMention] Console
+# 60| 0: [StringLiteral] "F(object)"
+# 63| 7: [Method] F
+# 63| -1: [TypeMention] Void
#-----| 2: (Parameters)
-# 67| 0: [Parameter] x
-# 67| -1: [TypeMention] double
-# 68| 4: [BlockStmt] {...}
-# 69| 0: [ExprStmt] ...;
-# 69| 0: [MethodCall] call to method WriteLine
-# 69| -1: [TypeAccess] access to type Console
-# 69| 0: [TypeMention] Console
-# 69| 0: [StringLiteral] "F(double)"
-# 72| 9: [Method] F
-# 72| -1: [TypeMention] Void
+# 63| 0: [Parameter] x
+# 63| -1: [TypeMention] int
+# 64| 4: [BlockStmt] {...}
+# 65| 0: [ExprStmt] ...;
+# 65| 0: [MethodCall] call to method WriteLine
+# 65| -1: [TypeAccess] access to type Console
+# 65| 0: [TypeMention] Console
+# 65| 0: [StringLiteral] "F(int)"
+# 68| 8: [Method] F
+# 68| -1: [TypeMention] Void
+#-----| 2: (Parameters)
+# 68| 0: [Parameter] x
+# 68| -1: [TypeMention] double
+# 69| 4: [BlockStmt] {...}
+# 70| 0: [ExprStmt] ...;
+# 70| 0: [MethodCall] call to method WriteLine
+# 70| -1: [TypeAccess] access to type Console
+# 70| 0: [TypeMention] Console
+# 70| 0: [StringLiteral] "F(double)"
+# 73| 9: [Method] F
+# 73| -1: [TypeMention] Void
#-----| 1: (Type parameters)
-# 72| 0: [TypeParameter] T
+# 73| 0: [TypeParameter] T
#-----| 2: (Parameters)
-# 72| 0: [Parameter] x
-# 72| -1: [TypeMention] T
-# 73| 4: [BlockStmt] {...}
-# 74| 0: [ExprStmt] ...;
-# 74| 0: [MethodCall] call to method WriteLine
-# 74| -1: [TypeAccess] access to type Console
-# 74| 0: [TypeMention] Console
-# 74| 0: [StringLiteral] "F(T)"
-# 77| 12: [Method] F
-# 77| -1: [TypeMention] Void
+# 73| 0: [Parameter] x
+# 73| -1: [TypeMention] T
+# 74| 4: [BlockStmt] {...}
+# 75| 0: [ExprStmt] ...;
+# 75| 0: [MethodCall] call to method WriteLine
+# 75| -1: [TypeAccess] access to type Console
+# 75| 0: [TypeMention] Console
+# 75| 0: [StringLiteral] "F(T)"
+# 78| 12: [Method] F
+# 78| -1: [TypeMention] Void
#-----| 2: (Parameters)
-# 77| 0: [Parameter] x
-# 77| -1: [TypeMention] double
-# 77| 1: [Parameter] y
-# 77| -1: [TypeMention] double
-# 78| 4: [BlockStmt] {...}
-# 79| 0: [ExprStmt] ...;
-# 79| 0: [MethodCall] call to method WriteLine
-# 79| -1: [TypeAccess] access to type Console
-# 79| 0: [TypeMention] Console
-# 79| 0: [StringLiteral] "F(double, double)"
-# 82| 13: [Method] Main
-# 82| -1: [TypeMention] Void
-# 83| 4: [BlockStmt] {...}
-# 84| 0: [ExprStmt] ...;
-# 84| 0: [MethodCall] call to method F
-# 85| 1: [ExprStmt] ...;
+# 78| 0: [Parameter] x
+# 78| -1: [TypeMention] double
+# 78| 1: [Parameter] y
+# 78| -1: [TypeMention] double
+# 79| 4: [BlockStmt] {...}
+# 80| 0: [ExprStmt] ...;
+# 80| 0: [MethodCall] call to method WriteLine
+# 80| -1: [TypeAccess] access to type Console
+# 80| 0: [TypeMention] Console
+# 80| 0: [StringLiteral] "F(double, double)"
+# 83| 13: [Method] Main
+# 83| -1: [TypeMention] Void
+# 84| 4: [BlockStmt] {...}
+# 85| 0: [ExprStmt] ...;
# 85| 0: [MethodCall] call to method F
-# 85| 0: [IntLiteral] 1
-# 86| 2: [ExprStmt] ...;
+# 86| 1: [ExprStmt] ...;
# 86| 0: [MethodCall] call to method F
-# 86| 0: [DoubleLiteral] 1
-# 87| 3: [ExprStmt] ...;
+# 86| 0: [IntLiteral] 1
+# 87| 2: [ExprStmt] ...;
# 87| 0: [MethodCall] call to method F
-# 87| 0: [StringLiteral] "abc"
-# 88| 4: [ExprStmt] ...;
+# 87| 0: [DoubleLiteral] 1
+# 88| 3: [ExprStmt] ...;
# 88| 0: [MethodCall] call to method F
-# 88| 0: [CastExpr] (...) ...
-# 88| 0: [TypeAccess] access to type Double
-# 88| 0: [TypeMention] double
-# 88| 1: [IntLiteral] 1
-# 89| 5: [ExprStmt] ...;
+# 88| 0: [StringLiteral] "abc"
+# 89| 4: [ExprStmt] ...;
# 89| 0: [MethodCall] call to method F
# 89| 0: [CastExpr] (...) ...
-# 89| 0: [TypeAccess] access to type Object
-# 89| 0: [TypeMention] object
+# 89| 0: [TypeAccess] access to type Double
+# 89| 0: [TypeMention] double
# 89| 1: [IntLiteral] 1
-# 90| 6: [ExprStmt] ...;
+# 90| 5: [ExprStmt] ...;
# 90| 0: [MethodCall] call to method F
-# 90| 0: [IntLiteral] 1
-# 91| 7: [ExprStmt] ...;
+# 90| 0: [CastExpr] (...) ...
+# 90| 0: [TypeAccess] access to type Object
+# 90| 0: [TypeMention] object
+# 90| 1: [IntLiteral] 1
+# 91| 6: [ExprStmt] ...;
# 91| 0: [MethodCall] call to method F
-# 91| 0: [CastExpr] (...) ...
-# 91| 1: [IntLiteral] 1
-# 91| 1: [CastExpr] (...) ...
-# 91| 1: [IntLiteral] 1
-# 96| 5: [Class] Extensions
-# 99| 4: [ExtensionMethod] ToInt32
-# 99| -1: [TypeMention] int
+# 91| 0: [IntLiteral] 1
+# 92| 7: [ExprStmt] ...;
+# 92| 0: [MethodCall] call to method F
+# 92| 0: [CastExpr] (...) ...
+# 92| 1: [IntLiteral] 1
+# 92| 1: [CastExpr] (...) ...
+# 92| 1: [IntLiteral] 1
+# 97| 5: [Class] Extensions
+# 100| 4: [ExtensionMethod] ToInt32
+# 100| -1: [TypeMention] int
#-----| 2: (Parameters)
-# 99| 0: [Parameter] s
-# 99| -1: [TypeMention] string
-# 100| 4: [BlockStmt] {...}
-# 101| 0: [ReturnStmt] return ...;
-# 101| 0: [MethodCall] call to method Parse
-# 101| -1: [TypeAccess] access to type Int32
-# 101| 0: [TypeMention] int
-# 101| 0: [ParameterAccess] access to parameter s
-# 104| 5: [ExtensionMethod] ToBool
-# 104| -1: [TypeMention] bool
+# 100| 0: [Parameter] s
+# 100| -1: [TypeMention] string
+# 101| 4: [BlockStmt] {...}
+# 102| 0: [ReturnStmt] return ...;
+# 102| 0: [MethodCall] call to method Parse
+# 102| -1: [TypeAccess] access to type Int32
+# 102| 0: [TypeMention] int
+# 102| 0: [ParameterAccess] access to parameter s
+# 105| 5: [ExtensionMethod] ToBool
+# 105| -1: [TypeMention] bool
#-----| 2: (Parameters)
-# 104| 0: [Parameter] s
-# 104| -1: [TypeMention] string
-# 104| 1: [Parameter] f
-# 104| -1: [TypeMention] Func
-# 104| 1: [TypeMention] string
-# 104| 2: [TypeMention] bool
-# 105| 4: [BlockStmt] {...}
-# 106| 0: [ReturnStmt] return ...;
-# 106| 0: [DelegateCall] delegate call
-# 106| -1: [ParameterAccess] access to parameter f
-# 106| 0: [ParameterAccess] access to parameter s
-# 109| 6: [ExtensionMethod] Slice
-# 109| -1: [TypeMention] T[]
-# 109| 1: [TypeMention] T
+# 105| 0: [Parameter] s
+# 105| -1: [TypeMention] string
+# 105| 1: [Parameter] f
+# 105| -1: [TypeMention] Func
+# 105| 1: [TypeMention] string
+# 105| 2: [TypeMention] bool
+# 106| 4: [BlockStmt] {...}
+# 107| 0: [ReturnStmt] return ...;
+# 107| 0: [DelegateCall] delegate call
+# 107| -1: [ParameterAccess] access to parameter f
+# 107| 0: [ParameterAccess] access to parameter s
+# 110| 6: [ExtensionMethod] Slice
+# 110| -1: [TypeMention] T[]
+# 110| 1: [TypeMention] T
#-----| 1: (Type parameters)
-# 109| 0: [TypeParameter] T
+# 110| 0: [TypeParameter] T
#-----| 2: (Parameters)
-# 109| 0: [Parameter] source
-# 109| -1: [TypeMention] T[]
-# 109| 1: [TypeMention] T
-# 109| 1: [Parameter] index
-# 109| -1: [TypeMention] int
-# 109| 2: [Parameter] count
-# 109| -1: [TypeMention] int
-# 110| 4: [BlockStmt] {...}
-# 111| 0: [IfStmt] if (...) ...
-# 111| 0: [LogicalOrExpr] ... || ...
-# 111| 0: [LogicalOrExpr] ... || ...
-# 111| 0: [LTExpr] ... < ...
-# 111| 0: [ParameterAccess] access to parameter index
-# 111| 1: [IntLiteral] 0
-# 111| 1: [LTExpr] ... < ...
-# 111| 0: [ParameterAccess] access to parameter count
-# 111| 1: [IntLiteral] 0
-# 111| 1: [LTExpr] ... < ...
-# 111| 0: [SubExpr] ... - ...
-# 111| 0: [PropertyCall] access to property Length
-# 111| -1: [ParameterAccess] access to parameter source
-# 111| 1: [ParameterAccess] access to parameter index
-# 111| 1: [ParameterAccess] access to parameter count
-# 112| 1: [ThrowStmt] throw ...;
-# 112| 0: [ObjectCreation] object creation of type ArgumentException
-# 112| 0: [TypeMention] ArgumentException
-# 113| 1: [LocalVariableDeclStmt] ... ...;
-# 113| 0: [LocalVariableDeclAndInitExpr] T[] result = ...
-# 113| -1: [TypeMention] T[]
-# 113| 1: [TypeMention] T
-# 113| 0: [LocalVariableAccess] access to local variable result
-# 113| 1: [ArrayCreation] array creation of type T[]
-# 113| -1: [TypeMention] T[]
-# 113| 1: [TypeMention] T
-# 113| 0: [ParameterAccess] access to parameter count
-# 114| 2: [ExprStmt] ...;
-# 114| 0: [MethodCall] call to method Copy
-# 114| -1: [TypeAccess] access to type Array
-# 114| 0: [TypeMention] Array
-# 114| 0: [ParameterAccess] access to parameter source
-# 114| 1: [ParameterAccess] access to parameter index
-# 114| 2: [LocalVariableAccess] access to local variable result
-# 114| 3: [IntLiteral] 0
-# 114| 4: [ParameterAccess] access to parameter count
-# 115| 3: [ReturnStmt] return ...;
-# 115| 0: [LocalVariableAccess] access to local variable result
-# 118| 8: [Method] CallToInt32
-# 118| -1: [TypeMention] int
-# 118| 4: [MethodCall] call to method ToInt32
-# 118| 0: [StringLiteral] "0"
-# 121| 6: [Class] TestExtensions
-# 124| 4: [Method] Main
-# 124| -1: [TypeMention] Void
-# 125| 4: [BlockStmt] {...}
-# 126| 0: [LocalVariableDeclStmt] ... ...;
-# 126| 0: [LocalVariableDeclAndInitExpr] String[] strings = ...
-# 126| -1: [TypeMention] String[]
-# 126| 1: [TypeMention] string
-# 126| 0: [LocalVariableAccess] access to local variable strings
-# 126| 1: [ArrayCreation] array creation of type String[]
-# 126| -1: [ArrayInitializer] { ..., ... }
-# 126| 0: [StringLiteral] "1"
-# 126| 1: [StringLiteral] "22"
-# 126| 2: [StringLiteral] "333"
-# 126| 3: [StringLiteral] "4444"
-# 127| 1: [ForeachStmt] foreach (... ... in ...) ...
-# 127| 0: [LocalVariableDeclExpr] String s
-# 127| 0: [TypeMention] string
-# 127| 1: [MethodCall] call to method Slice
-# 127| -1: [LocalVariableAccess] access to local variable strings
-# 127| 0: [IntLiteral] 1
-# 127| 1: [IntLiteral] 2
-# 128| 2: [BlockStmt] {...}
-# 129| 0: [ExprStmt] ...;
-# 129| 0: [MethodCall] call to method WriteLine
-# 129| -1: [TypeAccess] access to type Console
-# 129| 0: [TypeMention] Console
-# 129| 0: [MethodCall] call to method ToInt32
-# 129| -1: [LocalVariableAccess] access to local variable s
-# 132| 2: [ExprStmt] ...;
-# 132| 0: [MethodCall] call to method ToInt32
-# 132| -1: [TypeAccess] access to type Extensions
-# 132| 0: [TypeMention] Extensions
-# 132| 0: [StringLiteral] ""
-# 134| 3: [ExprStmt] ...;
-# 134| 0: [MethodCall] call to method ToBool
-# 134| -1: [TypeAccess] access to type Extensions
-# 134| 0: [TypeMention] Extensions
-# 134| 0: [StringLiteral] "true"
-# 134| 1: [ImplicitDelegateCreation] delegate creation of type Func
-# 134| 0: [MethodAccess] access to method Parse
-# 134| -1: [TypeAccess] access to type Boolean
-# 134| 0: [TypeMention] bool
-# 139| 7: [Class] TestDefaultParameters
-# 141| 4: [Method] Method1
-# 141| -1: [TypeMention] Void
+# 110| 0: [Parameter] source
+# 110| -1: [TypeMention] T[]
+# 110| 1: [TypeMention] T
+# 110| 1: [Parameter] index
+# 110| -1: [TypeMention] int
+# 110| 2: [Parameter] count
+# 110| -1: [TypeMention] int
+# 111| 4: [BlockStmt] {...}
+# 112| 0: [IfStmt] if (...) ...
+# 112| 0: [LogicalOrExpr] ... || ...
+# 112| 0: [LogicalOrExpr] ... || ...
+# 112| 0: [LTExpr] ... < ...
+# 112| 0: [ParameterAccess] access to parameter index
+# 112| 1: [IntLiteral] 0
+# 112| 1: [LTExpr] ... < ...
+# 112| 0: [ParameterAccess] access to parameter count
+# 112| 1: [IntLiteral] 0
+# 112| 1: [LTExpr] ... < ...
+# 112| 0: [SubExpr] ... - ...
+# 112| 0: [PropertyCall] access to property Length
+# 112| -1: [ParameterAccess] access to parameter source
+# 112| 1: [ParameterAccess] access to parameter index
+# 112| 1: [ParameterAccess] access to parameter count
+# 113| 1: [ThrowStmt] throw ...;
+# 113| 0: [ObjectCreation] object creation of type ArgumentException
+# 113| 0: [TypeMention] ArgumentException
+# 114| 1: [LocalVariableDeclStmt] ... ...;
+# 114| 0: [LocalVariableDeclAndInitExpr] T[] result = ...
+# 114| -1: [TypeMention] T[]
+# 114| 1: [TypeMention] T
+# 114| 0: [LocalVariableAccess] access to local variable result
+# 114| 1: [ArrayCreation] array creation of type T[]
+# 114| -1: [TypeMention] T[]
+# 114| 1: [TypeMention] T
+# 114| 0: [ParameterAccess] access to parameter count
+# 115| 2: [ExprStmt] ...;
+# 115| 0: [MethodCall] call to method Copy
+# 115| -1: [TypeAccess] access to type Array
+# 115| 0: [TypeMention] Array
+# 115| 0: [ParameterAccess] access to parameter source
+# 115| 1: [ParameterAccess] access to parameter index
+# 115| 2: [LocalVariableAccess] access to local variable result
+# 115| 3: [IntLiteral] 0
+# 115| 4: [ParameterAccess] access to parameter count
+# 116| 3: [ReturnStmt] return ...;
+# 116| 0: [LocalVariableAccess] access to local variable result
+# 119| 8: [Method] CallToInt32
+# 119| -1: [TypeMention] int
+# 119| 4: [MethodCall] call to method ToInt32
+# 119| 0: [StringLiteral] "0"
+# 122| 6: [Class] TestExtensions
+# 125| 4: [Method] Main
+# 125| -1: [TypeMention] Void
+# 126| 4: [BlockStmt] {...}
+# 127| 0: [LocalVariableDeclStmt] ... ...;
+# 127| 0: [LocalVariableDeclAndInitExpr] String[] strings = ...
+# 127| -1: [TypeMention] String[]
+# 127| 1: [TypeMention] string
+# 127| 0: [LocalVariableAccess] access to local variable strings
+# 127| 1: [ArrayCreation] array creation of type String[]
+# 127| -1: [ArrayInitializer] { ..., ... }
+# 127| 0: [StringLiteral] "1"
+# 127| 1: [StringLiteral] "22"
+# 127| 2: [StringLiteral] "333"
+# 127| 3: [StringLiteral] "4444"
+# 128| 1: [ForeachStmt] foreach (... ... in ...) ...
+# 128| 0: [LocalVariableDeclExpr] String s
+# 128| 0: [TypeMention] string
+# 128| 1: [MethodCall] call to method Slice
+# 128| -1: [LocalVariableAccess] access to local variable strings
+# 128| 0: [IntLiteral] 1
+# 128| 1: [IntLiteral] 2
+# 129| 2: [BlockStmt] {...}
+# 130| 0: [ExprStmt] ...;
+# 130| 0: [MethodCall] call to method WriteLine
+# 130| -1: [TypeAccess] access to type Console
+# 130| 0: [TypeMention] Console
+# 130| 0: [MethodCall] call to method ToInt32
+# 130| -1: [LocalVariableAccess] access to local variable s
+# 133| 2: [ExprStmt] ...;
+# 133| 0: [MethodCall] call to method ToInt32
+# 133| -1: [TypeAccess] access to type Extensions
+# 133| 0: [TypeMention] Extensions
+# 133| 0: [StringLiteral] ""
+# 135| 3: [ExprStmt] ...;
+# 135| 0: [MethodCall] call to method ToBool
+# 135| -1: [TypeAccess] access to type Extensions
+# 135| 0: [TypeMention] Extensions
+# 135| 0: [StringLiteral] "true"
+# 135| 1: [ImplicitDelegateCreation] delegate creation of type Func
+# 135| 0: [MethodAccess] access to method Parse
+# 135| -1: [TypeAccess] access to type Boolean
+# 135| 0: [TypeMention] bool
+# 140| 7: [Class] TestDefaultParameters
+# 142| 4: [Method] Method1
+# 142| -1: [TypeMention] Void
#-----| 2: (Parameters)
-# 141| 0: [Parameter] x
-# 141| -1: [TypeMention] int
-# 141| 1: [Parameter] y
-# 141| -1: [TypeMention] int
-# 142| 4: [BlockStmt] {...}
-# 145| 5: [Method] Method2
-# 145| -1: [TypeMention] Void
+# 142| 0: [Parameter] x
+# 142| -1: [TypeMention] int
+# 142| 1: [Parameter] y
+# 142| -1: [TypeMention] int
+# 143| 4: [BlockStmt] {...}
+# 146| 5: [Method] Method2
+# 146| -1: [TypeMention] Void
#-----| 2: (Parameters)
-# 145| 0: [Parameter] a
-# 145| -1: [TypeMention] int
-# 145| 1: [Parameter] b
-# 145| -1: [TypeMention] int
-# 145| 2: [Parameter] c
-# 145| -1: [TypeMention] int
-# 145| 1: [IntLiteral] 1
-# 145| 3: [Parameter] d
-# 145| -1: [TypeMention] int
-# 145| 1: [IntLiteral] 2
-# 145| 4: [Parameter] e
-# 145| -1: [TypeMention] string
-# 145| 1: [AddExpr] ... + ...
-# 145| 0: [StringLiteral] "a"
-# 145| 1: [StringLiteral] "b"
-# 146| 4: [BlockStmt] {...}
-# 149| 6: [InstanceConstructor] TestDefaultParameters
+# 146| 0: [Parameter] a
+# 146| -1: [TypeMention] int
+# 146| 1: [Parameter] b
+# 146| -1: [TypeMention] int
+# 146| 2: [Parameter] c
+# 146| -1: [TypeMention] int
+# 146| 1: [IntLiteral] 1
+# 146| 3: [Parameter] d
+# 146| -1: [TypeMention] int
+# 146| 1: [IntLiteral] 2
+# 146| 4: [Parameter] e
+# 146| -1: [TypeMention] string
+# 146| 1: [AddExpr] ... + ...
+# 146| 0: [StringLiteral] "a"
+# 146| 1: [StringLiteral] "b"
+# 147| 4: [BlockStmt] {...}
+# 150| 6: [InstanceConstructor] TestDefaultParameters
#-----| 2: (Parameters)
-# 149| 0: [Parameter] x
-# 149| -1: [TypeMention] int
-# 150| 4: [BlockStmt] {...}
-# 153| 7: [InstanceConstructor] TestDefaultParameters
+# 150| 0: [Parameter] x
+# 150| -1: [TypeMention] int
+# 151| 4: [BlockStmt] {...}
+# 154| 7: [InstanceConstructor] TestDefaultParameters
#-----| 2: (Parameters)
-# 153| 0: [Parameter] x
-# 153| -1: [TypeMention] string
-# 153| 1: [StringLiteral] "abc"
-# 153| 1: [Parameter] y
-# 153| -1: [TypeMention] double
-# 153| 1: [ObjectCreation] object creation of type Double
-# 153| 0: [TypeMention] double
-# 154| 4: [BlockStmt] {...}
-# 157| 8: [DelegateType] Del
+# 154| 0: [Parameter] x
+# 154| -1: [TypeMention] string
+# 154| 1: [StringLiteral] "abc"
+# 154| 1: [Parameter] y
+# 154| -1: [TypeMention] double
+# 154| 1: [ObjectCreation] object creation of type Double
+# 154| 0: [TypeMention] double
+# 155| 4: [BlockStmt] {...}
+# 158| 8: [DelegateType] Del
#-----| 2: (Parameters)
-# 157| 0: [Parameter] a
-# 157| -1: [TypeMention] string
-# 157| 1: [Parameter] b
-# 157| -1: [TypeMention] int
-# 157| 1: [IntLiteral] 12
-# 157| 2: [Parameter] c
-# 157| -1: [TypeMention] double
-# 157| 1: [ObjectCreation] object creation of type Double
-# 157| 0: [TypeMention] double
-# 159| 9: [Indexer] Item
-# 159| -1: [TypeMention] int
+# 158| 0: [Parameter] a
+# 158| -1: [TypeMention] string
+# 158| 1: [Parameter] b
+# 158| -1: [TypeMention] int
+# 158| 1: [IntLiteral] 12
+# 158| 2: [Parameter] c
+# 158| -1: [TypeMention] double
+# 158| 1: [ObjectCreation] object creation of type Double
+# 158| 0: [TypeMention] double
+# 160| 9: [Indexer] Item
+# 160| -1: [TypeMention] int
#-----| 1: (Parameters)
-# 159| 0: [Parameter] x
-# 159| -1: [TypeMention] int
-# 159| 1: [Parameter] y
-# 159| -1: [TypeMention] int
-# 159| 1: [IntLiteral] 0
-# 161| 3: [Getter] get_Item
+# 160| 0: [Parameter] x
+# 160| -1: [TypeMention] int
+# 160| 1: [Parameter] y
+# 160| -1: [TypeMention] int
+# 160| 1: [IntLiteral] 0
+# 162| 3: [Getter] get_Item
#-----| 2: (Parameters)
-# 159| 0: [Parameter] x
-# 159| 1: [Parameter] y
-# 159| 1: [IntLiteral] 0
-# 161| 4: [BlockStmt] {...}
-# 161| 0: [ReturnStmt] return ...;
-# 161| 0: [AddExpr] ... + ...
-# 161| 0: [ParameterAccess] access to parameter x
-# 161| 1: [ParameterAccess] access to parameter y
-# 162| 4: [Setter] set_Item
-#-----| 2: (Parameters)
-# 159| 0: [Parameter] x
-# 159| 1: [Parameter] y
-# 159| 1: [IntLiteral] 0
-# 162| 2: [Parameter] value
+# 160| 0: [Parameter] x
+# 160| 1: [Parameter] y
+# 160| 1: [IntLiteral] 0
# 162| 4: [BlockStmt] {...}
-# 166| 8: [Class] TestDefaultExtensionParameters
-# 168| 4: [ExtensionMethod] Plus
-# 168| -1: [TypeMention] int
+# 162| 0: [ReturnStmt] return ...;
+# 162| 0: [AddExpr] ... + ...
+# 162| 0: [ParameterAccess] access to parameter x
+# 162| 1: [ParameterAccess] access to parameter y
+# 163| 4: [Setter] set_Item
+#-----| 2: (Parameters)
+# 160| 0: [Parameter] x
+# 160| 1: [Parameter] y
+# 160| 1: [IntLiteral] 0
+# 163| 2: [Parameter] value
+# 163| 4: [BlockStmt] {...}
+# 167| 8: [Class] TestDefaultExtensionParameters
+# 169| 4: [ExtensionMethod] Plus
+# 169| -1: [TypeMention] int
#-----| 2: (Parameters)
-# 168| 0: [Parameter] left
-# 168| -1: [TypeMention] int
-# 168| 1: [Parameter] right
-# 168| -1: [TypeMention] int
-# 168| 1: [IntLiteral] 0
-# 169| 4: [BlockStmt] {...}
-# 170| 0: [ReturnStmt] return ...;
-# 170| 0: [AddExpr] ... + ...
-# 170| 0: [ParameterAccess] access to parameter left
-# 170| 1: [ParameterAccess] access to parameter right
-# 173| 5: [ExtensionMethod] SkipTwo
-# 173| -1: [TypeMention] IEnumerable
-# 173| 1: [TypeMention] T
+# 169| 0: [Parameter] left
+# 169| -1: [TypeMention] int
+# 169| 1: [Parameter] right
+# 169| -1: [TypeMention] int
+# 169| 1: [IntLiteral] 0
+# 170| 4: [BlockStmt] {...}
+# 171| 0: [ReturnStmt] return ...;
+# 171| 0: [AddExpr] ... + ...
+# 171| 0: [ParameterAccess] access to parameter left
+# 171| 1: [ParameterAccess] access to parameter right
+# 174| 5: [ExtensionMethod] SkipTwo
+# 174| -1: [TypeMention] IEnumerable
+# 174| 1: [TypeMention] T
#-----| 1: (Type parameters)
-# 173| 0: [TypeParameter] T
+# 174| 0: [TypeParameter] T
#-----| 2: (Parameters)
-# 173| 0: [Parameter] list
-# 173| -1: [TypeMention] IEnumerable
-# 173| 1: [TypeMention] T
-# 173| 1: [Parameter] i
-# 173| -1: [TypeMention] int
-# 173| 1: [IntLiteral] 1
-# 174| 4: [BlockStmt] {...}
-# 175| 0: [ReturnStmt] return ...;
-# 175| 0: [ParameterAccess] access to parameter list
-# 178| 7: [ExtensionMethod] SkipTwoInt
-# 178| -1: [TypeMention] IEnumerable
-# 178| 1: [TypeMention] int
+# 174| 0: [Parameter] list
+# 174| -1: [TypeMention] IEnumerable
+# 174| 1: [TypeMention] T
+# 174| 1: [Parameter] i
+# 174| -1: [TypeMention] int
+# 174| 1: [IntLiteral] 1
+# 175| 4: [BlockStmt] {...}
+# 176| 0: [ReturnStmt] return ...;
+# 176| 0: [ParameterAccess] access to parameter list
+# 179| 7: [ExtensionMethod] SkipTwoInt
+# 179| -1: [TypeMention] IEnumerable
+# 179| 1: [TypeMention] int
#-----| 2: (Parameters)
-# 178| 0: [Parameter] list
-# 178| -1: [TypeMention] IEnumerable
-# 178| 1: [TypeMention] int
-# 178| 1: [Parameter] i
-# 178| -1: [TypeMention] int
-# 178| 1: [IntLiteral] 1
-# 179| 4: [BlockStmt] {...}
-# 180| 0: [ReturnStmt] return ...;
-# 180| 0: [MethodCall] call to method SkipTwo
-# 180| -1: [ParameterAccess] access to parameter list
-# 180| 0: [ParameterAccess] access to parameter i
+# 179| 0: [Parameter] list
+# 179| -1: [TypeMention] IEnumerable
+# 179| 1: [TypeMention] int
+# 179| 1: [Parameter] i
+# 179| -1: [TypeMention] int
+# 179| 1: [IntLiteral] 1
+# 180| 4: [BlockStmt] {...}
+# 181| 0: [ReturnStmt] return ...;
+# 181| 0: [MethodCall] call to method SkipTwo
+# 181| -1: [ParameterAccess] access to parameter list
+# 181| 0: [ParameterAccess] access to parameter i
+# 185| 9: [Class] TestCollidingMethods<>
+#-----| 1: (Type parameters)
+# 185| 0: [TypeParameter] T
+# 187| 5: [Method] M
+# 187| -1: [TypeMention] Void
+#-----| 2: (Parameters)
+# 187| 0: [Parameter] p1
+# 187| -1: [TypeMention] T
+# 187| 1: [Parameter] p2
+# 187| -1: [TypeMention] int
+# 187| 4: [BlockStmt] {...}
+# 188| 6: [Method] M
+# 188| -1: [TypeMention] Void
+#-----| 2: (Parameters)
+# 188| 0: [Parameter] p1
+# 188| -1: [TypeMention] int
+# 188| 1: [Parameter] p2
+# 188| -1: [TypeMention] int
+# 188| 4: [BlockStmt] {...}
+# 190| 7: [Method] Calls
+# 190| -1: [TypeMention] Void
+# 191| 4: [BlockStmt] {...}
+# 192| 0: [LocalVariableDeclStmt] ... ...;
+# 192| 0: [LocalVariableDeclAndInitExpr] TestCollidingMethods x = ...
+# 192| -1: [TypeMention] TestCollidingMethods
+# 192| 0: [LocalVariableAccess] access to local variable x
+# 192| 1: [ObjectCreation] object creation of type TestCollidingMethods
+# 192| 0: [TypeMention] TestCollidingMethods
+# 192| 1: [TypeMention] int
+# 193| 1: [ExprStmt] ...;
+# 193| 0: [MethodCall] call to method M
+# 193| -1: [LocalVariableAccess] access to local variable x
+# 193| 0: [IntLiteral] 1
+# 193| 1: [IntLiteral] 1
+# 195| 2: [LocalVariableDeclStmt] ... ...;
+# 195| 0: [LocalVariableDeclAndInitExpr] TestCollidingMethods y = ...
+# 195| -1: [TypeMention] TestCollidingMethods
+# 195| 0: [LocalVariableAccess] access to local variable y
+# 195| 1: [ObjectCreation] object creation of type TestCollidingMethods
+# 195| 0: [TypeMention] TestCollidingMethods
+# 195| 1: [TypeMention] double
+# 196| 3: [ExprStmt] ...;
+# 196| 0: [MethodCall] call to method M
+# 196| -1: [LocalVariableAccess] access to local variable y
+# 196| 0: [DoubleLiteral] 1
+# 196| 1: [IntLiteral] 1
+# 197| 4: [ExprStmt] ...;
+# 197| 0: [MethodCall] call to method M
+# 197| -1: [LocalVariableAccess] access to local variable y
+# 197| 0: [IntLiteral] 1
+# 197| 1: [IntLiteral] 1
+# 200| 8: [Class] Nested
+# 202| 4: [InstanceConstructor] Nested
+#-----| 2: (Parameters)
+# 202| 0: [Parameter] p1
+# 202| -1: [TypeMention] int
+# 202| 4: [BlockStmt] {...}
+# 203| 5: [InstanceConstructor] Nested
+#-----| 2: (Parameters)
+# 203| 0: [Parameter] p1
+# 203| -1: [TypeMention] T
+# 204| 4: [BlockStmt] {...}
+# 205| 0: [LocalVariableDeclStmt] ... ...;
+# 205| 0: [LocalVariableDeclAndInitExpr] Nested x = ...
+# 205| -1: [TypeMention] Nested
+# 205| 0: [LocalVariableAccess] access to local variable x
+# 205| 1: [ObjectCreation] object creation of type Nested
+# 205| -1: [TypeMention] Nested
+# 205| 1: [TypeMention] TestCollidingMethods
+# 205| 1: [TypeMention] int
+# 205| 0: [IntLiteral] 1
+# 206| 1: [LocalVariableDeclStmt] ... ...;
+# 206| 0: [LocalVariableDeclAndInitExpr] Nested y = ...
+# 206| -1: [TypeMention] Nested
+# 206| 0: [LocalVariableAccess] access to local variable y
+# 206| 1: [ObjectCreation] object creation of type Nested
+# 206| -1: [TypeMention] Nested
+# 206| 1: [TypeMention] TestCollidingMethods
+# 206| 1: [TypeMention] double
+# 206| 0: [DoubleLiteral] 1
+# 207| 2: [LocalVariableDeclStmt] ... ...;
+# 207| 0: [LocalVariableDeclAndInitExpr] Nested z = ...
+# 207| -1: [TypeMention] Nested
+# 207| 0: [LocalVariableAccess] access to local variable z
+# 207| 1: [ObjectCreation] object creation of type Nested
+# 207| -1: [TypeMention] Nested
+# 207| 1: [TypeMention] TestCollidingMethods
+# 207| 1: [TypeMention] double
+# 207| 0: [IntLiteral] 1
diff --git a/csharp/ql/test/library-tests/methods/methods.cs b/csharp/ql/test/library-tests/methods/methods.cs
index f67676cad46..72e9b0409c0 100644
--- a/csharp/ql/test/library-tests/methods/methods.cs
+++ b/csharp/ql/test/library-tests/methods/methods.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
namespace Methods
{
@@ -180,4 +181,31 @@ namespace Methods
return list.SkipTwo(i);
}
}
+
+ public class TestCollidingMethods
+ {
+ public void M(T p1, int p2) { }
+ public void M(int p1, int p2) { }
+
+ public void Calls()
+ {
+ var x = new TestCollidingMethods();
+ x.M(1, 1);
+
+ var y = new TestCollidingMethods();
+ y.M(1.0, 1);
+ y.M(1, 1);
+ }
+
+ public class Nested
+ {
+ public Nested(int p1) { }
+ public Nested(T p1)
+ {
+ var x = new TestCollidingMethods.Nested(1);
+ var y = new TestCollidingMethods.Nested(1.0);
+ var z = new TestCollidingMethods.Nested(1);
+ }
+ }
+ }
}
From 3d60c146ad2cb31bb576cfd79085626e28d77c7a Mon Sep 17 00:00:00 2001
From: Tom Hvitved
Date: Tue, 25 May 2021 13:40:30 +0200
Subject: [PATCH 127/272] C#: Base IDs for constructed methods on their
unconstructed counterparts
---
.../Entities/Constructor.cs | 9 ++++
.../Entities/Method.cs | 46 +++++++++++++------
2 files changed, 40 insertions(+), 15 deletions(-)
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs
index 3f31108b57e..e3f77d9407e 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs
@@ -146,6 +146,15 @@ namespace Semmle.Extraction.CSharp.Entities
public override void WriteId(EscapingTextWriter trapFile)
{
+ if (!SymbolEqualityComparer.Default.Equals(Symbol, Symbol.OriginalDefinition))
+ {
+ trapFile.WriteSubId(ContainingType!);
+ trapFile.Write(".");
+ trapFile.WriteSubId(OriginalDefinition);
+ trapFile.Write(";constructor");
+ return;
+ }
+
if (Symbol.IsStatic)
trapFile.Write("static");
trapFile.WriteSubId(ContainingType!);
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs
index 12d0ce4cc10..e5822ccbf58 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs
@@ -129,6 +129,30 @@ namespace Semmle.Extraction.CSharp.Entities
///
private static void BuildMethodId(Method m, EscapingTextWriter trapFile)
{
+ if (!SymbolEqualityComparer.Default.Equals(m.Symbol, m.Symbol.OriginalDefinition))
+ {
+ if (!SymbolEqualityComparer.Default.Equals(m.Symbol, m.ConstructedFromSymbol))
+ {
+ trapFile.WriteSubId(Create(m.Context, m.ConstructedFromSymbol));
+ trapFile.Write('<');
+ // Encode the nullability of the type arguments in the label.
+ // Type arguments with different nullability can result in
+ // a constructed method with different nullability of its parameters and return type,
+ // so we need to create a distinct database entity for it.
+ trapFile.BuildList(",", m.Symbol.GetAnnotatedTypeArguments(), ta => { ta.Symbol.BuildOrWriteId(m.Context, trapFile, m.Symbol); trapFile.Write((int)ta.Nullability); });
+ trapFile.Write('>');
+ }
+ else
+ {
+ trapFile.WriteSubId(m.ContainingType!);
+ trapFile.Write(".");
+ trapFile.WriteSubId(m.OriginalDefinition);
+ }
+
+ WritePostfix(m, trapFile);
+ return;
+ }
+
m.Symbol.ReturnType.BuildOrWriteId(m.Context, trapFile, m.Symbol);
trapFile.Write(" ");
@@ -141,24 +165,16 @@ namespace Semmle.Extraction.CSharp.Entities
if (m.Symbol.IsGenericMethod)
{
- if (SymbolEqualityComparer.Default.Equals(m.Symbol, m.Symbol.OriginalDefinition))
- {
- trapFile.Write('`');
- trapFile.Write(m.Symbol.TypeParameters.Length);
- }
- else
- {
- trapFile.Write('<');
- // Encode the nullability of the type arguments in the label.
- // Type arguments with different nullability can result in
- // a constructed method with different nullability of its parameters and return type,
- // so we need to create a distinct database entity for it.
- trapFile.BuildList(",", m.Symbol.GetAnnotatedTypeArguments(), ta => { ta.Symbol.BuildOrWriteId(m.Context, trapFile, m.Symbol); trapFile.Write((int)ta.Nullability); });
- trapFile.Write('>');
- }
+ trapFile.Write('`');
+ trapFile.Write(m.Symbol.TypeParameters.Length);
}
AddParametersToId(m.Context, trapFile, m.Symbol);
+ WritePostfix(m, trapFile);
+ }
+
+ private static void WritePostfix(Method m, EscapingTextWriter trapFile)
+ {
switch (m.Symbol.MethodKind)
{
case MethodKind.PropertyGet:
From 5a3a011b8e4b0b578f58b13f2e1ba7dce96c2d58 Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Thu, 3 Jun 2021 11:17:01 +0200
Subject: [PATCH 128/272] Fix test results
---
.../library-tests/methods/DB-CHECK.expected | 130 ------------------
.../library-tests/methods/Methods5.expected | 2 -
.../methods/Parameters8.expected | 4 -
3 files changed, 136 deletions(-)
delete mode 100644 csharp/ql/test/library-tests/methods/DB-CHECK.expected
diff --git a/csharp/ql/test/library-tests/methods/DB-CHECK.expected b/csharp/ql/test/library-tests/methods/DB-CHECK.expected
deleted file mode 100644
index 4a99289a434..00000000000
--- a/csharp/ql/test/library-tests/methods/DB-CHECK.expected
+++ /dev/null
@@ -1,130 +0,0 @@
-[INVALID_KEY] predicate methods(@method id, string name, @type declaring_type_id, @type_or_ref type_id, @method unbound_id): The key set {id} does not functionally determine all fields.
-Here is a pair of tuples that agree on the key set but differ at index 4:
-Tuple 1 in row 28: (828,"M",827,1087,813)
-Tuple 2 in row 29: (828,"M",827,1087,1135)
- Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
- Relevant element: Full ID for 827: @"(294)<(366)>;type". The ID may expand to @"{@"{@"{@";namespace"}.Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"{@";namespace"}.System;namespace"}.Int32;type"}>;type"
- Relevant element: Full ID for 1087: @"(365);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"};typeRef"
- Relevant element: Full ID for 813: @"(365) (294).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@";namespace"}.Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
- Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
- Relevant element: Full ID for 827: @"(294)<(366)>;type". The ID may expand to @"{@"{@"{@";namespace"}.Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"{@";namespace"}.System;namespace"}.Int32;type"}>;type"
- Relevant element: Full ID for 1087: @"(365);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"};typeRef"
- Relevant element: Full ID for 1135: @"(365) (294).M((296) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@";namespace"}.Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}_0;typeparameter"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
-[INVALID_KEY] predicate constructors(@constructor id, string name, @type declaring_type_id, @constructor unbound_id): The key set {id} does not functionally determine all fields.
-Here is a pair of tuples that agree on the key set but differ at index 3:
-Tuple 1 in row 14: (897,"Nested",830,332)
-Tuple 2 in row 15: (897,"Nested",830,1143)
- Relevant element: Full ID for 897: @"(830)((366) p1);constructor". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.Nested;type"}({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1);constructor"
- Relevant element: Full ID for 830: @"(827).Nested;type". The ID may expand to @"{@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.Nested;type"
- Relevant element: Full ID for 332: @"(297)((296) p1);constructor". The ID may expand to @"{@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.Nested;type"}({@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}_0;typeparameter"} p1);constructor"
- Relevant element: Full ID for 897: @"(830)((366) p1);constructor". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.Nested;type"}({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1);constructor"
- Relevant element: Full ID for 830: @"(827).Nested;type". The ID may expand to @"{@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.Nested;type"
- Relevant element: Full ID for 1143: @"(297)((366) p1);constructor". The ID may expand to @"{@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.Nested;type"}({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1);constructor"
-[INVALID_KEY_SET] predicate params(@parameter id, string name, @type_or_ref type_id, int index, int mode, @parameterizable parent_id, @parameter unbound_id): The key set {name, parent_id} does not functionally determine all fields.
-Here is a pair of tuples that agree on the key set but differ at index 6:
-Tuple 1 in row 34: (1005,"p1",1083,0,0,828,1138)
-Tuple 2 in row 35: (1005,"p1",1083,0,0,828,1348)
- Relevant element: Full ID for 1005: @"(828)_0;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_0;parameter"
- Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
- Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
- Relevant element: Full ID for 1138: @"(1135)_0;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"(130).TestCollidingMethods`1;type"}_0;typeparameter"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_0;parameter"
- Relevant element: Full ID for 1005: @"(828)_0;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_0;parameter"
- Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
- Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
- Relevant element: Full ID for 1348: @"(813)_0;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_0;parameter"
-[INVALID_KEY_SET] predicate params(@parameter id, string name, @type_or_ref type_id, int index, int mode, @parameterizable parent_id, @parameter unbound_id): The key set {name, parent_id} does not functionally determine all fields.
-Here is a pair of tuples that agree on the key set but differ at index 6:
-Tuple 1 in row 42: (1031,"p1",1083,0,0,897,335)
-Tuple 2 in row 43: (1031,"p1",1083,0,0,897,1146)
- Relevant element: Full ID for 1031: @"(897)_0;parameter". The ID may expand to @"{@"{@"{@"(294)<(366)>;type"}.Nested;type"}({@"{@"(111).System;namespace"}.Int32;type"} p1);constructor"}_0;parameter"
- Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
- Relevant element: Full ID for 897: @"(830)((366) p1);constructor". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.Nested;type"}({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1);constructor"
- Relevant element: Full ID for 335: @"(332)_0;parameter". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}.Nested;type"}({@"{@"(130).TestCollidingMethods`1;type"}_0;typeparameter"} p1);constructor"}_0;parameter"
- Relevant element: Full ID for 1031: @"(897)_0;parameter". The ID may expand to @"{@"{@"{@"(294)<(366)>;type"}.Nested;type"}({@"{@"(111).System;namespace"}.Int32;type"} p1);constructor"}_0;parameter"
- Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
- Relevant element: Full ID for 897: @"(830)((366) p1);constructor". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.Nested;type"}({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1);constructor"
- Relevant element: Full ID for 1146: @"(1143)_0;parameter". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}.Nested;type"}({@"{@"(111).System;namespace"}.Int32;type"} p1);constructor"}_0;parameter"
-[INVALID_KEY_SET] predicate params(@parameter id, string name, @type_or_ref type_id, int index, int mode, @parameterizable parent_id, @parameter unbound_id): The key set {name, parent_id} does not functionally determine all fields.
-Here is a pair of tuples that agree on the key set but differ at index 6:
-Tuple 1 in row 36: (1006,"p2",1083,1,0,828,1140)
-Tuple 2 in row 37: (1006,"p2",1083,1,0,828,1350)
- Relevant element: Full ID for 1006: @"(828)_1;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_1;parameter"
- Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
- Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
- Relevant element: Full ID for 1140: @"(1135)_1;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"(130).TestCollidingMethods`1;type"}_0;typeparameter"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_1;parameter"
- Relevant element: Full ID for 1006: @"(828)_1;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_1;parameter"
- Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
- Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
- Relevant element: Full ID for 1350: @"(813)_1;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_1;parameter"
-[INVALID_KEY_SET] predicate params(@parameter id, string name, @type_or_ref type_id, int index, int mode, @parameterizable parent_id, @parameter unbound_id): The key set {index, parent_id} does not functionally determine all fields.
-Here is a pair of tuples that agree on the key set but differ at index 6:
-Tuple 1 in row 34: (1005,"p1",1083,0,0,828,1138)
-Tuple 2 in row 35: (1005,"p1",1083,0,0,828,1348)
- Relevant element: Full ID for 1005: @"(828)_0;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_0;parameter"
- Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
- Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
- Relevant element: Full ID for 1138: @"(1135)_0;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"(130).TestCollidingMethods`1;type"}_0;typeparameter"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_0;parameter"
- Relevant element: Full ID for 1005: @"(828)_0;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_0;parameter"
- Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
- Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
- Relevant element: Full ID for 1348: @"(813)_0;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_0;parameter"
-[INVALID_KEY_SET] predicate params(@parameter id, string name, @type_or_ref type_id, int index, int mode, @parameterizable parent_id, @parameter unbound_id): The key set {index, parent_id} does not functionally determine all fields.
-Here is a pair of tuples that agree on the key set but differ at index 6:
-Tuple 1 in row 42: (1031,"p1",1083,0,0,897,335)
-Tuple 2 in row 43: (1031,"p1",1083,0,0,897,1146)
- Relevant element: Full ID for 1031: @"(897)_0;parameter". The ID may expand to @"{@"{@"{@"(294)<(366)>;type"}.Nested;type"}({@"{@"(111).System;namespace"}.Int32;type"} p1);constructor"}_0;parameter"
- Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
- Relevant element: Full ID for 897: @"(830)((366) p1);constructor". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.Nested;type"}({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1);constructor"
- Relevant element: Full ID for 335: @"(332)_0;parameter". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}.Nested;type"}({@"{@"(130).TestCollidingMethods`1;type"}_0;typeparameter"} p1);constructor"}_0;parameter"
- Relevant element: Full ID for 1031: @"(897)_0;parameter". The ID may expand to @"{@"{@"{@"(294)<(366)>;type"}.Nested;type"}({@"{@"(111).System;namespace"}.Int32;type"} p1);constructor"}_0;parameter"
- Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
- Relevant element: Full ID for 897: @"(830)((366) p1);constructor". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.Nested;type"}({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1);constructor"
- Relevant element: Full ID for 1146: @"(1143)_0;parameter". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}.Nested;type"}({@"{@"(111).System;namespace"}.Int32;type"} p1);constructor"}_0;parameter"
-[INVALID_KEY_SET] predicate params(@parameter id, string name, @type_or_ref type_id, int index, int mode, @parameterizable parent_id, @parameter unbound_id): The key set {index, parent_id} does not functionally determine all fields.
-Here is a pair of tuples that agree on the key set but differ at index 6:
-Tuple 1 in row 36: (1006,"p2",1083,1,0,828,1140)
-Tuple 2 in row 37: (1006,"p2",1083,1,0,828,1350)
- Relevant element: Full ID for 1006: @"(828)_1;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_1;parameter"
- Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
- Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
- Relevant element: Full ID for 1140: @"(1135)_1;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"(130).TestCollidingMethods`1;type"}_0;typeparameter"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_1;parameter"
- Relevant element: Full ID for 1006: @"(828)_1;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_1;parameter"
- Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
- Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
- Relevant element: Full ID for 1350: @"(813)_1;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_1;parameter"
-[INVALID_KEY] predicate params(@parameter id, string name, @type_or_ref type_id, int index, int mode, @parameterizable parent_id, @parameter unbound_id): The key set {id} does not functionally determine all fields.
-Here is a pair of tuples that agree on the key set but differ at index 6:
-Tuple 1 in row 34: (1005,"p1",1083,0,0,828,1138)
-Tuple 2 in row 35: (1005,"p1",1083,0,0,828,1348)
- Relevant element: Full ID for 1005: @"(828)_0;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_0;parameter"
- Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
- Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
- Relevant element: Full ID for 1138: @"(1135)_0;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"(130).TestCollidingMethods`1;type"}_0;typeparameter"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_0;parameter"
- Relevant element: Full ID for 1005: @"(828)_0;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_0;parameter"
- Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
- Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
- Relevant element: Full ID for 1348: @"(813)_0;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_0;parameter"
-[INVALID_KEY] predicate params(@parameter id, string name, @type_or_ref type_id, int index, int mode, @parameterizable parent_id, @parameter unbound_id): The key set {id} does not functionally determine all fields.
-Here is a pair of tuples that agree on the key set but differ at index 6:
-Tuple 1 in row 36: (1006,"p2",1083,1,0,828,1140)
-Tuple 2 in row 37: (1006,"p2",1083,1,0,828,1350)
- Relevant element: Full ID for 1006: @"(828)_1;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_1;parameter"
- Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
- Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
- Relevant element: Full ID for 1140: @"(1135)_1;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"(130).TestCollidingMethods`1;type"}_0;typeparameter"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_1;parameter"
- Relevant element: Full ID for 1006: @"(828)_1;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_1;parameter"
- Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
- Relevant element: Full ID for 828: @"(365) (827).M((366) p1,(366) p2);method". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Void;type"} {@"{@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}<{@"{@"(111).System;namespace"}.Int32;type"}>;type"}.M({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1,{@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p2);method"
- Relevant element: Full ID for 1350: @"(813)_1;parameter". The ID may expand to @"{@"{@"{@"(111).System;namespace"}.Void;type"} {@"{@"(111).Methods;namespace"}.TestCollidingMethods`1;type"}.M({@"{@"(111).System;namespace"}.Int32;type"} p1,{@"{@"(111).System;namespace"}.Int32;type"} p2);method"}_1;parameter"
-[INVALID_KEY] predicate params(@parameter id, string name, @type_or_ref type_id, int index, int mode, @parameterizable parent_id, @parameter unbound_id): The key set {id} does not functionally determine all fields.
-Here is a pair of tuples that agree on the key set but differ at index 6:
-Tuple 1 in row 42: (1031,"p1",1083,0,0,897,335)
-Tuple 2 in row 43: (1031,"p1",1083,0,0,897,1146)
- Relevant element: Full ID for 1031: @"(897)_0;parameter". The ID may expand to @"{@"{@"{@"(294)<(366)>;type"}.Nested;type"}({@"{@"(111).System;namespace"}.Int32;type"} p1);constructor"}_0;parameter"
- Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
- Relevant element: Full ID for 897: @"(830)((366) p1);constructor". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.Nested;type"}({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1);constructor"
- Relevant element: Full ID for 335: @"(332)_0;parameter". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}.Nested;type"}({@"{@"(130).TestCollidingMethods`1;type"}_0;typeparameter"} p1);constructor"}_0;parameter"
- Relevant element: Full ID for 1031: @"(897)_0;parameter". The ID may expand to @"{@"{@"{@"(294)<(366)>;type"}.Nested;type"}({@"{@"(111).System;namespace"}.Int32;type"} p1);constructor"}_0;parameter"
- Relevant element: Full ID for 1083: @"(366);typeRef". The ID may expand to @"{@"{@"{@";namespace"}.System;namespace"}.Int32;type"};typeRef"
- Relevant element: Full ID for 897: @"(830)((366) p1);constructor". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}<{@"(112).Int32;type"}>;type"}.Nested;type"}({@"{@"{@";namespace"}.System;namespace"}.Int32;type"} p1);constructor"
- Relevant element: Full ID for 1146: @"(1143)_0;parameter". The ID may expand to @"{@"{@"{@"(130).TestCollidingMethods`1;type"}.Nested;type"}({@"{@"(111).System;namespace"}.Int32;type"} p1);constructor"}_0;parameter"
diff --git a/csharp/ql/test/library-tests/methods/Methods5.expected b/csharp/ql/test/library-tests/methods/Methods5.expected
index 03638800b01..01beaa41f6e 100644
--- a/csharp/ql/test/library-tests/methods/Methods5.expected
+++ b/csharp/ql/test/library-tests/methods/Methods5.expected
@@ -21,10 +21,8 @@
| methods.cs:125:21:125:24 | Main | methods.cs:110:27:110:34 | Slice | methods.cs:122:18:122:31 | TestExtensions |
| methods.cs:179:67:179:76 | SkipTwoInt | methods.cs:174:65:174:74 | SkipTwo | methods.cs:167:18:167:47 | TestDefaultExtensionParameters |
| methods.cs:190:21:190:25 | Calls | methods.cs:187:21:187:21 | M | methods.cs:185:18:185:40 | TestCollidingMethods<> |
-| methods.cs:190:21:190:25 | Calls | methods.cs:187:21:187:21 | M | methods.cs:185:18:185:40 | TestCollidingMethods<> |
| methods.cs:190:21:190:25 | Calls | methods.cs:188:21:188:21 | M | methods.cs:185:18:185:40 | TestCollidingMethods<> |
| methods.cs:190:21:190:25 | Calls | methods.cs:188:21:188:21 | M | methods.cs:185:18:185:40 | TestCollidingMethods<> |
| methods.cs:203:20:203:25 | Nested | methods.cs:202:20:202:25 | Nested | methods.cs:200:22:200:27 | Nested |
| methods.cs:203:20:203:25 | Nested | methods.cs:202:20:202:25 | Nested | methods.cs:200:22:200:27 | Nested |
| methods.cs:203:20:203:25 | Nested | methods.cs:203:20:203:25 | Nested | methods.cs:200:22:200:27 | Nested |
-| methods.cs:203:20:203:25 | Nested | methods.cs:203:20:203:25 | Nested | methods.cs:200:22:200:27 | Nested |
diff --git a/csharp/ql/test/library-tests/methods/Parameters8.expected b/csharp/ql/test/library-tests/methods/Parameters8.expected
index 6efd343a538..62c4d495009 100644
--- a/csharp/ql/test/library-tests/methods/Parameters8.expected
+++ b/csharp/ql/test/library-tests/methods/Parameters8.expected
@@ -47,10 +47,6 @@
| methods.cs:187:21:187:21 | M | methods.cs:187:33:187:34 | p2 |
| methods.cs:187:21:187:21 | M | methods.cs:187:33:187:34 | p2 |
| methods.cs:187:21:187:21 | M | methods.cs:187:33:187:34 | p2 |
-| methods.cs:187:21:187:21 | M | methods.cs:188:27:188:28 | p1 |
-| methods.cs:187:21:187:21 | M | methods.cs:188:35:188:36 | p2 |
-| methods.cs:188:21:188:21 | M | methods.cs:187:25:187:26 | p1 |
-| methods.cs:188:21:188:21 | M | methods.cs:187:33:187:34 | p2 |
| methods.cs:188:21:188:21 | M | methods.cs:188:27:188:28 | p1 |
| methods.cs:188:21:188:21 | M | methods.cs:188:27:188:28 | p1 |
| methods.cs:188:21:188:21 | M | methods.cs:188:27:188:28 | p1 |
From 9372e3b284188503b91666a128c6da25fc29a36b Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Thu, 3 Jun 2021 11:23:28 +0200
Subject: [PATCH 129/272] Python: Add aiohttp.web change-note
---
python/change-notes/2021-06-03-aiohttp-webserver-modeling.md | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 python/change-notes/2021-06-03-aiohttp-webserver-modeling.md
diff --git a/python/change-notes/2021-06-03-aiohttp-webserver-modeling.md b/python/change-notes/2021-06-03-aiohttp-webserver-modeling.md
new file mode 100644
index 00000000000..a62a9c4b75e
--- /dev/null
+++ b/python/change-notes/2021-06-03-aiohttp-webserver-modeling.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* Added modeling of sources/sinks when using `aiohttp.web` to create web servers.
From 2e851cd5f04be353d0f1b94821d39719b554d900 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Thu, 3 Jun 2021 11:38:15 +0200
Subject: [PATCH 130/272] Python: Improve `yarl.URL` modeling
---
.../ql/src/semmle/python/frameworks/Yarl.qll | 11 +++
.../frameworks/aiohttp/taint_test.py | 67 +------------------
.../frameworks/yarl/ConceptsTest.expected | 0
.../frameworks/yarl/ConceptsTest.ql | 2 +
.../frameworks/yarl/InlineTaintTest.expected | 3 +
.../frameworks/yarl/InlineTaintTest.ql | 1 +
.../library-tests/frameworks/yarl/options | 1 +
.../frameworks/yarl/taint_test.py | 64 ++++++++++++++++++
8 files changed, 85 insertions(+), 64 deletions(-)
create mode 100644 python/ql/test/library-tests/frameworks/yarl/ConceptsTest.expected
create mode 100644 python/ql/test/library-tests/frameworks/yarl/ConceptsTest.ql
create mode 100644 python/ql/test/library-tests/frameworks/yarl/InlineTaintTest.expected
create mode 100644 python/ql/test/library-tests/frameworks/yarl/InlineTaintTest.ql
create mode 100644 python/ql/test/library-tests/frameworks/yarl/options
create mode 100644 python/ql/test/library-tests/frameworks/yarl/taint_test.py
diff --git a/python/ql/src/semmle/python/frameworks/Yarl.qll b/python/ql/src/semmle/python/frameworks/Yarl.qll
index a998d8e0ca1..b91a407832f 100644
--- a/python/ql/src/semmle/python/frameworks/Yarl.qll
+++ b/python/ql/src/semmle/python/frameworks/Yarl.qll
@@ -34,6 +34,11 @@ module Yarl {
*/
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
+ /** A direct instantiation of `yarl.URL`. */
+ private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
+ ClassInstantiation() { this = API::moduleImport("yarl").getMember("URL").getACall() }
+ }
+
/** Gets a reference to an instance of `yarl.URL`. */
private DataFlow::LocalSourceNode instance(DataFlow::TypeTracker t) {
t.start() and
@@ -52,6 +57,12 @@ module Yarl {
*/
class YarlUrlAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
+ // class instantiation
+ exists(ClassInstantiation call |
+ nodeFrom in [call.getArg(0), call.getArgByName("val")] and
+ nodeTo = call
+ )
+ or
// Methods
//
// TODO: When we have tools that make it easy, model these properly to handle
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py b/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
index dcf21f92687..1b70879d6d3 100644
--- a/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
+++ b/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
@@ -5,11 +5,12 @@ async def test_taint(request: web.Request): # $ requestHandler
ensure_tainted(
request, # $ tainted
- # yarl.URL instances
+ # yarl.URL instances, see tests under `yarl` framework tests
# https://yarl.readthedocs.io/en/stable/api.html#yarl.URL
- # see below
request.url, # $ tainted
+ request.url.human_repr(), # $ tainted
request.rel_url, # $ tainted
+ request.rel_url.human_repr(), # $ tainted
request.forwarded, # $ tainted
@@ -130,68 +131,6 @@ async def test_taint(request: web.Request): # $ requestHandler
request.config_dict,
)
- # TODO: Should have a better way to capture that we in fact _do_ model this as a
- # an instance of the right class, and have the actual taint_test for that in a
- # different file!
- import yarl
-
- ensure_tainted(
- # see https://yarl.readthedocs.io/en/stable/api.html#yarl.URL
- request.url.user, # $ tainted
- request.url.raw_user, # $ tainted
-
- request.url.password, # $ tainted
- request.url.raw_password, # $ tainted
-
- request.url.host, # $ tainted
- request.url.raw_host, # $ tainted
-
- request.url.port, # $ tainted
- request.url.explicit_port, # $ tainted
-
- request.url.authority, # $ tainted
- request.url.raw_authority, # $ tainted
-
- request.url.path, # $ tainted
- request.url.raw_path, # $ tainted
-
- request.url.path_qs, # $ tainted
- request.url.raw_path_qs, # $ tainted
-
- request.url.query_string, # $ tainted
- request.url.raw_query_string, # $ tainted
-
- request.url.fragment, # $ tainted
- request.url.raw_fragment, # $ tainted
-
- request.url.parts, # $ tainted
- request.url.raw_parts, # $ tainted
-
- request.url.name, # $ tainted
- request.url.raw_name, # $ tainted
-
- # multidict.MultiDictProxy[str]
- request.url.query, # $ tainted
- request.url.query.getone("key"), # $ tainted
-
- request.url.with_scheme("foo"), # $ tainted
- request.url.with_user("foo"), # $ tainted
- request.url.with_password("foo"), # $ tainted
- request.url.with_host("foo"), # $ tainted
- request.url.with_port("foo"), # $ tainted
- request.url.with_path("foo"), # $ tainted
- request.url.with_query({"foo": 42}), # $ tainted
- request.url.with_query(foo=42), # $ tainted
- request.url.update_query({"foo": 42}), # $ tainted
- request.url.update_query(foo=42), # $ tainted
- request.url.with_fragment("foo"), # $ tainted
- request.url.with_name("foo"), # $ tainted
-
- request.url.join(yarl.URL("wat.html")), # $ tainted
-
- request.url.human_repr(), # $ tainted
- )
-
class TaintTestClass(web.View):
def get(self): # $ requestHandler
diff --git a/python/ql/test/library-tests/frameworks/yarl/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/yarl/ConceptsTest.expected
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/python/ql/test/library-tests/frameworks/yarl/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/yarl/ConceptsTest.ql
new file mode 100644
index 00000000000..b557a0bccb6
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/yarl/ConceptsTest.ql
@@ -0,0 +1,2 @@
+import python
+import experimental.meta.ConceptsTest
diff --git a/python/ql/test/library-tests/frameworks/yarl/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/yarl/InlineTaintTest.expected
new file mode 100644
index 00000000000..79d760d87f4
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/yarl/InlineTaintTest.expected
@@ -0,0 +1,3 @@
+argumentToEnsureNotTaintedNotMarkedAsSpurious
+untaintedArgumentToEnsureTaintedNotMarkedAsMissing
+failures
diff --git a/python/ql/test/library-tests/frameworks/yarl/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/yarl/InlineTaintTest.ql
new file mode 100644
index 00000000000..027ad8667be
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/yarl/InlineTaintTest.ql
@@ -0,0 +1 @@
+import experimental.meta.InlineTaintTest
diff --git a/python/ql/test/library-tests/frameworks/yarl/options b/python/ql/test/library-tests/frameworks/yarl/options
new file mode 100644
index 00000000000..cfef58cf2b2
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/yarl/options
@@ -0,0 +1 @@
+semmle-extractor-options: --max-import-depth=1 --lang=3
diff --git a/python/ql/test/library-tests/frameworks/yarl/taint_test.py b/python/ql/test/library-tests/frameworks/yarl/taint_test.py
new file mode 100644
index 00000000000..9fa012a9f2f
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/yarl/taint_test.py
@@ -0,0 +1,64 @@
+import yarl
+
+
+url = yarl.URL(TAINTED_STRING)
+
+
+ensure_tainted(
+ url, # $ tainted
+
+ # see https://yarl.readthedocs.io/en/stable/api.html#yarl.URL
+ url.user, # $ tainted
+ url.raw_user, # $ tainted
+
+ url.password, # $ tainted
+ url.raw_password, # $ tainted
+
+ url.host, # $ tainted
+ url.raw_host, # $ tainted
+
+ url.port, # $ tainted
+ url.explicit_port, # $ tainted
+
+ url.authority, # $ tainted
+ url.raw_authority, # $ tainted
+
+ url.path, # $ tainted
+ url.raw_path, # $ tainted
+
+ url.path_qs, # $ tainted
+ url.raw_path_qs, # $ tainted
+
+ url.query_string, # $ tainted
+ url.raw_query_string, # $ tainted
+
+ url.fragment, # $ tainted
+ url.raw_fragment, # $ tainted
+
+ url.parts, # $ tainted
+ url.raw_parts, # $ tainted
+
+ url.name, # $ tainted
+ url.raw_name, # $ tainted
+
+ # multidict.MultiDictProxy[str]
+ url.query, # $ tainted
+ url.query.getone("key"), # $ tainted
+
+ url.with_scheme("foo"), # $ tainted
+ url.with_user("foo"), # $ tainted
+ url.with_password("foo"), # $ tainted
+ url.with_host("foo"), # $ tainted
+ url.with_port("foo"), # $ tainted
+ url.with_path("foo"), # $ tainted
+ url.with_query({"foo": 42}), # $ tainted
+ url.with_query(foo=42), # $ tainted
+ url.update_query({"foo": 42}), # $ tainted
+ url.update_query(foo=42), # $ tainted
+ url.with_fragment("foo"), # $ tainted
+ url.with_name("foo"), # $ tainted
+
+ url.join(yarl.URL("wat.html")), # $ tainted
+
+ url.human_repr(), # $ tainted
+)
From e9acea8643c3ccac3568134c938fcb6509c942d5 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Thu, 3 Jun 2021 11:50:49 +0200
Subject: [PATCH 131/272] Python: Improve `multidict` modeling
---
.../semmle/python/frameworks/Multidict.qll | 20 ++++++++-
.../frameworks/aiohttp/taint_test.py | 26 ++----------
.../multidict/ConceptsTest.expected | 0
.../frameworks/multidict/ConceptsTest.ql | 2 +
.../multidict/InlineTaintTest.expected | 3 ++
.../frameworks/multidict/InlineTaintTest.ql | 1 +
.../frameworks/multidict/options | 1 +
.../frameworks/multidict/taint_test.py | 41 +++++++++++++++++++
8 files changed, 70 insertions(+), 24 deletions(-)
create mode 100644 python/ql/test/library-tests/frameworks/multidict/ConceptsTest.expected
create mode 100644 python/ql/test/library-tests/frameworks/multidict/ConceptsTest.ql
create mode 100644 python/ql/test/library-tests/frameworks/multidict/InlineTaintTest.expected
create mode 100644 python/ql/test/library-tests/frameworks/multidict/InlineTaintTest.ql
create mode 100644 python/ql/test/library-tests/frameworks/multidict/options
create mode 100644 python/ql/test/library-tests/frameworks/multidict/taint_test.py
diff --git a/python/ql/src/semmle/python/frameworks/Multidict.qll b/python/ql/src/semmle/python/frameworks/Multidict.qll
index 7a2c2bf0751..6a04bd2b8d2 100644
--- a/python/ql/src/semmle/python/frameworks/Multidict.qll
+++ b/python/ql/src/semmle/python/frameworks/Multidict.qll
@@ -24,6 +24,11 @@ module Multidict {
* See https://multidict.readthedocs.io/en/stable/multidict.html#multidictproxy
*/
module MultiDictProxy {
+ /** Gets a reference to a `MultiDictProxy` class. */
+ API::Node classRef() {
+ result = API::moduleImport("multidict").getMember(["MultiDictProxy", "CIMultiDictProxy"])
+ }
+
/**
* A source of instances of `multidict.MultiDictProxy`, extend this class to model
* new instances.
@@ -37,7 +42,12 @@ module Multidict {
*/
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
- /** Gets a reference to an instance of `multidict.MultiDictProxy`. */
+ /** A direct instantiation of a `MultiDictProxy` class. */
+ private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
+ ClassInstantiation() { this = classRef().getACall() }
+ }
+
+ /** Gets a reference to an instance of a `MultiDictProxy` class. */
private DataFlow::LocalSourceNode instance(DataFlow::TypeTracker t) {
t.start() and
result instanceof InstanceSource
@@ -45,7 +55,7 @@ module Multidict {
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
}
- /** Gets a reference to an instance of `multidict.MultiDictProxy`. */
+ /** Gets a reference to an instance of a `MultiDictProxy` class. */
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
/**
@@ -55,6 +65,12 @@ module Multidict {
*/
class MultiDictProxyAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
+ // class instantiation
+ exists(ClassInstantiation call |
+ nodeFrom = call.getArg(0) and
+ nodeTo = call
+ )
+ or
// Methods
//
// TODO: When we have tools that make it easy, model these properly to handle
diff --git a/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py b/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
index 1b70879d6d3..2bc3e529402 100644
--- a/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
+++ b/python/ql/test/library-tests/frameworks/aiohttp/taint_test.py
@@ -5,8 +5,7 @@ async def test_taint(request: web.Request): # $ requestHandler
ensure_tainted(
request, # $ tainted
- # yarl.URL instances, see tests under `yarl` framework tests
- # https://yarl.readthedocs.io/en/stable/api.html#yarl.URL
+ # yarl.URL (see `yarl` framework tests)
request.url, # $ tainted
request.url.human_repr(), # $ tainted
request.rel_url, # $ tainted
@@ -25,28 +24,11 @@ async def test_taint(request: web.Request): # $ requestHandler
request.match_info["key"], # $ tainted
request.match_info.get("key"), # $ tainted
- # multidict.MultiDictProxy[str]
- # see https://multidict.readthedocs.io/en/stable/multidict.html#multidict.MultiDictProxy
- # TODO: Should have a better way to capture that we in fact _do_ model this as a
- # an instance of the right class, and have the actual taint_test for that in a
- # different file!
+ # multidict.MultiDictProxy[str] (see `multidict` framework tests)
request.query, # $ tainted
- request.query["key"], # $ tainted
- request.query.get("key"), # $ tainted
request.query.getone("key"), # $ tainted
- request.query.getall("key"), # $ tainted
- request.query.keys(), # $ MISSING: tainted
- request.query.values(), # $ tainted
- request.query.items(), # $ tainted
- request.query.copy(), # $ tainted
- list(request.query), # $ tainted
- iter(request.query), # $ tainted
- # multidict.CIMultiDictProxy[str]
- # see https://multidict.readthedocs.io/en/stable/multidict.html#multidict.CIMultiDictProxy
- # TODO: Should have a better way to capture that we in fact _do_ model this as a
- # an instance of the right class, and have the actual taint_test for that in a
- # different file!
+ # multidict.CIMultiDictProxy[str] (see `multidict` framework tests)
request.headers, # $ tainted
request.headers.getone("key"), # $ tainted
@@ -99,7 +81,7 @@ async def test_taint(request: web.Request): # $ requestHandler
# aiohttp.multipart.MultipartReader
await request.multipart(), # $ tainted
- # multidict.MultiDictProxy[str]
+ # multidict.MultiDictProxy[str] (see `multidict` framework tests)
await request.post(), # $ tainted
(await request.post()).getone("key"), # $ MISSING: tainted
)
diff --git a/python/ql/test/library-tests/frameworks/multidict/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/multidict/ConceptsTest.expected
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/python/ql/test/library-tests/frameworks/multidict/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/multidict/ConceptsTest.ql
new file mode 100644
index 00000000000..b557a0bccb6
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/multidict/ConceptsTest.ql
@@ -0,0 +1,2 @@
+import python
+import experimental.meta.ConceptsTest
diff --git a/python/ql/test/library-tests/frameworks/multidict/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/multidict/InlineTaintTest.expected
new file mode 100644
index 00000000000..79d760d87f4
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/multidict/InlineTaintTest.expected
@@ -0,0 +1,3 @@
+argumentToEnsureNotTaintedNotMarkedAsSpurious
+untaintedArgumentToEnsureTaintedNotMarkedAsMissing
+failures
diff --git a/python/ql/test/library-tests/frameworks/multidict/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/multidict/InlineTaintTest.ql
new file mode 100644
index 00000000000..027ad8667be
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/multidict/InlineTaintTest.ql
@@ -0,0 +1 @@
+import experimental.meta.InlineTaintTest
diff --git a/python/ql/test/library-tests/frameworks/multidict/options b/python/ql/test/library-tests/frameworks/multidict/options
new file mode 100644
index 00000000000..cfef58cf2b2
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/multidict/options
@@ -0,0 +1 @@
+semmle-extractor-options: --max-import-depth=1 --lang=3
diff --git a/python/ql/test/library-tests/frameworks/multidict/taint_test.py b/python/ql/test/library-tests/frameworks/multidict/taint_test.py
new file mode 100644
index 00000000000..8fbac79888f
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/multidict/taint_test.py
@@ -0,0 +1,41 @@
+import multidict
+
+# TODO: This is an invalid MultiDictProxy construction... but for the purpose of
+# taint-test, this should be good enough.
+mdp = multidict.MultiDictProxy(TAINTED_STRING)
+
+ensure_tainted(
+ # see https://multidict.readthedocs.io/en/stable/multidict.html#multidict.MultiDictProxy
+
+ mdp, # $ tainted
+ mdp["key"], # $ tainted
+ mdp.get("key"), # $ tainted
+ mdp.getone("key"), # $ tainted
+ mdp.getall("key"), # $ tainted
+ mdp.keys(), # $ MISSING: tainted
+ mdp.values(), # $ tainted
+ mdp.items(), # $ tainted
+ mdp.copy(), # $ tainted
+ list(mdp), # $ tainted
+ iter(mdp), # $ tainted
+)
+
+# TODO: This is an invalid CIMultiDictProxy construction... but for the purpose of
+# taint-test, this should be good enough.
+ci_mdp = multidict.CIMultiDictProxy(TAINTED_STRING)
+
+ensure_tainted(
+ # see https://multidict.readthedocs.io/en/stable/multidict.html#multidict.CIMultiDictProxy
+
+ ci_mdp, # $ tainted
+ ci_mdp["key"], # $ tainted
+ ci_mdp.get("key"), # $ tainted
+ ci_mdp.getone("key"), # $ tainted
+ ci_mdp.getall("key"), # $ tainted
+ ci_mdp.keys(), # $ MISSING: tainted
+ ci_mdp.values(), # $ tainted
+ ci_mdp.items(), # $ tainted
+ ci_mdp.copy(), # $ tainted
+ list(ci_mdp), # $ tainted
+ iter(ci_mdp), # $ tainted
+)
From 793e3db085b34191836c2cf31661f70243e2cb1e Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Thu, 3 Jun 2021 11:54:05 +0200
Subject: [PATCH 132/272] C#: Change compilation settings to include all
non-public symbols
---
.../Semmle.Extraction.CSharp/Extractor/Extractor.cs | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs
index 179d1f14de5..1dd6d817b01 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs
@@ -416,9 +416,10 @@ namespace Semmle.Extraction.CSharp
compilerArguments.CompilationName,
syntaxTrees,
references,
- compilerArguments.CompilationOptions.
- WithAssemblyIdentityComparer(DesktopAssemblyIdentityComparer.Default).
- WithStrongNameProvider(new DesktopStrongNameProvider(compilerArguments.KeyFileSearchPaths))
+ compilerArguments.CompilationOptions
+ .WithAssemblyIdentityComparer(DesktopAssemblyIdentityComparer.Default)
+ .WithStrongNameProvider(new DesktopStrongNameProvider(compilerArguments.KeyFileSearchPaths))
+ .WithMetadataImportOptions(MetadataImportOptions.All)
);
},
(compilation, options) => analyser.EndInitialize(compilerArguments, options, compilation),
From 79bef11cf7cc888fb828f750216b41b87320f7d3 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Thu, 3 Jun 2021 12:10:29 +0200
Subject: [PATCH 133/272] Python: Use "new" SensitiveDataHeuristics
---
.../dataflow/new/SensitiveDataSources.qll | 21 +++++++++----------
...WeakSensitiveDataHashingCustomizations.qll | 12 ++++++-----
2 files changed, 17 insertions(+), 16 deletions(-)
diff --git a/python/ql/src/semmle/python/dataflow/new/SensitiveDataSources.qll b/python/ql/src/semmle/python/dataflow/new/SensitiveDataSources.qll
index 42be7aacfe9..ea3f135ed98 100644
--- a/python/ql/src/semmle/python/dataflow/new/SensitiveDataSources.qll
+++ b/python/ql/src/semmle/python/dataflow/new/SensitiveDataSources.qll
@@ -9,6 +9,12 @@ private import semmle.python.dataflow.new.DataFlow
private import semmle.python.Frameworks
private import semmle.python.Concepts
private import semmle.python.security.SensitiveData as OldSensitiveData
+private import semmle.python.security.internal.SensitiveDataHeuristics as SensitiveDataHeuristics
+
+// We export these explicitly, so we don't also export the `HeuristicNames` module.
+class SensitiveDataClassification = SensitiveDataHeuristics::SensitiveDataClassification;
+
+module SensitiveDataClassification = SensitiveDataHeuristics::SensitiveDataClassification;
/**
* A data flow source of sensitive data, such as secrets, certificates, or passwords.
@@ -22,13 +28,9 @@ class SensitiveDataSource extends DataFlow::Node {
SensitiveDataSource() { this = range }
/**
- * INTERNAL: Do not use.
- *
- * This will be rewritten to have better types soon, and therefore should only be used internally until then.
- *
* Gets the classification of the sensitive data.
*/
- string getClassification() { result = range.getClassification() }
+ SensitiveDataClassification getClassification() { result = range.getClassification() }
}
/** Provides a class for modeling new sources of sensitive data, such as secrets, certificates, or passwords. */
@@ -41,22 +43,19 @@ module SensitiveDataSource {
*/
abstract class Range extends DataFlow::Node {
/**
- * INTERNAL: Do not use.
- *
- * This will be rewritten to have better types soon, and therefore should only be used internally until then.
- *
* Gets the classification of the sensitive data.
*/
- abstract string getClassification();
+ abstract SensitiveDataClassification getClassification();
}
}
+// TODO: rewrite this to not rely on the old points-to implementation
private class PortOfOldModeling extends SensitiveDataSource::Range {
OldSensitiveData::SensitiveData::Source oldSensitiveSource;
PortOfOldModeling() { this.asCfgNode() = oldSensitiveSource }
- override string getClassification() {
+ override SensitiveDataClassification getClassification() {
exists(OldSensitiveData::SensitiveData classification |
oldSensitiveSource.isSourceOf(classification)
|
diff --git a/python/ql/src/semmle/python/security/dataflow/WeakSensitiveDataHashingCustomizations.qll b/python/ql/src/semmle/python/security/dataflow/WeakSensitiveDataHashingCustomizations.qll
index 8b9d6f4e83f..c867c1e9924 100644
--- a/python/ql/src/semmle/python/security/dataflow/WeakSensitiveDataHashingCustomizations.qll
+++ b/python/ql/src/semmle/python/security/dataflow/WeakSensitiveDataHashingCustomizations.qll
@@ -52,7 +52,9 @@ module NormalHashFunction {
* A source of sensitive data, considered as a flow source.
*/
class SensitiveDataSourceAsSource extends Source, SensitiveDataSource {
- override string getClassification() { result = SensitiveDataSource.super.getClassification() }
+ override SensitiveDataClassification getClassification() {
+ result = SensitiveDataSource.super.getClassification()
+ }
}
/** The input to a hashing operation using a weak algorithm, considered as a flow sink. */
@@ -120,12 +122,12 @@ module ComputationallyExpensiveHashFunction {
*/
class PasswordSourceAsSource extends Source, SensitiveDataSource {
PasswordSourceAsSource() {
- // TODO: once https://github.com/github/codeql/pull/5739 has been merged,
- // don't use hardcoded value anymore
- SensitiveDataSource.super.getClassification() = "password"
+ SensitiveDataSource.super.getClassification() = SensitiveDataClassification::password()
}
- override string getClassification() { result = SensitiveDataSource.super.getClassification() }
+ override SensitiveDataClassification getClassification() {
+ result = SensitiveDataSource.super.getClassification()
+ }
}
/**
From e543c6c6659322e41cae74868b26d34f921f4c1d Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 3 Jun 2021 12:23:05 +0200
Subject: [PATCH 134/272] add a `js/client-side-unvalidated-url-redirection`
sink for the `history` library
---
.../ClientSideUrlRedirectCustomizations.qll | 15 +++++++++++++++
.../ClientSideUrlRedirect.expected | 12 ++++++++++++
.../CWE-601/ClientSideUrlRedirect/tst13.js | 7 +++++++
3 files changed, 34 insertions(+)
diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll
index ed182831eea..9bca989e984 100644
--- a/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll
+++ b/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll
@@ -191,6 +191,21 @@ module ClientSideUrlRedirect {
}
}
+ /**
+ * A write to the location using the [history](https://npmjs.com/package/history) library
+ */
+ class HistoryWriteUrlSink extends ScriptUrlSink {
+ HistoryWriteUrlSink() {
+ this =
+ API::moduleImport("history")
+ .getMember(["createBrowserHistory", "createHashHistory"])
+ .getReturn()
+ .getMember(["push", "replace"])
+ .getACall()
+ .getArgument(0)
+ }
+ }
+
/**
* A call to change the current url with a Next.js router.
*/
diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected
index e835fa95acc..a7148512587 100644
--- a/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected
@@ -129,6 +129,12 @@ nodes
| tst13.js:52:34:52:34 | e |
| tst13.js:53:28:53:28 | e |
| tst13.js:53:28:53:28 | e |
+| tst13.js:59:9:59:52 | payload |
+| tst13.js:59:19:59:42 | documen ... .search |
+| tst13.js:59:19:59:42 | documen ... .search |
+| tst13.js:59:19:59:52 | documen ... bstr(1) |
+| tst13.js:61:18:61:24 | payload |
+| tst13.js:61:18:61:24 | payload |
| tst.js:2:19:2:69 | /.*redi ... n.href) |
| tst.js:2:19:2:72 | /.*redi ... ref)[1] |
| tst.js:2:19:2:72 | /.*redi ... ref)[1] |
@@ -306,6 +312,11 @@ edges
| tst13.js:52:34:52:34 | e | tst13.js:53:28:53:28 | e |
| tst13.js:52:34:52:34 | e | tst13.js:53:28:53:28 | e |
| tst13.js:52:34:52:34 | e | tst13.js:53:28:53:28 | e |
+| tst13.js:59:9:59:52 | payload | tst13.js:61:18:61:24 | payload |
+| tst13.js:59:9:59:52 | payload | tst13.js:61:18:61:24 | payload |
+| tst13.js:59:19:59:42 | documen ... .search | tst13.js:59:19:59:52 | documen ... bstr(1) |
+| tst13.js:59:19:59:42 | documen ... .search | tst13.js:59:19:59:52 | documen ... bstr(1) |
+| tst13.js:59:19:59:52 | documen ... bstr(1) | tst13.js:59:9:59:52 | payload |
| tst.js:2:19:2:69 | /.*redi ... n.href) | tst.js:2:19:2:72 | /.*redi ... ref)[1] |
| tst.js:2:19:2:69 | /.*redi ... n.href) | tst.js:2:19:2:72 | /.*redi ... ref)[1] |
| tst.js:2:47:2:63 | document.location | tst.js:2:47:2:68 | documen ... on.href |
@@ -397,6 +408,7 @@ edges
| tst13.js:44:14:44:20 | payload | tst13.js:2:19:2:42 | documen ... .search | tst13.js:44:14:44:20 | payload | Untrusted URL redirection due to $@. | tst13.js:2:19:2:42 | documen ... .search | user-provided value |
| tst13.js:50:23:50:23 | e | tst13.js:49:32:49:32 | e | tst13.js:50:23:50:23 | e | Untrusted URL redirection due to $@. | tst13.js:49:32:49:32 | e | user-provided value |
| tst13.js:53:28:53:28 | e | tst13.js:52:34:52:34 | e | tst13.js:53:28:53:28 | e | Untrusted URL redirection due to $@. | tst13.js:52:34:52:34 | e | user-provided value |
+| tst13.js:61:18:61:24 | payload | tst13.js:59:19:59:42 | documen ... .search | tst13.js:61:18:61:24 | payload | Untrusted URL redirection due to $@. | tst13.js:59:19:59:42 | documen ... .search | user-provided value |
| tst.js:2:19:2:72 | /.*redi ... ref)[1] | tst.js:2:47:2:63 | document.location | tst.js:2:19:2:72 | /.*redi ... ref)[1] | Untrusted URL redirection due to $@. | tst.js:2:47:2:63 | document.location | user-provided value |
| tst.js:2:19:2:72 | /.*redi ... ref)[1] | tst.js:2:47:2:68 | documen ... on.href | tst.js:2:19:2:72 | /.*redi ... ref)[1] | Untrusted URL redirection due to $@. | tst.js:2:47:2:68 | documen ... on.href | user-provided value |
| tst.js:6:20:6:59 | indirec ... ref)[1] | tst.js:6:34:6:50 | document.location | tst.js:6:20:6:59 | indirec ... ref)[1] | Untrusted URL redirection due to $@. | tst.js:6:34:6:50 | document.location | user-provided value |
diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/tst13.js b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/tst13.js
index 7a71cf06c08..873001ca626 100644
--- a/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/tst13.js
+++ b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/tst13.js
@@ -53,3 +53,10 @@ function foo() {
self.importScripts(e); // NOT OK
}
})();
+
+const history = require('history').createBrowserHistory();
+function bar() {
+ var payload = document.location.search.substr(1);
+
+ history.push(payload); // NOT OK
+}
\ No newline at end of file
From 608a0314df72762f48f5da40351fd8952d7e4dd5 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 3 Jun 2021 12:33:25 +0200
Subject: [PATCH 135/272] add location reads from the `history` libary as
client-side remote flow
---
.../javascript/security/dataflow/DOM.qll | 27 +++++++++++++++++++
.../ClientSideUrlRedirect.expected | 12 +++++++++
.../CWE-601/ClientSideUrlRedirect/tst13.js | 8 +++++-
3 files changed, 46 insertions(+), 1 deletion(-)
diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/DOM.qll b/javascript/ql/src/semmle/javascript/security/dataflow/DOM.qll
index 204f04e9c52..42f5924f6aa 100644
--- a/javascript/ql/src/semmle/javascript/security/dataflow/DOM.qll
+++ b/javascript/ql/src/semmle/javascript/security/dataflow/DOM.qll
@@ -272,3 +272,30 @@ private class WindowLocationFlowSource extends ClientSideRemoteFlowSource {
override ClientSideRemoteFlowKind getKind() { result = kind }
}
+
+/**
+ * A user-controlled location value read from the [history](http://npmjs.org/package/history) library.
+ */
+private class HistoryLibaryRemoteFlow extends ClientSideRemoteFlowSource {
+ ClientSideRemoteFlowKind kind;
+
+ HistoryLibaryRemoteFlow() {
+ exists(API::Node loc |
+ loc =
+ API::moduleImport("history")
+ .getMember(["createBrowserHistory", "createHashHistory"])
+ .getReturn()
+ .getMember("location")
+ |
+ this = loc.getMember("hash").getAnImmediateUse() and kind.isFragment()
+ or
+ this = loc.getMember("pathname").getAnImmediateUse() and kind.isPath()
+ or
+ this = loc.getMember("search").getAnImmediateUse() and kind.isQuery()
+ )
+ }
+
+ override string getSourceType() { result = "Window location" }
+
+ override ClientSideRemoteFlowKind getKind() { result = kind }
+}
diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected
index a7148512587..d3edd76979b 100644
--- a/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected
@@ -135,6 +135,12 @@ nodes
| tst13.js:59:19:59:52 | documen ... bstr(1) |
| tst13.js:61:18:61:24 | payload |
| tst13.js:61:18:61:24 | payload |
+| tst13.js:65:9:65:49 | payload |
+| tst13.js:65:19:65:39 | history ... on.hash |
+| tst13.js:65:19:65:39 | history ... on.hash |
+| tst13.js:65:19:65:49 | history ... bstr(1) |
+| tst13.js:67:21:67:27 | payload |
+| tst13.js:67:21:67:27 | payload |
| tst.js:2:19:2:69 | /.*redi ... n.href) |
| tst.js:2:19:2:72 | /.*redi ... ref)[1] |
| tst.js:2:19:2:72 | /.*redi ... ref)[1] |
@@ -317,6 +323,11 @@ edges
| tst13.js:59:19:59:42 | documen ... .search | tst13.js:59:19:59:52 | documen ... bstr(1) |
| tst13.js:59:19:59:42 | documen ... .search | tst13.js:59:19:59:52 | documen ... bstr(1) |
| tst13.js:59:19:59:52 | documen ... bstr(1) | tst13.js:59:9:59:52 | payload |
+| tst13.js:65:9:65:49 | payload | tst13.js:67:21:67:27 | payload |
+| tst13.js:65:9:65:49 | payload | tst13.js:67:21:67:27 | payload |
+| tst13.js:65:19:65:39 | history ... on.hash | tst13.js:65:19:65:49 | history ... bstr(1) |
+| tst13.js:65:19:65:39 | history ... on.hash | tst13.js:65:19:65:49 | history ... bstr(1) |
+| tst13.js:65:19:65:49 | history ... bstr(1) | tst13.js:65:9:65:49 | payload |
| tst.js:2:19:2:69 | /.*redi ... n.href) | tst.js:2:19:2:72 | /.*redi ... ref)[1] |
| tst.js:2:19:2:69 | /.*redi ... n.href) | tst.js:2:19:2:72 | /.*redi ... ref)[1] |
| tst.js:2:47:2:63 | document.location | tst.js:2:47:2:68 | documen ... on.href |
@@ -409,6 +420,7 @@ edges
| tst13.js:50:23:50:23 | e | tst13.js:49:32:49:32 | e | tst13.js:50:23:50:23 | e | Untrusted URL redirection due to $@. | tst13.js:49:32:49:32 | e | user-provided value |
| tst13.js:53:28:53:28 | e | tst13.js:52:34:52:34 | e | tst13.js:53:28:53:28 | e | Untrusted URL redirection due to $@. | tst13.js:52:34:52:34 | e | user-provided value |
| tst13.js:61:18:61:24 | payload | tst13.js:59:19:59:42 | documen ... .search | tst13.js:61:18:61:24 | payload | Untrusted URL redirection due to $@. | tst13.js:59:19:59:42 | documen ... .search | user-provided value |
+| tst13.js:67:21:67:27 | payload | tst13.js:65:19:65:39 | history ... on.hash | tst13.js:67:21:67:27 | payload | Untrusted URL redirection due to $@. | tst13.js:65:19:65:39 | history ... on.hash | user-provided value |
| tst.js:2:19:2:72 | /.*redi ... ref)[1] | tst.js:2:47:2:63 | document.location | tst.js:2:19:2:72 | /.*redi ... ref)[1] | Untrusted URL redirection due to $@. | tst.js:2:47:2:63 | document.location | user-provided value |
| tst.js:2:19:2:72 | /.*redi ... ref)[1] | tst.js:2:47:2:68 | documen ... on.href | tst.js:2:19:2:72 | /.*redi ... ref)[1] | Untrusted URL redirection due to $@. | tst.js:2:47:2:68 | documen ... on.href | user-provided value |
| tst.js:6:20:6:59 | indirec ... ref)[1] | tst.js:6:34:6:50 | document.location | tst.js:6:20:6:59 | indirec ... ref)[1] | Untrusted URL redirection due to $@. | tst.js:6:34:6:50 | document.location | user-provided value |
diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/tst13.js b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/tst13.js
index 873001ca626..63956a651a2 100644
--- a/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/tst13.js
+++ b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/tst13.js
@@ -54,9 +54,15 @@ function foo() {
}
})();
-const history = require('history').createBrowserHistory();
function bar() {
+ const history = require('history').createBrowserHistory();
var payload = document.location.search.substr(1);
history.push(payload); // NOT OK
+}
+function baz() {
+ const history = require('history').createHashHistory();
+ var payload = history.location.hash.substr(1);
+
+ history.replace(payload); // NOT OK
}
\ No newline at end of file
From d30f53a21ab199a8dbfeb9c99e6bab5d6584086c Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 3 Jun 2021 12:35:39 +0200
Subject: [PATCH 136/272] add change note
---
javascript/change-notes/2021-06-03-history.md | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 javascript/change-notes/2021-06-03-history.md
diff --git a/javascript/change-notes/2021-06-03-history.md b/javascript/change-notes/2021-06-03-history.md
new file mode 100644
index 00000000000..da5054a2630
--- /dev/null
+++ b/javascript/change-notes/2021-06-03-history.md
@@ -0,0 +1,4 @@
+lgtm,codescanning
+* Taint sources and sinks from the [history](https://npmjs.com/package/history) library are now recognized.
+ Affected packages are
+ [history](https://www.npmjs.com/package/history)
\ No newline at end of file
From 1ce7c631ffacb593c8ae75ea521b10b3f11abce6 Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Thu, 3 Jun 2021 13:01:42 +0200
Subject: [PATCH 137/272] Fix failing tests
---
.../conversion/reftype/RefType.expected | 9 +++
.../csharp8/UnmanagedGenericStructs.ql | 2 +-
.../dataflow/global/GetAnOutNode.expected | 6 ++
.../dataflow/library/FlowSummaries.expected | 70 +++++++++++++++++++
.../ql/test/library-tests/methods/Methods1.ql | 3 +-
.../Stubs/MinimalStubsFromSource.expected | 2 +-
6 files changed, 89 insertions(+), 3 deletions(-)
diff --git a/csharp/ql/test/library-tests/conversion/reftype/RefType.expected b/csharp/ql/test/library-tests/conversion/reftype/RefType.expected
index cdcfcf78f96..88f34c17293 100644
--- a/csharp/ql/test/library-tests/conversion/reftype/RefType.expected
+++ b/csharp/ql/test/library-tests/conversion/reftype/RefType.expected
@@ -1,5 +1,7 @@
| Byte[] | Object |
| Byte[] | dynamic |
+| Byte[][] | Object |
+| Byte[][] | dynamic |
| C1 | Object |
| C1 | dynamic |
| C1[] | ICollection |
@@ -183,6 +185,8 @@
| IReadOnlyList | dynamic |
| Int16[] | Object |
| Int16[] | dynamic |
+| Int32[,] | Object |
+| Int32[,] | dynamic |
| Int32[] | Object |
| Int32[] | dynamic |
| Int64[] | Object |
@@ -221,12 +225,15 @@
| T5 | C1 |
| T5 | Object |
| T5 | dynamic |
+| UInt16[][] | Object |
+| UInt16[][] | dynamic |
| UInt32[] | Object |
| UInt32[] | dynamic |
| UInt64[] | Object |
| UInt64[] | dynamic |
| dynamic | Object |
| null | Byte[] |
+| null | Byte[][] |
| null | C1 |
| null | C1[] |
| null | C2 |
@@ -279,6 +286,7 @@
| null | IReadOnlyList |
| null | IReadOnlyList |
| null | Int16[] |
+| null | Int32[,] |
| null | Int32[] |
| null | Int64[] |
| null | Object |
@@ -289,6 +297,7 @@
| null | T4 |
| null | T4[] |
| null | T5 |
+| null | UInt16[][] |
| null | UInt32[] |
| null | UInt64[] |
| null | dynamic |
diff --git a/csharp/ql/test/library-tests/csharp8/UnmanagedGenericStructs.ql b/csharp/ql/test/library-tests/csharp8/UnmanagedGenericStructs.ql
index 316e9458ae6..640454a71f9 100644
--- a/csharp/ql/test/library-tests/csharp8/UnmanagedGenericStructs.ql
+++ b/csharp/ql/test/library-tests/csharp8/UnmanagedGenericStructs.ql
@@ -1,5 +1,5 @@
import csharp
from TypeParameter tp
-where tp.getConstraints().hasUnmanagedTypeConstraint()
+where tp.getConstraints().hasUnmanagedTypeConstraint() and tp.fromSource()
select tp, "This type parameter is unmanaged."
diff --git a/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected b/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected
index 7f8a89af2e2..db98e8b9d0b 100644
--- a/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected
+++ b/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected
@@ -227,6 +227,10 @@
| file://:0:0:0:0 | [summary] call to continuationFunction in ContinueWith | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 0 in ContinueWith |
| file://:0:0:0:0 | [summary] call to continuationFunction in ContinueWith | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 0 in ContinueWith |
| file://:0:0:0:0 | [summary] call to continuationFunction in ContinueWith | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 0 in ContinueWith |
+| file://:0:0:0:0 | [summary] call to continuationFunction in ContinueWith | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 0 in ContinueWith |
+| file://:0:0:0:0 | [summary] call to continuationFunction in ContinueWith | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 0 in ContinueWith |
+| file://:0:0:0:0 | [summary] call to continuationFunction in ContinueWith | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 0 in ContinueWith |
+| file://:0:0:0:0 | [summary] call to continuationFunction in ContinueWith | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 0 in ContinueWith |
| file://:0:0:0:0 | [summary] call to elementSelector in GroupBy | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 2 in GroupBy |
| file://:0:0:0:0 | [summary] call to elementSelector in GroupBy | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 2 in GroupBy |
| file://:0:0:0:0 | [summary] call to elementSelector in GroupBy | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 2 in GroupBy |
@@ -313,3 +317,5 @@
| file://:0:0:0:0 | [summary] call to valueFactory in Lazy | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 0 in Lazy |
| file://:0:0:0:0 | [summary] call to valueFactory in Lazy | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 0 in Lazy |
| file://:0:0:0:0 | [summary] call to valueFactory in Lazy | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 0 in Lazy |
+| file://:0:0:0:0 | [summary] call to valueFactory in Lazy | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 0 in Lazy |
+| file://:0:0:0:0 | [summary] call to valueSelector in Task | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 0 in Task |
diff --git a/csharp/ql/test/library-tests/dataflow/library/FlowSummaries.expected b/csharp/ql/test/library-tests/dataflow/library/FlowSummaries.expected
index faaef439eb5..81782ee2d7c 100644
--- a/csharp/ql/test/library-tests/dataflow/library/FlowSummaries.expected
+++ b/csharp/ql/test/library-tests/dataflow/library/FlowSummaries.expected
@@ -255,6 +255,7 @@
| System.Collections.Concurrent.ConcurrentStack<>.CopyTo(Array, int) | element of argument -1 -> element of return (out parameter 0) | true |
| System.Collections.Concurrent.ConcurrentStack<>.CopyTo(T[], int) | element of argument -1 -> element of return (out parameter 0) | true |
| System.Collections.Concurrent.ConcurrentStack<>.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true |
+| System.Collections.Concurrent.ConcurrentStack<>.GetEnumerator(Node) | element of argument -1 -> property Current of return (normal) | true |
| System.Collections.Concurrent.IProducerConsumerCollection<>.CopyTo(T[], int) | element of argument -1 -> element of return (out parameter 0) | true |
| System.Collections.Concurrent.OrderablePartitioner<>.EnumerableDropIndices.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true |
| System.Collections.Concurrent.Partitioner.d__7.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true |
@@ -405,6 +406,7 @@
| System.Collections.Generic.SortedList<,>.Add(object, object) | argument 1 -> property Value of element of argument -1 | true |
| System.Collections.Generic.SortedList<,>.CopyTo(Array, int) | element of argument -1 -> element of return (out parameter 0) | true |
| System.Collections.Generic.SortedList<,>.CopyTo(KeyValuePair[], int) | element of argument -1 -> element of return (out parameter 0) | true |
+| System.Collections.Generic.SortedList<,>.GetByIndex(int) | property Value of element of argument -1 -> return (normal) | true |
| System.Collections.Generic.SortedList<,>.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true |
| System.Collections.Generic.SortedList<,>.KeyList.Add(TKey) | argument 0 -> element of argument -1 | true |
| System.Collections.Generic.SortedList<,>.KeyList.CopyTo(Array, int) | element of argument -1 -> element of return (out parameter 0) | true |
@@ -467,6 +469,8 @@
| System.Collections.Hashtable.SyncHashtable.Clone() | element of argument 0 -> element of return (normal) | true |
| System.Collections.Hashtable.SyncHashtable.CopyTo(Array, int) | element of argument -1 -> element of return (out parameter 0) | true |
| System.Collections.Hashtable.SyncHashtable.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true |
+| System.Collections.Hashtable.SyncHashtable.SyncHashtable(Hashtable) | property Key of element of argument 0 -> property Key of element of return (normal) | true |
+| System.Collections.Hashtable.SyncHashtable.SyncHashtable(Hashtable) | property Value of element of argument 0 -> property Value of element of return (normal) | true |
| System.Collections.Hashtable.SyncHashtable.get_Item(object) | property Value of element of argument -1 -> return (normal) | true |
| System.Collections.Hashtable.SyncHashtable.get_Keys() | property Key of element of argument -1 -> element of return (normal) | true |
| System.Collections.Hashtable.SyncHashtable.get_Values() | property Value of element of argument -1 -> element of return (normal) | true |
@@ -585,6 +589,8 @@
| System.Collections.SortedList.SyncSortedList.CopyTo(Array, int) | element of argument -1 -> element of return (out parameter 0) | true |
| System.Collections.SortedList.SyncSortedList.GetByIndex(int) | property Value of element of argument -1 -> return (normal) | true |
| System.Collections.SortedList.SyncSortedList.GetValueList() | property Value of element of argument -1 -> element of return (normal) | true |
+| System.Collections.SortedList.SyncSortedList.SyncSortedList(SortedList) | property Key of element of argument 0 -> property Key of element of return (normal) | true |
+| System.Collections.SortedList.SyncSortedList.SyncSortedList(SortedList) | property Value of element of argument 0 -> property Value of element of return (normal) | true |
| System.Collections.SortedList.SyncSortedList.get_Item(object) | property Value of element of argument -1 -> return (normal) | true |
| System.Collections.SortedList.SyncSortedList.set_Item(object, object) | argument 0 -> property Key of element of argument -1 | true |
| System.Collections.SortedList.SyncSortedList.set_Item(object, object) | argument 1 -> property Value of element of argument -1 | true |
@@ -633,6 +639,8 @@
| System.Collections.Specialized.OrderedDictionary.AsReadOnly() | element of argument 0 -> element of return (normal) | true |
| System.Collections.Specialized.OrderedDictionary.CopyTo(Array, int) | element of argument -1 -> element of return (out parameter 0) | true |
| System.Collections.Specialized.OrderedDictionary.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true |
+| System.Collections.Specialized.OrderedDictionary.OrderedDictionary(OrderedDictionary) | property Key of element of argument 0 -> property Key of element of return (normal) | true |
+| System.Collections.Specialized.OrderedDictionary.OrderedDictionary(OrderedDictionary) | property Value of element of argument 0 -> property Value of element of return (normal) | true |
| System.Collections.Specialized.OrderedDictionary.OrderedDictionaryKeyValueCollection.CopyTo(Array, int) | element of argument -1 -> element of return (out parameter 0) | true |
| System.Collections.Specialized.OrderedDictionary.OrderedDictionaryKeyValueCollection.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true |
| System.Collections.Specialized.OrderedDictionary.get_Item(int) | property Value of element of argument -1 -> return (normal) | true |
@@ -725,6 +733,10 @@
| System.ComponentModel.PropertyDescriptorCollection.PropertyDescriptorCollection(PropertyDescriptor[]) | property Value of element of argument 0 -> property Value of element of return (normal) | true |
| System.ComponentModel.PropertyDescriptorCollection.PropertyDescriptorCollection(PropertyDescriptor[], bool) | property Key of element of argument 0 -> property Key of element of return (normal) | true |
| System.ComponentModel.PropertyDescriptorCollection.PropertyDescriptorCollection(PropertyDescriptor[], bool) | property Value of element of argument 0 -> property Value of element of return (normal) | true |
+| System.ComponentModel.PropertyDescriptorCollection.PropertyDescriptorCollection(PropertyDescriptor[], int, String[], IComparer) | property Key of element of argument 0 -> property Key of element of return (normal) | true |
+| System.ComponentModel.PropertyDescriptorCollection.PropertyDescriptorCollection(PropertyDescriptor[], int, String[], IComparer) | property Key of element of argument 2 -> property Key of element of return (normal) | true |
+| System.ComponentModel.PropertyDescriptorCollection.PropertyDescriptorCollection(PropertyDescriptor[], int, String[], IComparer) | property Value of element of argument 0 -> property Value of element of return (normal) | true |
+| System.ComponentModel.PropertyDescriptorCollection.PropertyDescriptorCollection(PropertyDescriptor[], int, String[], IComparer) | property Value of element of argument 2 -> property Value of element of return (normal) | true |
| System.ComponentModel.PropertyDescriptorCollection.get_Item(int) | element of argument -1 -> return (normal) | true |
| System.ComponentModel.PropertyDescriptorCollection.get_Item(int) | property Value of element of argument -1 -> return (normal) | true |
| System.ComponentModel.PropertyDescriptorCollection.get_Item(object) | element of argument -1 -> return (normal) | true |
@@ -747,12 +759,28 @@
| System.Convert.ChangeType(object, Type, IFormatProvider) | argument 0 -> return (normal) | false |
| System.Convert.ChangeType(object, TypeCode) | argument 0 -> return (normal) | false |
| System.Convert.ChangeType(object, TypeCode, IFormatProvider) | argument 0 -> return (normal) | false |
+| System.Convert.ConvertToBase64Array(char*, byte*, int, int, bool) | argument 0 -> return (normal) | false |
+| System.Convert.CopyToTempBufferWithoutWhiteSpace(ReadOnlySpan, Span, out int, out int) | argument 0 -> return (normal) | false |
+| System.Convert.Decode(ref char, ref sbyte) | argument 0 -> return (normal) | false |
+| System.Convert.DefaultToType(IConvertible, Type, IFormatProvider) | argument 0 -> return (normal) | false |
| System.Convert.FromBase64CharArray(Char[], int, int) | argument 0 -> return (normal) | false |
+| System.Convert.FromBase64CharPtr(char*, int) | argument 0 -> return (normal) | false |
| System.Convert.FromBase64String(string) | argument 0 -> return (normal) | false |
+| System.Convert.FromBase64_ComputeResultLength(char*, int) | argument 0 -> return (normal) | false |
| System.Convert.FromHexString(ReadOnlySpan) | argument 0 -> return (normal) | false |
| System.Convert.FromHexString(string) | argument 0 -> return (normal) | false |
| System.Convert.GetTypeCode(object) | argument 0 -> return (normal) | false |
| System.Convert.IsDBNull(object) | argument 0 -> return (normal) | false |
+| System.Convert.IsSpace(char) | argument 0 -> return (normal) | false |
+| System.Convert.ThrowByteOverflowException() | argument 0 -> return (normal) | false |
+| System.Convert.ThrowCharOverflowException() | argument 0 -> return (normal) | false |
+| System.Convert.ThrowInt16OverflowException() | argument 0 -> return (normal) | false |
+| System.Convert.ThrowInt32OverflowException() | argument 0 -> return (normal) | false |
+| System.Convert.ThrowInt64OverflowException() | argument 0 -> return (normal) | false |
+| System.Convert.ThrowSByteOverflowException() | argument 0 -> return (normal) | false |
+| System.Convert.ThrowUInt16OverflowException() | argument 0 -> return (normal) | false |
+| System.Convert.ThrowUInt32OverflowException() | argument 0 -> return (normal) | false |
+| System.Convert.ThrowUInt64OverflowException() | argument 0 -> return (normal) | false |
| System.Convert.ToBase64CharArray(Byte[], int, int, Char[], int) | argument 0 -> return (normal) | false |
| System.Convert.ToBase64CharArray(Byte[], int, int, Char[], int, Base64FormattingOptions) | argument 0 -> return (normal) | false |
| System.Convert.ToBase64String(Byte[]) | argument 0 -> return (normal) | false |
@@ -760,6 +788,7 @@
| System.Convert.ToBase64String(Byte[], int, int) | argument 0 -> return (normal) | false |
| System.Convert.ToBase64String(Byte[], int, int, Base64FormattingOptions) | argument 0 -> return (normal) | false |
| System.Convert.ToBase64String(ReadOnlySpan, Base64FormattingOptions) | argument 0 -> return (normal) | false |
+| System.Convert.ToBase64_CalculateAndValidateOutputLength(int, bool) | argument 0 -> return (normal) | false |
| System.Convert.ToBoolean(DateTime) | argument 0 -> return (normal) | false |
| System.Convert.ToBoolean(bool) | argument 0 -> return (normal) | false |
| System.Convert.ToBoolean(byte) | argument 0 -> return (normal) | false |
@@ -1059,9 +1088,11 @@
| System.Convert.ToUInt64(uint) | argument 0 -> return (normal) | false |
| System.Convert.ToUInt64(ulong) | argument 0 -> return (normal) | false |
| System.Convert.ToUInt64(ushort) | argument 0 -> return (normal) | false |
+| System.Convert.TryDecodeFromUtf16(ReadOnlySpan, Span, out int, out int) | argument 0 -> return (normal) | false |
| System.Convert.TryFromBase64Chars(ReadOnlySpan, Span, out int) | argument 0 -> return (normal) | false |
| System.Convert.TryFromBase64String(string, Span, out int) | argument 0 -> return (normal) | false |
| System.Convert.TryToBase64Chars(ReadOnlySpan, Span, out int, Base64FormattingOptions) | argument 0 -> return (normal) | false |
+| System.Convert.WriteThreeLowOrderBytes(ref byte, int) | argument 0 -> return (normal) | false |
| System.Diagnostics.Tracing.CounterPayload.d__51.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true |
| System.Diagnostics.Tracing.CounterPayload.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true |
| System.Diagnostics.Tracing.EventPayload.Add(KeyValuePair) | argument 0 -> element of argument -1 | true |
@@ -1070,6 +1101,10 @@
| System.Diagnostics.Tracing.EventPayload.Add(string, object) | argument 0 -> property Key of element of argument -1 | true |
| System.Diagnostics.Tracing.EventPayload.Add(string, object) | argument 1 -> property Value of element of argument -1 | true |
| System.Diagnostics.Tracing.EventPayload.CopyTo(KeyValuePair[], int) | element of argument -1 -> element of return (out parameter 0) | true |
+| System.Diagnostics.Tracing.EventPayload.EventPayload(List, List