From dc58f6fa8748fe947f6a039d5dc332e4cc48cadf Mon Sep 17 00:00:00 2001
From: dilanbhalla
Date: Thu, 25 Jun 2020 11:39:09 -0700
Subject: [PATCH 001/725] function/class synatax
---
.../Classes/NamingConventionsClasses.qhelp | 30 +++++++++++++++++++
.../src/Classes/NamingConventionsClasses.ql | 17 +++++++++++
.../NamingConventionsFunctions.qhelp | 30 +++++++++++++++++++
.../Functions/NamingConventionsFunctions.ql | 17 +++++++++++
.../Naming/NamingConventionsClasses.expected | 1 +
.../Naming/NamingConventionsClasses.py | 11 +++++++
.../Naming/NamingConventionsClasses.qlref | 1 +
.../NamingConventionsFunctions.expected | 1 +
.../general/NamingConventionsFunctions.py | 9 ++++++
.../general/NamingConventionsFunctions.qlref | 1 +
10 files changed, 118 insertions(+)
create mode 100644 python/ql/src/Classes/NamingConventionsClasses.qhelp
create mode 100644 python/ql/src/Classes/NamingConventionsClasses.ql
create mode 100644 python/ql/src/Functions/NamingConventionsFunctions.qhelp
create mode 100644 python/ql/src/Functions/NamingConventionsFunctions.ql
create mode 100644 python/ql/test/query-tests/Classes/Naming/NamingConventionsClasses.expected
create mode 100644 python/ql/test/query-tests/Classes/Naming/NamingConventionsClasses.py
create mode 100644 python/ql/test/query-tests/Classes/Naming/NamingConventionsClasses.qlref
create mode 100644 python/ql/test/query-tests/Functions/general/NamingConventionsFunctions.expected
create mode 100644 python/ql/test/query-tests/Functions/general/NamingConventionsFunctions.py
create mode 100644 python/ql/test/query-tests/Functions/general/NamingConventionsFunctions.qlref
diff --git a/python/ql/src/Classes/NamingConventionsClasses.qhelp b/python/ql/src/Classes/NamingConventionsClasses.qhelp
new file mode 100644
index 00000000000..7a6c0ca13dd
--- /dev/null
+++ b/python/ql/src/Classes/NamingConventionsClasses.qhelp
@@ -0,0 +1,30 @@
+
+
+
+
+
+A class name that begins with a lowercase letter does not follow standard
+naming conventions. This decreases code readability. For example, class background.
+
+
+
+
+
+
+Write the class name beginning with an uppercase letter. For example, class Background.
+
+
+
+
+
+
+
+ Guido van Rossum, Barry Warsaw, Nick Coghlan PEP 8 -- Style Guide for Python Code
+
+
+
+
+
+
diff --git a/python/ql/src/Classes/NamingConventionsClasses.ql b/python/ql/src/Classes/NamingConventionsClasses.ql
new file mode 100644
index 00000000000..a1744f5d3e6
--- /dev/null
+++ b/python/ql/src/Classes/NamingConventionsClasses.ql
@@ -0,0 +1,17 @@
+/**
+ * @name Misnamed class
+ * @description A class name that begins with a lowercase letter decreases readability.
+ * @kind problem
+ * @problem.severity recommendation
+ * @precision medium
+ * @id python/misnamed-type
+ * @tags maintainability
+ */
+
+import python
+
+from Class c
+where
+ c.inSource() and
+ not c.getName().substring(0, 1).toUpperCase() = c.getName().substring(0, 1)
+select c, "Class names should start in uppercase."
diff --git a/python/ql/src/Functions/NamingConventionsFunctions.qhelp b/python/ql/src/Functions/NamingConventionsFunctions.qhelp
new file mode 100644
index 00000000000..15014045542
--- /dev/null
+++ b/python/ql/src/Functions/NamingConventionsFunctions.qhelp
@@ -0,0 +1,30 @@
+
+
+
+
+
+A function name that begins with an uppercase letter does not follow standard
+naming conventions. This decreases code readability. For example, Jump.
+
+
+
+
+
+
+Write the function name beginning with an lowercase letter. For example, jump.
+
+
+
+
+
+
+
+ Guido van Rossum, Barry Warsaw, Nick Coghlan PEP 8 -- Style Guide for Python Code
+
+
+
+
+
+
diff --git a/python/ql/src/Functions/NamingConventionsFunctions.ql b/python/ql/src/Functions/NamingConventionsFunctions.ql
new file mode 100644
index 00000000000..3ed619bc084
--- /dev/null
+++ b/python/ql/src/Functions/NamingConventionsFunctions.ql
@@ -0,0 +1,17 @@
+/**
+ * @name Misnamed function
+ * @description A function name that begins with an uppercase letter decreases readability.
+ * @kind problem
+ * @problem.severity recommendation
+ * @precision medium
+ * @id python/misnamed-function
+ * @tags maintainability
+ */
+
+import python
+
+from Function f
+where
+ f.inSource() and
+ not f.getName().substring(0, 1).toLowerCase() = f.getName().substring(0, 1)
+select f, "Function names should start in lowercase."
diff --git a/python/ql/test/query-tests/Classes/Naming/NamingConventionsClasses.expected b/python/ql/test/query-tests/Classes/Naming/NamingConventionsClasses.expected
new file mode 100644
index 00000000000..8e6dea7fce4
--- /dev/null
+++ b/python/ql/test/query-tests/Classes/Naming/NamingConventionsClasses.expected
@@ -0,0 +1 @@
+| NamingConventionsClasses.py:2:1:2:14 | Class badName | Class names should start in uppercase. |
diff --git a/python/ql/test/query-tests/Classes/Naming/NamingConventionsClasses.py b/python/ql/test/query-tests/Classes/Naming/NamingConventionsClasses.py
new file mode 100644
index 00000000000..c07bdb57234
--- /dev/null
+++ b/python/ql/test/query-tests/Classes/Naming/NamingConventionsClasses.py
@@ -0,0 +1,11 @@
+# BAD, do not start class or interface name with lowercase letter
+class badName:
+
+ def hello(self):
+ print("hello")
+
+# Good, class name starts with capital letter
+class GoodName:
+
+ def hello(self):
+ print("hello")
\ No newline at end of file
diff --git a/python/ql/test/query-tests/Classes/Naming/NamingConventionsClasses.qlref b/python/ql/test/query-tests/Classes/Naming/NamingConventionsClasses.qlref
new file mode 100644
index 00000000000..01ad29859da
--- /dev/null
+++ b/python/ql/test/query-tests/Classes/Naming/NamingConventionsClasses.qlref
@@ -0,0 +1 @@
+Classes/NamingConventionsClasses.ql
\ No newline at end of file
diff --git a/python/ql/test/query-tests/Functions/general/NamingConventionsFunctions.expected b/python/ql/test/query-tests/Functions/general/NamingConventionsFunctions.expected
new file mode 100644
index 00000000000..d87fe6c16f3
--- /dev/null
+++ b/python/ql/test/query-tests/Functions/general/NamingConventionsFunctions.expected
@@ -0,0 +1 @@
+| NamingConventionsFunctions.py:4:5:4:25 | Function HelloWorld | Function names should start in lowercase. |
diff --git a/python/ql/test/query-tests/Functions/general/NamingConventionsFunctions.py b/python/ql/test/query-tests/Functions/general/NamingConventionsFunctions.py
new file mode 100644
index 00000000000..fb3e89ab8e9
--- /dev/null
+++ b/python/ql/test/query-tests/Functions/general/NamingConventionsFunctions.py
@@ -0,0 +1,9 @@
+class Test:
+
+ # BAD, do not start function name with uppercase letter
+ def HelloWorld(self):
+ print("hello world")
+
+ # GOOD, function name starts with lowercase letter
+ def hello_world(self):
+ print("hello world")
\ No newline at end of file
diff --git a/python/ql/test/query-tests/Functions/general/NamingConventionsFunctions.qlref b/python/ql/test/query-tests/Functions/general/NamingConventionsFunctions.qlref
new file mode 100644
index 00000000000..da4780ecbe9
--- /dev/null
+++ b/python/ql/test/query-tests/Functions/general/NamingConventionsFunctions.qlref
@@ -0,0 +1 @@
+Functions/NamingConventionsFunctions.ql
\ No newline at end of file
From dc73fcc4e856cbfc3ea5967013dcf858679563f8 Mon Sep 17 00:00:00 2001
From: dilanbhalla
Date: Wed, 1 Jul 2020 09:54:58 -0700
Subject: [PATCH 002/725] moved to experimental
---
.../{ => experimental}/Classes/NamingConventionsClasses.qhelp | 0
.../src/{ => experimental}/Classes/NamingConventionsClasses.ql | 0
.../Functions/NamingConventionsFunctions.qhelp | 0
.../{ => experimental}/Functions/NamingConventionsFunctions.ql | 0
.../query-tests/Classes/Naming/NamingConventionsClasses.expected | 0
.../query-tests/Classes/Naming/NamingConventionsClasses.py | 0
.../query-tests/Classes/Naming/NamingConventionsClasses.qlref | 1 +
.../Functions/general/NamingConventionsFunctions.expected | 0
.../query-tests/Functions/general/NamingConventionsFunctions.py | 0
.../Functions/general/NamingConventionsFunctions.qlref | 1 +
.../query-tests/Classes/Naming/NamingConventionsClasses.qlref | 1 -
.../Functions/general/NamingConventionsFunctions.qlref | 1 -
12 files changed, 2 insertions(+), 2 deletions(-)
rename python/ql/src/{ => experimental}/Classes/NamingConventionsClasses.qhelp (100%)
rename python/ql/src/{ => experimental}/Classes/NamingConventionsClasses.ql (100%)
rename python/ql/src/{ => experimental}/Functions/NamingConventionsFunctions.qhelp (100%)
rename python/ql/src/{ => experimental}/Functions/NamingConventionsFunctions.ql (100%)
rename python/ql/test/{ => experimental}/query-tests/Classes/Naming/NamingConventionsClasses.expected (100%)
rename python/ql/test/{ => experimental}/query-tests/Classes/Naming/NamingConventionsClasses.py (100%)
create mode 100644 python/ql/test/experimental/query-tests/Classes/Naming/NamingConventionsClasses.qlref
rename python/ql/test/{ => experimental}/query-tests/Functions/general/NamingConventionsFunctions.expected (100%)
rename python/ql/test/{ => experimental}/query-tests/Functions/general/NamingConventionsFunctions.py (100%)
create mode 100644 python/ql/test/experimental/query-tests/Functions/general/NamingConventionsFunctions.qlref
delete mode 100644 python/ql/test/query-tests/Classes/Naming/NamingConventionsClasses.qlref
delete mode 100644 python/ql/test/query-tests/Functions/general/NamingConventionsFunctions.qlref
diff --git a/python/ql/src/Classes/NamingConventionsClasses.qhelp b/python/ql/src/experimental/Classes/NamingConventionsClasses.qhelp
similarity index 100%
rename from python/ql/src/Classes/NamingConventionsClasses.qhelp
rename to python/ql/src/experimental/Classes/NamingConventionsClasses.qhelp
diff --git a/python/ql/src/Classes/NamingConventionsClasses.ql b/python/ql/src/experimental/Classes/NamingConventionsClasses.ql
similarity index 100%
rename from python/ql/src/Classes/NamingConventionsClasses.ql
rename to python/ql/src/experimental/Classes/NamingConventionsClasses.ql
diff --git a/python/ql/src/Functions/NamingConventionsFunctions.qhelp b/python/ql/src/experimental/Functions/NamingConventionsFunctions.qhelp
similarity index 100%
rename from python/ql/src/Functions/NamingConventionsFunctions.qhelp
rename to python/ql/src/experimental/Functions/NamingConventionsFunctions.qhelp
diff --git a/python/ql/src/Functions/NamingConventionsFunctions.ql b/python/ql/src/experimental/Functions/NamingConventionsFunctions.ql
similarity index 100%
rename from python/ql/src/Functions/NamingConventionsFunctions.ql
rename to python/ql/src/experimental/Functions/NamingConventionsFunctions.ql
diff --git a/python/ql/test/query-tests/Classes/Naming/NamingConventionsClasses.expected b/python/ql/test/experimental/query-tests/Classes/Naming/NamingConventionsClasses.expected
similarity index 100%
rename from python/ql/test/query-tests/Classes/Naming/NamingConventionsClasses.expected
rename to python/ql/test/experimental/query-tests/Classes/Naming/NamingConventionsClasses.expected
diff --git a/python/ql/test/query-tests/Classes/Naming/NamingConventionsClasses.py b/python/ql/test/experimental/query-tests/Classes/Naming/NamingConventionsClasses.py
similarity index 100%
rename from python/ql/test/query-tests/Classes/Naming/NamingConventionsClasses.py
rename to python/ql/test/experimental/query-tests/Classes/Naming/NamingConventionsClasses.py
diff --git a/python/ql/test/experimental/query-tests/Classes/Naming/NamingConventionsClasses.qlref b/python/ql/test/experimental/query-tests/Classes/Naming/NamingConventionsClasses.qlref
new file mode 100644
index 00000000000..7ed945d782c
--- /dev/null
+++ b/python/ql/test/experimental/query-tests/Classes/Naming/NamingConventionsClasses.qlref
@@ -0,0 +1 @@
+experimental/Classes/NamingConventionsClasses.ql
\ No newline at end of file
diff --git a/python/ql/test/query-tests/Functions/general/NamingConventionsFunctions.expected b/python/ql/test/experimental/query-tests/Functions/general/NamingConventionsFunctions.expected
similarity index 100%
rename from python/ql/test/query-tests/Functions/general/NamingConventionsFunctions.expected
rename to python/ql/test/experimental/query-tests/Functions/general/NamingConventionsFunctions.expected
diff --git a/python/ql/test/query-tests/Functions/general/NamingConventionsFunctions.py b/python/ql/test/experimental/query-tests/Functions/general/NamingConventionsFunctions.py
similarity index 100%
rename from python/ql/test/query-tests/Functions/general/NamingConventionsFunctions.py
rename to python/ql/test/experimental/query-tests/Functions/general/NamingConventionsFunctions.py
diff --git a/python/ql/test/experimental/query-tests/Functions/general/NamingConventionsFunctions.qlref b/python/ql/test/experimental/query-tests/Functions/general/NamingConventionsFunctions.qlref
new file mode 100644
index 00000000000..0204694de0a
--- /dev/null
+++ b/python/ql/test/experimental/query-tests/Functions/general/NamingConventionsFunctions.qlref
@@ -0,0 +1 @@
+experimental/Functions/NamingConventionsFunctions.ql
\ No newline at end of file
diff --git a/python/ql/test/query-tests/Classes/Naming/NamingConventionsClasses.qlref b/python/ql/test/query-tests/Classes/Naming/NamingConventionsClasses.qlref
deleted file mode 100644
index 01ad29859da..00000000000
--- a/python/ql/test/query-tests/Classes/Naming/NamingConventionsClasses.qlref
+++ /dev/null
@@ -1 +0,0 @@
-Classes/NamingConventionsClasses.ql
\ No newline at end of file
diff --git a/python/ql/test/query-tests/Functions/general/NamingConventionsFunctions.qlref b/python/ql/test/query-tests/Functions/general/NamingConventionsFunctions.qlref
deleted file mode 100644
index da4780ecbe9..00000000000
--- a/python/ql/test/query-tests/Functions/general/NamingConventionsFunctions.qlref
+++ /dev/null
@@ -1 +0,0 @@
-Functions/NamingConventionsFunctions.ql
\ No newline at end of file
From 26b030f8ccaf1b1922623a035e15cef239ec7314 Mon Sep 17 00:00:00 2001
From: dilanbhalla
Date: Tue, 7 Jul 2020 10:52:26 -0700
Subject: [PATCH 003/725] fixed pr suggestions
---
.../Classes/NamingConventionsClasses.qhelp | 2 +-
.../Classes/NamingConventionsClasses.ql | 14 ++++++++++----
.../Functions/NamingConventionsFunctions.qhelp | 2 +-
.../Functions/NamingConventionsFunctions.ql | 14 ++++++++++----
4 files changed, 22 insertions(+), 10 deletions(-)
diff --git a/python/ql/src/experimental/Classes/NamingConventionsClasses.qhelp b/python/ql/src/experimental/Classes/NamingConventionsClasses.qhelp
index 7a6c0ca13dd..1a7f4bc45a4 100644
--- a/python/ql/src/experimental/Classes/NamingConventionsClasses.qhelp
+++ b/python/ql/src/experimental/Classes/NamingConventionsClasses.qhelp
@@ -22,7 +22,7 @@ Write the class name beginning with an uppercase letter. For example, clas
Guido van Rossum, Barry Warsaw, Nick Coghlan PEP 8 -- Style Guide for Python Code
-
+ Python Class Names
diff --git a/python/ql/src/experimental/Classes/NamingConventionsClasses.ql b/python/ql/src/experimental/Classes/NamingConventionsClasses.ql
index a1744f5d3e6..d4919c3ece8 100644
--- a/python/ql/src/experimental/Classes/NamingConventionsClasses.ql
+++ b/python/ql/src/experimental/Classes/NamingConventionsClasses.ql
@@ -3,15 +3,21 @@
* @description A class name that begins with a lowercase letter decreases readability.
* @kind problem
* @problem.severity recommendation
- * @precision medium
- * @id python/misnamed-type
+ * @id py/misnamed-class
* @tags maintainability
*/
import python
-from Class c
+from Class c, string first_char
where
c.inSource() and
- not c.getName().substring(0, 1).toUpperCase() = c.getName().substring(0, 1)
+ first_char = c.getName().prefix(1) and
+ not first_char = first_char.toUpperCase() and
+ not exists(Class c1, string first_char1 |
+ c1 != c and
+ c1.getLocation().getFile() = c.getLocation().getFile() and
+ first_char1 = c1.getName().prefix(1) and
+ not first_char1 = first_char1.toUpperCase()
+ )
select c, "Class names should start in uppercase."
diff --git a/python/ql/src/experimental/Functions/NamingConventionsFunctions.qhelp b/python/ql/src/experimental/Functions/NamingConventionsFunctions.qhelp
index 15014045542..46d948592ff 100644
--- a/python/ql/src/experimental/Functions/NamingConventionsFunctions.qhelp
+++ b/python/ql/src/experimental/Functions/NamingConventionsFunctions.qhelp
@@ -22,7 +22,7 @@ Write the function name beginning with an lowercase letter. For example, j
Guido van Rossum, Barry Warsaw, Nick Coghlan PEP 8 -- Style Guide for Python Code
-
+ Python Function and Variable Names
diff --git a/python/ql/src/experimental/Functions/NamingConventionsFunctions.ql b/python/ql/src/experimental/Functions/NamingConventionsFunctions.ql
index 3ed619bc084..80dc0e99cd8 100644
--- a/python/ql/src/experimental/Functions/NamingConventionsFunctions.ql
+++ b/python/ql/src/experimental/Functions/NamingConventionsFunctions.ql
@@ -3,15 +3,21 @@
* @description A function name that begins with an uppercase letter decreases readability.
* @kind problem
* @problem.severity recommendation
- * @precision medium
- * @id python/misnamed-function
+ * @id py/misnamed-function
* @tags maintainability
*/
import python
-from Function f
+from Function f, string first_char
where
f.inSource() and
- not f.getName().substring(0, 1).toLowerCase() = f.getName().substring(0, 1)
+ first_char = f.getName().prefix(1) and
+ not first_char = first_char.toLowerCase() and
+ not exists(Function f1, string first_char1 |
+ f1 != f and
+ f1.getLocation().getFile() = f.getLocation().getFile() and
+ first_char1 = f1.getName().prefix(1) and
+ not first_char1 = first_char1.toLowerCase()
+ )
select f, "Function names should start in lowercase."
From 7d79be71d1fbac90c30482142a33b48388da9eff Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Mon, 27 Apr 2020 13:57:30 -0700
Subject: [PATCH 004/725] C++: taint tracking conf in DefaultTaintTracking
Switch from using additional flow steps with a DataFlow::Configuration
in DefaultTaintTracking to using a TaintTracking::Configuration. This
makes future improvements to TaintTracking::Configuration reflected in
DefaultTaintTracking without further effort. It also removes the
predictability constraint in DefaultTaintTracking, which increases the
number of results, with both new true positives and new false positives.
Those may need to be addressed on a per-query basis.
There are some additional regressions from losing pointer/object
conflation for arguments. Those can be worked around by adding that
conflation to TaintTracking::Configuration until precise indirect
parameter flow is ready.
---
.../cpp/ir/dataflow/DefaultTaintTracking.qll | 29 ++--
.../CWE-022/semmle/tests/TaintedPath.expected | 10 --
.../UncontrolledProcessOperation.expected | 48 ------
.../CWE-134/semmle/argv/argvLocal.expected | 156 ------------------
.../CWE-134/semmle/funcs/funcsLocal.expected | 52 ------
...olledFormatStringThroughGlobalVar.expected | 3 -
.../ArithmeticUncontrolled.expected | 17 ++
.../CWE/CWE-190/semmle/uncontrolled/test.cpp | 2 +-
.../TaintedCondition.expected | 26 ++-
9 files changed, 42 insertions(+), 301 deletions(-)
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
index 5129078545c..ae2066398a1 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
@@ -9,6 +9,7 @@ private import semmle.code.cpp.ir.dataflow.internal.DataFlowDispatch as Dispatch
private import semmle.code.cpp.controlflow.IRGuards
private import semmle.code.cpp.models.interfaces.Taint
private import semmle.code.cpp.models.interfaces.DataFlow
+private import semmle.code.cpp.ir.dataflow.TaintTracking2
/**
* A predictable instruction is one where an external user can predict
@@ -113,7 +114,7 @@ private class ToGlobalVarTaintTrackingCfg extends DataFlow::Configuration {
override predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
}
-private class FromGlobalVarTaintTrackingCfg extends DataFlow2::Configuration {
+private class FromGlobalVarTaintTrackingCfg extends DataFlow3::Configuration {
FromGlobalVarTaintTrackingCfg() { this = "FromGlobalVarTaintTrackingCfg" }
override predicate isSource(DataFlow::Node source) {
@@ -560,7 +561,7 @@ module TaintedWithPath {
string toString() { result = "TaintTrackingConfiguration" }
}
- private class AdjustedConfiguration extends DataFlow3::Configuration {
+ private class AdjustedConfiguration extends TaintTracking2::Configuration {
AdjustedConfiguration() { this = "AdjustedConfiguration" }
override predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
@@ -569,9 +570,7 @@ module TaintedWithPath {
exists(TaintTrackingConfiguration cfg | cfg.isSink(adjustedSink(sink)))
}
- override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
- commonTaintStep(n1, n2)
- or
+ override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
exists(TaintTrackingConfiguration cfg | cfg.taintThroughGlobals() |
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
or
@@ -579,9 +578,9 @@ module TaintedWithPath {
)
}
- override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
+ override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
- override predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
+ override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
}
/*
@@ -599,11 +598,11 @@ module TaintedWithPath {
*/
private newtype TPathNode =
- TWrapPathNode(DataFlow3::PathNode n) or
+ TWrapPathNode(DataFlow2::PathNode n) or
// There's a single newtype constructor for both sources and sinks since
// that makes it easiest to deal with the case where source = sink.
TEndpointPathNode(Element e) {
- exists(AdjustedConfiguration cfg, DataFlow3::Node sourceNode, DataFlow3::Node sinkNode |
+ exists(AdjustedConfiguration cfg, DataFlow2::Node sourceNode, DataFlow2::Node sinkNode |
cfg.hasFlow(sourceNode, sinkNode)
|
sourceNode = getNodeForSource(e)
@@ -633,7 +632,7 @@ module TaintedWithPath {
}
private class WrapPathNode extends PathNode, TWrapPathNode {
- DataFlow3::PathNode inner() { this = TWrapPathNode(result) }
+ DataFlow2::PathNode inner() { this = TWrapPathNode(result) }
override string toString() { result = this.inner().toString() }
@@ -671,25 +670,25 @@ module TaintedWithPath {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
query predicate edges(PathNode a, PathNode b) {
- DataFlow3::PathGraph::edges(a.(WrapPathNode).inner(), b.(WrapPathNode).inner())
+ DataFlow2::PathGraph::edges(a.(WrapPathNode).inner(), b.(WrapPathNode).inner())
or
// To avoid showing trivial-looking steps, we _replace_ the last node instead
// of adding an edge out of it.
exists(WrapPathNode sinkNode |
- DataFlow3::PathGraph::edges(a.(WrapPathNode).inner(), sinkNode.inner()) and
+ DataFlow2::PathGraph::edges(a.(WrapPathNode).inner(), sinkNode.inner()) and
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
)
or
// Same for the first node
exists(WrapPathNode sourceNode |
- DataFlow3::PathGraph::edges(sourceNode.inner(), b.(WrapPathNode).inner()) and
+ DataFlow2::PathGraph::edges(sourceNode.inner(), b.(WrapPathNode).inner()) and
sourceNode.inner().getNode() = getNodeForSource(a.(InitialPathNode).inner())
)
or
// Finally, handle the case where the path goes directly from a source to a
// sink, meaning that they both need to be translated.
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
- DataFlow3::PathGraph::edges(sourceNode.inner(), sinkNode.inner()) and
+ DataFlow2::PathGraph::edges(sourceNode.inner(), sinkNode.inner()) and
sourceNode.inner().getNode() = getNodeForSource(a.(InitialPathNode).inner()) and
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
)
@@ -711,7 +710,7 @@ module TaintedWithPath {
* the computation.
*/
predicate taintedWithPath(Expr source, Element tainted, PathNode sourceNode, PathNode sinkNode) {
- exists(AdjustedConfiguration cfg, DataFlow3::Node flowSource, DataFlow3::Node flowSink |
+ exists(AdjustedConfiguration cfg, DataFlow2::Node flowSource, DataFlow2::Node flowSink |
source = sourceNode.(InitialPathNode).inner() and
flowSource = getNodeForSource(source) and
cfg.hasFlow(flowSource, flowSink) and
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected
index d7db531fbde..58e3dda0964 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected
@@ -1,13 +1,3 @@
edges
-| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | (const char *)... |
-| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | (const char *)... |
-| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName |
-| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName |
nodes
-| test.c:9:23:9:26 | argv | semmle.label | argv |
-| test.c:9:23:9:26 | argv | semmle.label | argv |
-| test.c:17:11:17:18 | (const char *)... | semmle.label | (const char *)... |
-| test.c:17:11:17:18 | (const char *)... | semmle.label | (const char *)... |
-| test.c:17:11:17:18 | fileName | semmle.label | fileName |
#select
-| test.c:17:11:17:18 | fileName | test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName | This argument to a file access function is derived from $@ and then passed to fopen(filename) | test.c:9:23:9:26 | argv | user input (argv) |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected
index 0c2778abaf4..c53e4f5be87 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected
@@ -1,43 +1,17 @@
edges
-| test.cpp:24:30:24:36 | *command | test.cpp:26:10:26:16 | command |
-| test.cpp:24:30:24:36 | *command | test.cpp:26:10:26:16 | command |
| test.cpp:24:30:24:36 | command | test.cpp:26:10:26:16 | command |
| test.cpp:24:30:24:36 | command | test.cpp:26:10:26:16 | command |
-| test.cpp:29:30:29:36 | *command | test.cpp:31:10:31:16 | command |
-| test.cpp:29:30:29:36 | *command | test.cpp:31:10:31:16 | command |
| test.cpp:29:30:29:36 | command | test.cpp:31:10:31:16 | command |
| test.cpp:29:30:29:36 | command | test.cpp:31:10:31:16 | command |
-| test.cpp:42:18:42:23 | call to getenv | test.cpp:24:30:24:36 | *command |
| test.cpp:42:18:42:23 | call to getenv | test.cpp:24:30:24:36 | command |
-| test.cpp:42:18:42:34 | (const char *)... | test.cpp:24:30:24:36 | *command |
| test.cpp:42:18:42:34 | (const char *)... | test.cpp:24:30:24:36 | command |
-| test.cpp:43:18:43:23 | call to getenv | test.cpp:29:30:29:36 | *command |
| test.cpp:43:18:43:23 | call to getenv | test.cpp:29:30:29:36 | command |
-| test.cpp:43:18:43:34 | (const char *)... | test.cpp:29:30:29:36 | *command |
| test.cpp:43:18:43:34 | (const char *)... | test.cpp:29:30:29:36 | command |
-| test.cpp:56:12:56:17 | buffer | test.cpp:62:10:62:15 | (const char *)... |
-| test.cpp:56:12:56:17 | buffer | test.cpp:62:10:62:15 | buffer |
-| test.cpp:56:12:56:17 | buffer | test.cpp:63:10:63:13 | (const char *)... |
-| test.cpp:56:12:56:17 | buffer | test.cpp:63:10:63:13 | data |
-| test.cpp:56:12:56:17 | fgets output argument | test.cpp:62:10:62:15 | (const char *)... |
-| test.cpp:56:12:56:17 | fgets output argument | test.cpp:62:10:62:15 | buffer |
-| test.cpp:56:12:56:17 | fgets output argument | test.cpp:63:10:63:13 | (const char *)... |
-| test.cpp:56:12:56:17 | fgets output argument | test.cpp:63:10:63:13 | data |
-| test.cpp:76:12:76:17 | buffer | test.cpp:78:10:78:15 | (const char *)... |
-| test.cpp:76:12:76:17 | buffer | test.cpp:78:10:78:15 | buffer |
-| test.cpp:76:12:76:17 | buffer | test.cpp:79:10:79:13 | (const char *)... |
-| test.cpp:76:12:76:17 | buffer | test.cpp:79:10:79:13 | data |
-| test.cpp:76:12:76:17 | fgets output argument | test.cpp:78:10:78:15 | (const char *)... |
-| test.cpp:76:12:76:17 | fgets output argument | test.cpp:78:10:78:15 | buffer |
-| test.cpp:76:12:76:17 | fgets output argument | test.cpp:79:10:79:13 | (const char *)... |
-| test.cpp:76:12:76:17 | fgets output argument | test.cpp:79:10:79:13 | data |
nodes
-| test.cpp:24:30:24:36 | *command | semmle.label | *command |
| test.cpp:24:30:24:36 | command | semmle.label | command |
| test.cpp:26:10:26:16 | command | semmle.label | command |
| test.cpp:26:10:26:16 | command | semmle.label | command |
| test.cpp:26:10:26:16 | command | semmle.label | command |
-| test.cpp:29:30:29:36 | *command | semmle.label | *command |
| test.cpp:29:30:29:36 | command | semmle.label | command |
| test.cpp:31:10:31:16 | command | semmle.label | command |
| test.cpp:31:10:31:16 | command | semmle.label | command |
@@ -45,31 +19,9 @@ nodes
| test.cpp:42:7:42:16 | Argument 0 | semmle.label | Argument 0 |
| test.cpp:42:18:42:23 | call to getenv | semmle.label | call to getenv |
| test.cpp:42:18:42:34 | (const char *)... | semmle.label | (const char *)... |
-| test.cpp:42:18:42:34 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| test.cpp:43:7:43:16 | Argument 0 | semmle.label | Argument 0 |
| test.cpp:43:18:43:23 | call to getenv | semmle.label | call to getenv |
| test.cpp:43:18:43:34 | (const char *)... | semmle.label | (const char *)... |
-| test.cpp:43:18:43:34 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| test.cpp:56:12:56:17 | buffer | semmle.label | buffer |
-| test.cpp:56:12:56:17 | fgets output argument | semmle.label | fgets output argument |
-| test.cpp:62:10:62:15 | (const char *)... | semmle.label | (const char *)... |
-| test.cpp:62:10:62:15 | (const char *)... | semmle.label | (const char *)... |
-| test.cpp:62:10:62:15 | buffer | semmle.label | buffer |
-| test.cpp:63:10:63:13 | (const char *)... | semmle.label | (const char *)... |
-| test.cpp:63:10:63:13 | (const char *)... | semmle.label | (const char *)... |
-| test.cpp:63:10:63:13 | data | semmle.label | data |
-| test.cpp:76:12:76:17 | buffer | semmle.label | buffer |
-| test.cpp:76:12:76:17 | fgets output argument | semmle.label | fgets output argument |
-| test.cpp:78:10:78:15 | (const char *)... | semmle.label | (const char *)... |
-| test.cpp:78:10:78:15 | (const char *)... | semmle.label | (const char *)... |
-| test.cpp:78:10:78:15 | buffer | semmle.label | buffer |
-| test.cpp:79:10:79:13 | (const char *)... | semmle.label | (const char *)... |
-| test.cpp:79:10:79:13 | (const char *)... | semmle.label | (const char *)... |
-| test.cpp:79:10:79:13 | data | semmle.label | data |
#select
| test.cpp:26:10:26:16 | command | test.cpp:42:18:42:23 | call to getenv | test.cpp:26:10:26:16 | command | The value of this argument may come from $@ and is being passed to system | test.cpp:42:18:42:23 | call to getenv | call to getenv |
| test.cpp:31:10:31:16 | command | test.cpp:43:18:43:23 | call to getenv | test.cpp:31:10:31:16 | command | The value of this argument may come from $@ and is being passed to system | test.cpp:43:18:43:23 | call to getenv | call to getenv |
-| test.cpp:62:10:62:15 | buffer | test.cpp:56:12:56:17 | buffer | test.cpp:62:10:62:15 | buffer | The value of this argument may come from $@ and is being passed to system | test.cpp:56:12:56:17 | buffer | buffer |
-| test.cpp:63:10:63:13 | data | test.cpp:56:12:56:17 | buffer | test.cpp:63:10:63:13 | data | The value of this argument may come from $@ and is being passed to system | test.cpp:56:12:56:17 | buffer | buffer |
-| test.cpp:78:10:78:15 | buffer | test.cpp:76:12:76:17 | buffer | test.cpp:78:10:78:15 | buffer | The value of this argument may come from $@ and is being passed to system | test.cpp:76:12:76:17 | buffer | buffer |
-| test.cpp:79:10:79:13 | data | test.cpp:76:12:76:17 | buffer | test.cpp:79:10:79:13 | data | The value of this argument may come from $@ and is being passed to system | test.cpp:76:12:76:17 | buffer | buffer |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected
index ac52436676c..72d1df8a59b 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected
@@ -61,69 +61,6 @@ edges
| argvLocal.c:106:9:106:13 | access to array | argvLocal.c:106:9:106:13 | access to array |
| argvLocal.c:110:9:110:11 | * ... | argvLocal.c:110:9:110:11 | (const char *)... |
| argvLocal.c:110:9:110:11 | * ... | argvLocal.c:110:9:110:11 | * ... |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:116:9:116:10 | (const char *)... |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:116:9:116:10 | (const char *)... |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:116:9:116:10 | i3 |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:116:9:116:10 | i3 |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | array to pointer conversion |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | array to pointer conversion |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | i3 |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | i3 |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | printWrapper output argument |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | printWrapper output argument |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:121:9:121:10 | (const char *)... |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:121:9:121:10 | (const char *)... |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:121:9:121:10 | i4 |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:121:9:121:10 | i4 |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | printWrapper output argument |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | printWrapper output argument |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:135:9:135:12 | (const char *)... |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:135:9:135:12 | (const char *)... |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:135:9:135:12 | ... ++ |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:135:9:135:12 | ... ++ |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:136:15:136:18 | -- ... |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:136:15:136:18 | -- ... |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:136:15:136:18 | -- ... |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:136:15:136:18 | -- ... |
-| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:121:9:121:10 | (const char *)... |
-| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:121:9:121:10 | i4 |
-| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:122:15:122:16 | i4 |
-| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:122:15:122:16 | i4 |
-| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:122:15:122:16 | printWrapper output argument |
-| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:135:9:135:12 | (const char *)... |
-| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:135:9:135:12 | ... ++ |
-| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:136:15:136:18 | -- ... |
-| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:136:15:136:18 | -- ... |
-| argvLocal.c:122:15:122:16 | printWrapper output argument | argvLocal.c:135:9:135:12 | (const char *)... |
-| argvLocal.c:122:15:122:16 | printWrapper output argument | argvLocal.c:135:9:135:12 | ... ++ |
-| argvLocal.c:122:15:122:16 | printWrapper output argument | argvLocal.c:136:15:136:18 | -- ... |
-| argvLocal.c:122:15:122:16 | printWrapper output argument | argvLocal.c:136:15:136:18 | -- ... |
-| argvLocal.c:126:10:126:13 | argv | argvLocal.c:127:9:127:10 | (const char *)... |
-| argvLocal.c:126:10:126:13 | argv | argvLocal.c:127:9:127:10 | (const char *)... |
-| argvLocal.c:126:10:126:13 | argv | argvLocal.c:127:9:127:10 | i5 |
-| argvLocal.c:126:10:126:13 | argv | argvLocal.c:127:9:127:10 | i5 |
-| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | array to pointer conversion |
-| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | array to pointer conversion |
-| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | i5 |
-| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | i5 |
-| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | printWrapper output argument |
-| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | printWrapper output argument |
-| argvLocal.c:126:10:126:13 | argv | argvLocal.c:131:9:131:14 | (const char *)... |
-| argvLocal.c:126:10:126:13 | argv | argvLocal.c:131:9:131:14 | (const char *)... |
-| argvLocal.c:126:10:126:13 | argv | argvLocal.c:131:9:131:14 | ... + ... |
-| argvLocal.c:126:10:126:13 | argv | argvLocal.c:131:9:131:14 | ... + ... |
-| argvLocal.c:126:10:126:13 | argv | argvLocal.c:132:15:132:20 | ... + ... |
-| argvLocal.c:126:10:126:13 | argv | argvLocal.c:132:15:132:20 | ... + ... |
-| argvLocal.c:126:10:126:13 | argv | argvLocal.c:132:15:132:20 | ... + ... |
-| argvLocal.c:126:10:126:13 | argv | argvLocal.c:132:15:132:20 | ... + ... |
-| argvLocal.c:128:15:128:16 | printWrapper output argument | argvLocal.c:131:9:131:14 | (const char *)... |
-| argvLocal.c:128:15:128:16 | printWrapper output argument | argvLocal.c:131:9:131:14 | ... + ... |
-| argvLocal.c:128:15:128:16 | printWrapper output argument | argvLocal.c:132:15:132:20 | ... + ... |
-| argvLocal.c:128:15:128:16 | printWrapper output argument | argvLocal.c:132:15:132:20 | ... + ... |
| argvLocal.c:149:11:149:14 | argv | argvLocal.c:150:9:150:10 | (const char *)... |
| argvLocal.c:149:11:149:14 | argv | argvLocal.c:150:9:150:10 | (const char *)... |
| argvLocal.c:149:11:149:14 | argv | argvLocal.c:150:9:150:10 | i8 |
@@ -134,22 +71,6 @@ edges
| argvLocal.c:149:11:149:14 | argv | argvLocal.c:151:15:151:16 | i8 |
| argvLocal.c:149:11:149:14 | argv | argvLocal.c:151:15:151:16 | i8 |
| argvLocal.c:149:11:149:14 | argv | argvLocal.c:151:15:151:16 | i8 |
-| argvLocal.c:156:23:156:26 | argv | argvLocal.c:157:9:157:10 | (const char *)... |
-| argvLocal.c:156:23:156:26 | argv | argvLocal.c:157:9:157:10 | (const char *)... |
-| argvLocal.c:156:23:156:26 | argv | argvLocal.c:157:9:157:10 | i9 |
-| argvLocal.c:156:23:156:26 | argv | argvLocal.c:157:9:157:10 | i9 |
-| argvLocal.c:156:23:156:26 | argv | argvLocal.c:158:15:158:16 | i9 |
-| argvLocal.c:156:23:156:26 | argv | argvLocal.c:158:15:158:16 | i9 |
-| argvLocal.c:156:23:156:26 | argv | argvLocal.c:158:15:158:16 | i9 |
-| argvLocal.c:156:23:156:26 | argv | argvLocal.c:158:15:158:16 | i9 |
-| argvLocal.c:163:22:163:25 | argv | argvLocal.c:164:9:164:11 | (const char *)... |
-| argvLocal.c:163:22:163:25 | argv | argvLocal.c:164:9:164:11 | (const char *)... |
-| argvLocal.c:163:22:163:25 | argv | argvLocal.c:164:9:164:11 | i91 |
-| argvLocal.c:163:22:163:25 | argv | argvLocal.c:164:9:164:11 | i91 |
-| argvLocal.c:163:22:163:25 | argv | argvLocal.c:165:15:165:17 | i91 |
-| argvLocal.c:163:22:163:25 | argv | argvLocal.c:165:15:165:17 | i91 |
-| argvLocal.c:163:22:163:25 | argv | argvLocal.c:165:15:165:17 | i91 |
-| argvLocal.c:163:22:163:25 | argv | argvLocal.c:165:15:165:17 | i91 |
| argvLocal.c:168:18:168:21 | argv | argvLocal.c:169:9:169:20 | (char *)... |
| argvLocal.c:168:18:168:21 | argv | argvLocal.c:169:9:169:20 | (char *)... |
| argvLocal.c:168:18:168:21 | argv | argvLocal.c:169:9:169:20 | (const char *)... |
@@ -165,10 +86,6 @@ edges
| argvLocal.c:168:18:168:21 | argv | argvLocal.c:170:24:170:26 | i10 |
| argvLocal.c:168:18:168:21 | argv | argvLocal.c:170:24:170:26 | i10 |
nodes
-| argvLocal.c:9:25:9:31 | *correct | semmle.label | *correct |
-| argvLocal.c:9:25:9:31 | correct | semmle.label | correct |
-| argvLocal.c:10:9:10:15 | Chi | semmle.label | Chi |
-| argvLocal.c:10:9:10:15 | Chi | semmle.label | Chi |
| argvLocal.c:95:9:95:12 | argv | semmle.label | argv |
| argvLocal.c:95:9:95:12 | argv | semmle.label | argv |
| argvLocal.c:95:9:95:15 | (const char *)... | semmle.label | (const char *)... |
@@ -209,49 +126,6 @@ nodes
| argvLocal.c:111:15:111:17 | * ... | semmle.label | * ... |
| argvLocal.c:111:15:111:17 | * ... | semmle.label | * ... |
| argvLocal.c:111:15:111:17 | * ... | semmle.label | * ... |
-| argvLocal.c:115:13:115:16 | argv | semmle.label | argv |
-| argvLocal.c:115:13:115:16 | argv | semmle.label | argv |
-| argvLocal.c:116:9:116:10 | (const char *)... | semmle.label | (const char *)... |
-| argvLocal.c:116:9:116:10 | (const char *)... | semmle.label | (const char *)... |
-| argvLocal.c:116:9:116:10 | i3 | semmle.label | i3 |
-| argvLocal.c:117:2:117:13 | Argument 0 | semmle.label | Argument 0 |
-| argvLocal.c:117:15:117:16 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| argvLocal.c:117:15:117:16 | array to pointer conversion | semmle.label | array to pointer conversion |
-| argvLocal.c:117:15:117:16 | array to pointer conversion | semmle.label | array to pointer conversion |
-| argvLocal.c:117:15:117:16 | i3 | semmle.label | i3 |
-| argvLocal.c:117:15:117:16 | printWrapper output argument | semmle.label | printWrapper output argument |
-| argvLocal.c:121:9:121:10 | (const char *)... | semmle.label | (const char *)... |
-| argvLocal.c:121:9:121:10 | (const char *)... | semmle.label | (const char *)... |
-| argvLocal.c:121:9:121:10 | i4 | semmle.label | i4 |
-| argvLocal.c:122:2:122:13 | Argument 0 | semmle.label | Argument 0 |
-| argvLocal.c:122:15:122:16 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| argvLocal.c:122:15:122:16 | i4 | semmle.label | i4 |
-| argvLocal.c:122:15:122:16 | i4 | semmle.label | i4 |
-| argvLocal.c:122:15:122:16 | i4 | semmle.label | i4 |
-| argvLocal.c:122:15:122:16 | printWrapper output argument | semmle.label | printWrapper output argument |
-| argvLocal.c:126:10:126:13 | argv | semmle.label | argv |
-| argvLocal.c:126:10:126:13 | argv | semmle.label | argv |
-| argvLocal.c:127:9:127:10 | (const char *)... | semmle.label | (const char *)... |
-| argvLocal.c:127:9:127:10 | (const char *)... | semmle.label | (const char *)... |
-| argvLocal.c:127:9:127:10 | i5 | semmle.label | i5 |
-| argvLocal.c:128:2:128:13 | Argument 0 | semmle.label | Argument 0 |
-| argvLocal.c:128:15:128:16 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| argvLocal.c:128:15:128:16 | array to pointer conversion | semmle.label | array to pointer conversion |
-| argvLocal.c:128:15:128:16 | array to pointer conversion | semmle.label | array to pointer conversion |
-| argvLocal.c:128:15:128:16 | i5 | semmle.label | i5 |
-| argvLocal.c:128:15:128:16 | printWrapper output argument | semmle.label | printWrapper output argument |
-| argvLocal.c:131:9:131:14 | (const char *)... | semmle.label | (const char *)... |
-| argvLocal.c:131:9:131:14 | (const char *)... | semmle.label | (const char *)... |
-| argvLocal.c:131:9:131:14 | ... + ... | semmle.label | ... + ... |
-| argvLocal.c:132:15:132:20 | ... + ... | semmle.label | ... + ... |
-| argvLocal.c:132:15:132:20 | ... + ... | semmle.label | ... + ... |
-| argvLocal.c:132:15:132:20 | ... + ... | semmle.label | ... + ... |
-| argvLocal.c:135:9:135:12 | (const char *)... | semmle.label | (const char *)... |
-| argvLocal.c:135:9:135:12 | (const char *)... | semmle.label | (const char *)... |
-| argvLocal.c:135:9:135:12 | ... ++ | semmle.label | ... ++ |
-| argvLocal.c:136:15:136:18 | -- ... | semmle.label | -- ... |
-| argvLocal.c:136:15:136:18 | -- ... | semmle.label | -- ... |
-| argvLocal.c:136:15:136:18 | -- ... | semmle.label | -- ... |
| argvLocal.c:144:9:144:10 | (const char *)... | semmle.label | (const char *)... |
| argvLocal.c:144:9:144:10 | (const char *)... | semmle.label | (const char *)... |
| argvLocal.c:144:9:144:10 | i7 | semmle.label | i7 |
@@ -270,22 +144,6 @@ nodes
| argvLocal.c:151:15:151:16 | i8 | semmle.label | i8 |
| argvLocal.c:151:15:151:16 | i8 | semmle.label | i8 |
| argvLocal.c:151:15:151:16 | i8 | semmle.label | i8 |
-| argvLocal.c:156:23:156:26 | argv | semmle.label | argv |
-| argvLocal.c:156:23:156:26 | argv | semmle.label | argv |
-| argvLocal.c:157:9:157:10 | (const char *)... | semmle.label | (const char *)... |
-| argvLocal.c:157:9:157:10 | (const char *)... | semmle.label | (const char *)... |
-| argvLocal.c:157:9:157:10 | i9 | semmle.label | i9 |
-| argvLocal.c:158:15:158:16 | i9 | semmle.label | i9 |
-| argvLocal.c:158:15:158:16 | i9 | semmle.label | i9 |
-| argvLocal.c:158:15:158:16 | i9 | semmle.label | i9 |
-| argvLocal.c:163:22:163:25 | argv | semmle.label | argv |
-| argvLocal.c:163:22:163:25 | argv | semmle.label | argv |
-| argvLocal.c:164:9:164:11 | (const char *)... | semmle.label | (const char *)... |
-| argvLocal.c:164:9:164:11 | (const char *)... | semmle.label | (const char *)... |
-| argvLocal.c:164:9:164:11 | i91 | semmle.label | i91 |
-| argvLocal.c:165:15:165:17 | i91 | semmle.label | i91 |
-| argvLocal.c:165:15:165:17 | i91 | semmle.label | i91 |
-| argvLocal.c:165:15:165:17 | i91 | semmle.label | i91 |
| argvLocal.c:168:18:168:21 | argv | semmle.label | argv |
| argvLocal.c:168:18:168:21 | argv | semmle.label | argv |
| argvLocal.c:169:9:169:20 | (char *)... | semmle.label | (char *)... |
@@ -309,23 +167,9 @@ nodes
| argvLocal.c:107:15:107:19 | access to array | argvLocal.c:105:14:105:17 | argv | argvLocal.c:107:15:107:19 | access to array | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:105:14:105:17 | argv | argv |
| argvLocal.c:110:9:110:11 | * ... | argvLocal.c:105:14:105:17 | argv | argvLocal.c:110:9:110:11 | * ... | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:105:14:105:17 | argv | argv |
| argvLocal.c:111:15:111:17 | * ... | argvLocal.c:105:14:105:17 | argv | argvLocal.c:111:15:111:17 | * ... | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:105:14:105:17 | argv | argv |
-| argvLocal.c:116:9:116:10 | i3 | argvLocal.c:115:13:115:16 | argv | argvLocal.c:116:9:116:10 | i3 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:115:13:115:16 | argv | argv |
-| argvLocal.c:117:15:117:16 | i3 | argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | i3 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:115:13:115:16 | argv | argv |
-| argvLocal.c:121:9:121:10 | i4 | argvLocal.c:115:13:115:16 | argv | argvLocal.c:121:9:121:10 | i4 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:115:13:115:16 | argv | argv |
-| argvLocal.c:122:15:122:16 | i4 | argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:115:13:115:16 | argv | argv |
-| argvLocal.c:127:9:127:10 | i5 | argvLocal.c:126:10:126:13 | argv | argvLocal.c:127:9:127:10 | i5 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:126:10:126:13 | argv | argv |
-| argvLocal.c:128:15:128:16 | i5 | argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | i5 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:126:10:126:13 | argv | argv |
-| argvLocal.c:131:9:131:14 | ... + ... | argvLocal.c:126:10:126:13 | argv | argvLocal.c:131:9:131:14 | ... + ... | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:126:10:126:13 | argv | argv |
-| argvLocal.c:132:15:132:20 | ... + ... | argvLocal.c:126:10:126:13 | argv | argvLocal.c:132:15:132:20 | ... + ... | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:126:10:126:13 | argv | argv |
-| argvLocal.c:135:9:135:12 | ... ++ | argvLocal.c:115:13:115:16 | argv | argvLocal.c:135:9:135:12 | ... ++ | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:115:13:115:16 | argv | argv |
-| argvLocal.c:136:15:136:18 | -- ... | argvLocal.c:115:13:115:16 | argv | argvLocal.c:136:15:136:18 | -- ... | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:115:13:115:16 | argv | argv |
| argvLocal.c:144:9:144:10 | i7 | argvLocal.c:100:7:100:10 | argv | argvLocal.c:144:9:144:10 | i7 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:100:7:100:10 | argv | argv |
| argvLocal.c:145:15:145:16 | i7 | argvLocal.c:100:7:100:10 | argv | argvLocal.c:145:15:145:16 | i7 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:100:7:100:10 | argv | argv |
| argvLocal.c:150:9:150:10 | i8 | argvLocal.c:149:11:149:14 | argv | argvLocal.c:150:9:150:10 | i8 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:149:11:149:14 | argv | argv |
| argvLocal.c:151:15:151:16 | i8 | argvLocal.c:149:11:149:14 | argv | argvLocal.c:151:15:151:16 | i8 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:149:11:149:14 | argv | argv |
-| argvLocal.c:157:9:157:10 | i9 | argvLocal.c:156:23:156:26 | argv | argvLocal.c:157:9:157:10 | i9 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:156:23:156:26 | argv | argv |
-| argvLocal.c:158:15:158:16 | i9 | argvLocal.c:156:23:156:26 | argv | argvLocal.c:158:15:158:16 | i9 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:156:23:156:26 | argv | argv |
-| argvLocal.c:164:9:164:11 | i91 | argvLocal.c:163:22:163:25 | argv | argvLocal.c:164:9:164:11 | i91 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:163:22:163:25 | argv | argv |
-| argvLocal.c:165:15:165:17 | i91 | argvLocal.c:163:22:163:25 | argv | argvLocal.c:165:15:165:17 | i91 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:163:22:163:25 | argv | argv |
| argvLocal.c:169:18:169:20 | i10 | argvLocal.c:168:18:168:21 | argv | argvLocal.c:169:18:169:20 | i10 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:168:18:168:21 | argv | argv |
| argvLocal.c:170:24:170:26 | i10 | argvLocal.c:168:18:168:21 | argv | argvLocal.c:170:24:170:26 | i10 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:168:18:168:21 | argv | argv |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/funcs/funcsLocal.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/funcs/funcsLocal.expected
index a05e392ecf2..1beb428a832 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/funcs/funcsLocal.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/funcs/funcsLocal.expected
@@ -1,83 +1,31 @@
edges
-| funcsLocal.c:16:8:16:9 | fread output argument | funcsLocal.c:17:9:17:10 | (const char *)... |
-| funcsLocal.c:16:8:16:9 | fread output argument | funcsLocal.c:17:9:17:10 | i1 |
-| funcsLocal.c:16:8:16:9 | fread output argument | funcsLocal.c:58:9:58:10 | (const char *)... |
-| funcsLocal.c:16:8:16:9 | fread output argument | funcsLocal.c:58:9:58:10 | e1 |
-| funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:17:9:17:10 | (const char *)... |
-| funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:17:9:17:10 | i1 |
-| funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:58:9:58:10 | (const char *)... |
-| funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:58:9:58:10 | e1 |
-| funcsLocal.c:26:8:26:9 | fgets output argument | funcsLocal.c:27:9:27:10 | (const char *)... |
-| funcsLocal.c:26:8:26:9 | fgets output argument | funcsLocal.c:27:9:27:10 | i3 |
-| funcsLocal.c:26:8:26:9 | i3 | funcsLocal.c:27:9:27:10 | (const char *)... |
-| funcsLocal.c:26:8:26:9 | i3 | funcsLocal.c:27:9:27:10 | i3 |
| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | (const char *)... |
| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | (const char *)... |
| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | i4 |
| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | i4 |
| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | i4 |
| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | i4 |
-| funcsLocal.c:31:19:31:21 | fgets output argument | funcsLocal.c:32:9:32:10 | (const char *)... |
-| funcsLocal.c:31:19:31:21 | fgets output argument | funcsLocal.c:32:9:32:10 | i4 |
-| funcsLocal.c:31:19:31:21 | i41 | funcsLocal.c:32:9:32:10 | (const char *)... |
-| funcsLocal.c:31:19:31:21 | i41 | funcsLocal.c:32:9:32:10 | i4 |
-| funcsLocal.c:36:7:36:8 | gets output argument | funcsLocal.c:37:9:37:10 | (const char *)... |
-| funcsLocal.c:36:7:36:8 | gets output argument | funcsLocal.c:37:9:37:10 | i5 |
-| funcsLocal.c:36:7:36:8 | i5 | funcsLocal.c:37:9:37:10 | (const char *)... |
-| funcsLocal.c:36:7:36:8 | i5 | funcsLocal.c:37:9:37:10 | i5 |
| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | (const char *)... |
| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | (const char *)... |
| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | i6 |
| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | i6 |
| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | i6 |
| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | i6 |
-| funcsLocal.c:41:18:41:20 | gets output argument | funcsLocal.c:42:9:42:10 | (const char *)... |
-| funcsLocal.c:41:18:41:20 | gets output argument | funcsLocal.c:42:9:42:10 | i6 |
-| funcsLocal.c:41:18:41:20 | i61 | funcsLocal.c:42:9:42:10 | (const char *)... |
-| funcsLocal.c:41:18:41:20 | i61 | funcsLocal.c:42:9:42:10 | i6 |
nodes
-| funcsLocal.c:16:8:16:9 | fread output argument | semmle.label | fread output argument |
-| funcsLocal.c:16:8:16:9 | i1 | semmle.label | i1 |
-| funcsLocal.c:17:9:17:10 | (const char *)... | semmle.label | (const char *)... |
-| funcsLocal.c:17:9:17:10 | (const char *)... | semmle.label | (const char *)... |
-| funcsLocal.c:17:9:17:10 | i1 | semmle.label | i1 |
-| funcsLocal.c:26:8:26:9 | fgets output argument | semmle.label | fgets output argument |
-| funcsLocal.c:26:8:26:9 | i3 | semmle.label | i3 |
-| funcsLocal.c:27:9:27:10 | (const char *)... | semmle.label | (const char *)... |
-| funcsLocal.c:27:9:27:10 | (const char *)... | semmle.label | (const char *)... |
-| funcsLocal.c:27:9:27:10 | i3 | semmle.label | i3 |
| funcsLocal.c:31:13:31:17 | call to fgets | semmle.label | call to fgets |
| funcsLocal.c:31:13:31:17 | call to fgets | semmle.label | call to fgets |
-| funcsLocal.c:31:19:31:21 | fgets output argument | semmle.label | fgets output argument |
-| funcsLocal.c:31:19:31:21 | i41 | semmle.label | i41 |
| funcsLocal.c:32:9:32:10 | (const char *)... | semmle.label | (const char *)... |
| funcsLocal.c:32:9:32:10 | (const char *)... | semmle.label | (const char *)... |
| funcsLocal.c:32:9:32:10 | i4 | semmle.label | i4 |
| funcsLocal.c:32:9:32:10 | i4 | semmle.label | i4 |
| funcsLocal.c:32:9:32:10 | i4 | semmle.label | i4 |
-| funcsLocal.c:36:7:36:8 | gets output argument | semmle.label | gets output argument |
-| funcsLocal.c:36:7:36:8 | i5 | semmle.label | i5 |
-| funcsLocal.c:37:9:37:10 | (const char *)... | semmle.label | (const char *)... |
-| funcsLocal.c:37:9:37:10 | (const char *)... | semmle.label | (const char *)... |
-| funcsLocal.c:37:9:37:10 | i5 | semmle.label | i5 |
| funcsLocal.c:41:13:41:16 | call to gets | semmle.label | call to gets |
| funcsLocal.c:41:13:41:16 | call to gets | semmle.label | call to gets |
-| funcsLocal.c:41:18:41:20 | gets output argument | semmle.label | gets output argument |
-| funcsLocal.c:41:18:41:20 | i61 | semmle.label | i61 |
| funcsLocal.c:42:9:42:10 | (const char *)... | semmle.label | (const char *)... |
| funcsLocal.c:42:9:42:10 | (const char *)... | semmle.label | (const char *)... |
| funcsLocal.c:42:9:42:10 | i6 | semmle.label | i6 |
| funcsLocal.c:42:9:42:10 | i6 | semmle.label | i6 |
| funcsLocal.c:42:9:42:10 | i6 | semmle.label | i6 |
-| funcsLocal.c:58:9:58:10 | (const char *)... | semmle.label | (const char *)... |
-| funcsLocal.c:58:9:58:10 | (const char *)... | semmle.label | (const char *)... |
-| funcsLocal.c:58:9:58:10 | e1 | semmle.label | e1 |
#select
-| funcsLocal.c:17:9:17:10 | i1 | funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:17:9:17:10 | i1 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:16:8:16:9 | i1 | fread |
-| funcsLocal.c:27:9:27:10 | i3 | funcsLocal.c:26:8:26:9 | i3 | funcsLocal.c:27:9:27:10 | i3 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:26:8:26:9 | i3 | fgets |
| funcsLocal.c:32:9:32:10 | i4 | funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | i4 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:31:13:31:17 | call to fgets | fgets |
-| funcsLocal.c:32:9:32:10 | i4 | funcsLocal.c:31:19:31:21 | i41 | funcsLocal.c:32:9:32:10 | i4 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:31:19:31:21 | i41 | fgets |
-| funcsLocal.c:37:9:37:10 | i5 | funcsLocal.c:36:7:36:8 | i5 | funcsLocal.c:37:9:37:10 | i5 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:36:7:36:8 | i5 | gets |
| funcsLocal.c:42:9:42:10 | i6 | funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | i6 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:41:13:41:16 | call to gets | gets |
-| funcsLocal.c:42:9:42:10 | i6 | funcsLocal.c:41:18:41:20 | i61 | funcsLocal.c:42:9:42:10 | i6 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:41:18:41:20 | i61 | gets |
-| funcsLocal.c:58:9:58:10 | e1 | funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:58:9:58:10 | e1 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:16:8:16:9 | i1 | fread |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected
index 826a659755d..598d7df193c 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected
@@ -13,8 +13,6 @@ edges
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 |
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 |
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 |
-| globalVars.c:11:22:11:25 | *argv | globalVars.c:12:2:12:15 | Store |
-| globalVars.c:11:22:11:25 | argv | globalVars.c:11:22:11:25 | *argv |
| globalVars.c:11:22:11:25 | argv | globalVars.c:12:2:12:15 | Store |
| globalVars.c:12:2:12:15 | Store | globalVars.c:8:7:8:10 | copy |
| globalVars.c:15:21:15:23 | val | globalVars.c:16:2:16:12 | Store |
@@ -33,7 +31,6 @@ edges
nodes
| globalVars.c:8:7:8:10 | copy | semmle.label | copy |
| globalVars.c:9:7:9:11 | copy2 | semmle.label | copy2 |
-| globalVars.c:11:22:11:25 | *argv | semmle.label | *argv |
| globalVars.c:11:22:11:25 | argv | semmle.label | argv |
| globalVars.c:12:2:12:15 | Store | semmle.label | Store |
| globalVars.c:15:21:15:23 | val | semmle.label | val |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/ArithmeticUncontrolled.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/ArithmeticUncontrolled.expected
index 6a897653265..0ae5dc31fae 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/ArithmeticUncontrolled.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/ArithmeticUncontrolled.expected
@@ -35,6 +35,14 @@ edges
| test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r |
| test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r |
| test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r |
+| test.c:81:14:81:17 | call to rand | test.c:83:9:83:9 | r |
+| test.c:81:14:81:17 | call to rand | test.c:83:9:83:9 | r |
+| test.c:81:14:81:17 | call to rand | test.c:83:9:83:9 | r |
+| test.c:81:14:81:17 | call to rand | test.c:83:9:83:9 | r |
+| test.c:81:23:81:26 | call to rand | test.c:83:9:83:9 | r |
+| test.c:81:23:81:26 | call to rand | test.c:83:9:83:9 | r |
+| test.c:81:23:81:26 | call to rand | test.c:83:9:83:9 | r |
+| test.c:81:23:81:26 | call to rand | test.c:83:9:83:9 | r |
| test.c:99:14:99:19 | call to rand | test.c:100:5:100:5 | r |
| test.c:99:14:99:19 | call to rand | test.c:100:5:100:5 | r |
| test.c:99:14:99:19 | call to rand | test.c:100:5:100:5 | r |
@@ -102,6 +110,13 @@ nodes
| test.c:77:9:77:9 | r | semmle.label | r |
| test.c:77:9:77:9 | r | semmle.label | r |
| test.c:77:9:77:9 | r | semmle.label | r |
+| test.c:81:14:81:17 | call to rand | semmle.label | call to rand |
+| test.c:81:14:81:17 | call to rand | semmle.label | call to rand |
+| test.c:81:23:81:26 | call to rand | semmle.label | call to rand |
+| test.c:81:23:81:26 | call to rand | semmle.label | call to rand |
+| test.c:83:9:83:9 | r | semmle.label | r |
+| test.c:83:9:83:9 | r | semmle.label | r |
+| test.c:83:9:83:9 | r | semmle.label | r |
| test.c:99:14:99:19 | call to rand | semmle.label | call to rand |
| test.c:99:14:99:19 | call to rand | semmle.label | call to rand |
| test.c:100:5:100:5 | r | semmle.label | r |
@@ -140,6 +155,8 @@ nodes
| test.c:56:5:56:5 | r | test.c:54:13:54:16 | call to rand | test.c:56:5:56:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:54:13:54:16 | call to rand | Uncontrolled value |
| test.c:67:5:67:5 | r | test.c:66:13:66:16 | call to rand | test.c:67:5:67:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:66:13:66:16 | call to rand | Uncontrolled value |
| test.c:77:9:77:9 | r | test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:75:13:75:19 | ... ^ ... | Uncontrolled value |
+| test.c:83:9:83:9 | r | test.c:81:14:81:17 | call to rand | test.c:83:9:83:9 | r | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:81:14:81:17 | call to rand | Uncontrolled value |
+| test.c:83:9:83:9 | r | test.c:81:23:81:26 | call to rand | test.c:83:9:83:9 | r | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:81:23:81:26 | call to rand | Uncontrolled value |
| test.c:100:5:100:5 | r | test.c:99:14:99:19 | call to rand | test.c:100:5:100:5 | r | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:99:14:99:19 | call to rand | Uncontrolled value |
| test.cpp:25:7:25:7 | r | test.cpp:8:9:8:12 | call to rand | test.cpp:25:7:25:7 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:8:9:8:12 | call to rand | Uncontrolled value |
| test.cpp:31:7:31:7 | r | test.cpp:13:10:13:13 | call to rand | test.cpp:31:7:31:7 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:13:10:13:13 | call to rand | Uncontrolled value |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/test.cpp
index 5d4d7e8c5c3..d3dc3352a29 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/test.cpp
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/test.cpp
@@ -28,7 +28,7 @@ void randomTester2()
{
int r;
get_rand2(&r);
- r = r + 100; // BAD [NOT DETECTED]
+ r = r + 100; // BAD
}
{
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-807/semmle/TaintedCondition/TaintedCondition.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-807/semmle/TaintedCondition/TaintedCondition.expected
index cc5004047cf..71cd8ce2361 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-807/semmle/TaintedCondition/TaintedCondition.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-807/semmle/TaintedCondition/TaintedCondition.expected
@@ -1,37 +1,31 @@
edges
| test.cpp:20:29:20:34 | call to getenv | test.cpp:24:10:24:35 | ! ... |
| test.cpp:20:29:20:34 | call to getenv | test.cpp:24:11:24:16 | call to strcmp |
-| test.cpp:20:29:20:34 | call to getenv | test.cpp:24:11:24:16 | call to strcmp |
-| test.cpp:20:29:20:34 | call to getenv | test.cpp:24:11:24:35 | (bool)... |
| test.cpp:20:29:20:34 | call to getenv | test.cpp:41:10:41:38 | ! ... |
| test.cpp:20:29:20:34 | call to getenv | test.cpp:41:11:41:16 | call to strcmp |
-| test.cpp:20:29:20:34 | call to getenv | test.cpp:41:11:41:16 | call to strcmp |
-| test.cpp:20:29:20:34 | call to getenv | test.cpp:41:11:41:38 | (bool)... |
| test.cpp:20:29:20:47 | (const char *)... | test.cpp:24:10:24:35 | ! ... |
| test.cpp:20:29:20:47 | (const char *)... | test.cpp:24:11:24:16 | call to strcmp |
-| test.cpp:20:29:20:47 | (const char *)... | test.cpp:24:11:24:16 | call to strcmp |
-| test.cpp:20:29:20:47 | (const char *)... | test.cpp:24:11:24:35 | (bool)... |
| test.cpp:20:29:20:47 | (const char *)... | test.cpp:41:10:41:38 | ! ... |
| test.cpp:20:29:20:47 | (const char *)... | test.cpp:41:11:41:16 | call to strcmp |
-| test.cpp:20:29:20:47 | (const char *)... | test.cpp:41:11:41:16 | call to strcmp |
-| test.cpp:20:29:20:47 | (const char *)... | test.cpp:41:11:41:38 | (bool)... |
-| test.cpp:24:11:24:16 | call to strcmp | test.cpp:24:10:24:35 | ! ... |
-| test.cpp:24:11:24:16 | call to strcmp | test.cpp:24:11:24:35 | (bool)... |
-| test.cpp:41:11:41:16 | call to strcmp | test.cpp:41:10:41:38 | ! ... |
-| test.cpp:41:11:41:16 | call to strcmp | test.cpp:41:11:41:38 | (bool)... |
+| test.cpp:29:27:29:32 | call to getenv | test.cpp:30:10:30:37 | ! ... |
+| test.cpp:29:27:29:32 | call to getenv | test.cpp:30:11:30:16 | call to strcmp |
+| test.cpp:29:27:29:42 | (const char *)... | test.cpp:30:10:30:37 | ! ... |
+| test.cpp:29:27:29:42 | (const char *)... | test.cpp:30:11:30:16 | call to strcmp |
nodes
| test.cpp:20:29:20:34 | call to getenv | semmle.label | call to getenv |
| test.cpp:20:29:20:47 | (const char *)... | semmle.label | (const char *)... |
| test.cpp:24:10:24:35 | ! ... | semmle.label | ! ... |
| test.cpp:24:11:24:16 | call to strcmp | semmle.label | call to strcmp |
| test.cpp:24:11:24:16 | call to strcmp | semmle.label | call to strcmp |
-| test.cpp:24:11:24:35 | (bool)... | semmle.label | (bool)... |
-| test.cpp:24:11:24:35 | (bool)... | semmle.label | (bool)... |
+| test.cpp:29:27:29:32 | call to getenv | semmle.label | call to getenv |
+| test.cpp:29:27:29:42 | (const char *)... | semmle.label | (const char *)... |
+| test.cpp:30:10:30:37 | ! ... | semmle.label | ! ... |
+| test.cpp:30:11:30:16 | call to strcmp | semmle.label | call to strcmp |
+| test.cpp:30:11:30:16 | call to strcmp | semmle.label | call to strcmp |
| test.cpp:41:10:41:38 | ! ... | semmle.label | ! ... |
| test.cpp:41:11:41:16 | call to strcmp | semmle.label | call to strcmp |
| test.cpp:41:11:41:16 | call to strcmp | semmle.label | call to strcmp |
-| test.cpp:41:11:41:38 | (bool)... | semmle.label | (bool)... |
-| test.cpp:41:11:41:38 | (bool)... | semmle.label | (bool)... |
#select
| test.cpp:24:10:24:35 | ! ... | test.cpp:20:29:20:34 | call to getenv | test.cpp:24:10:24:35 | ! ... | Reliance on untrusted input $@ to raise privilege at $@ | test.cpp:20:29:20:34 | call to getenv | call to getenv | test.cpp:25:9:25:27 | ... = ... | ... = ... |
+| test.cpp:30:10:30:37 | ! ... | test.cpp:29:27:29:32 | call to getenv | test.cpp:30:10:30:37 | ! ... | Reliance on untrusted input $@ to raise privilege at $@ | test.cpp:29:27:29:32 | call to getenv | call to getenv | test.cpp:31:9:31:27 | ... = ... | ... = ... |
| test.cpp:41:10:41:38 | ! ... | test.cpp:20:29:20:34 | call to getenv | test.cpp:41:10:41:38 | ! ... | Reliance on untrusted input $@ to raise privilege at $@ | test.cpp:20:29:20:34 | call to getenv | call to getenv | test.cpp:42:8:42:26 | ... = ... | ... = ... |
From fbe857d1fa8a3b577e043a74155b804214522373 Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Thu, 30 Apr 2020 14:25:17 -0700
Subject: [PATCH 005/725] C++: require that other operands be predictable
This brings back a constraint that was lost when switching
DefaultTaintTracking to use a TaintTracking::Configuration
---
.../cpp/ir/dataflow/DefaultTaintTracking.qll | 19 +++++++++++++++++++
.../ArithmeticUncontrolled.expected | 17 -----------------
.../TaintedCondition.expected | 10 ----------
3 files changed, 19 insertions(+), 27 deletions(-)
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
index ae2066398a1..0ab6248a6bd 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
@@ -213,6 +213,25 @@ private predicate nodeIsBarrierIn(DataFlow::Node node) {
// `getNodeForSource`.
node = DataFlow::definitionByReferenceNodeFromArgument(source)
)
+ or
+ // don't use dataflow into binary instructions if both operands are unpredictable
+ exists(BinaryInstruction iTo |
+ iTo = node.asInstruction() and
+ not predictableInstruction(iTo.getLeft()) and
+ not predictableInstruction(iTo.getRight())
+ )
+ or
+ // don't use dataflow through calls to pure functions if two or more operands
+ // are unpredictable
+ exists(Instruction iFrom1, Instruction iFrom2, CallInstruction iTo |
+ iTo = node.asInstruction() and
+ isPureFunction(iTo.getStaticCallTarget().getName()) and
+ iFrom1 = iTo.getAnArgument() and
+ iFrom2 = iTo.getAnArgument() and
+ not predictableInstruction(iFrom1) and
+ not predictableInstruction(iFrom2) and
+ iFrom1 != iFrom2
+ )
}
cached
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/ArithmeticUncontrolled.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/ArithmeticUncontrolled.expected
index 0ae5dc31fae..6a897653265 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/ArithmeticUncontrolled.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/ArithmeticUncontrolled.expected
@@ -35,14 +35,6 @@ edges
| test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r |
| test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r |
| test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r |
-| test.c:81:14:81:17 | call to rand | test.c:83:9:83:9 | r |
-| test.c:81:14:81:17 | call to rand | test.c:83:9:83:9 | r |
-| test.c:81:14:81:17 | call to rand | test.c:83:9:83:9 | r |
-| test.c:81:14:81:17 | call to rand | test.c:83:9:83:9 | r |
-| test.c:81:23:81:26 | call to rand | test.c:83:9:83:9 | r |
-| test.c:81:23:81:26 | call to rand | test.c:83:9:83:9 | r |
-| test.c:81:23:81:26 | call to rand | test.c:83:9:83:9 | r |
-| test.c:81:23:81:26 | call to rand | test.c:83:9:83:9 | r |
| test.c:99:14:99:19 | call to rand | test.c:100:5:100:5 | r |
| test.c:99:14:99:19 | call to rand | test.c:100:5:100:5 | r |
| test.c:99:14:99:19 | call to rand | test.c:100:5:100:5 | r |
@@ -110,13 +102,6 @@ nodes
| test.c:77:9:77:9 | r | semmle.label | r |
| test.c:77:9:77:9 | r | semmle.label | r |
| test.c:77:9:77:9 | r | semmle.label | r |
-| test.c:81:14:81:17 | call to rand | semmle.label | call to rand |
-| test.c:81:14:81:17 | call to rand | semmle.label | call to rand |
-| test.c:81:23:81:26 | call to rand | semmle.label | call to rand |
-| test.c:81:23:81:26 | call to rand | semmle.label | call to rand |
-| test.c:83:9:83:9 | r | semmle.label | r |
-| test.c:83:9:83:9 | r | semmle.label | r |
-| test.c:83:9:83:9 | r | semmle.label | r |
| test.c:99:14:99:19 | call to rand | semmle.label | call to rand |
| test.c:99:14:99:19 | call to rand | semmle.label | call to rand |
| test.c:100:5:100:5 | r | semmle.label | r |
@@ -155,8 +140,6 @@ nodes
| test.c:56:5:56:5 | r | test.c:54:13:54:16 | call to rand | test.c:56:5:56:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:54:13:54:16 | call to rand | Uncontrolled value |
| test.c:67:5:67:5 | r | test.c:66:13:66:16 | call to rand | test.c:67:5:67:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:66:13:66:16 | call to rand | Uncontrolled value |
| test.c:77:9:77:9 | r | test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:75:13:75:19 | ... ^ ... | Uncontrolled value |
-| test.c:83:9:83:9 | r | test.c:81:14:81:17 | call to rand | test.c:83:9:83:9 | r | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:81:14:81:17 | call to rand | Uncontrolled value |
-| test.c:83:9:83:9 | r | test.c:81:23:81:26 | call to rand | test.c:83:9:83:9 | r | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:81:23:81:26 | call to rand | Uncontrolled value |
| test.c:100:5:100:5 | r | test.c:99:14:99:19 | call to rand | test.c:100:5:100:5 | r | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:99:14:99:19 | call to rand | Uncontrolled value |
| test.cpp:25:7:25:7 | r | test.cpp:8:9:8:12 | call to rand | test.cpp:25:7:25:7 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:8:9:8:12 | call to rand | Uncontrolled value |
| test.cpp:31:7:31:7 | r | test.cpp:13:10:13:13 | call to rand | test.cpp:31:7:31:7 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:13:10:13:13 | call to rand | Uncontrolled value |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-807/semmle/TaintedCondition/TaintedCondition.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-807/semmle/TaintedCondition/TaintedCondition.expected
index 71cd8ce2361..e04a20830d0 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-807/semmle/TaintedCondition/TaintedCondition.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-807/semmle/TaintedCondition/TaintedCondition.expected
@@ -7,25 +7,15 @@ edges
| test.cpp:20:29:20:47 | (const char *)... | test.cpp:24:11:24:16 | call to strcmp |
| test.cpp:20:29:20:47 | (const char *)... | test.cpp:41:10:41:38 | ! ... |
| test.cpp:20:29:20:47 | (const char *)... | test.cpp:41:11:41:16 | call to strcmp |
-| test.cpp:29:27:29:32 | call to getenv | test.cpp:30:10:30:37 | ! ... |
-| test.cpp:29:27:29:32 | call to getenv | test.cpp:30:11:30:16 | call to strcmp |
-| test.cpp:29:27:29:42 | (const char *)... | test.cpp:30:10:30:37 | ! ... |
-| test.cpp:29:27:29:42 | (const char *)... | test.cpp:30:11:30:16 | call to strcmp |
nodes
| test.cpp:20:29:20:34 | call to getenv | semmle.label | call to getenv |
| test.cpp:20:29:20:47 | (const char *)... | semmle.label | (const char *)... |
| test.cpp:24:10:24:35 | ! ... | semmle.label | ! ... |
| test.cpp:24:11:24:16 | call to strcmp | semmle.label | call to strcmp |
| test.cpp:24:11:24:16 | call to strcmp | semmle.label | call to strcmp |
-| test.cpp:29:27:29:32 | call to getenv | semmle.label | call to getenv |
-| test.cpp:29:27:29:42 | (const char *)... | semmle.label | (const char *)... |
-| test.cpp:30:10:30:37 | ! ... | semmle.label | ! ... |
-| test.cpp:30:11:30:16 | call to strcmp | semmle.label | call to strcmp |
-| test.cpp:30:11:30:16 | call to strcmp | semmle.label | call to strcmp |
| test.cpp:41:10:41:38 | ! ... | semmle.label | ! ... |
| test.cpp:41:11:41:16 | call to strcmp | semmle.label | call to strcmp |
| test.cpp:41:11:41:16 | call to strcmp | semmle.label | call to strcmp |
#select
| test.cpp:24:10:24:35 | ! ... | test.cpp:20:29:20:34 | call to getenv | test.cpp:24:10:24:35 | ! ... | Reliance on untrusted input $@ to raise privilege at $@ | test.cpp:20:29:20:34 | call to getenv | call to getenv | test.cpp:25:9:25:27 | ... = ... | ... = ... |
-| test.cpp:30:10:30:37 | ! ... | test.cpp:29:27:29:32 | call to getenv | test.cpp:30:10:30:37 | ! ... | Reliance on untrusted input $@ to raise privilege at $@ | test.cpp:29:27:29:32 | call to getenv | call to getenv | test.cpp:31:9:31:27 | ... = ... | ... = ... |
| test.cpp:41:10:41:38 | ! ... | test.cpp:20:29:20:34 | call to getenv | test.cpp:41:10:41:38 | ! ... | Reliance on untrusted input $@ to raise privilege at $@ | test.cpp:20:29:20:34 | call to getenv | call to getenv | test.cpp:42:8:42:26 | ... = ... | ... = ... |
From 95ed5465de9b0db3b98db81b9b0cd2f995361949 Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Tue, 5 May 2020 13:30:45 -0700
Subject: [PATCH 006/725] C++: improve handling of function arguments in DTT
---
.../cpp/ir/dataflow/DefaultTaintTracking.qll | 23 +++++++-
.../defaulttainttracking.cpp | 2 +-
.../dataflow/DefaultTaintTracking/tainted.ql | 8 ++-
.../DefaultTaintTracking/test_diff.ql | 25 ++++++---
.../dataflow/security-taint/tainted_diff.ql | 10 +++-
.../dataflow/security-taint/tainted_ir.ql | 12 +++-
.../CWE-022/semmle/tests/TaintedPath.expected | 12 ++++
.../UncontrolledProcessOperation.expected | 36 ++++++++++++
.../CWE-134/semmle/argv/argvLocal.expected | 43 ++++++++++++++
.../CWE-134/semmle/funcs/funcsLocal.expected | 56 +++++++++++++++++++
10 files changed, 211 insertions(+), 16 deletions(-)
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
index 0ab6248a6bd..62e7f7b9738 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
@@ -10,6 +10,7 @@ private import semmle.code.cpp.controlflow.IRGuards
private import semmle.code.cpp.models.interfaces.Taint
private import semmle.code.cpp.models.interfaces.DataFlow
private import semmle.code.cpp.ir.dataflow.TaintTracking2
+private import semmle.code.cpp.ir.dataflow.internal.ModelUtil
/**
* A predictable instruction is one where an external user can predict
@@ -218,7 +219,10 @@ private predicate nodeIsBarrierIn(DataFlow::Node node) {
exists(BinaryInstruction iTo |
iTo = node.asInstruction() and
not predictableInstruction(iTo.getLeft()) and
- not predictableInstruction(iTo.getRight())
+ not predictableInstruction(iTo.getRight()) and
+ // propagate taint from either the pointer or the offset, regardless of predictability
+ not iTo instanceof PointerArithmeticInstruction
+
)
or
// don't use dataflow through calls to pure functions if two or more operands
@@ -468,6 +472,8 @@ private Element adjustedSink(DataFlow::Node sink) {
or
// Taint `e1 += e2`, `e &= e2` and friends when `e1` or `e2` is tainted.
result.(AssignOperation).getAnOperand() = sink.asExpr()
+ or
+ result = sink.asOperand().(SideEffectOperand).getUse().(ReadSideEffectInstruction).getArgumentDef().getUnconvertedResultExpression()
}
/**
@@ -590,11 +596,26 @@ module TaintedWithPath {
}
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
+ // Steps into and out of global variables
exists(TaintTrackingConfiguration cfg | cfg.taintThroughGlobals() |
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
or
readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable))
)
+ or
+ // Step to return value of a modeled function when an input taints the
+ // dereference of the return value
+ exists(CallInstruction call, Function func, FunctionInput modelIn, FunctionOutput modelOut |
+ n1 = callInput(call, modelIn) and
+ (
+ func.(TaintFunction).hasTaintFlow(modelIn, modelOut)
+ or
+ func.(DataFlowFunction).hasDataFlow(modelIn, modelOut)
+ ) and
+ call.getStaticCallTarget() = func and
+ modelOut.isReturnValueDeref() and
+ call = n2.asInstruction()
+ )
}
override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/defaulttainttracking.cpp b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/defaulttainttracking.cpp
index 0ca85d29bab..8540b20708d 100644
--- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/defaulttainttracking.cpp
+++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/defaulttainttracking.cpp
@@ -9,7 +9,7 @@
-int main() {
+int test() {
diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.ql b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.ql
index bbd94cab403..3382775b6d9 100644
--- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.ql
+++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.ql
@@ -1,5 +1,11 @@
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking
+class SourceConfiguration extends TaintedWithPath::TaintTrackingConfiguration {
+ override predicate isSink(Element e) {
+ any()
+ }
+}
+
from Expr source, Element tainted
-where tainted(source, tainted)
+where TaintedWithPath::taintedWithPath(source, tainted, _, _)
select source, tainted
diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.ql b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.ql
index b31147fd423..40bed5680c0 100644
--- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.ql
+++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.ql
@@ -3,17 +3,26 @@ import semmle.code.cpp.security.Security
import semmle.code.cpp.security.TaintTrackingImpl as ASTTaintTracking
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking as IRDefaultTaintTracking
+class SourceConfiguration extends IRDefaultTaintTracking::TaintedWithPath::TaintTrackingConfiguration {
+ override predicate isSink(Element e) { any() }
+}
+
predicate astFlow(Expr source, Element sink) { ASTTaintTracking::tainted(source, sink) }
-predicate irFlow(Expr source, Element sink) { IRDefaultTaintTracking::tainted(source, sink) }
+predicate irFlow(Expr source, Element sink) {
+ IRDefaultTaintTracking::TaintedWithPath::taintedWithPath(source, sink, _, _)
+}
from Expr source, Element sink, string note
where
- astFlow(source, sink) and
- not irFlow(source, sink) and
- note = "AST only"
- or
- irFlow(source, sink) and
- not astFlow(source, sink) and
- note = "IR only"
+ not sink instanceof Parameter and
+ (
+ astFlow(source, sink) and
+ not irFlow(source, sink) and
+ note = "AST only"
+ or
+ irFlow(source, sink) and
+ not astFlow(source, sink) and
+ note = "IR only"
+ )
select source, sink, note
diff --git a/cpp/ql/test/library-tests/dataflow/security-taint/tainted_diff.ql b/cpp/ql/test/library-tests/dataflow/security-taint/tainted_diff.ql
index 13a1b7b6f3f..f3ed14d16b1 100644
--- a/cpp/ql/test/library-tests/dataflow/security-taint/tainted_diff.ql
+++ b/cpp/ql/test/library-tests/dataflow/security-taint/tainted_diff.ql
@@ -2,14 +2,20 @@ import semmle.code.cpp.security.TaintTrackingImpl as AST
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking as IR
import cpp
+class SourceConfiguration extends IR::TaintedWithPath::TaintTrackingConfiguration {
+ override predicate isSink(Element e) {
+ any()
+ }
+}
+
from Expr source, Element tainted, string side
where
AST::taintedIncludingGlobalVars(source, tainted, _) and
- not IR::taintedIncludingGlobalVars(source, tainted, _) and
+ not IR::TaintedWithPath::taintedWithPath(source, tainted, _, _) and
not tainted.getLocation().getFile().getExtension() = "h" and
side = "AST only"
or
- IR::taintedIncludingGlobalVars(source, tainted, _) and
+ IR::TaintedWithPath::taintedWithPath(source, tainted, _, _) and
not AST::taintedIncludingGlobalVars(source, tainted, _) and
not tainted.getLocation().getFile().getExtension() = "h" and
side = "IR only"
diff --git a/cpp/ql/test/library-tests/dataflow/security-taint/tainted_ir.ql b/cpp/ql/test/library-tests/dataflow/security-taint/tainted_ir.ql
index 6d8effe7ffe..77f04d301f0 100644
--- a/cpp/ql/test/library-tests/dataflow/security-taint/tainted_ir.ql
+++ b/cpp/ql/test/library-tests/dataflow/security-taint/tainted_ir.ql
@@ -1,7 +1,13 @@
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking
-from Expr source, Element tainted, string globalVar
+class SourceConfiguration extends TaintedWithPath::TaintTrackingConfiguration {
+ override predicate isSink(Element e) {
+ any()
+ }
+}
+
+from Expr source, Element tainted
where
- taintedIncludingGlobalVars(source, tainted, globalVar) and
+ TaintedWithPath::taintedWithPath(source, tainted, _, _) and
not tainted.getLocation().getFile().getExtension() = "h"
-select source, tainted, globalVar
+select source, tainted
\ No newline at end of file
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected
index 58e3dda0964..03d1bfd25b3 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected
@@ -1,3 +1,15 @@
+WARNING: Unused predicate sinkInstructionNodes (C:/semmle/code/ql/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql:64,13-33)
+WARNING: Unused predicate sinkOperand (C:/semmle/code/ql/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql:70,13-24)
edges
+| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | Argument 0 indirection |
+| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | Argument 0 indirection |
+| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName |
+| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName |
nodes
+| test.c:9:23:9:26 | argv | semmle.label | argv |
+| test.c:9:23:9:26 | argv | semmle.label | argv |
+| test.c:17:11:17:18 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| test.c:17:11:17:18 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| test.c:17:11:17:18 | fileName | semmle.label | fileName |
#select
+| test.c:17:11:17:18 | fileName | test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName | This argument to a file access function is derived from $@ and then passed to fopen(filename) | test.c:9:23:9:26 | argv | user input (argv) |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected
index c53e4f5be87..06ac4c9e499 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected
@@ -7,6 +7,22 @@ edges
| test.cpp:42:18:42:34 | (const char *)... | test.cpp:24:30:24:36 | command |
| test.cpp:43:18:43:23 | call to getenv | test.cpp:29:30:29:36 | command |
| test.cpp:43:18:43:34 | (const char *)... | test.cpp:29:30:29:36 | command |
+| test.cpp:56:12:56:17 | buffer | test.cpp:62:10:62:15 | Argument 0 indirection |
+| test.cpp:56:12:56:17 | buffer | test.cpp:62:10:62:15 | buffer |
+| test.cpp:56:12:56:17 | buffer | test.cpp:63:10:63:13 | Argument 0 indirection |
+| test.cpp:56:12:56:17 | buffer | test.cpp:63:10:63:13 | data |
+| test.cpp:56:12:56:17 | fgets output argument | test.cpp:62:10:62:15 | Argument 0 indirection |
+| test.cpp:56:12:56:17 | fgets output argument | test.cpp:62:10:62:15 | buffer |
+| test.cpp:56:12:56:17 | fgets output argument | test.cpp:63:10:63:13 | Argument 0 indirection |
+| test.cpp:56:12:56:17 | fgets output argument | test.cpp:63:10:63:13 | data |
+| test.cpp:76:12:76:17 | buffer | test.cpp:78:10:78:15 | Argument 0 indirection |
+| test.cpp:76:12:76:17 | buffer | test.cpp:78:10:78:15 | buffer |
+| test.cpp:76:12:76:17 | buffer | test.cpp:79:10:79:13 | Argument 0 indirection |
+| test.cpp:76:12:76:17 | buffer | test.cpp:79:10:79:13 | data |
+| test.cpp:76:12:76:17 | fgets output argument | test.cpp:78:10:78:15 | Argument 0 indirection |
+| test.cpp:76:12:76:17 | fgets output argument | test.cpp:78:10:78:15 | buffer |
+| test.cpp:76:12:76:17 | fgets output argument | test.cpp:79:10:79:13 | Argument 0 indirection |
+| test.cpp:76:12:76:17 | fgets output argument | test.cpp:79:10:79:13 | data |
nodes
| test.cpp:24:30:24:36 | command | semmle.label | command |
| test.cpp:26:10:26:16 | command | semmle.label | command |
@@ -22,6 +38,26 @@ nodes
| test.cpp:43:7:43:16 | Argument 0 | semmle.label | Argument 0 |
| test.cpp:43:18:43:23 | call to getenv | semmle.label | call to getenv |
| test.cpp:43:18:43:34 | (const char *)... | semmle.label | (const char *)... |
+| test.cpp:56:12:56:17 | buffer | semmle.label | buffer |
+| test.cpp:56:12:56:17 | fgets output argument | semmle.label | fgets output argument |
+| test.cpp:62:10:62:15 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| test.cpp:62:10:62:15 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| test.cpp:62:10:62:15 | buffer | semmle.label | buffer |
+| test.cpp:63:10:63:13 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| test.cpp:63:10:63:13 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| test.cpp:63:10:63:13 | data | semmle.label | data |
+| test.cpp:76:12:76:17 | buffer | semmle.label | buffer |
+| test.cpp:76:12:76:17 | fgets output argument | semmle.label | fgets output argument |
+| test.cpp:78:10:78:15 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| test.cpp:78:10:78:15 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| test.cpp:78:10:78:15 | buffer | semmle.label | buffer |
+| test.cpp:79:10:79:13 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| test.cpp:79:10:79:13 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| test.cpp:79:10:79:13 | data | semmle.label | data |
#select
| test.cpp:26:10:26:16 | command | test.cpp:42:18:42:23 | call to getenv | test.cpp:26:10:26:16 | command | The value of this argument may come from $@ and is being passed to system | test.cpp:42:18:42:23 | call to getenv | call to getenv |
| test.cpp:31:10:31:16 | command | test.cpp:43:18:43:23 | call to getenv | test.cpp:31:10:31:16 | command | The value of this argument may come from $@ and is being passed to system | test.cpp:43:18:43:23 | call to getenv | call to getenv |
+| test.cpp:62:10:62:15 | buffer | test.cpp:56:12:56:17 | buffer | test.cpp:62:10:62:15 | buffer | The value of this argument may come from $@ and is being passed to system | test.cpp:56:12:56:17 | buffer | buffer |
+| test.cpp:63:10:63:13 | data | test.cpp:56:12:56:17 | buffer | test.cpp:63:10:63:13 | data | The value of this argument may come from $@ and is being passed to system | test.cpp:56:12:56:17 | buffer | buffer |
+| test.cpp:78:10:78:15 | buffer | test.cpp:76:12:76:17 | buffer | test.cpp:78:10:78:15 | buffer | The value of this argument may come from $@ and is being passed to system | test.cpp:76:12:76:17 | buffer | buffer |
+| test.cpp:79:10:79:13 | data | test.cpp:76:12:76:17 | buffer | test.cpp:79:10:79:13 | data | The value of this argument may come from $@ and is being passed to system | test.cpp:76:12:76:17 | buffer | buffer |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected
index 72d1df8a59b..35008e71818 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected
@@ -61,6 +61,28 @@ edges
| argvLocal.c:106:9:106:13 | access to array | argvLocal.c:106:9:106:13 | access to array |
| argvLocal.c:110:9:110:11 | * ... | argvLocal.c:110:9:110:11 | (const char *)... |
| argvLocal.c:110:9:110:11 | * ... | argvLocal.c:110:9:110:11 | * ... |
+| argvLocal.c:126:10:126:13 | argv | argvLocal.c:127:9:127:10 | Argument 0 indirection |
+| argvLocal.c:126:10:126:13 | argv | argvLocal.c:127:9:127:10 | Argument 0 indirection |
+| argvLocal.c:126:10:126:13 | argv | argvLocal.c:127:9:127:10 | i5 |
+| argvLocal.c:126:10:126:13 | argv | argvLocal.c:127:9:127:10 | i5 |
+| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | Argument 0 indirection |
+| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | Argument 0 indirection |
+| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | i5 |
+| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | i5 |
+| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | printWrapper output argument |
+| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | printWrapper output argument |
+| argvLocal.c:126:10:126:13 | argv | argvLocal.c:131:9:131:14 | ... + ... |
+| argvLocal.c:126:10:126:13 | argv | argvLocal.c:131:9:131:14 | ... + ... |
+| argvLocal.c:126:10:126:13 | argv | argvLocal.c:131:9:131:14 | Argument 0 indirection |
+| argvLocal.c:126:10:126:13 | argv | argvLocal.c:131:9:131:14 | Argument 0 indirection |
+| argvLocal.c:126:10:126:13 | argv | argvLocal.c:132:15:132:20 | ... + ... |
+| argvLocal.c:126:10:126:13 | argv | argvLocal.c:132:15:132:20 | ... + ... |
+| argvLocal.c:126:10:126:13 | argv | argvLocal.c:132:15:132:20 | Argument 0 indirection |
+| argvLocal.c:126:10:126:13 | argv | argvLocal.c:132:15:132:20 | Argument 0 indirection |
+| argvLocal.c:128:15:128:16 | printWrapper output argument | argvLocal.c:131:9:131:14 | ... + ... |
+| argvLocal.c:128:15:128:16 | printWrapper output argument | argvLocal.c:131:9:131:14 | Argument 0 indirection |
+| argvLocal.c:128:15:128:16 | printWrapper output argument | argvLocal.c:132:15:132:20 | ... + ... |
+| argvLocal.c:128:15:128:16 | printWrapper output argument | argvLocal.c:132:15:132:20 | Argument 0 indirection |
| argvLocal.c:149:11:149:14 | argv | argvLocal.c:150:9:150:10 | (const char *)... |
| argvLocal.c:149:11:149:14 | argv | argvLocal.c:150:9:150:10 | (const char *)... |
| argvLocal.c:149:11:149:14 | argv | argvLocal.c:150:9:150:10 | i8 |
@@ -86,6 +108,8 @@ edges
| argvLocal.c:168:18:168:21 | argv | argvLocal.c:170:24:170:26 | i10 |
| argvLocal.c:168:18:168:21 | argv | argvLocal.c:170:24:170:26 | i10 |
nodes
+| argvLocal.c:9:25:9:31 | *correct | semmle.label | *correct |
+| argvLocal.c:10:9:10:15 | Chi | semmle.label | Chi |
| argvLocal.c:95:9:95:12 | argv | semmle.label | argv |
| argvLocal.c:95:9:95:12 | argv | semmle.label | argv |
| argvLocal.c:95:9:95:15 | (const char *)... | semmle.label | (const char *)... |
@@ -126,6 +150,21 @@ nodes
| argvLocal.c:111:15:111:17 | * ... | semmle.label | * ... |
| argvLocal.c:111:15:111:17 | * ... | semmle.label | * ... |
| argvLocal.c:111:15:111:17 | * ... | semmle.label | * ... |
+| argvLocal.c:126:10:126:13 | argv | semmle.label | argv |
+| argvLocal.c:126:10:126:13 | argv | semmle.label | argv |
+| argvLocal.c:127:9:127:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| argvLocal.c:127:9:127:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| argvLocal.c:127:9:127:10 | i5 | semmle.label | i5 |
+| argvLocal.c:128:15:128:16 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| argvLocal.c:128:15:128:16 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| argvLocal.c:128:15:128:16 | i5 | semmle.label | i5 |
+| argvLocal.c:128:15:128:16 | printWrapper output argument | semmle.label | printWrapper output argument |
+| argvLocal.c:131:9:131:14 | ... + ... | semmle.label | ... + ... |
+| argvLocal.c:131:9:131:14 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| argvLocal.c:131:9:131:14 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| argvLocal.c:132:15:132:20 | ... + ... | semmle.label | ... + ... |
+| argvLocal.c:132:15:132:20 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| argvLocal.c:132:15:132:20 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| argvLocal.c:144:9:144:10 | (const char *)... | semmle.label | (const char *)... |
| argvLocal.c:144:9:144:10 | (const char *)... | semmle.label | (const char *)... |
| argvLocal.c:144:9:144:10 | i7 | semmle.label | i7 |
@@ -167,6 +206,10 @@ nodes
| argvLocal.c:107:15:107:19 | access to array | argvLocal.c:105:14:105:17 | argv | argvLocal.c:107:15:107:19 | access to array | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:105:14:105:17 | argv | argv |
| argvLocal.c:110:9:110:11 | * ... | argvLocal.c:105:14:105:17 | argv | argvLocal.c:110:9:110:11 | * ... | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:105:14:105:17 | argv | argv |
| argvLocal.c:111:15:111:17 | * ... | argvLocal.c:105:14:105:17 | argv | argvLocal.c:111:15:111:17 | * ... | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:105:14:105:17 | argv | argv |
+| argvLocal.c:127:9:127:10 | i5 | argvLocal.c:126:10:126:13 | argv | argvLocal.c:127:9:127:10 | i5 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:126:10:126:13 | argv | argv |
+| argvLocal.c:128:15:128:16 | i5 | argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | i5 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:126:10:126:13 | argv | argv |
+| argvLocal.c:131:9:131:14 | ... + ... | argvLocal.c:126:10:126:13 | argv | argvLocal.c:131:9:131:14 | ... + ... | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:126:10:126:13 | argv | argv |
+| argvLocal.c:132:15:132:20 | ... + ... | argvLocal.c:126:10:126:13 | argv | argvLocal.c:132:15:132:20 | ... + ... | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:126:10:126:13 | argv | argv |
| argvLocal.c:144:9:144:10 | i7 | argvLocal.c:100:7:100:10 | argv | argvLocal.c:144:9:144:10 | i7 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:100:7:100:10 | argv | argv |
| argvLocal.c:145:15:145:16 | i7 | argvLocal.c:100:7:100:10 | argv | argvLocal.c:145:15:145:16 | i7 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:100:7:100:10 | argv | argv |
| argvLocal.c:150:9:150:10 | i8 | argvLocal.c:149:11:149:14 | argv | argvLocal.c:150:9:150:10 | i8 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:149:11:149:14 | argv | argv |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/funcs/funcsLocal.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/funcs/funcsLocal.expected
index 1beb428a832..5d04ac6149d 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/funcs/funcsLocal.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/funcs/funcsLocal.expected
@@ -1,31 +1,87 @@
edges
+| funcsLocal.c:16:8:16:9 | fread output argument | funcsLocal.c:17:9:17:10 | Argument 0 indirection |
+| funcsLocal.c:16:8:16:9 | fread output argument | funcsLocal.c:17:9:17:10 | i1 |
+| funcsLocal.c:16:8:16:9 | fread output argument | funcsLocal.c:58:9:58:10 | Argument 0 indirection |
+| funcsLocal.c:16:8:16:9 | fread output argument | funcsLocal.c:58:9:58:10 | e1 |
+| funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:17:9:17:10 | Argument 0 indirection |
+| funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:17:9:17:10 | i1 |
+| funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:58:9:58:10 | Argument 0 indirection |
+| funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:58:9:58:10 | e1 |
+| funcsLocal.c:26:8:26:9 | fgets output argument | funcsLocal.c:27:9:27:10 | Argument 0 indirection |
+| funcsLocal.c:26:8:26:9 | fgets output argument | funcsLocal.c:27:9:27:10 | i3 |
+| funcsLocal.c:26:8:26:9 | i3 | funcsLocal.c:27:9:27:10 | Argument 0 indirection |
+| funcsLocal.c:26:8:26:9 | i3 | funcsLocal.c:27:9:27:10 | i3 |
| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | (const char *)... |
| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | (const char *)... |
| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | i4 |
| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | i4 |
| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | i4 |
| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | i4 |
+| funcsLocal.c:31:19:31:21 | fgets output argument | funcsLocal.c:32:9:32:10 | Argument 0 indirection |
+| funcsLocal.c:31:19:31:21 | fgets output argument | funcsLocal.c:32:9:32:10 | i4 |
+| funcsLocal.c:31:19:31:21 | i41 | funcsLocal.c:32:9:32:10 | Argument 0 indirection |
+| funcsLocal.c:31:19:31:21 | i41 | funcsLocal.c:32:9:32:10 | i4 |
+| funcsLocal.c:36:7:36:8 | gets output argument | funcsLocal.c:37:9:37:10 | Argument 0 indirection |
+| funcsLocal.c:36:7:36:8 | gets output argument | funcsLocal.c:37:9:37:10 | i5 |
+| funcsLocal.c:36:7:36:8 | i5 | funcsLocal.c:37:9:37:10 | Argument 0 indirection |
+| funcsLocal.c:36:7:36:8 | i5 | funcsLocal.c:37:9:37:10 | i5 |
| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | (const char *)... |
| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | (const char *)... |
| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | i6 |
| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | i6 |
| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | i6 |
| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | i6 |
+| funcsLocal.c:41:18:41:20 | gets output argument | funcsLocal.c:42:9:42:10 | Argument 0 indirection |
+| funcsLocal.c:41:18:41:20 | gets output argument | funcsLocal.c:42:9:42:10 | i6 |
+| funcsLocal.c:41:18:41:20 | i61 | funcsLocal.c:42:9:42:10 | Argument 0 indirection |
+| funcsLocal.c:41:18:41:20 | i61 | funcsLocal.c:42:9:42:10 | i6 |
nodes
+| funcsLocal.c:16:8:16:9 | fread output argument | semmle.label | fread output argument |
+| funcsLocal.c:16:8:16:9 | i1 | semmle.label | i1 |
+| funcsLocal.c:17:9:17:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| funcsLocal.c:17:9:17:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| funcsLocal.c:17:9:17:10 | i1 | semmle.label | i1 |
+| funcsLocal.c:26:8:26:9 | fgets output argument | semmle.label | fgets output argument |
+| funcsLocal.c:26:8:26:9 | i3 | semmle.label | i3 |
+| funcsLocal.c:27:9:27:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| funcsLocal.c:27:9:27:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| funcsLocal.c:27:9:27:10 | i3 | semmle.label | i3 |
| funcsLocal.c:31:13:31:17 | call to fgets | semmle.label | call to fgets |
| funcsLocal.c:31:13:31:17 | call to fgets | semmle.label | call to fgets |
+| funcsLocal.c:31:19:31:21 | fgets output argument | semmle.label | fgets output argument |
+| funcsLocal.c:31:19:31:21 | i41 | semmle.label | i41 |
| funcsLocal.c:32:9:32:10 | (const char *)... | semmle.label | (const char *)... |
| funcsLocal.c:32:9:32:10 | (const char *)... | semmle.label | (const char *)... |
+| funcsLocal.c:32:9:32:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| funcsLocal.c:32:9:32:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| funcsLocal.c:32:9:32:10 | i4 | semmle.label | i4 |
| funcsLocal.c:32:9:32:10 | i4 | semmle.label | i4 |
| funcsLocal.c:32:9:32:10 | i4 | semmle.label | i4 |
+| funcsLocal.c:36:7:36:8 | gets output argument | semmle.label | gets output argument |
+| funcsLocal.c:36:7:36:8 | i5 | semmle.label | i5 |
+| funcsLocal.c:37:9:37:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| funcsLocal.c:37:9:37:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| funcsLocal.c:37:9:37:10 | i5 | semmle.label | i5 |
| funcsLocal.c:41:13:41:16 | call to gets | semmle.label | call to gets |
| funcsLocal.c:41:13:41:16 | call to gets | semmle.label | call to gets |
+| funcsLocal.c:41:18:41:20 | gets output argument | semmle.label | gets output argument |
+| funcsLocal.c:41:18:41:20 | i61 | semmle.label | i61 |
| funcsLocal.c:42:9:42:10 | (const char *)... | semmle.label | (const char *)... |
| funcsLocal.c:42:9:42:10 | (const char *)... | semmle.label | (const char *)... |
+| funcsLocal.c:42:9:42:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| funcsLocal.c:42:9:42:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| funcsLocal.c:42:9:42:10 | i6 | semmle.label | i6 |
| funcsLocal.c:42:9:42:10 | i6 | semmle.label | i6 |
| funcsLocal.c:42:9:42:10 | i6 | semmle.label | i6 |
+| funcsLocal.c:58:9:58:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| funcsLocal.c:58:9:58:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| funcsLocal.c:58:9:58:10 | e1 | semmle.label | e1 |
#select
+| funcsLocal.c:17:9:17:10 | i1 | funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:17:9:17:10 | i1 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:16:8:16:9 | i1 | fread |
+| funcsLocal.c:27:9:27:10 | i3 | funcsLocal.c:26:8:26:9 | i3 | funcsLocal.c:27:9:27:10 | i3 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:26:8:26:9 | i3 | fgets |
| funcsLocal.c:32:9:32:10 | i4 | funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | i4 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:31:13:31:17 | call to fgets | fgets |
+| funcsLocal.c:32:9:32:10 | i4 | funcsLocal.c:31:19:31:21 | i41 | funcsLocal.c:32:9:32:10 | i4 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:31:19:31:21 | i41 | fgets |
+| funcsLocal.c:37:9:37:10 | i5 | funcsLocal.c:36:7:36:8 | i5 | funcsLocal.c:37:9:37:10 | i5 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:36:7:36:8 | i5 | gets |
| funcsLocal.c:42:9:42:10 | i6 | funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | i6 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:41:13:41:16 | call to gets | gets |
+| funcsLocal.c:42:9:42:10 | i6 | funcsLocal.c:41:18:41:20 | i61 | funcsLocal.c:42:9:42:10 | i6 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:41:18:41:20 | i61 | gets |
+| funcsLocal.c:58:9:58:10 | e1 | funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:58:9:58:10 | e1 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:16:8:16:9 | i1 | fread |
From afbeca0d54e4beb06ce200a5b7128ceb0916d6c7 Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Mon, 9 Nov 2020 13:24:31 -0800
Subject: [PATCH 007/725] C++: Accept test outputs
---
.../CWE-022/semmle/tests/TaintedPath.expected | 2 -
.../CWE/CWE-079/semmle/CgiXss/CgiXss.expected | 12 ------
.../semmle/tests/UnboundedWrite.expected | 40 +++++++++++++++++++
...olledFormatStringThroughGlobalVar.expected | 3 --
4 files changed, 40 insertions(+), 17 deletions(-)
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected
index 03d1bfd25b3..7383fbbd215 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected
@@ -1,5 +1,3 @@
-WARNING: Unused predicate sinkInstructionNodes (C:/semmle/code/ql/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql:64,13-33)
-WARNING: Unused predicate sinkOperand (C:/semmle/code/ql/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql:70,13-24)
edges
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | Argument 0 indirection |
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | Argument 0 indirection |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-079/semmle/CgiXss/CgiXss.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-079/semmle/CgiXss/CgiXss.expected
index 6f08104489e..20bb97a9f66 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-079/semmle/CgiXss/CgiXss.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-079/semmle/CgiXss/CgiXss.expected
@@ -1,30 +1,20 @@
edges
-| search.c:14:24:14:28 | *query | search.c:17:8:17:12 | (const char *)... |
-| search.c:14:24:14:28 | *query | search.c:17:8:17:12 | query |
| search.c:14:24:14:28 | query | search.c:17:8:17:12 | (const char *)... |
| search.c:14:24:14:28 | query | search.c:17:8:17:12 | query |
| search.c:14:24:14:28 | query | search.c:17:8:17:12 | query |
-| search.c:22:24:22:28 | *query | search.c:23:39:23:43 | query |
-| search.c:22:24:22:28 | *query | search.c:23:39:23:43 | query |
| search.c:22:24:22:28 | query | search.c:23:39:23:43 | query |
| search.c:22:24:22:28 | query | search.c:23:39:23:43 | query |
-| search.c:41:21:41:26 | call to getenv | search.c:14:24:14:28 | *query |
-| search.c:41:21:41:26 | call to getenv | search.c:14:24:14:28 | *query |
| search.c:41:21:41:26 | call to getenv | search.c:14:24:14:28 | query |
| search.c:41:21:41:26 | call to getenv | search.c:14:24:14:28 | query |
-| search.c:41:21:41:26 | call to getenv | search.c:22:24:22:28 | *query |
-| search.c:41:21:41:26 | call to getenv | search.c:22:24:22:28 | *query |
| search.c:41:21:41:26 | call to getenv | search.c:22:24:22:28 | query |
| search.c:41:21:41:26 | call to getenv | search.c:22:24:22:28 | query |
nodes
-| search.c:14:24:14:28 | *query | semmle.label | *query |
| search.c:14:24:14:28 | query | semmle.label | query |
| search.c:17:8:17:12 | (const char *)... | semmle.label | (const char *)... |
| search.c:17:8:17:12 | (const char *)... | semmle.label | (const char *)... |
| search.c:17:8:17:12 | query | semmle.label | query |
| search.c:17:8:17:12 | query | semmle.label | query |
| search.c:17:8:17:12 | query | semmle.label | query |
-| search.c:22:24:22:28 | *query | semmle.label | *query |
| search.c:22:24:22:28 | query | semmle.label | query |
| search.c:23:39:23:43 | query | semmle.label | query |
| search.c:23:39:23:43 | query | semmle.label | query |
@@ -32,9 +22,7 @@ nodes
| search.c:41:21:41:26 | call to getenv | semmle.label | call to getenv |
| search.c:41:21:41:26 | call to getenv | semmle.label | call to getenv |
| search.c:45:5:45:15 | Argument 0 | semmle.label | Argument 0 |
-| search.c:45:17:45:25 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| search.c:47:5:47:15 | Argument 0 | semmle.label | Argument 0 |
-| search.c:47:17:47:25 | Argument 0 indirection | semmle.label | Argument 0 indirection |
#select
| search.c:17:8:17:12 | query | search.c:41:21:41:26 | call to getenv | search.c:17:8:17:12 | query | Cross-site scripting vulnerability due to $@. | search.c:41:21:41:26 | call to getenv | this query data |
| search.c:23:39:23:43 | query | search.c:41:21:41:26 | call to getenv | search.c:23:39:23:43 | query | Cross-site scripting vulnerability due to $@. | search.c:41:21:41:26 | call to getenv | this query data |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-120/semmle/tests/UnboundedWrite.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-120/semmle/tests/UnboundedWrite.expected
index 365f9ed0aa9..7aed6f01dc3 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-120/semmle/tests/UnboundedWrite.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-120/semmle/tests/UnboundedWrite.expected
@@ -7,12 +7,42 @@ edges
| tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | access to array |
| tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | access to array |
| tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | access to array |
+| tests.c:28:22:28:25 | argv | tests.c:31:15:31:23 | Argument 1 indirection |
+| tests.c:28:22:28:25 | argv | tests.c:31:15:31:23 | Argument 1 indirection |
+| tests.c:28:22:28:25 | argv | tests.c:31:15:31:23 | buffer100 |
+| tests.c:28:22:28:25 | argv | tests.c:31:15:31:23 | buffer100 |
+| tests.c:28:22:28:25 | argv | tests.c:33:21:33:29 | Argument 2 indirection |
+| tests.c:28:22:28:25 | argv | tests.c:33:21:33:29 | Argument 2 indirection |
+| tests.c:28:22:28:25 | argv | tests.c:33:21:33:29 | buffer100 |
+| tests.c:28:22:28:25 | argv | tests.c:33:21:33:29 | buffer100 |
| tests.c:28:22:28:28 | access to array | tests.c:28:22:28:28 | (const char *)... |
| tests.c:28:22:28:28 | access to array | tests.c:28:22:28:28 | access to array |
+| tests.c:28:22:28:28 | access to array | tests.c:31:15:31:23 | Argument 1 indirection |
+| tests.c:28:22:28:28 | access to array | tests.c:31:15:31:23 | buffer100 |
+| tests.c:28:22:28:28 | access to array | tests.c:33:21:33:29 | Argument 2 indirection |
+| tests.c:28:22:28:28 | access to array | tests.c:33:21:33:29 | buffer100 |
| tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | access to array |
| tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | access to array |
| tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | access to array |
| tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | access to array |
+| tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | access to array |
+| tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | access to array |
+| tests.c:29:28:29:31 | argv | tests.c:31:15:31:23 | Argument 1 indirection |
+| tests.c:29:28:29:31 | argv | tests.c:31:15:31:23 | Argument 1 indirection |
+| tests.c:29:28:29:31 | argv | tests.c:31:15:31:23 | buffer100 |
+| tests.c:29:28:29:31 | argv | tests.c:31:15:31:23 | buffer100 |
+| tests.c:29:28:29:31 | argv | tests.c:33:21:33:29 | Argument 2 indirection |
+| tests.c:29:28:29:31 | argv | tests.c:33:21:33:29 | Argument 2 indirection |
+| tests.c:29:28:29:31 | argv | tests.c:33:21:33:29 | buffer100 |
+| tests.c:29:28:29:31 | argv | tests.c:33:21:33:29 | buffer100 |
+| tests.c:29:28:29:34 | access to array | tests.c:31:15:31:23 | Argument 1 indirection |
+| tests.c:29:28:29:34 | access to array | tests.c:31:15:31:23 | buffer100 |
+| tests.c:29:28:29:34 | access to array | tests.c:33:21:33:29 | Argument 2 indirection |
+| tests.c:29:28:29:34 | access to array | tests.c:33:21:33:29 | buffer100 |
+| tests.c:31:15:31:23 | buffer100 | tests.c:33:21:33:29 | Argument 2 indirection |
+| tests.c:31:15:31:23 | buffer100 | tests.c:33:21:33:29 | buffer100 |
+| tests.c:31:15:31:23 | scanf output argument | tests.c:33:21:33:29 | Argument 2 indirection |
+| tests.c:31:15:31:23 | scanf output argument | tests.c:33:21:33:29 | buffer100 |
| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | (const char *)... |
| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | (const char *)... |
| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | access to array |
@@ -36,11 +66,16 @@ nodes
| tests.c:29:28:29:34 | access to array | semmle.label | access to array |
| tests.c:29:28:29:34 | access to array | semmle.label | access to array |
| tests.c:29:28:29:34 | access to array | semmle.label | access to array |
+| tests.c:31:15:31:23 | Argument 1 indirection | semmle.label | Argument 1 indirection |
+| tests.c:31:15:31:23 | Argument 1 indirection | semmle.label | Argument 1 indirection |
| tests.c:31:15:31:23 | array to pointer conversion | semmle.label | array to pointer conversion |
| tests.c:31:15:31:23 | array to pointer conversion | semmle.label | array to pointer conversion |
| tests.c:31:15:31:23 | buffer100 | semmle.label | buffer100 |
| tests.c:31:15:31:23 | buffer100 | semmle.label | buffer100 |
| tests.c:31:15:31:23 | buffer100 | semmle.label | buffer100 |
+| tests.c:31:15:31:23 | scanf output argument | semmle.label | scanf output argument |
+| tests.c:33:21:33:29 | Argument 2 indirection | semmle.label | Argument 2 indirection |
+| tests.c:33:21:33:29 | Argument 2 indirection | semmle.label | Argument 2 indirection |
| tests.c:33:21:33:29 | array to pointer conversion | semmle.label | array to pointer conversion |
| tests.c:33:21:33:29 | array to pointer conversion | semmle.label | array to pointer conversion |
| tests.c:33:21:33:29 | buffer100 | semmle.label | buffer100 |
@@ -56,6 +91,11 @@ nodes
#select
| tests.c:28:3:28:9 | call to sprintf | tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | access to array | This 'call to sprintf' with input from $@ may overflow the destination. | tests.c:28:22:28:25 | argv | argv |
| tests.c:29:3:29:9 | call to sprintf | tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | access to array | This 'call to sprintf' with input from $@ may overflow the destination. | tests.c:29:28:29:31 | argv | argv |
+| tests.c:31:15:31:23 | buffer100 | tests.c:28:22:28:25 | argv | tests.c:31:15:31:23 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:28:22:28:25 | argv | argv |
+| tests.c:31:15:31:23 | buffer100 | tests.c:29:28:29:31 | argv | tests.c:31:15:31:23 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:29:28:29:31 | argv | argv |
| tests.c:31:15:31:23 | buffer100 | tests.c:31:15:31:23 | buffer100 | tests.c:31:15:31:23 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:31:15:31:23 | buffer100 | buffer100 |
+| tests.c:33:21:33:29 | buffer100 | tests.c:28:22:28:25 | argv | tests.c:33:21:33:29 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:28:22:28:25 | argv | argv |
+| tests.c:33:21:33:29 | buffer100 | tests.c:29:28:29:31 | argv | tests.c:33:21:33:29 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:29:28:29:31 | argv | argv |
+| tests.c:33:21:33:29 | buffer100 | tests.c:31:15:31:23 | buffer100 | tests.c:33:21:33:29 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:31:15:31:23 | buffer100 | buffer100 |
| tests.c:33:21:33:29 | buffer100 | tests.c:33:21:33:29 | buffer100 | tests.c:33:21:33:29 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:33:21:33:29 | buffer100 | buffer100 |
| tests.c:34:25:34:33 | buffer100 | tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | access to array | This 'sscanf string argument' with input from $@ may overflow the destination. | tests.c:34:10:34:13 | argv | argv |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected
index 598d7df193c..14593adce48 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected
@@ -17,8 +17,6 @@ edges
| globalVars.c:12:2:12:15 | Store | globalVars.c:8:7:8:10 | copy |
| globalVars.c:15:21:15:23 | val | globalVars.c:16:2:16:12 | Store |
| globalVars.c:16:2:16:12 | Store | globalVars.c:9:7:9:11 | copy2 |
-| globalVars.c:24:11:24:14 | argv | globalVars.c:11:22:11:25 | *argv |
-| globalVars.c:24:11:24:14 | argv | globalVars.c:11:22:11:25 | *argv |
| globalVars.c:24:11:24:14 | argv | globalVars.c:11:22:11:25 | argv |
| globalVars.c:24:11:24:14 | argv | globalVars.c:11:22:11:25 | argv |
| globalVars.c:27:9:27:12 | copy | globalVars.c:27:9:27:12 | (const char *)... |
@@ -36,7 +34,6 @@ nodes
| globalVars.c:15:21:15:23 | val | semmle.label | val |
| globalVars.c:16:2:16:12 | Store | semmle.label | Store |
| globalVars.c:24:2:24:9 | Argument 0 | semmle.label | Argument 0 |
-| globalVars.c:24:11:24:14 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| globalVars.c:24:11:24:14 | argv | semmle.label | argv |
| globalVars.c:24:11:24:14 | argv | semmle.label | argv |
| globalVars.c:27:9:27:12 | (const char *)... | semmle.label | (const char *)... |
From 04ad94d1cc1b027b9ccbc06ae2afb9b9d6c7f29e Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Wed, 1 Jul 2020 14:48:19 -0700
Subject: [PATCH 008/725] C++: model taint from pointers to aliased buffers
---
.../dataflow/internal/TaintTrackingUtil.qll | 18 +++
.../CWE-134/semmle/argv/argvLocal.expected | 105 ++++++++++++++++++
2 files changed, 123 insertions(+)
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll
index 202d3f1c14e..1cbddcd70ae 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll
@@ -142,4 +142,22 @@ predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut) {
modelMidOut.isParameterDeref(indexMid) and
modelMidIn.isParameter(indexMid)
)
+ or
+ // Taint flow from a pointer argument to an output, when the model specifies flow from the deref
+ // to that output, but the deref is not modeled in the IR for the caller.
+ exists(
+ CallInstruction call, ReadSideEffectInstruction read, Function func,
+ FunctionInput modelIn, FunctionOutput modelOut
+ |
+ read.getSideEffectOperand() = callInput(call, modelIn).asOperand() and
+ read.getArgumentDef() = nodeIn.asInstruction() and
+ not read.getSideEffect().isResultModeled() and
+ call.getStaticCallTarget() = func and
+ (
+ func.(DataFlowFunction).hasDataFlow(modelIn, modelOut)
+ or
+ func.(TaintFunction).hasTaintFlow(modelIn, modelOut)
+ ) and
+ nodeOut.asInstruction() = callOutput(call, modelOut)
+ )
}
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected
index 35008e71818..bd5653e97fa 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected
@@ -61,6 +61,47 @@ edges
| argvLocal.c:106:9:106:13 | access to array | argvLocal.c:106:9:106:13 | access to array |
| argvLocal.c:110:9:110:11 | * ... | argvLocal.c:110:9:110:11 | (const char *)... |
| argvLocal.c:110:9:110:11 | * ... | argvLocal.c:110:9:110:11 | * ... |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:116:9:116:10 | Argument 0 indirection |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:116:9:116:10 | Argument 0 indirection |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:116:9:116:10 | i3 |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:116:9:116:10 | i3 |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | Argument 0 indirection |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | Argument 0 indirection |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | i3 |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | i3 |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | printWrapper output argument |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | printWrapper output argument |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:121:9:121:10 | Argument 0 indirection |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:121:9:121:10 | Argument 0 indirection |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:121:9:121:10 | i4 |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:121:9:121:10 | i4 |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | Argument 0 indirection |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | Argument 0 indirection |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | printWrapper output argument |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | printWrapper output argument |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:135:9:135:12 | ... ++ |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:135:9:135:12 | ... ++ |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:135:9:135:12 | Argument 0 indirection |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:135:9:135:12 | Argument 0 indirection |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:136:15:136:18 | -- ... |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:136:15:136:18 | -- ... |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:136:15:136:18 | Argument 0 indirection |
+| argvLocal.c:115:13:115:16 | argv | argvLocal.c:136:15:136:18 | Argument 0 indirection |
+| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:121:9:121:10 | Argument 0 indirection |
+| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:121:9:121:10 | i4 |
+| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:122:15:122:16 | Argument 0 indirection |
+| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:122:15:122:16 | i4 |
+| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:122:15:122:16 | printWrapper output argument |
+| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:135:9:135:12 | ... ++ |
+| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:135:9:135:12 | Argument 0 indirection |
+| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:136:15:136:18 | -- ... |
+| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:136:15:136:18 | Argument 0 indirection |
+| argvLocal.c:122:15:122:16 | printWrapper output argument | argvLocal.c:135:9:135:12 | ... ++ |
+| argvLocal.c:122:15:122:16 | printWrapper output argument | argvLocal.c:135:9:135:12 | Argument 0 indirection |
+| argvLocal.c:122:15:122:16 | printWrapper output argument | argvLocal.c:136:15:136:18 | -- ... |
+| argvLocal.c:122:15:122:16 | printWrapper output argument | argvLocal.c:136:15:136:18 | Argument 0 indirection |
| argvLocal.c:126:10:126:13 | argv | argvLocal.c:127:9:127:10 | Argument 0 indirection |
| argvLocal.c:126:10:126:13 | argv | argvLocal.c:127:9:127:10 | Argument 0 indirection |
| argvLocal.c:126:10:126:13 | argv | argvLocal.c:127:9:127:10 | i5 |
@@ -93,6 +134,22 @@ edges
| argvLocal.c:149:11:149:14 | argv | argvLocal.c:151:15:151:16 | i8 |
| argvLocal.c:149:11:149:14 | argv | argvLocal.c:151:15:151:16 | i8 |
| argvLocal.c:149:11:149:14 | argv | argvLocal.c:151:15:151:16 | i8 |
+| argvLocal.c:156:23:156:26 | argv | argvLocal.c:157:9:157:10 | Argument 0 indirection |
+| argvLocal.c:156:23:156:26 | argv | argvLocal.c:157:9:157:10 | Argument 0 indirection |
+| argvLocal.c:156:23:156:26 | argv | argvLocal.c:157:9:157:10 | i9 |
+| argvLocal.c:156:23:156:26 | argv | argvLocal.c:157:9:157:10 | i9 |
+| argvLocal.c:156:23:156:26 | argv | argvLocal.c:158:15:158:16 | Argument 0 indirection |
+| argvLocal.c:156:23:156:26 | argv | argvLocal.c:158:15:158:16 | Argument 0 indirection |
+| argvLocal.c:156:23:156:26 | argv | argvLocal.c:158:15:158:16 | i9 |
+| argvLocal.c:156:23:156:26 | argv | argvLocal.c:158:15:158:16 | i9 |
+| argvLocal.c:163:22:163:25 | argv | argvLocal.c:164:9:164:11 | Argument 0 indirection |
+| argvLocal.c:163:22:163:25 | argv | argvLocal.c:164:9:164:11 | Argument 0 indirection |
+| argvLocal.c:163:22:163:25 | argv | argvLocal.c:164:9:164:11 | i91 |
+| argvLocal.c:163:22:163:25 | argv | argvLocal.c:164:9:164:11 | i91 |
+| argvLocal.c:163:22:163:25 | argv | argvLocal.c:165:15:165:17 | Argument 0 indirection |
+| argvLocal.c:163:22:163:25 | argv | argvLocal.c:165:15:165:17 | Argument 0 indirection |
+| argvLocal.c:163:22:163:25 | argv | argvLocal.c:165:15:165:17 | i91 |
+| argvLocal.c:163:22:163:25 | argv | argvLocal.c:165:15:165:17 | i91 |
| argvLocal.c:168:18:168:21 | argv | argvLocal.c:169:9:169:20 | (char *)... |
| argvLocal.c:168:18:168:21 | argv | argvLocal.c:169:9:169:20 | (char *)... |
| argvLocal.c:168:18:168:21 | argv | argvLocal.c:169:9:169:20 | (const char *)... |
@@ -150,6 +207,22 @@ nodes
| argvLocal.c:111:15:111:17 | * ... | semmle.label | * ... |
| argvLocal.c:111:15:111:17 | * ... | semmle.label | * ... |
| argvLocal.c:111:15:111:17 | * ... | semmle.label | * ... |
+| argvLocal.c:115:13:115:16 | argv | semmle.label | argv |
+| argvLocal.c:115:13:115:16 | argv | semmle.label | argv |
+| argvLocal.c:116:9:116:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| argvLocal.c:116:9:116:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| argvLocal.c:116:9:116:10 | i3 | semmle.label | i3 |
+| argvLocal.c:117:15:117:16 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| argvLocal.c:117:15:117:16 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| argvLocal.c:117:15:117:16 | i3 | semmle.label | i3 |
+| argvLocal.c:117:15:117:16 | printWrapper output argument | semmle.label | printWrapper output argument |
+| argvLocal.c:121:9:121:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| argvLocal.c:121:9:121:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| argvLocal.c:121:9:121:10 | i4 | semmle.label | i4 |
+| argvLocal.c:122:15:122:16 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| argvLocal.c:122:15:122:16 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| argvLocal.c:122:15:122:16 | i4 | semmle.label | i4 |
+| argvLocal.c:122:15:122:16 | printWrapper output argument | semmle.label | printWrapper output argument |
| argvLocal.c:126:10:126:13 | argv | semmle.label | argv |
| argvLocal.c:126:10:126:13 | argv | semmle.label | argv |
| argvLocal.c:127:9:127:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
@@ -165,6 +238,12 @@ nodes
| argvLocal.c:132:15:132:20 | ... + ... | semmle.label | ... + ... |
| argvLocal.c:132:15:132:20 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| argvLocal.c:132:15:132:20 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| argvLocal.c:135:9:135:12 | ... ++ | semmle.label | ... ++ |
+| argvLocal.c:135:9:135:12 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| argvLocal.c:135:9:135:12 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| argvLocal.c:136:15:136:18 | -- ... | semmle.label | -- ... |
+| argvLocal.c:136:15:136:18 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| argvLocal.c:136:15:136:18 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| argvLocal.c:144:9:144:10 | (const char *)... | semmle.label | (const char *)... |
| argvLocal.c:144:9:144:10 | (const char *)... | semmle.label | (const char *)... |
| argvLocal.c:144:9:144:10 | i7 | semmle.label | i7 |
@@ -183,6 +262,22 @@ nodes
| argvLocal.c:151:15:151:16 | i8 | semmle.label | i8 |
| argvLocal.c:151:15:151:16 | i8 | semmle.label | i8 |
| argvLocal.c:151:15:151:16 | i8 | semmle.label | i8 |
+| argvLocal.c:156:23:156:26 | argv | semmle.label | argv |
+| argvLocal.c:156:23:156:26 | argv | semmle.label | argv |
+| argvLocal.c:157:9:157:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| argvLocal.c:157:9:157:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| argvLocal.c:157:9:157:10 | i9 | semmle.label | i9 |
+| argvLocal.c:158:15:158:16 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| argvLocal.c:158:15:158:16 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| argvLocal.c:158:15:158:16 | i9 | semmle.label | i9 |
+| argvLocal.c:163:22:163:25 | argv | semmle.label | argv |
+| argvLocal.c:163:22:163:25 | argv | semmle.label | argv |
+| argvLocal.c:164:9:164:11 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| argvLocal.c:164:9:164:11 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| argvLocal.c:164:9:164:11 | i91 | semmle.label | i91 |
+| argvLocal.c:165:15:165:17 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| argvLocal.c:165:15:165:17 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| argvLocal.c:165:15:165:17 | i91 | semmle.label | i91 |
| argvLocal.c:168:18:168:21 | argv | semmle.label | argv |
| argvLocal.c:168:18:168:21 | argv | semmle.label | argv |
| argvLocal.c:169:9:169:20 | (char *)... | semmle.label | (char *)... |
@@ -206,13 +301,23 @@ nodes
| argvLocal.c:107:15:107:19 | access to array | argvLocal.c:105:14:105:17 | argv | argvLocal.c:107:15:107:19 | access to array | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:105:14:105:17 | argv | argv |
| argvLocal.c:110:9:110:11 | * ... | argvLocal.c:105:14:105:17 | argv | argvLocal.c:110:9:110:11 | * ... | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:105:14:105:17 | argv | argv |
| argvLocal.c:111:15:111:17 | * ... | argvLocal.c:105:14:105:17 | argv | argvLocal.c:111:15:111:17 | * ... | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:105:14:105:17 | argv | argv |
+| argvLocal.c:116:9:116:10 | i3 | argvLocal.c:115:13:115:16 | argv | argvLocal.c:116:9:116:10 | i3 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:115:13:115:16 | argv | argv |
+| argvLocal.c:117:15:117:16 | i3 | argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | i3 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:115:13:115:16 | argv | argv |
+| argvLocal.c:121:9:121:10 | i4 | argvLocal.c:115:13:115:16 | argv | argvLocal.c:121:9:121:10 | i4 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:115:13:115:16 | argv | argv |
+| argvLocal.c:122:15:122:16 | i4 | argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:115:13:115:16 | argv | argv |
| argvLocal.c:127:9:127:10 | i5 | argvLocal.c:126:10:126:13 | argv | argvLocal.c:127:9:127:10 | i5 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:126:10:126:13 | argv | argv |
| argvLocal.c:128:15:128:16 | i5 | argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | i5 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:126:10:126:13 | argv | argv |
| argvLocal.c:131:9:131:14 | ... + ... | argvLocal.c:126:10:126:13 | argv | argvLocal.c:131:9:131:14 | ... + ... | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:126:10:126:13 | argv | argv |
| argvLocal.c:132:15:132:20 | ... + ... | argvLocal.c:126:10:126:13 | argv | argvLocal.c:132:15:132:20 | ... + ... | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:126:10:126:13 | argv | argv |
+| argvLocal.c:135:9:135:12 | ... ++ | argvLocal.c:115:13:115:16 | argv | argvLocal.c:135:9:135:12 | ... ++ | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:115:13:115:16 | argv | argv |
+| argvLocal.c:136:15:136:18 | -- ... | argvLocal.c:115:13:115:16 | argv | argvLocal.c:136:15:136:18 | -- ... | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:115:13:115:16 | argv | argv |
| argvLocal.c:144:9:144:10 | i7 | argvLocal.c:100:7:100:10 | argv | argvLocal.c:144:9:144:10 | i7 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:100:7:100:10 | argv | argv |
| argvLocal.c:145:15:145:16 | i7 | argvLocal.c:100:7:100:10 | argv | argvLocal.c:145:15:145:16 | i7 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:100:7:100:10 | argv | argv |
| argvLocal.c:150:9:150:10 | i8 | argvLocal.c:149:11:149:14 | argv | argvLocal.c:150:9:150:10 | i8 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:149:11:149:14 | argv | argv |
| argvLocal.c:151:15:151:16 | i8 | argvLocal.c:149:11:149:14 | argv | argvLocal.c:151:15:151:16 | i8 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:149:11:149:14 | argv | argv |
+| argvLocal.c:157:9:157:10 | i9 | argvLocal.c:156:23:156:26 | argv | argvLocal.c:157:9:157:10 | i9 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:156:23:156:26 | argv | argv |
+| argvLocal.c:158:15:158:16 | i9 | argvLocal.c:156:23:156:26 | argv | argvLocal.c:158:15:158:16 | i9 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:156:23:156:26 | argv | argv |
+| argvLocal.c:164:9:164:11 | i91 | argvLocal.c:163:22:163:25 | argv | argvLocal.c:164:9:164:11 | i91 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:163:22:163:25 | argv | argv |
+| argvLocal.c:165:15:165:17 | i91 | argvLocal.c:163:22:163:25 | argv | argvLocal.c:165:15:165:17 | i91 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:163:22:163:25 | argv | argv |
| argvLocal.c:169:18:169:20 | i10 | argvLocal.c:168:18:168:21 | argv | argvLocal.c:169:18:169:20 | i10 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:168:18:168:21 | argv | argv |
| argvLocal.c:170:24:170:26 | i10 | argvLocal.c:168:18:168:21 | argv | argvLocal.c:170:24:170:26 | i10 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:168:18:168:21 | argv | argv |
From 2a6ba40a93972f345a821805e8cd245b27d650a4 Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Tue, 10 Nov 2020 13:59:35 -0800
Subject: [PATCH 009/725] C++: Accept more test changes
---
.../security-taint/tainted_diff.expected | 25 ++++
.../security-taint/tainted_ir.expected | 117 +++++++-----------
.../dataflow/taint-tests/test_diff.expected | 1 -
.../dataflow/taint-tests/test_ir.expected | 1 +
4 files changed, 72 insertions(+), 72 deletions(-)
diff --git a/cpp/ql/test/library-tests/dataflow/security-taint/tainted_diff.expected b/cpp/ql/test/library-tests/dataflow/security-taint/tainted_diff.expected
index 26441b08ba4..9050ad322f8 100644
--- a/cpp/ql/test/library-tests/dataflow/security-taint/tainted_diff.expected
+++ b/cpp/ql/test/library-tests/dataflow/security-taint/tainted_diff.expected
@@ -1,19 +1,44 @@
+| test.cpp:23:23:23:28 | call to getenv | test.cpp:8:24:8:25 | s1 | AST only |
+| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:14:23:19 | envStr | AST only |
+| test.cpp:38:23:38:28 | call to getenv | test.cpp:8:24:8:25 | s1 | AST only |
+| test.cpp:38:23:38:28 | call to getenv | test.cpp:38:14:38:19 | envStr | AST only |
+| test.cpp:49:23:49:28 | call to getenv | test.cpp:8:24:8:25 | s1 | AST only |
+| test.cpp:49:23:49:28 | call to getenv | test.cpp:45:13:45:24 | envStrGlobal | AST only |
+| test.cpp:49:23:49:28 | call to getenv | test.cpp:49:14:49:19 | envStr | AST only |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:15:50:24 | envStr_ptr | AST only |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:28:50:40 | & ... | AST only |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:29:50:40 | envStrGlobal | AST only |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:52:2:52:12 | * ... | AST only |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:52:3:52:12 | envStr_ptr | AST only |
+| test.cpp:60:29:60:34 | call to getenv | test.cpp:10:27:10:27 | s | AST only |
+| test.cpp:60:29:60:34 | call to getenv | test.cpp:60:18:60:25 | userName | AST only |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:11:20:11:21 | s1 | AST only |
+| test.cpp:68:28:68:33 | call to getenv | test.cpp:11:36:11:37 | s2 | AST only |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:67:7:67:13 | copying | AST only |
+| test.cpp:68:28:68:33 | call to getenv | test.cpp:68:17:68:24 | userName | AST only |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:69:10:69:13 | copy | AST only |
+| test.cpp:68:28:68:33 | call to getenv | test.cpp:70:5:70:10 | call to strcpy | AST only |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:70:12:70:15 | copy | AST only |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:71:12:71:15 | copy | AST only |
+| test.cpp:75:20:75:25 | call to getenv | test.cpp:15:22:15:25 | nptr | AST only |
+| test.cpp:83:28:83:33 | call to getenv | test.cpp:8:24:8:25 | s1 | AST only |
| test.cpp:83:28:83:33 | call to getenv | test.cpp:11:20:11:21 | s1 | AST only |
+| test.cpp:83:28:83:33 | call to getenv | test.cpp:11:36:11:37 | s2 | AST only |
+| test.cpp:83:28:83:33 | call to getenv | test.cpp:83:17:83:24 | userName | AST only |
| test.cpp:83:28:83:33 | call to getenv | test.cpp:85:8:85:11 | copy | AST only |
+| test.cpp:83:28:83:33 | call to getenv | test.cpp:86:2:86:7 | call to strcpy | AST only |
| test.cpp:83:28:83:33 | call to getenv | test.cpp:86:9:86:12 | copy | AST only |
+| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | (const char *)... | AST only |
+| test.cpp:100:12:100:15 | call to gets | test.cpp:98:8:98:14 | pointer | AST only |
| test.cpp:100:12:100:15 | call to gets | test.cpp:100:2:100:8 | pointer | AST only |
+| test.cpp:100:17:100:22 | buffer | test.cpp:93:18:93:18 | s | AST only |
| test.cpp:100:17:100:22 | buffer | test.cpp:97:7:97:12 | buffer | AST only |
| test.cpp:100:17:100:22 | buffer | test.cpp:100:17:100:22 | array to pointer conversion | IR only |
+| test.cpp:106:28:106:33 | call to getenv | test.cpp:8:24:8:25 | s1 | AST only |
| test.cpp:106:28:106:33 | call to getenv | test.cpp:11:20:11:21 | s1 | AST only |
+| test.cpp:106:28:106:33 | call to getenv | test.cpp:11:36:11:37 | s2 | AST only |
+| test.cpp:106:28:106:33 | call to getenv | test.cpp:106:17:106:24 | userName | AST only |
| test.cpp:106:28:106:33 | call to getenv | test.cpp:108:8:108:11 | copy | AST only |
+| test.cpp:106:28:106:33 | call to getenv | test.cpp:109:2:109:7 | call to strcpy | AST only |
| test.cpp:106:28:106:33 | call to getenv | test.cpp:109:9:109:12 | copy | AST only |
+| test.cpp:106:28:106:33 | call to getenv | test.cpp:111:14:111:17 | (const char *)... | AST only |
diff --git a/cpp/ql/test/library-tests/dataflow/security-taint/tainted_ir.expected b/cpp/ql/test/library-tests/dataflow/security-taint/tainted_ir.expected
index 50c90bccb2b..7f3044e6715 100644
--- a/cpp/ql/test/library-tests/dataflow/security-taint/tainted_ir.expected
+++ b/cpp/ql/test/library-tests/dataflow/security-taint/tainted_ir.expected
@@ -1,71 +1,46 @@
-| test.cpp:23:23:23:28 | call to getenv | test.cpp:8:24:8:25 | s1 | |
-| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:14:23:19 | envStr | |
-| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:23:23:28 | call to getenv | |
-| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:23:23:40 | (const char *)... | |
-| test.cpp:23:23:23:28 | call to getenv | test.cpp:25:6:25:29 | ! ... | |
-| test.cpp:23:23:23:28 | call to getenv | test.cpp:25:7:25:12 | call to strcmp | |
-| test.cpp:23:23:23:28 | call to getenv | test.cpp:25:7:25:29 | (bool)... | |
-| test.cpp:23:23:23:28 | call to getenv | test.cpp:25:14:25:19 | envStr | |
-| test.cpp:23:23:23:28 | call to getenv | test.cpp:29:6:29:28 | ! ... | |
-| test.cpp:23:23:23:28 | call to getenv | test.cpp:29:7:29:12 | call to strcmp | |
-| test.cpp:23:23:23:28 | call to getenv | test.cpp:29:7:29:28 | (bool)... | |
-| test.cpp:23:23:23:28 | call to getenv | test.cpp:29:14:29:19 | envStr | |
-| test.cpp:38:23:38:28 | call to getenv | test.cpp:8:24:8:25 | s1 | |
-| test.cpp:38:23:38:28 | call to getenv | test.cpp:38:14:38:19 | envStr | |
-| test.cpp:38:23:38:28 | call to getenv | test.cpp:38:23:38:28 | call to getenv | |
-| test.cpp:38:23:38:28 | call to getenv | test.cpp:38:23:38:40 | (const char *)... | |
-| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:14:40:19 | envStr | |
-| test.cpp:49:23:49:28 | call to getenv | test.cpp:8:24:8:25 | s1 | |
-| test.cpp:49:23:49:28 | call to getenv | test.cpp:45:13:45:24 | envStrGlobal | |
-| test.cpp:49:23:49:28 | call to getenv | test.cpp:49:14:49:19 | envStr | |
-| test.cpp:49:23:49:28 | call to getenv | test.cpp:49:23:49:28 | call to getenv | |
-| test.cpp:49:23:49:28 | call to getenv | test.cpp:49:23:49:40 | (const char *)... | |
-| test.cpp:49:23:49:28 | call to getenv | test.cpp:52:16:52:21 | envStr | |
-| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:6:54:35 | ! ... | |
-| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:7:54:12 | call to strcmp | |
-| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:7:54:35 | (bool)... | |
-| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:14:54:25 | envStrGlobal | |
-| test.cpp:60:29:60:34 | call to getenv | test.cpp:10:27:10:27 | s | |
-| test.cpp:60:29:60:34 | call to getenv | test.cpp:60:18:60:25 | userName | |
-| test.cpp:60:29:60:34 | call to getenv | test.cpp:60:29:60:34 | call to getenv | |
-| test.cpp:60:29:60:34 | call to getenv | test.cpp:60:29:60:47 | (const char *)... | |
-| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:25:64:32 | userName | |
-| test.cpp:68:28:68:33 | call to getenv | test.cpp:11:36:11:37 | s2 | |
-| test.cpp:68:28:68:33 | call to getenv | test.cpp:68:17:68:24 | userName | |
-| test.cpp:68:28:68:33 | call to getenv | test.cpp:68:28:68:33 | call to getenv | |
-| test.cpp:68:28:68:33 | call to getenv | test.cpp:68:28:68:46 | (const char *)... | |
-| test.cpp:68:28:68:33 | call to getenv | test.cpp:70:5:70:10 | call to strcpy | |
-| test.cpp:68:28:68:33 | call to getenv | test.cpp:70:18:70:25 | userName | |
-| test.cpp:75:20:75:25 | call to getenv | test.cpp:15:22:15:25 | nptr | |
-| test.cpp:75:20:75:25 | call to getenv | test.cpp:75:15:75:18 | call to atoi | |
-| test.cpp:75:20:75:25 | call to getenv | test.cpp:75:20:75:25 | call to getenv | |
-| test.cpp:75:20:75:25 | call to getenv | test.cpp:75:20:75:45 | (const char *)... | |
-| test.cpp:83:28:83:33 | call to getenv | test.cpp:8:24:8:25 | s1 | |
-| test.cpp:83:28:83:33 | call to getenv | test.cpp:11:36:11:37 | s2 | |
-| test.cpp:83:28:83:33 | call to getenv | test.cpp:83:17:83:24 | userName | |
-| test.cpp:83:28:83:33 | call to getenv | test.cpp:83:28:83:33 | call to getenv | |
-| test.cpp:83:28:83:33 | call to getenv | test.cpp:83:28:83:46 | (const char *)... | |
-| test.cpp:83:28:83:33 | call to getenv | test.cpp:86:2:86:7 | call to strcpy | |
-| test.cpp:83:28:83:33 | call to getenv | test.cpp:86:15:86:22 | userName | |
-| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:6:88:27 | ! ... | |
-| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:7:88:12 | call to strcmp | |
-| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:7:88:27 | (bool)... | |
-| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | (const char *)... | |
-| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | copy | |
-| test.cpp:100:12:100:15 | call to gets | test.cpp:98:8:98:14 | pointer | |
-| test.cpp:100:12:100:15 | call to gets | test.cpp:100:12:100:15 | call to gets | |
-| test.cpp:100:17:100:22 | buffer | test.cpp:93:18:93:18 | s | |
-| test.cpp:100:17:100:22 | buffer | test.cpp:100:17:100:22 | array to pointer conversion | |
-| test.cpp:100:17:100:22 | buffer | test.cpp:100:17:100:22 | buffer | |
-| test.cpp:106:28:106:33 | call to getenv | test.cpp:8:24:8:25 | s1 | |
-| test.cpp:106:28:106:33 | call to getenv | test.cpp:11:36:11:37 | s2 | |
-| test.cpp:106:28:106:33 | call to getenv | test.cpp:106:17:106:24 | userName | |
-| test.cpp:106:28:106:33 | call to getenv | test.cpp:106:28:106:33 | call to getenv | |
-| test.cpp:106:28:106:33 | call to getenv | test.cpp:106:28:106:46 | (const char *)... | |
-| test.cpp:106:28:106:33 | call to getenv | test.cpp:109:2:109:7 | call to strcpy | |
-| test.cpp:106:28:106:33 | call to getenv | test.cpp:109:15:109:22 | userName | |
-| test.cpp:106:28:106:33 | call to getenv | test.cpp:111:6:111:27 | ! ... | |
-| test.cpp:106:28:106:33 | call to getenv | test.cpp:111:7:111:12 | call to strcmp | |
-| test.cpp:106:28:106:33 | call to getenv | test.cpp:111:7:111:27 | (bool)... | |
-| test.cpp:106:28:106:33 | call to getenv | test.cpp:111:14:111:17 | (const char *)... | |
-| test.cpp:106:28:106:33 | call to getenv | test.cpp:111:14:111:17 | copy | |
+| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:23:23:28 | call to getenv |
+| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:23:23:40 | (const char *)... |
+| test.cpp:23:23:23:28 | call to getenv | test.cpp:25:6:25:29 | ! ... |
+| test.cpp:23:23:23:28 | call to getenv | test.cpp:25:7:25:12 | call to strcmp |
+| test.cpp:23:23:23:28 | call to getenv | test.cpp:25:7:25:29 | (bool)... |
+| test.cpp:23:23:23:28 | call to getenv | test.cpp:25:14:25:19 | envStr |
+| test.cpp:23:23:23:28 | call to getenv | test.cpp:29:6:29:28 | ! ... |
+| test.cpp:23:23:23:28 | call to getenv | test.cpp:29:7:29:12 | call to strcmp |
+| test.cpp:23:23:23:28 | call to getenv | test.cpp:29:7:29:28 | (bool)... |
+| test.cpp:23:23:23:28 | call to getenv | test.cpp:29:14:29:19 | envStr |
+| test.cpp:38:23:38:28 | call to getenv | test.cpp:38:23:38:28 | call to getenv |
+| test.cpp:38:23:38:28 | call to getenv | test.cpp:38:23:38:40 | (const char *)... |
+| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:14:40:19 | envStr |
+| test.cpp:49:23:49:28 | call to getenv | test.cpp:49:23:49:28 | call to getenv |
+| test.cpp:49:23:49:28 | call to getenv | test.cpp:49:23:49:40 | (const char *)... |
+| test.cpp:49:23:49:28 | call to getenv | test.cpp:52:16:52:21 | envStr |
+| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:6:54:35 | ! ... |
+| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:7:54:12 | call to strcmp |
+| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:7:54:35 | (bool)... |
+| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:14:54:25 | envStrGlobal |
+| test.cpp:60:29:60:34 | call to getenv | test.cpp:60:29:60:34 | call to getenv |
+| test.cpp:60:29:60:34 | call to getenv | test.cpp:60:29:60:47 | (const char *)... |
+| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:25:64:32 | userName |
+| test.cpp:68:28:68:33 | call to getenv | test.cpp:68:28:68:33 | call to getenv |
+| test.cpp:68:28:68:33 | call to getenv | test.cpp:68:28:68:46 | (const char *)... |
+| test.cpp:68:28:68:33 | call to getenv | test.cpp:70:18:70:25 | userName |
+| test.cpp:75:20:75:25 | call to getenv | test.cpp:75:15:75:18 | call to atoi |
+| test.cpp:75:20:75:25 | call to getenv | test.cpp:75:20:75:25 | call to getenv |
+| test.cpp:75:20:75:25 | call to getenv | test.cpp:75:20:75:45 | (const char *)... |
+| test.cpp:83:28:83:33 | call to getenv | test.cpp:83:28:83:33 | call to getenv |
+| test.cpp:83:28:83:33 | call to getenv | test.cpp:83:28:83:46 | (const char *)... |
+| test.cpp:83:28:83:33 | call to getenv | test.cpp:86:15:86:22 | userName |
+| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:6:88:27 | ! ... |
+| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:7:88:12 | call to strcmp |
+| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:7:88:27 | (bool)... |
+| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | copy |
+| test.cpp:100:12:100:15 | call to gets | test.cpp:100:12:100:15 | call to gets |
+| test.cpp:100:17:100:22 | buffer | test.cpp:100:17:100:22 | array to pointer conversion |
+| test.cpp:100:17:100:22 | buffer | test.cpp:100:17:100:22 | buffer |
+| test.cpp:106:28:106:33 | call to getenv | test.cpp:106:28:106:33 | call to getenv |
+| test.cpp:106:28:106:33 | call to getenv | test.cpp:106:28:106:46 | (const char *)... |
+| test.cpp:106:28:106:33 | call to getenv | test.cpp:109:15:109:22 | userName |
+| test.cpp:106:28:106:33 | call to getenv | test.cpp:111:6:111:27 | ! ... |
+| test.cpp:106:28:106:33 | call to getenv | test.cpp:111:7:111:12 | call to strcmp |
+| test.cpp:106:28:106:33 | call to getenv | test.cpp:111:7:111:27 | (bool)... |
+| test.cpp:106:28:106:33 | call to getenv | test.cpp:111:14:111:17 | copy |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
index a9eaa898a11..88f79183d2a 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
@@ -200,7 +200,6 @@
| taint.cpp:42:7:42:13 | taint.cpp:35:12:35:17 | AST only |
| taint.cpp:43:7:43:13 | taint.cpp:37:22:37:27 | AST only |
| taint.cpp:137:7:137:9 | taint.cpp:120:11:120:16 | AST only |
-| taint.cpp:195:7:195:7 | taint.cpp:192:23:192:28 | AST only |
| taint.cpp:195:7:195:7 | taint.cpp:193:6:193:6 | AST only |
| taint.cpp:236:3:236:6 | taint.cpp:223:10:223:15 | AST only |
| taint.cpp:372:7:372:7 | taint.cpp:365:24:365:29 | AST only |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected
index 5ba1707dd76..e84ce0d4c2e 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected
@@ -513,6 +513,7 @@
| taint.cpp:168:8:168:14 | tainted | taint.cpp:164:19:164:24 | call to source |
| taint.cpp:173:8:173:13 | Argument 0 indirection | taint.cpp:164:19:164:24 | call to source |
| taint.cpp:181:8:181:9 | * ... | taint.cpp:185:11:185:16 | call to source |
+| taint.cpp:195:7:195:7 | x | taint.cpp:192:23:192:28 | source |
| taint.cpp:210:7:210:7 | x | taint.cpp:207:6:207:11 | call to source |
| taint.cpp:215:7:215:7 | x | taint.cpp:207:6:207:11 | call to source |
| taint.cpp:216:7:216:7 | y | taint.cpp:207:6:207:11 | call to source |
From 68040b717e55045593ab63d7687059aefa74f7cc Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Thu, 12 Nov 2020 14:32:19 -0800
Subject: [PATCH 010/725] C++: autoformat
---
.../library-tests/dataflow/security-taint/tainted_ir.ql | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/cpp/ql/test/library-tests/dataflow/security-taint/tainted_ir.ql b/cpp/ql/test/library-tests/dataflow/security-taint/tainted_ir.ql
index 77f04d301f0..2a07e444cf9 100644
--- a/cpp/ql/test/library-tests/dataflow/security-taint/tainted_ir.ql
+++ b/cpp/ql/test/library-tests/dataflow/security-taint/tainted_ir.ql
@@ -1,13 +1,11 @@
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking
class SourceConfiguration extends TaintedWithPath::TaintTrackingConfiguration {
- override predicate isSink(Element e) {
- any()
- }
+ override predicate isSink(Element e) { any() }
}
from Expr source, Element tainted
where
TaintedWithPath::taintedWithPath(source, tainted, _, _) and
not tainted.getLocation().getFile().getExtension() = "h"
-select source, tainted
\ No newline at end of file
+select source, tainted
From bd00988c372ba09d0c88f3aa2eafc93bd4aae54f Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Thu, 12 Nov 2020 14:38:53 -0800
Subject: [PATCH 011/725] C++: accept test output for DefaultTaintTracking
---
.../DefaultTaintTracking/tainted.expected | 162 ++----------------
.../DefaultTaintTracking/test_diff.expected | 91 ++++------
2 files changed, 48 insertions(+), 205 deletions(-)
diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.expected b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.expected
index ea756d0efc5..9f8ed0d28d3 100644
--- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.expected
+++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.expected
@@ -1,105 +1,55 @@
-| defaulttainttracking.cpp:16:16:16:21 | call to getenv | defaulttainttracking.cpp:16:8:16:14 | call to _strdup |
-| defaulttainttracking.cpp:16:16:16:21 | call to getenv | defaulttainttracking.cpp:16:8:16:29 | (const char *)... |
| defaulttainttracking.cpp:16:16:16:21 | call to getenv | defaulttainttracking.cpp:16:16:16:21 | call to getenv |
| defaulttainttracking.cpp:16:16:16:21 | call to getenv | defaulttainttracking.cpp:16:16:16:28 | (const char *)... |
-| defaulttainttracking.cpp:16:16:16:21 | call to getenv | shared.h:5:23:5:31 | sinkparam |
-| defaulttainttracking.cpp:16:16:16:21 | call to getenv | shared.h:13:27:13:32 | string |
-| defaulttainttracking.cpp:17:15:17:20 | call to getenv | defaulttainttracking.cpp:17:8:17:13 | call to strdup |
-| defaulttainttracking.cpp:17:15:17:20 | call to getenv | defaulttainttracking.cpp:17:8:17:28 | (const char *)... |
| defaulttainttracking.cpp:17:15:17:20 | call to getenv | defaulttainttracking.cpp:17:15:17:20 | call to getenv |
| defaulttainttracking.cpp:17:15:17:20 | call to getenv | defaulttainttracking.cpp:17:15:17:27 | (const char *)... |
-| defaulttainttracking.cpp:17:15:17:20 | call to getenv | shared.h:5:23:5:31 | sinkparam |
-| defaulttainttracking.cpp:17:15:17:20 | call to getenv | shared.h:12:26:12:31 | string |
| defaulttainttracking.cpp:18:27:18:32 | call to getenv | defaulttainttracking.cpp:18:27:18:32 | call to getenv |
| defaulttainttracking.cpp:18:27:18:32 | call to getenv | defaulttainttracking.cpp:18:27:18:39 | (const char *)... |
-| defaulttainttracking.cpp:18:27:18:32 | call to getenv | shared.h:14:38:14:49 | const_string |
-| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:22:8:22:13 | call to strcat |
-| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:22:8:22:33 | (const char *)... |
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:22:20:22:25 | call to getenv |
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:22:20:22:32 | (const char *)... |
-| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:24:8:24:10 | (const char *)... |
-| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:24:8:24:10 | array to pointer conversion |
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:24:8:24:10 | buf |
-| defaulttainttracking.cpp:22:20:22:25 | call to getenv | shared.h:5:23:5:31 | sinkparam |
-| defaulttainttracking.cpp:22:20:22:25 | call to getenv | shared.h:10:38:10:39 | s2 |
-| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:31:40:31:53 | dotted_address |
-| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:32:11:32:26 | (unnamed parameter 0) |
-| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:38:11:38:21 | env_pointer |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:38:25:38:30 | call to getenv |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:38:25:38:37 | (void *)... |
-| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:22:39:22 | a |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:26:39:34 | call to inet_addr |
-| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:36:39:61 | (const char *)... |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:50:39:61 | & ... |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:40:10:40:10 | a |
-| defaulttainttracking.cpp:64:10:64:15 | call to getenv | defaulttainttracking.cpp:45:20:45:29 | (unnamed parameter 0) |
-| defaulttainttracking.cpp:64:10:64:15 | call to getenv | defaulttainttracking.cpp:52:24:52:24 | p |
-| defaulttainttracking.cpp:64:10:64:15 | call to getenv | defaulttainttracking.cpp:57:24:57:24 | p |
| defaulttainttracking.cpp:64:10:64:15 | call to getenv | defaulttainttracking.cpp:58:14:58:14 | p |
| defaulttainttracking.cpp:64:10:64:15 | call to getenv | defaulttainttracking.cpp:64:10:64:15 | call to getenv |
| defaulttainttracking.cpp:64:10:64:15 | call to getenv | defaulttainttracking.cpp:64:10:64:22 | (const char *)... |
-| defaulttainttracking.cpp:64:10:64:15 | call to getenv | shared.h:5:23:5:31 | sinkparam |
-| defaulttainttracking.cpp:66:17:66:22 | call to getenv | defaulttainttracking.cpp:52:24:52:24 | p |
-| defaulttainttracking.cpp:66:17:66:22 | call to getenv | defaulttainttracking.cpp:57:24:57:24 | p |
| defaulttainttracking.cpp:66:17:66:22 | call to getenv | defaulttainttracking.cpp:58:14:58:14 | p |
| defaulttainttracking.cpp:66:17:66:22 | call to getenv | defaulttainttracking.cpp:66:17:66:22 | call to getenv |
| defaulttainttracking.cpp:66:17:66:22 | call to getenv | defaulttainttracking.cpp:66:17:66:29 | (const char *)... |
-| defaulttainttracking.cpp:66:17:66:22 | call to getenv | shared.h:5:23:5:31 | sinkparam |
-| defaulttainttracking.cpp:67:28:67:33 | call to getenv | defaulttainttracking.cpp:52:24:52:24 | p |
-| defaulttainttracking.cpp:67:28:67:33 | call to getenv | defaulttainttracking.cpp:57:24:57:24 | p |
| defaulttainttracking.cpp:67:28:67:33 | call to getenv | defaulttainttracking.cpp:58:14:58:14 | p |
| defaulttainttracking.cpp:67:28:67:33 | call to getenv | defaulttainttracking.cpp:67:28:67:33 | call to getenv |
| defaulttainttracking.cpp:67:28:67:33 | call to getenv | defaulttainttracking.cpp:67:28:67:40 | (const char *)... |
-| defaulttainttracking.cpp:67:28:67:33 | call to getenv | shared.h:5:23:5:31 | sinkparam |
-| defaulttainttracking.cpp:68:29:68:34 | call to getenv | defaulttainttracking.cpp:52:24:52:24 | p |
-| defaulttainttracking.cpp:68:29:68:34 | call to getenv | defaulttainttracking.cpp:57:24:57:24 | p |
| defaulttainttracking.cpp:68:29:68:34 | call to getenv | defaulttainttracking.cpp:58:14:58:14 | p |
| defaulttainttracking.cpp:68:29:68:34 | call to getenv | defaulttainttracking.cpp:68:29:68:34 | call to getenv |
| defaulttainttracking.cpp:68:29:68:34 | call to getenv | defaulttainttracking.cpp:68:29:68:41 | (const char *)... |
-| defaulttainttracking.cpp:68:29:68:34 | call to getenv | shared.h:5:23:5:31 | sinkparam |
-| defaulttainttracking.cpp:69:33:69:38 | call to getenv | defaulttainttracking.cpp:52:24:52:24 | p |
-| defaulttainttracking.cpp:69:33:69:38 | call to getenv | defaulttainttracking.cpp:57:24:57:24 | p |
| defaulttainttracking.cpp:69:33:69:38 | call to getenv | defaulttainttracking.cpp:58:14:58:14 | p |
| defaulttainttracking.cpp:69:33:69:38 | call to getenv | defaulttainttracking.cpp:69:33:69:38 | call to getenv |
| defaulttainttracking.cpp:69:33:69:38 | call to getenv | defaulttainttracking.cpp:69:33:69:45 | (const char *)... |
-| defaulttainttracking.cpp:69:33:69:38 | call to getenv | shared.h:5:23:5:31 | sinkparam |
-| defaulttainttracking.cpp:72:11:72:16 | call to getenv | defaulttainttracking.cpp:45:20:45:29 | (unnamed parameter 0) |
-| defaulttainttracking.cpp:72:11:72:16 | call to getenv | defaulttainttracking.cpp:52:24:52:24 | p |
| defaulttainttracking.cpp:72:11:72:16 | call to getenv | defaulttainttracking.cpp:72:11:72:16 | call to getenv |
| defaulttainttracking.cpp:72:11:72:16 | call to getenv | defaulttainttracking.cpp:72:11:72:23 | (const char *)... |
-| defaulttainttracking.cpp:74:18:74:23 | call to getenv | defaulttainttracking.cpp:52:24:52:24 | p |
| defaulttainttracking.cpp:74:18:74:23 | call to getenv | defaulttainttracking.cpp:74:18:74:23 | call to getenv |
| defaulttainttracking.cpp:74:18:74:23 | call to getenv | defaulttainttracking.cpp:74:18:74:30 | (const char *)... |
-| defaulttainttracking.cpp:75:29:75:34 | call to getenv | defaulttainttracking.cpp:52:24:52:24 | p |
| defaulttainttracking.cpp:75:29:75:34 | call to getenv | defaulttainttracking.cpp:75:29:75:34 | call to getenv |
| defaulttainttracking.cpp:75:29:75:34 | call to getenv | defaulttainttracking.cpp:75:29:75:41 | (const char *)... |
-| defaulttainttracking.cpp:76:30:76:35 | call to getenv | defaulttainttracking.cpp:52:24:52:24 | p |
| defaulttainttracking.cpp:76:30:76:35 | call to getenv | defaulttainttracking.cpp:76:30:76:35 | call to getenv |
| defaulttainttracking.cpp:76:30:76:35 | call to getenv | defaulttainttracking.cpp:76:30:76:42 | (const char *)... |
-| defaulttainttracking.cpp:77:34:77:39 | call to getenv | defaulttainttracking.cpp:52:24:52:24 | p |
| defaulttainttracking.cpp:77:34:77:39 | call to getenv | defaulttainttracking.cpp:77:34:77:39 | call to getenv |
| defaulttainttracking.cpp:77:34:77:39 | call to getenv | defaulttainttracking.cpp:77:34:77:46 | (const char *)... |
-| defaulttainttracking.cpp:79:30:79:35 | call to getenv | defaulttainttracking.cpp:57:24:57:24 | p |
| defaulttainttracking.cpp:79:30:79:35 | call to getenv | defaulttainttracking.cpp:58:14:58:14 | p |
| defaulttainttracking.cpp:79:30:79:35 | call to getenv | defaulttainttracking.cpp:79:30:79:35 | call to getenv |
| defaulttainttracking.cpp:79:30:79:35 | call to getenv | defaulttainttracking.cpp:79:30:79:42 | (const char *)... |
-| defaulttainttracking.cpp:79:30:79:35 | call to getenv | shared.h:5:23:5:31 | sinkparam |
-| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:84:17:84:17 | t |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:16 | call to move |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:32 | (const char *)... |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:32 | (reference dereference) |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:18:88:23 | call to getenv |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:18:88:30 | (reference to) |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:18:88:30 | temporary object |
-| defaulttainttracking.cpp:88:18:88:23 | call to getenv | shared.h:5:23:5:31 | sinkparam |
-| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:91:42:91:44 | arg |
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:92:12:92:14 | arg |
-| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:96:11:96:12 | p2 |
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:97:27:97:32 | call to getenv |
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:98:10:98:11 | (const char *)... |
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:98:10:98:11 | p2 |
-| defaulttainttracking.cpp:97:27:97:32 | call to getenv | shared.h:5:23:5:31 | sinkparam |
-| defaulttainttracking.cpp:110:17:110:22 | call to getenv | defaulttainttracking.cpp:110:7:110:13 | tainted |
| defaulttainttracking.cpp:110:17:110:22 | call to getenv | defaulttainttracking.cpp:110:17:110:22 | call to getenv |
| defaulttainttracking.cpp:110:17:110:22 | call to getenv | defaulttainttracking.cpp:110:17:110:32 | (int)... |
| defaulttainttracking.cpp:110:17:110:22 | call to getenv | defaulttainttracking.cpp:110:17:110:32 | access to array |
@@ -109,17 +59,14 @@
| defaulttainttracking.cpp:133:9:133:14 | call to getenv | defaulttainttracking.cpp:133:9:133:24 | (int)... |
| defaulttainttracking.cpp:133:9:133:14 | call to getenv | defaulttainttracking.cpp:133:9:133:24 | access to array |
| defaulttainttracking.cpp:133:9:133:14 | call to getenv | defaulttainttracking.cpp:134:10:134:10 | x |
-| defaulttainttracking.cpp:133:9:133:14 | call to getenv | shared.h:6:15:6:23 | sinkparam |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:140:11:140:16 | call to getenv |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:140:11:140:26 | (int)... |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:140:11:140:26 | access to array |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:166:10:166:10 | x |
-| defaulttainttracking.cpp:140:11:140:16 | call to getenv | shared.h:6:15:6:23 | sinkparam |
| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:157:9:157:14 | call to getenv |
| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:157:9:157:24 | (int)... |
| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:157:9:157:24 | access to array |
| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:159:10:159:10 | x |
-| defaulttainttracking.cpp:157:9:157:14 | call to getenv | shared.h:6:15:6:23 | sinkparam |
| defaulttainttracking.cpp:170:11:170:16 | call to getenv | defaulttainttracking.cpp:170:11:170:16 | call to getenv |
| defaulttainttracking.cpp:170:11:170:16 | call to getenv | defaulttainttracking.cpp:170:11:170:26 | (int)... |
| defaulttainttracking.cpp:170:11:170:16 | call to getenv | defaulttainttracking.cpp:170:11:170:26 | access to array |
@@ -135,84 +82,48 @@
| defaulttainttracking.cpp:208:27:208:32 | call to getenv | defaulttainttracking.cpp:208:27:208:32 | call to getenv |
| defaulttainttracking.cpp:208:27:208:32 | call to getenv | defaulttainttracking.cpp:208:27:208:42 | (int)... |
| defaulttainttracking.cpp:208:27:208:32 | call to getenv | defaulttainttracking.cpp:208:27:208:42 | access to array |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:218:8:218:8 | s |
| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:218:12:218:17 | call to getenv |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:224:2:224:7 | call to memcpy |
| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:224:17:224:17 | (const void *)... |
| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:224:17:224:17 | s |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:228:7:228:12 | (const char *)... |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:228:7:228:12 | array to pointer conversion |
| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:228:7:228:12 | buffer |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:229:7:229:10 | (const char *)... |
| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:229:7:229:10 | ptr1 |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:232:7:232:10 | (const char *)... |
| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:232:7:232:10 | ptr3 |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | shared.h:5:23:5:31 | sinkparam |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | shared.h:17:36:17:37 | s2 |
-| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:240:8:240:8 | s |
| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:240:12:240:17 | call to getenv |
-| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:246:2:246:7 | call to memcpy |
| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:246:16:246:16 | (const void *)... |
| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:246:16:246:16 | s |
-| defaulttainttracking.cpp:240:12:240:17 | call to getenv | shared.h:17:36:17:37 | s2 |
| dispatch.cpp:28:29:28:34 | call to getenv | dispatch.cpp:28:24:28:27 | call to atoi |
| dispatch.cpp:28:29:28:34 | call to getenv | dispatch.cpp:28:29:28:34 | call to getenv |
| dispatch.cpp:28:29:28:34 | call to getenv | dispatch.cpp:28:29:28:45 | (const char *)... |
-| dispatch.cpp:28:29:28:34 | call to getenv | shared.h:8:22:8:25 | nptr |
| dispatch.cpp:29:32:29:37 | call to getenv | dispatch.cpp:29:27:29:30 | call to atoi |
| dispatch.cpp:29:32:29:37 | call to getenv | dispatch.cpp:29:32:29:37 | call to getenv |
| dispatch.cpp:29:32:29:37 | call to getenv | dispatch.cpp:29:32:29:48 | (const char *)... |
-| dispatch.cpp:29:32:29:37 | call to getenv | shared.h:8:22:8:25 | nptr |
-| dispatch.cpp:31:28:31:33 | call to getenv | dispatch.cpp:7:20:7:28 | sinkParam |
| dispatch.cpp:31:28:31:33 | call to getenv | dispatch.cpp:8:8:8:16 | sinkParam |
| dispatch.cpp:31:28:31:33 | call to getenv | dispatch.cpp:31:23:31:26 | call to atoi |
| dispatch.cpp:31:28:31:33 | call to getenv | dispatch.cpp:31:28:31:33 | call to getenv |
| dispatch.cpp:31:28:31:33 | call to getenv | dispatch.cpp:31:28:31:44 | (const char *)... |
-| dispatch.cpp:31:28:31:33 | call to getenv | shared.h:6:15:6:23 | sinkparam |
-| dispatch.cpp:31:28:31:33 | call to getenv | shared.h:8:22:8:25 | nptr |
-| dispatch.cpp:32:31:32:36 | call to getenv | dispatch.cpp:7:20:7:28 | sinkParam |
| dispatch.cpp:32:31:32:36 | call to getenv | dispatch.cpp:8:8:8:16 | sinkParam |
| dispatch.cpp:32:31:32:36 | call to getenv | dispatch.cpp:32:26:32:29 | call to atoi |
| dispatch.cpp:32:31:32:36 | call to getenv | dispatch.cpp:32:31:32:36 | call to getenv |
| dispatch.cpp:32:31:32:36 | call to getenv | dispatch.cpp:32:31:32:47 | (const char *)... |
-| dispatch.cpp:32:31:32:36 | call to getenv | shared.h:6:15:6:23 | sinkparam |
-| dispatch.cpp:32:31:32:36 | call to getenv | shared.h:8:22:8:25 | nptr |
-| dispatch.cpp:34:22:34:27 | call to getenv | dispatch.cpp:7:20:7:28 | sinkParam |
| dispatch.cpp:34:22:34:27 | call to getenv | dispatch.cpp:8:8:8:16 | sinkParam |
| dispatch.cpp:34:22:34:27 | call to getenv | dispatch.cpp:34:17:34:20 | call to atoi |
| dispatch.cpp:34:22:34:27 | call to getenv | dispatch.cpp:34:22:34:27 | call to getenv |
| dispatch.cpp:34:22:34:27 | call to getenv | dispatch.cpp:34:22:34:38 | (const char *)... |
-| dispatch.cpp:34:22:34:27 | call to getenv | shared.h:6:15:6:23 | sinkparam |
-| dispatch.cpp:34:22:34:27 | call to getenv | shared.h:8:22:8:25 | nptr |
-| globals.cpp:5:20:5:25 | call to getenv | globals.cpp:5:12:5:16 | local |
| globals.cpp:5:20:5:25 | call to getenv | globals.cpp:5:20:5:25 | call to getenv |
| globals.cpp:5:20:5:25 | call to getenv | globals.cpp:6:10:6:14 | (const char *)... |
| globals.cpp:5:20:5:25 | call to getenv | globals.cpp:6:10:6:14 | local |
-| globals.cpp:5:20:5:25 | call to getenv | shared.h:5:23:5:31 | sinkparam |
-| globals.cpp:13:15:13:20 | call to getenv | globals.cpp:9:8:9:14 | global1 |
| globals.cpp:13:15:13:20 | call to getenv | globals.cpp:13:15:13:20 | call to getenv |
-| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:16:15:16:21 | global2 |
| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:23:15:23:20 | call to getenv |
-| stl.cpp:62:25:62:30 | call to getenv | shared.h:5:23:5:31 | sinkparam |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:21:29:21:29 | s |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:43:78:43:104 | (unnamed parameter 0) |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:43:114:43:118 | (unnamed parameter 1) |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:44:176:44:178 | str |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:62:25:62:30 | call to getenv |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:63:30:63:30 | s |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:64:36:64:36 | s |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:68:8:68:8 | a |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:68:12:68:17 | call to source |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:70:16:70:21 | call to source |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:70:16:70:23 | (const char *)... |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:70:16:70:24 | call to basic_string |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:72:7:72:7 | (const char *)... |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:72:7:72:7 | a |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:74:7:74:7 | (const string)... |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:74:7:74:7 | (reference to) |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:74:7:74:7 | c |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:76:7:76:7 | (const basic_string, allocator>)... |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:76:7:76:7 | c |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:76:9:76:13 | call to c_str |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:21 | call to source |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:23 | (const char *)... |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:24 | call to basic_string |
@@ -231,80 +142,60 @@
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:9:87:16 | (const char *)... |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:18:87:18 | call to operator<< |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:18:87:26 | (reference dereference) |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:88:6:88:6 | call to operator<< |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:88:6:88:10 | (reference dereference) |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:88:9:88:9 | (const basic_string, allocator>)... |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:88:9:88:9 | (reference to) |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:88:9:88:9 | t |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:91:7:91:9 | (const stringstream)... |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:91:7:91:9 | (reference to) |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:91:7:91:9 | ss2 |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:93:7:93:9 | (const stringstream)... |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:93:7:93:9 | (reference to) |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:93:7:93:9 | ss4 |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:94:7:94:9 | (const stringstream)... |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:94:7:94:9 | (reference to) |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:94:7:94:9 | ss5 |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:9 | (const basic_stringstream, allocator>)... |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:9 | ss2 |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:9 | (const basic_stringstream, allocator>)... |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:15 | (const string)... |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:15 | (reference to) |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:15 | temporary object |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:11:96:13 | call to str |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:9 | ss4 |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:99:7:99:9 | (const basic_stringstream, allocator>)... |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:99:7:99:9 | ss5 |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:15 | (const string)... |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:15 | (reference to) |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:15 | temporary object |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:11:98:13 | call to str |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:118:10:118:15 | call to source |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:125:16:125:28 | call to basic_string |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:125:17:125:26 | call to user_input |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:125:17:125:28 | (const char *)... |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:126:7:126:11 | (const basic_string, allocator>)... |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:126:7:126:11 | path1 |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:128:9:128:13 | path2 |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:126:13:126:17 | call to c_str |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:19 | call to user_input |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:21 | (const char *)... |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:21 | call to basic_string |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:21 | temporary object |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:130:7:130:11 | (const basic_string, allocator>)... |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:130:7:130:11 | path2 |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:130:13:130:17 | call to c_str |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:132:15:132:24 | call to user_input |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:132:15:132:26 | (const char *)... |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:132:15:132:27 | call to basic_string |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:133:7:133:11 | (const basic_string, allocator>)... |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:133:7:133:11 | path3 |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:138:14:138:15 | cs |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:133:13:133:17 | call to c_str |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:138:19:138:24 | call to source |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:138:19:138:26 | (const char *)... |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:141:17:141:18 | cs |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:141:17:141:19 | call to basic_string |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:143:7:143:8 | cs |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:144:7:144:8 | (const string)... |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:144:7:144:8 | (reference to) |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:144:7:144:8 | ss |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:149:14:149:15 | cs |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:149:19:149:24 | call to source |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:149:19:149:26 | (const char *)... |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:152:17:152:18 | cs |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:152:17:152:19 | call to basic_string |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:155:7:155:8 | (const basic_string, allocator>)... |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:155:7:155:8 | ss |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:158:7:158:8 | (const string)... |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:158:7:158:8 | (reference to) |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:155:10:155:14 | call to c_str |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:157:7:157:8 | cs |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:158:7:158:8 | ss |
-| test_diff.cpp:92:10:92:13 | argv | shared.h:5:23:5:31 | sinkparam |
| test_diff.cpp:92:10:92:13 | argv | test_diff.cpp:92:10:92:13 | argv |
| test_diff.cpp:92:10:92:13 | argv | test_diff.cpp:92:10:92:16 | (const char *)... |
| test_diff.cpp:92:10:92:13 | argv | test_diff.cpp:92:10:92:16 | access to array |
-| test_diff.cpp:94:32:94:35 | argv | shared.h:6:15:6:23 | sinkparam |
| test_diff.cpp:94:32:94:35 | argv | test_diff.cpp:94:10:94:36 | reinterpret_cast... |
| test_diff.cpp:94:32:94:35 | argv | test_diff.cpp:94:32:94:35 | argv |
-| test_diff.cpp:96:26:96:29 | argv | shared.h:5:23:5:31 | sinkparam |
-| test_diff.cpp:96:26:96:29 | argv | test_diff.cpp:16:39:16:39 | a |
| test_diff.cpp:96:26:96:29 | argv | test_diff.cpp:17:10:17:10 | a |
| test_diff.cpp:96:26:96:29 | argv | test_diff.cpp:96:26:96:29 | argv |
| test_diff.cpp:96:26:96:29 | argv | test_diff.cpp:96:26:96:32 | (const char *)... |
| test_diff.cpp:96:26:96:29 | argv | test_diff.cpp:96:26:96:32 | access to array |
-| test_diff.cpp:98:18:98:21 | argv | shared.h:5:23:5:31 | sinkparam |
-| test_diff.cpp:98:18:98:21 | argv | test_diff.cpp:16:39:16:39 | a |
| test_diff.cpp:98:18:98:21 | argv | test_diff.cpp:17:10:17:10 | a |
-| test_diff.cpp:98:18:98:21 | argv | test_diff.cpp:98:13:98:13 | p |
| test_diff.cpp:98:18:98:21 | argv | test_diff.cpp:98:17:98:21 | & ... |
| test_diff.cpp:98:18:98:21 | argv | test_diff.cpp:98:18:98:21 | argv |
| test_diff.cpp:98:18:98:21 | argv | test_diff.cpp:100:10:100:14 | (const char *)... |
@@ -315,65 +206,40 @@
| test_diff.cpp:98:18:98:21 | argv | test_diff.cpp:102:26:102:30 | * ... |
| test_diff.cpp:98:18:98:21 | argv | test_diff.cpp:102:27:102:27 | p |
| test_diff.cpp:98:18:98:21 | argv | test_diff.cpp:102:27:102:30 | access to array |
-| test_diff.cpp:104:12:104:15 | argv | shared.h:5:23:5:31 | sinkparam |
| test_diff.cpp:104:12:104:15 | argv | test_diff.cpp:104:10:104:20 | (const char *)... |
| test_diff.cpp:104:12:104:15 | argv | test_diff.cpp:104:10:104:20 | * ... |
| test_diff.cpp:104:12:104:15 | argv | test_diff.cpp:104:11:104:20 | (...) |
| test_diff.cpp:104:12:104:15 | argv | test_diff.cpp:104:12:104:15 | argv |
| test_diff.cpp:104:12:104:15 | argv | test_diff.cpp:104:12:104:19 | ... + ... |
-| test_diff.cpp:108:10:108:13 | argv | shared.h:5:23:5:31 | sinkparam |
-| test_diff.cpp:108:10:108:13 | argv | test_diff.cpp:24:20:24:29 | (unnamed parameter 0) |
-| test_diff.cpp:108:10:108:13 | argv | test_diff.cpp:29:24:29:24 | p |
| test_diff.cpp:108:10:108:13 | argv | test_diff.cpp:30:14:30:14 | p |
| test_diff.cpp:108:10:108:13 | argv | test_diff.cpp:108:10:108:13 | argv |
| test_diff.cpp:108:10:108:13 | argv | test_diff.cpp:108:10:108:16 | (const char *)... |
| test_diff.cpp:108:10:108:13 | argv | test_diff.cpp:108:10:108:16 | access to array |
-| test_diff.cpp:111:10:111:13 | argv | test_diff.cpp:24:20:24:29 | (unnamed parameter 0) |
-| test_diff.cpp:111:10:111:13 | argv | test_diff.cpp:36:24:36:24 | p |
| test_diff.cpp:111:10:111:13 | argv | test_diff.cpp:111:10:111:13 | argv |
| test_diff.cpp:111:10:111:13 | argv | test_diff.cpp:111:10:111:16 | (const char *)... |
| test_diff.cpp:111:10:111:13 | argv | test_diff.cpp:111:10:111:16 | access to array |
-| test_diff.cpp:115:11:115:14 | argv | shared.h:5:23:5:31 | sinkparam |
-| test_diff.cpp:115:11:115:14 | argv | test_diff.cpp:24:20:24:29 | (unnamed parameter 0) |
-| test_diff.cpp:115:11:115:14 | argv | test_diff.cpp:41:24:41:24 | p |
| test_diff.cpp:115:11:115:14 | argv | test_diff.cpp:42:14:42:14 | p |
-| test_diff.cpp:115:11:115:14 | argv | test_diff.cpp:52:24:52:24 | p |
| test_diff.cpp:115:11:115:14 | argv | test_diff.cpp:53:37:53:37 | p |
| test_diff.cpp:115:11:115:14 | argv | test_diff.cpp:115:11:115:14 | argv |
| test_diff.cpp:115:11:115:14 | argv | test_diff.cpp:115:11:115:17 | (const char *)... |
| test_diff.cpp:115:11:115:14 | argv | test_diff.cpp:115:11:115:17 | access to array |
-| test_diff.cpp:118:26:118:29 | argv | test_diff.cpp:60:24:60:24 | p |
| test_diff.cpp:118:26:118:29 | argv | test_diff.cpp:61:34:61:34 | p |
-| test_diff.cpp:118:26:118:29 | argv | test_diff.cpp:88:24:88:24 | p |
| test_diff.cpp:118:26:118:29 | argv | test_diff.cpp:118:26:118:29 | argv |
| test_diff.cpp:118:26:118:29 | argv | test_diff.cpp:118:26:118:32 | (const char *)... |
| test_diff.cpp:118:26:118:29 | argv | test_diff.cpp:118:26:118:32 | access to array |
-| test_diff.cpp:121:23:121:26 | argv | shared.h:5:23:5:31 | sinkparam |
-| test_diff.cpp:121:23:121:26 | argv | test_diff.cpp:60:24:60:24 | p |
| test_diff.cpp:121:23:121:26 | argv | test_diff.cpp:61:34:61:34 | p |
-| test_diff.cpp:121:23:121:26 | argv | test_diff.cpp:67:24:67:24 | p |
| test_diff.cpp:121:23:121:26 | argv | test_diff.cpp:68:14:68:14 | p |
| test_diff.cpp:121:23:121:26 | argv | test_diff.cpp:121:23:121:26 | argv |
| test_diff.cpp:121:23:121:26 | argv | test_diff.cpp:121:23:121:29 | (const char *)... |
| test_diff.cpp:121:23:121:26 | argv | test_diff.cpp:121:23:121:29 | access to array |
-| test_diff.cpp:124:19:124:22 | argv | shared.h:5:23:5:31 | sinkparam |
-| test_diff.cpp:124:19:124:22 | argv | test_diff.cpp:24:20:24:29 | (unnamed parameter 0) |
-| test_diff.cpp:124:19:124:22 | argv | test_diff.cpp:76:24:76:24 | p |
-| test_diff.cpp:124:19:124:22 | argv | test_diff.cpp:81:24:81:24 | p |
| test_diff.cpp:124:19:124:22 | argv | test_diff.cpp:82:14:82:14 | p |
| test_diff.cpp:124:19:124:22 | argv | test_diff.cpp:124:19:124:22 | argv |
| test_diff.cpp:124:19:124:22 | argv | test_diff.cpp:124:19:124:25 | (const char *)... |
| test_diff.cpp:124:19:124:22 | argv | test_diff.cpp:124:19:124:25 | access to array |
-| test_diff.cpp:126:43:126:46 | argv | shared.h:5:23:5:31 | sinkparam |
-| test_diff.cpp:126:43:126:46 | argv | test_diff.cpp:76:24:76:24 | p |
-| test_diff.cpp:126:43:126:46 | argv | test_diff.cpp:81:24:81:24 | p |
| test_diff.cpp:126:43:126:46 | argv | test_diff.cpp:82:14:82:14 | p |
| test_diff.cpp:126:43:126:46 | argv | test_diff.cpp:126:43:126:46 | argv |
| test_diff.cpp:126:43:126:46 | argv | test_diff.cpp:126:43:126:49 | (const char *)... |
| test_diff.cpp:126:43:126:46 | argv | test_diff.cpp:126:43:126:49 | access to array |
-| test_diff.cpp:128:44:128:47 | argv | shared.h:5:23:5:31 | sinkparam |
-| test_diff.cpp:128:44:128:47 | argv | test_diff.cpp:76:24:76:24 | p |
-| test_diff.cpp:128:44:128:47 | argv | test_diff.cpp:81:24:81:24 | p |
| test_diff.cpp:128:44:128:47 | argv | test_diff.cpp:82:14:82:14 | p |
| test_diff.cpp:128:44:128:47 | argv | test_diff.cpp:128:44:128:47 | argv |
| test_diff.cpp:128:44:128:47 | argv | test_diff.cpp:128:44:128:50 | (const char *)... |
diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.expected b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.expected
index 8d7963b2156..ffc640c15f3 100644
--- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.expected
+++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.expected
@@ -1,44 +1,38 @@
-| defaulttainttracking.cpp:16:16:16:21 | call to getenv | defaulttainttracking.cpp:16:8:16:14 | call to _strdup | IR only |
-| defaulttainttracking.cpp:16:16:16:21 | call to getenv | defaulttainttracking.cpp:16:8:16:29 | (const char *)... | IR only |
-| defaulttainttracking.cpp:16:16:16:21 | call to getenv | shared.h:5:23:5:31 | sinkparam | IR only |
+| defaulttainttracking.cpp:17:15:17:20 | call to getenv | defaulttainttracking.cpp:17:8:17:13 | call to strdup | AST only |
+| defaulttainttracking.cpp:17:15:17:20 | call to getenv | defaulttainttracking.cpp:17:8:17:28 | (const char *)... | AST only |
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:21:8:21:10 | buf | AST only |
+| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:22:8:22:13 | call to strcat | AST only |
+| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:22:8:22:33 | (const char *)... | AST only |
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:22:15:22:17 | buf | AST only |
-| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:24:8:24:10 | (const char *)... | IR only |
-| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:24:8:24:10 | array to pointer conversion | IR only |
-| defaulttainttracking.cpp:22:20:22:25 | call to getenv | shared.h:10:21:10:22 | s1 | AST only |
+| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:38:11:38:21 | env_pointer | AST only |
+| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:22:39:22 | a | AST only |
+| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:36:39:61 | (const char *)... | AST only |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:51:39:61 | env_pointer | AST only |
-| defaulttainttracking.cpp:64:10:64:15 | call to getenv | defaulttainttracking.cpp:52:24:52:24 | p | IR only |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:16 | call to move | IR only |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:32 | (const char *)... | IR only |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:32 | (reference dereference) | IR only |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:18:88:30 | (reference to) | IR only |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:18:88:30 | temporary object | IR only |
-| defaulttainttracking.cpp:88:18:88:23 | call to getenv | shared.h:5:23:5:31 | sinkparam | IR only |
-| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:91:31:91:33 | ret | AST only |
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:92:5:92:8 | * ... | AST only |
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:92:6:92:8 | ret | AST only |
-| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:96:11:96:12 | p2 | IR only |
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:98:10:98:11 | (const char *)... | IR only |
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:98:10:98:11 | p2 | IR only |
-| defaulttainttracking.cpp:97:27:97:32 | call to getenv | shared.h:5:23:5:31 | sinkparam | IR only |
+| defaulttainttracking.cpp:110:17:110:22 | call to getenv | defaulttainttracking.cpp:110:7:110:13 | tainted | AST only |
| defaulttainttracking.cpp:110:17:110:22 | call to getenv | defaulttainttracking.cpp:111:8:111:8 | y | AST only |
| defaulttainttracking.cpp:133:9:133:14 | call to getenv | defaulttainttracking.cpp:126:16:126:16 | x | IR only |
| defaulttainttracking.cpp:133:9:133:14 | call to getenv | defaulttainttracking.cpp:133:5:133:5 | x | AST only |
| defaulttainttracking.cpp:133:9:133:14 | call to getenv | defaulttainttracking.cpp:134:10:134:10 | x | IR only |
-| defaulttainttracking.cpp:133:9:133:14 | call to getenv | shared.h:6:15:6:23 | sinkparam | IR only |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:140:7:140:7 | x | AST only |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:166:10:166:10 | x | IR only |
-| defaulttainttracking.cpp:140:11:140:16 | call to getenv | shared.h:6:15:6:23 | sinkparam | IR only |
| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:157:5:157:5 | x | AST only |
| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:159:10:159:10 | x | IR only |
-| defaulttainttracking.cpp:157:9:157:14 | call to getenv | shared.h:6:15:6:23 | sinkparam | IR only |
| defaulttainttracking.cpp:170:11:170:16 | call to getenv | defaulttainttracking.cpp:170:7:170:7 | x | AST only |
| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:181:7:181:7 | x | AST only |
| defaulttainttracking.cpp:195:11:195:16 | call to getenv | defaulttainttracking.cpp:195:7:195:7 | x | AST only |
| defaulttainttracking.cpp:201:13:201:18 | call to getenv | defaulttainttracking.cpp:201:9:201:9 | x | AST only |
| defaulttainttracking.cpp:208:27:208:32 | call to getenv | defaulttainttracking.cpp:208:23:208:23 | x | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:213:11:213:14 | (unnamed parameter 0) | AST only |
| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:217:7:217:12 | buffer | AST only |
+| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:218:8:218:8 | s | AST only |
| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:219:8:219:11 | ptr1 | AST only |
| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:219:16:219:19 | ptr2 | AST only |
| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:220:8:220:11 | ptr3 | AST only |
@@ -48,29 +42,30 @@
| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:223:2:223:5 | ptr2 | AST only |
| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:223:9:223:13 | & ... | AST only |
| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:223:10:223:13 | ptr1 | AST only |
+| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:224:2:224:7 | call to memcpy | AST only |
| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:224:9:224:14 | buffer | AST only |
| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:225:2:225:5 | ptr3 | AST only |
| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:225:9:225:14 | buffer | AST only |
| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:226:2:226:5 | ptr4 | AST only |
| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:226:9:226:13 | & ... | AST only |
| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:226:10:226:13 | ptr3 | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:228:7:228:12 | (const char *)... | IR only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:228:7:228:12 | array to pointer conversion | IR only |
+| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:229:7:229:10 | (const char *)... | AST only |
| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:230:7:230:10 | ptr2 | AST only |
| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:231:7:231:11 | (const char *)... | AST only |
| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:231:7:231:11 | * ... | AST only |
| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:231:8:231:11 | ptr2 | AST only |
+| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:232:7:232:10 | (const char *)... | AST only |
| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:233:7:233:10 | ptr4 | AST only |
| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:234:7:234:11 | (const char *)... | AST only |
| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:234:7:234:11 | * ... | AST only |
| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:234:8:234:11 | ptr4 | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | shared.h:17:20:17:21 | s1 | AST only |
-| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:213:11:213:14 | (unnamed parameter 0) | AST only |
+| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:240:8:240:8 | s | AST only |
| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:241:8:241:11 | ptr1 | AST only |
| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:241:16:241:19 | ptr2 | AST only |
| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:245:2:245:5 | ptr2 | AST only |
| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:245:9:245:13 | & ... | AST only |
| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:245:10:245:13 | ptr1 | AST only |
+| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:246:2:246:7 | call to memcpy | AST only |
| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:246:9:246:13 | * ... | AST only |
| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:246:10:246:13 | ptr2 | AST only |
| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:251:7:251:10 | (const char *)... | AST only |
@@ -79,21 +74,17 @@
| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:253:7:253:11 | (const char *)... | AST only |
| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:253:7:253:11 | * ... | AST only |
| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:253:8:253:11 | ptr2 | AST only |
-| defaulttainttracking.cpp:240:12:240:17 | call to getenv | shared.h:5:23:5:31 | sinkparam | AST only |
-| defaulttainttracking.cpp:240:12:240:17 | call to getenv | shared.h:17:20:17:21 | s1 | AST only |
+| globals.cpp:5:20:5:25 | call to getenv | globals.cpp:5:12:5:16 | local | AST only |
+| globals.cpp:13:15:13:20 | call to getenv | globals.cpp:9:8:9:14 | global1 | AST only |
| globals.cpp:13:15:13:20 | call to getenv | globals.cpp:13:5:13:11 | global1 | AST only |
+| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:16:15:16:21 | global2 | AST only |
| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:23:5:23:11 | global2 | AST only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:43:78:43:104 | (unnamed parameter 0) | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:44:176:44:178 | str | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:62:7:62:12 | source | AST only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:63:30:63:30 | s | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:64:36:64:36 | s | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:68:8:68:8 | a | AST only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:70:16:70:24 | call to basic_string | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:74:7:74:7 | (const string)... | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:74:7:74:7 | (reference to) | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:74:7:74:7 | c | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:76:7:76:7 | (const basic_string, allocator>)... | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:76:7:76:7 | c | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:76:9:76:13 | call to c_str | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:24 | call to basic_string | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:6:85:6 | call to operator<< | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:6:85:17 | (reference dereference) | IR only |
@@ -105,52 +96,38 @@
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:9:87:16 | (const char *)... | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:18:87:18 | call to operator<< | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:18:87:26 | (reference dereference) | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:88:6:88:6 | call to operator<< | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:88:6:88:10 | (reference dereference) | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:88:9:88:9 | (const basic_string, allocator>)... | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:88:9:88:9 | (reference to) | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:88:9:88:9 | t | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:91:7:91:9 | (const stringstream)... | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:91:7:91:9 | (reference to) | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:91:7:91:9 | ss2 | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:93:7:93:9 | (const stringstream)... | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:93:7:93:9 | (reference to) | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:93:7:93:9 | ss4 | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:94:7:94:9 | (const stringstream)... | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:94:7:94:9 | (reference to) | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:94:7:94:9 | ss5 | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:9 | (const basic_stringstream, allocator>)... | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:9 | ss2 | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:9 | (const basic_stringstream, allocator>)... | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:15 | (const string)... | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:15 | (reference to) | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:15 | temporary object | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:11:96:13 | call to str | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:9 | ss4 | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:99:7:99:9 | (const basic_stringstream, allocator>)... | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:99:7:99:9 | ss5 | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:15 | (const string)... | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:15 | (reference to) | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:15 | temporary object | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:11:98:13 | call to str | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:117:7:117:16 | user_input | AST only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:125:16:125:28 | call to basic_string | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:126:7:126:11 | (const basic_string, allocator>)... | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:126:7:126:11 | path1 | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:128:9:128:13 | path2 | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:126:13:126:17 | call to c_str | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:21 | call to basic_string | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:21 | temporary object | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:130:7:130:11 | (const basic_string, allocator>)... | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:130:7:130:11 | path2 | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:130:13:130:17 | call to c_str | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:132:15:132:27 | call to basic_string | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:133:7:133:11 | (const basic_string, allocator>)... | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:133:7:133:11 | path3 | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:133:13:133:17 | call to c_str | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:138:14:138:15 | cs | AST only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:141:17:141:19 | call to basic_string | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:144:7:144:8 | (const string)... | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:144:7:144:8 | (reference to) | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:144:7:144:8 | ss | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:149:14:149:15 | cs | AST only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:152:17:152:19 | call to basic_string | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:155:7:155:8 | (const basic_string, allocator>)... | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:155:7:155:8 | ss | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:157:7:157:8 | cs | AST only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:158:7:158:8 | (const string)... | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:158:7:158:8 | (reference to) | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:155:10:155:14 | call to c_str | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:158:7:158:8 | ss | IR only |
+| test_diff.cpp:98:18:98:21 | argv | test_diff.cpp:98:13:98:13 | p | AST only |
| test_diff.cpp:104:12:104:15 | argv | test_diff.cpp:104:11:104:20 | (...) | IR only |
-| test_diff.cpp:108:10:108:13 | argv | test_diff.cpp:36:24:36:24 | p | AST only |
-| test_diff.cpp:111:10:111:13 | argv | shared.h:5:23:5:31 | sinkparam | AST only |
-| test_diff.cpp:111:10:111:13 | argv | test_diff.cpp:29:24:29:24 | p | AST only |
| test_diff.cpp:111:10:111:13 | argv | test_diff.cpp:30:14:30:14 | p | AST only |
-| test_diff.cpp:124:19:124:22 | argv | test_diff.cpp:76:24:76:24 | p | IR only |
From 525aeb65510ff406c52495722ad3492be36ee5e3 Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Fri, 13 Nov 2020 16:14:07 -0800
Subject: [PATCH 012/725] C++: autoformat
---
.../code/cpp/ir/dataflow/DefaultTaintTracking.qll | 10 ++++++++--
.../dataflow/DefaultTaintTracking/tainted.ql | 4 +---
2 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
index 62e7f7b9738..38acc20f44f 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
@@ -222,7 +222,6 @@ private predicate nodeIsBarrierIn(DataFlow::Node node) {
not predictableInstruction(iTo.getRight()) and
// propagate taint from either the pointer or the offset, regardless of predictability
not iTo instanceof PointerArithmeticInstruction
-
)
or
// don't use dataflow through calls to pure functions if two or more operands
@@ -473,7 +472,14 @@ private Element adjustedSink(DataFlow::Node sink) {
// Taint `e1 += e2`, `e &= e2` and friends when `e1` or `e2` is tainted.
result.(AssignOperation).getAnOperand() = sink.asExpr()
or
- result = sink.asOperand().(SideEffectOperand).getUse().(ReadSideEffectInstruction).getArgumentDef().getUnconvertedResultExpression()
+ result =
+ sink
+ .asOperand()
+ .(SideEffectOperand)
+ .getUse()
+ .(ReadSideEffectInstruction)
+ .getArgumentDef()
+ .getUnconvertedResultExpression()
}
/**
diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.ql b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.ql
index 3382775b6d9..240418f9bf4 100644
--- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.ql
+++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.ql
@@ -1,9 +1,7 @@
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking
class SourceConfiguration extends TaintedWithPath::TaintTrackingConfiguration {
- override predicate isSink(Element e) {
- any()
- }
+ override predicate isSink(Element e) { any() }
}
from Expr source, Element tainted
From c2e44fa180954ac5aa64eda2e020562282a9b6de Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Tue, 17 Nov 2020 09:28:39 -0800
Subject: [PATCH 013/725] C++: autoformat
---
.../library-tests/dataflow/security-taint/tainted_diff.ql | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/cpp/ql/test/library-tests/dataflow/security-taint/tainted_diff.ql b/cpp/ql/test/library-tests/dataflow/security-taint/tainted_diff.ql
index f3ed14d16b1..37bd9f5437d 100644
--- a/cpp/ql/test/library-tests/dataflow/security-taint/tainted_diff.ql
+++ b/cpp/ql/test/library-tests/dataflow/security-taint/tainted_diff.ql
@@ -3,9 +3,7 @@ import semmle.code.cpp.ir.dataflow.DefaultTaintTracking as IR
import cpp
class SourceConfiguration extends IR::TaintedWithPath::TaintTrackingConfiguration {
- override predicate isSink(Element e) {
- any()
- }
+ override predicate isSink(Element e) { any() }
}
from Expr source, Element tainted, string side
From 5aed82a210f2e4e23ead18c41990e061696214ed Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Tue, 17 Nov 2020 13:44:20 -0800
Subject: [PATCH 014/725] C++: Autoformat more
---
.../code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll
index 1cbddcd70ae..0ea24dc7862 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll
@@ -146,8 +146,8 @@ predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut) {
// Taint flow from a pointer argument to an output, when the model specifies flow from the deref
// to that output, but the deref is not modeled in the IR for the caller.
exists(
- CallInstruction call, ReadSideEffectInstruction read, Function func,
- FunctionInput modelIn, FunctionOutput modelOut
+ CallInstruction call, ReadSideEffectInstruction read, Function func, FunctionInput modelIn,
+ FunctionOutput modelOut
|
read.getSideEffectOperand() = callInput(call, modelIn).asOperand() and
read.getArgumentDef() = nodeIn.asInstruction() and
From ce2db21f15d68ff69835e11367a02e3a1832d13d Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Wed, 6 Jan 2021 17:26:53 +0000
Subject: [PATCH 015/725] Query to detect hash without salt
---
.../Security/CWE/CWE-759/HashWithoutSalt.java | 14 +++
.../CWE/CWE-759/HashWithoutSalt.qhelp | 29 ++++++
.../Security/CWE/CWE-759/HashWithoutSalt.ql | 93 +++++++++++++++++++
.../security/CWE-759/HashWithoutSalt.expected | 11 +++
.../security/CWE-759/HashWithoutSalt.java | 40 ++++++++
.../security/CWE-759/HashWithoutSalt.qlref | 1 +
6 files changed, 188 insertions(+)
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.java
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.qhelp
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.expected
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.qlref
diff --git a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.java b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.java
new file mode 100644
index 00000000000..91d39eb5798
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.java
@@ -0,0 +1,14 @@
+public class HashWithoutSalt {
+ // BAD - Hash without a salt.
+ public void getSHA256Hash(String password) throws NoSuchAlgorithmException {
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ byte[] messageDigest = md.digest(password.getBytes());
+ }
+
+ // GOOD - Hash with a salt.
+ public void getSHA256Hash(String password, byte[] salt) throws NoSuchAlgorithmException {
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ md.update(salt);
+ byte[] messageDigest = md.digest(password.getBytes());
+ }
+}
\ No newline at end of file
diff --git a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.qhelp b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.qhelp
new file mode 100644
index 00000000000..a3875c35c37
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.qhelp
@@ -0,0 +1,29 @@
+
+
+
+
+ In cryptography, "salt" is random data that are used as an additional input to a one-way function that hashes a password or pass-phrase. It makes dictionary attacks more difficult.
+
+ Without a salt, it is much easier for attackers to pre-compute the hash value using dictionary attack techniques such as rainbow tables to crack passwords.
+
+
+
+ Use a long random salt of at least 32 bytes then use the combination of password and salt to hash a password or password phrase.
+
+
+
+ The following example shows two ways of hashing. In the 'BAD' case, no salt is provided. In the 'GOOD' case, a salt is provided.
+
+
+
+
+
+ DZone:
+ A Look at Java Cryptography
+
+
+ CWE:
+ CWE-759: Use of a One-Way Hash without a Salt
+
+
+
diff --git a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
new file mode 100644
index 00000000000..5e308838072
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
@@ -0,0 +1,93 @@
+/**
+ * @id java/hash-without-salt
+ * @name Use of a One-Way Hash without a Salt
+ * @description Hashed passwords without a salt are vulnerable to dictionary attacks.
+ * @kind path-problem
+ * @tags security
+ * external/cwe-759
+ */
+
+import java
+import semmle.code.java.dataflow.TaintTracking
+import DataFlow::PathGraph
+
+/** The Java class `java.security.MessageDigest` */
+class MessageDigest extends RefType {
+ MessageDigest() { this.hasQualifiedName("java.security", "MessageDigest") }
+}
+
+/** The method `digest()` declared in `java.security.MessageDigest`. */
+class MDDigestMethod extends Method {
+ MDDigestMethod() {
+ getDeclaringType() instanceof MessageDigest and
+ hasName("digest")
+ }
+}
+
+/** The method `update()` declared in `java.security.MessageDigest`. */
+class MDUpdateMethod extends Method {
+ MDUpdateMethod() {
+ getDeclaringType() instanceof MessageDigest and
+ hasName("update")
+ }
+}
+
+/**
+ * Gets a regular expression for matching common names of variables that indicate the value being held is a password.
+ */
+string getPasswordRegex() { result = "(?i).*pass(wd|word|code|phrase).*" }
+
+/** Finds variables that hold password information judging by their names. */
+class PasswordVarExpr extends Expr {
+ PasswordVarExpr() {
+ exists(Variable v | this = v.getAnAccess() | v.getName().regexpMatch(getPasswordRegex()))
+ }
+}
+
+/** Taint configuration tracking flow from an expression whose name suggests it holds password data to a method call that generates a hash without a salt. */
+class HashWithoutSaltConfiguration extends TaintTracking::Configuration {
+ HashWithoutSaltConfiguration() { this = "HashWithoutSaltConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) { source.asExpr() instanceof PasswordVarExpr }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(
+ MethodAccess mda, MethodAccess mua // invoke `md.digest()` with only one call of `md.update(password)`, that is, without the call of `md.update(digest)`
+ |
+ sink.asExpr() = mda.getQualifier() and
+ mda.getMethod() instanceof MDDigestMethod and
+ mda.getNumArgument() = 0 and // md.digest()
+ mua.getMethod() instanceof MDUpdateMethod and // md.update(password)
+ mua.getQualifier() = mda.getQualifier().(VarAccess).getVariable().getAnAccess() and
+ not exists(MethodAccess mua2 |
+ mua2.getMethod() instanceof MDUpdateMethod and // md.update(salt)
+ mua2.getQualifier() = mua.getQualifier().(VarAccess).getVariable().getAnAccess() and
+ mua2 != mua
+ )
+ )
+ or
+ // invoke `md.digest(password)` without another call of `md.update(salt)`
+ exists(MethodAccess mda |
+ sink.asExpr() = mda and
+ mda.getMethod() instanceof MDDigestMethod and // md.digest(password)
+ mda.getNumArgument() = 1 and
+ not exists(MethodAccess mua |
+ mua.getMethod() instanceof MDUpdateMethod and // md.update(salt)
+ mua.getQualifier() = mda.getQualifier().(VarAccess).getVariable().getAnAccess()
+ )
+ )
+ }
+
+ /** Holds for additional steps such as `passwordStr.getBytes()` */
+ override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
+ exists(MethodAccess ma |
+ pred.asExpr() = ma.getAnArgument() and
+ (succ.asExpr() = ma or succ.asExpr() = ma.getQualifier())
+ )
+ }
+}
+
+from DataFlow::PathNode source, DataFlow::PathNode sink, HashWithoutSaltConfiguration c
+where c.hasFlowPath(source, sink)
+select sink.getNode(), source, sink, "$@ is hashed without a salt.", source.getNode(),
+ "The password"
diff --git a/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.expected b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.expected
new file mode 100644
index 00000000000..4983391a231
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.expected
@@ -0,0 +1,11 @@
+edges
+| HashWithoutSalt.java:9:36:9:43 | password : String | HashWithoutSalt.java:9:26:9:55 | digest(...) |
+| HashWithoutSalt.java:15:13:15:20 | password : String | HashWithoutSalt.java:16:26:16:27 | md |
+nodes
+| HashWithoutSalt.java:9:26:9:55 | digest(...) | semmle.label | digest(...) |
+| HashWithoutSalt.java:9:36:9:43 | password : String | semmle.label | password : String |
+| HashWithoutSalt.java:15:13:15:20 | password : String | semmle.label | password : String |
+| HashWithoutSalt.java:16:26:16:27 | md | semmle.label | md |
+#select
+| HashWithoutSalt.java:9:26:9:55 | digest(...) | HashWithoutSalt.java:9:36:9:43 | password : String | HashWithoutSalt.java:9:26:9:55 | digest(...) | $@ is hashed without a salt. | HashWithoutSalt.java:9:36:9:43 | password | The password |
+| HashWithoutSalt.java:16:26:16:27 | md | HashWithoutSalt.java:15:13:15:20 | password : String | HashWithoutSalt.java:16:26:16:27 | md | $@ is hashed without a salt. | HashWithoutSalt.java:15:13:15:20 | password | The password |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java
new file mode 100644
index 00000000000..809f923d2c3
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java
@@ -0,0 +1,40 @@
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+public class HashWithoutSalt {
+ // BAD - Hash without a salt.
+ public void getSHA256Hash(String password) throws NoSuchAlgorithmException {
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ byte[] messageDigest = md.digest(password.getBytes());
+ }
+
+ // BAD - Hash without a salt.
+ public void getSHA256Hash2(String password) throws NoSuchAlgorithmException {
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ md.update(password.getBytes());
+ byte[] messageDigest = md.digest();
+ }
+
+ // GOOD - Hash with a salt.
+ public void getSHA256Hash(String password, byte[] salt) throws NoSuchAlgorithmException {
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ md.update(salt);
+ byte[] messageDigest = md.digest(password.getBytes());
+ }
+
+ // GOOD - Hash with a salt.
+ public void getSHA256Hash2(String password, byte[] salt) throws NoSuchAlgorithmException {
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ md.update(salt);
+ md.update(password.getBytes());
+ byte[] messageDigest = md.digest();
+ }
+
+ public static byte[] getSalt() throws NoSuchAlgorithmException {
+ SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
+ byte[] salt = new byte[16];
+ sr.nextBytes(salt);
+ return salt;
+ }
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.qlref b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.qlref
new file mode 100644
index 00000000000..a8233ca37d2
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
From 19ff00bad421034aabac6cc6d5891a9b2441aa72 Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Thu, 7 Jan 2021 13:15:30 +0000
Subject: [PATCH 016/725] Enhance the additional step flow and update qldoc
---
.../CWE/CWE-759/HashWithoutSalt.qhelp | 2 +-
.../Security/CWE/CWE-759/HashWithoutSalt.ql | 22 +++++++++----------
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.qhelp b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.qhelp
index a3875c35c37..3418645bc97 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.qhelp
+++ b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.qhelp
@@ -2,7 +2,7 @@
- In cryptography, "salt" is random data that are used as an additional input to a one-way function that hashes a password or pass-phrase. It makes dictionary attacks more difficult.
+ In cryptography, a salt is some random data used as an additional input to a one-way function that hashes a password or pass-phrase. It makes dictionary attacks more difficult.
Without a salt, it is much easier for attackers to pre-compute the hash value using dictionary attack techniques such as rainbow tables to crack passwords.
diff --git a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
index 5e308838072..6cc7de4f847 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
@@ -1,8 +1,8 @@
/**
- * @id java/hash-without-salt
- * @name Use of a One-Way Hash without a Salt
+ * @name Use of a hash function without a salt
* @description Hashed passwords without a salt are vulnerable to dictionary attacks.
* @kind path-problem
+ * @id java/hash-without-salt
* @tags security
* external/cwe-759
*/
@@ -11,7 +11,7 @@ import java
import semmle.code.java.dataflow.TaintTracking
import DataFlow::PathGraph
-/** The Java class `java.security.MessageDigest` */
+/** The Java class `java.security.MessageDigest`. */
class MessageDigest extends RefType {
MessageDigest() { this.hasQualifiedName("java.security", "MessageDigest") }
}
@@ -19,22 +19,20 @@ class MessageDigest extends RefType {
/** The method `digest()` declared in `java.security.MessageDigest`. */
class MDDigestMethod extends Method {
MDDigestMethod() {
- getDeclaringType() instanceof MessageDigest and
- hasName("digest")
+ this.getDeclaringType() instanceof MessageDigest and
+ this.hasName("digest")
}
}
/** The method `update()` declared in `java.security.MessageDigest`. */
class MDUpdateMethod extends Method {
MDUpdateMethod() {
- getDeclaringType() instanceof MessageDigest and
- hasName("update")
+ this.getDeclaringType() instanceof MessageDigest and
+ this.hasName("update")
}
}
-/**
- * Gets a regular expression for matching common names of variables that indicate the value being held is a password.
- */
+/** Gets a regular expression for matching common names of variables that indicate the value being held is a password. */
string getPasswordRegex() { result = "(?i).*pass(wd|word|code|phrase).*" }
/** Finds variables that hold password information judging by their names. */
@@ -78,9 +76,11 @@ class HashWithoutSaltConfiguration extends TaintTracking::Configuration {
)
}
- /** Holds for additional steps such as `passwordStr.getBytes()` */
+ /** Holds for additional steps that flow to a method call of `update` or `digest` declared in `java.security.MessageDigest`. */
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(MethodAccess ma |
+ ma.getMethod().getDeclaringType() instanceof MessageDigest and
+ ma.getMethod().hasName(["digest", "update"]) and
pred.asExpr() = ma.getAnArgument() and
(succ.asExpr() = ma or succ.asExpr() = ma.getQualifier())
)
From b56fe2b25f644866e4ae0d6a9474843c95c1eeba Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Thu, 7 Jan 2021 16:31:21 +0000
Subject: [PATCH 017/725] Remove specific method name in additional taint step
---
.../src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
index 6cc7de4f847..eae6c0ed149 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
@@ -76,11 +76,10 @@ class HashWithoutSaltConfiguration extends TaintTracking::Configuration {
)
}
- /** Holds for additional steps that flow to a method call of `update` or `digest` declared in `java.security.MessageDigest`. */
+ /** Holds for additional steps that flow to additional method calls of the type `java.security.MessageDigest`. */
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(MethodAccess ma |
ma.getMethod().getDeclaringType() instanceof MessageDigest and
- ma.getMethod().hasName(["digest", "update"]) and
pred.asExpr() = ma.getAnArgument() and
(succ.asExpr() = ma or succ.asExpr() = ma.getQualifier())
)
From 39103af71833732617518f68af71dbba5d743cc9 Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Fri, 8 Jan 2021 13:02:57 +0000
Subject: [PATCH 018/725] Remove additional taint step
---
.../Security/CWE/CWE-759/HashWithoutSalt.ql | 19 +++++--------------
.../security/CWE-759/HashWithoutSalt.expected | 12 ++++++------
2 files changed, 11 insertions(+), 20 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
index eae6c0ed149..bb74f24f347 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
@@ -50,13 +50,13 @@ class HashWithoutSaltConfiguration extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) {
exists(
- MethodAccess mda, MethodAccess mua // invoke `md.digest()` with only one call of `md.update(password)`, that is, without the call of `md.update(digest)`
+ MethodAccess mua, MethodAccess mda // invoke `md.digest()` with only one call of `md.update(password)`, that is, without the call of `md.update(digest)`
|
- sink.asExpr() = mda.getQualifier() and
+ sink.asExpr() = mua.getArgument(0) and
+ mua.getMethod() instanceof MDUpdateMethod and // md.update(password)
mda.getMethod() instanceof MDDigestMethod and
mda.getNumArgument() = 0 and // md.digest()
- mua.getMethod() instanceof MDUpdateMethod and // md.update(password)
- mua.getQualifier() = mda.getQualifier().(VarAccess).getVariable().getAnAccess() and
+ mda.getQualifier() = mua.getQualifier().(VarAccess).getVariable().getAnAccess() and
not exists(MethodAccess mua2 |
mua2.getMethod() instanceof MDUpdateMethod and // md.update(salt)
mua2.getQualifier() = mua.getQualifier().(VarAccess).getVariable().getAnAccess() and
@@ -66,7 +66,7 @@ class HashWithoutSaltConfiguration extends TaintTracking::Configuration {
or
// invoke `md.digest(password)` without another call of `md.update(salt)`
exists(MethodAccess mda |
- sink.asExpr() = mda and
+ sink.asExpr() = mda.getArgument(0) and
mda.getMethod() instanceof MDDigestMethod and // md.digest(password)
mda.getNumArgument() = 1 and
not exists(MethodAccess mua |
@@ -75,15 +75,6 @@ class HashWithoutSaltConfiguration extends TaintTracking::Configuration {
)
)
}
-
- /** Holds for additional steps that flow to additional method calls of the type `java.security.MessageDigest`. */
- override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
- exists(MethodAccess ma |
- ma.getMethod().getDeclaringType() instanceof MessageDigest and
- pred.asExpr() = ma.getAnArgument() and
- (succ.asExpr() = ma or succ.asExpr() = ma.getQualifier())
- )
- }
}
from DataFlow::PathNode source, DataFlow::PathNode sink, HashWithoutSaltConfiguration c
diff --git a/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.expected b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.expected
index 4983391a231..d1ed0b00338 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.expected
@@ -1,11 +1,11 @@
edges
-| HashWithoutSalt.java:9:36:9:43 | password : String | HashWithoutSalt.java:9:26:9:55 | digest(...) |
-| HashWithoutSalt.java:15:13:15:20 | password : String | HashWithoutSalt.java:16:26:16:27 | md |
+| HashWithoutSalt.java:9:36:9:43 | password : String | HashWithoutSalt.java:9:36:9:54 | getBytes(...) |
+| HashWithoutSalt.java:15:13:15:20 | password : String | HashWithoutSalt.java:15:13:15:31 | getBytes(...) |
nodes
-| HashWithoutSalt.java:9:26:9:55 | digest(...) | semmle.label | digest(...) |
| HashWithoutSalt.java:9:36:9:43 | password : String | semmle.label | password : String |
+| HashWithoutSalt.java:9:36:9:54 | getBytes(...) | semmle.label | getBytes(...) |
| HashWithoutSalt.java:15:13:15:20 | password : String | semmle.label | password : String |
-| HashWithoutSalt.java:16:26:16:27 | md | semmle.label | md |
+| HashWithoutSalt.java:15:13:15:31 | getBytes(...) | semmle.label | getBytes(...) |
#select
-| HashWithoutSalt.java:9:26:9:55 | digest(...) | HashWithoutSalt.java:9:36:9:43 | password : String | HashWithoutSalt.java:9:26:9:55 | digest(...) | $@ is hashed without a salt. | HashWithoutSalt.java:9:36:9:43 | password | The password |
-| HashWithoutSalt.java:16:26:16:27 | md | HashWithoutSalt.java:15:13:15:20 | password : String | HashWithoutSalt.java:16:26:16:27 | md | $@ is hashed without a salt. | HashWithoutSalt.java:15:13:15:20 | password | The password |
+| HashWithoutSalt.java:9:36:9:54 | getBytes(...) | HashWithoutSalt.java:9:36:9:43 | password : String | HashWithoutSalt.java:9:36:9:54 | getBytes(...) | $@ is hashed without a salt. | HashWithoutSalt.java:9:36:9:43 | password | The password |
+| HashWithoutSalt.java:15:13:15:31 | getBytes(...) | HashWithoutSalt.java:15:13:15:20 | password : String | HashWithoutSalt.java:15:13:15:31 | getBytes(...) | $@ is hashed without a salt. | HashWithoutSalt.java:15:13:15:20 | password | The password |
From 86c04e6971b10569e4209817a1a497dd51a9308e Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Mon, 11 Jan 2021 16:59:57 +0000
Subject: [PATCH 019/725] Detect the scenario of passwords concatenated with a
salt to reduce FPs
---
.../Security/CWE/CWE-759/HashWithoutSalt.ql | 14 +++++++
.../security/CWE-759/HashWithoutSalt.expected | 16 ++++----
.../security/CWE-759/HashWithoutSalt.java | 39 ++++++++++++++-----
3 files changed, 52 insertions(+), 17 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
index bb74f24f347..8330d5b9a42 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
@@ -75,6 +75,20 @@ class HashWithoutSaltConfiguration extends TaintTracking::Configuration {
)
)
}
+
+ /**
+ * Holds if a password is concatenated with a salt then hashed together through the call `System.arraycopy(password.getBytes(), ...)`. For example,
+ * `System.arraycopy(password.getBytes(), 0, allBytes, 0, password.getBytes().length);`
+ * `System.arraycopy(salt, 0, allBytes, password.getBytes().length, salt.length);`
+ * `byte[] messageDigest = md.digest(allBytes);`
+ */
+ override predicate isSanitizer(DataFlow::Node node) {
+ exists(MethodAccess ma |
+ ma.getMethod().getDeclaringType().hasQualifiedName("java.lang", "System") and
+ ma.getMethod().hasName("arraycopy") and
+ ma.getArgument(0) = node.asExpr()
+ )
+ }
}
from DataFlow::PathNode source, DataFlow::PathNode sink, HashWithoutSaltConfiguration c
diff --git a/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.expected b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.expected
index d1ed0b00338..2077f2d6266 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.expected
@@ -1,11 +1,11 @@
edges
-| HashWithoutSalt.java:9:36:9:43 | password : String | HashWithoutSalt.java:9:36:9:54 | getBytes(...) |
-| HashWithoutSalt.java:15:13:15:20 | password : String | HashWithoutSalt.java:15:13:15:31 | getBytes(...) |
+| HashWithoutSalt.java:10:36:10:43 | password : String | HashWithoutSalt.java:10:36:10:54 | getBytes(...) |
+| HashWithoutSalt.java:17:13:17:20 | password : String | HashWithoutSalt.java:17:13:17:31 | getBytes(...) |
nodes
-| HashWithoutSalt.java:9:36:9:43 | password : String | semmle.label | password : String |
-| HashWithoutSalt.java:9:36:9:54 | getBytes(...) | semmle.label | getBytes(...) |
-| HashWithoutSalt.java:15:13:15:20 | password : String | semmle.label | password : String |
-| HashWithoutSalt.java:15:13:15:31 | getBytes(...) | semmle.label | getBytes(...) |
+| HashWithoutSalt.java:10:36:10:43 | password : String | semmle.label | password : String |
+| HashWithoutSalt.java:10:36:10:54 | getBytes(...) | semmle.label | getBytes(...) |
+| HashWithoutSalt.java:17:13:17:20 | password : String | semmle.label | password : String |
+| HashWithoutSalt.java:17:13:17:31 | getBytes(...) | semmle.label | getBytes(...) |
#select
-| HashWithoutSalt.java:9:36:9:54 | getBytes(...) | HashWithoutSalt.java:9:36:9:43 | password : String | HashWithoutSalt.java:9:36:9:54 | getBytes(...) | $@ is hashed without a salt. | HashWithoutSalt.java:9:36:9:43 | password | The password |
-| HashWithoutSalt.java:15:13:15:31 | getBytes(...) | HashWithoutSalt.java:15:13:15:20 | password : String | HashWithoutSalt.java:15:13:15:31 | getBytes(...) | $@ is hashed without a salt. | HashWithoutSalt.java:15:13:15:20 | password | The password |
+| HashWithoutSalt.java:10:36:10:54 | getBytes(...) | HashWithoutSalt.java:10:36:10:43 | password : String | HashWithoutSalt.java:10:36:10:54 | getBytes(...) | $@ is hashed without a salt. | HashWithoutSalt.java:10:36:10:43 | password | The password |
+| HashWithoutSalt.java:17:13:17:31 | getBytes(...) | HashWithoutSalt.java:17:13:17:20 | password : String | HashWithoutSalt.java:17:13:17:31 | getBytes(...) | $@ is hashed without a salt. | HashWithoutSalt.java:17:13:17:20 | password | The password |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java
index 809f923d2c3..53d406a4fca 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java
@@ -1,40 +1,61 @@
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
+import java.util.Base64;
public class HashWithoutSalt {
// BAD - Hash without a salt.
- public void getSHA256Hash(String password) throws NoSuchAlgorithmException {
+ public String getSHA256Hash(String password) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] messageDigest = md.digest(password.getBytes());
+ return Base64.getEncoder().encodeToString(messageDigest);
}
// BAD - Hash without a salt.
- public void getSHA256Hash2(String password) throws NoSuchAlgorithmException {
+ public String getSHA256Hash2(String password) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(password.getBytes());
byte[] messageDigest = md.digest();
+ return Base64.getEncoder().encodeToString(messageDigest);
}
// GOOD - Hash with a salt.
- public void getSHA256Hash(String password, byte[] salt) throws NoSuchAlgorithmException {
+ public String getSHA256Hash(String password, byte[] salt) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(salt);
byte[] messageDigest = md.digest(password.getBytes());
+ return Base64.getEncoder().encodeToString(messageDigest);
}
// GOOD - Hash with a salt.
- public void getSHA256Hash2(String password, byte[] salt) throws NoSuchAlgorithmException {
+ public String getSHA256Hash2(String password, byte[] salt) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(salt);
md.update(password.getBytes());
byte[] messageDigest = md.digest();
+ return Base64.getEncoder().encodeToString(messageDigest);
+ }
+
+ // GOOD - Hash with a salt concatenated with the password.
+ public String getSHA256Hash3(String password, byte[] salt) throws NoSuchAlgorithmException {
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+
+ byte[] passBytes = password.getBytes();
+ byte[] allBytes = new byte[passBytes.length + salt.length];
+ System.arraycopy(passBytes, 0, allBytes, 0, passBytes.length);
+ System.arraycopy(salt, 0, allBytes, passBytes.length, salt.length);
+ byte[] messageDigest = md.digest(allBytes);
+
+ byte[] cipherBytes = new byte[32 + salt.length]; // SHA-256 is 32 bytes long
+ System.arraycopy(messageDigest, 0, cipherBytes, 0, 32);
+ System.arraycopy(salt, 0, cipherBytes, 32, salt.length);
+ return Base64.getEncoder().encodeToString(cipherBytes);
}
public static byte[] getSalt() throws NoSuchAlgorithmException {
- SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
- byte[] salt = new byte[16];
- sr.nextBytes(salt);
- return salt;
- }
+ SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
+ byte[] salt = new byte[16];
+ sr.nextBytes(salt);
+ return salt;
+ }
}
From 3af8773dd6c6639578fa309bba819dbbf5d308f6 Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Fri, 15 Jan 2021 16:20:31 +0000
Subject: [PATCH 020/725] Add more cases
---
.../Security/CWE/CWE-759/HashWithoutSalt.ql | 14 ++++++++++---
.../security/CWE-759/HashWithoutSalt.java | 17 ++++++++++++++++
.../query-tests/security/CWE-759/SHA256.java | 20 +++++++++++++++++++
3 files changed, 48 insertions(+), 3 deletions(-)
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-759/SHA256.java
diff --git a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
index 8330d5b9a42..4f4f4180159 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
@@ -38,7 +38,12 @@ string getPasswordRegex() { result = "(?i).*pass(wd|word|code|phrase).*" }
/** Finds variables that hold password information judging by their names. */
class PasswordVarExpr extends Expr {
PasswordVarExpr() {
- exists(Variable v | this = v.getAnAccess() | v.getName().regexpMatch(getPasswordRegex()))
+ exists(Variable v | this = v.getAnAccess() |
+ (
+ v.getName().toLowerCase().regexpMatch(getPasswordRegex()) and
+ not v.getName().toLowerCase().matches("%hash%") // Exclude variable names such as `passwordHash` since their values were already hashed
+ )
+ )
}
}
@@ -77,17 +82,20 @@ class HashWithoutSaltConfiguration extends TaintTracking::Configuration {
}
/**
- * Holds if a password is concatenated with a salt then hashed together through the call `System.arraycopy(password.getBytes(), ...)`. For example,
+ * Holds if a password is concatenated with a salt then hashed together through the call `System.arraycopy(password.getBytes(), ...)`, for example,
* `System.arraycopy(password.getBytes(), 0, allBytes, 0, password.getBytes().length);`
* `System.arraycopy(salt, 0, allBytes, password.getBytes().length, salt.length);`
* `byte[] messageDigest = md.digest(allBytes);`
+ * Or the password is concatenated with a salt as a string.
*/
override predicate isSanitizer(DataFlow::Node node) {
exists(MethodAccess ma |
ma.getMethod().getDeclaringType().hasQualifiedName("java.lang", "System") and
ma.getMethod().hasName("arraycopy") and
ma.getArgument(0) = node.asExpr()
- )
+ ) // System.arraycopy(password.getBytes(), ...)
+ or
+ exists(AddExpr e | node.asExpr() = e.getAnOperand()) // password+salt
}
}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java
index 53d406a4fca..44004f3a8c9 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java
@@ -52,6 +52,23 @@ public class HashWithoutSalt {
return Base64.getEncoder().encodeToString(cipherBytes);
}
+ // GOOD - Hash with a given salt stored somewhere else.
+ public String getSHA256Hash(String password, String salt) throws NoSuchAlgorithmException {
+ return hash(password+salt);
+ }
+
+ // GOOD - Hash with a salt for a variable named passwordHash, whose value is a hash used as an input for a hashing function.
+ public String getSHA256Hash3(String passwordHash) throws NoSuchAlgorithmException {
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ byte[] messageDigest = md.digest(passwordHash.getBytes());
+ return Base64.getEncoder().encodeToString(messageDigest);
+ }
+
+ private String hash(String payload) {
+ MessageDigest alg = MessageDigest.getInstance("SHA-256");
+ return Base64.getEncoder().encodeToString(alg.digest(payload.getBytes(java.nio.charset.StandardCharsets.UTF_8)));
+ }
+
public static byte[] getSalt() throws NoSuchAlgorithmException {
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
byte[] salt = new byte[16];
diff --git a/java/ql/test/experimental/query-tests/security/CWE-759/SHA256.java b/java/ql/test/experimental/query-tests/security/CWE-759/SHA256.java
new file mode 100644
index 00000000000..0d46f71d193
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-759/SHA256.java
@@ -0,0 +1,20 @@
+import java.security.MessageDigest;
+
+public class SHA256 {
+ MessageDigest md;
+ public int getBlockSize() {return 32;}
+ public void init() throws Exception {
+ try { md = MessageDigest.getInstance("SHA-256"); }
+ catch (Exception e){
+ System.err.println(e);
+ }
+ }
+
+ public void update(byte[] foo, int start, int len) throws Exception {
+ md.update(foo, start, len);
+ }
+
+ public byte[] digest() throws Exception {
+ return md.digest();
+ }
+}
\ No newline at end of file
From 048167d39adef862a6a3191d5ed56ccc5d953d92 Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Mon, 18 Jan 2021 04:23:30 +0000
Subject: [PATCH 021/725] Revamp the query to reduce FPs introduced by wrapper
calls
---
.../Security/CWE/CWE-759/HashWithoutSalt.ql | 46 +++++++++++++------
.../security/CWE-759/HashWithoutSalt.java | 38 ++++++++++++++-
.../query-tests/security/CWE-759/SHA256.java | 7 +--
3 files changed, 72 insertions(+), 19 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
index 4f4f4180159..15361071610 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
@@ -55,29 +55,17 @@ class HashWithoutSaltConfiguration extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) {
exists(
- MethodAccess mua, MethodAccess mda // invoke `md.digest()` with only one call of `md.update(password)`, that is, without the call of `md.update(digest)`
+ MethodAccess mua // invoke `md.update(password)` without the call of `md.update(digest)`
|
sink.asExpr() = mua.getArgument(0) and
- mua.getMethod() instanceof MDUpdateMethod and // md.update(password)
- mda.getMethod() instanceof MDDigestMethod and
- mda.getNumArgument() = 0 and // md.digest()
- mda.getQualifier() = mua.getQualifier().(VarAccess).getVariable().getAnAccess() and
- not exists(MethodAccess mua2 |
- mua2.getMethod() instanceof MDUpdateMethod and // md.update(salt)
- mua2.getQualifier() = mua.getQualifier().(VarAccess).getVariable().getAnAccess() and
- mua2 != mua
- )
+ mua.getMethod() instanceof MDUpdateMethod // md.update(password)
)
or
// invoke `md.digest(password)` without another call of `md.update(salt)`
exists(MethodAccess mda |
sink.asExpr() = mda.getArgument(0) and
mda.getMethod() instanceof MDDigestMethod and // md.digest(password)
- mda.getNumArgument() = 1 and
- not exists(MethodAccess mua |
- mua.getMethod() instanceof MDUpdateMethod and // md.update(salt)
- mua.getQualifier() = mda.getQualifier().(VarAccess).getVariable().getAnAccess()
- )
+ mda.getNumArgument() = 1
)
}
@@ -96,9 +84,37 @@ class HashWithoutSaltConfiguration extends TaintTracking::Configuration {
) // System.arraycopy(password.getBytes(), ...)
or
exists(AddExpr e | node.asExpr() = e.getAnOperand()) // password+salt
+ or
+ exists(MethodAccess mua, MethodAccess ma |
+ ma.getArgument(0) = node.asExpr() and // Detect wrapper methods that invoke `md.update(salt)`
+ ma != mua and
+ (
+ mua.getQualifier().(VarAccess).getVariable().getAnAccess() = ma.getQualifier()
+ or
+ mua.getAnArgument().(VarAccess).getVariable().getAnAccess() = ma.getQualifier()
+ or
+ mua.getQualifier().(VarAccess).getVariable().getAnAccess() = ma.getAnArgument()
+ or
+ mua.getAnArgument().(VarAccess).getVariable().getAnAccess() = ma.getAnArgument()
+ ) and
+ isMDUpdateCall(mua.getMethod())
+ )
}
}
+/** Holds if a method invokes `md.update(salt)`. */
+predicate isMDUpdateCall(Callable caller) {
+ caller instanceof MDUpdateMethod
+ or
+ exists(Callable callee |
+ caller.polyCalls(callee) and
+ (
+ callee instanceof MDUpdateMethod or
+ isMDUpdateCall(callee)
+ )
+ )
+}
+
from DataFlow::PathNode source, DataFlow::PathNode sink, HashWithoutSaltConfiguration c
where c.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "$@ is hashed without a salt.", source.getNode(),
diff --git a/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java
index 44004f3a8c9..852898f5481 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java
@@ -64,7 +64,43 @@ public class HashWithoutSalt {
return Base64.getEncoder().encodeToString(messageDigest);
}
- private String hash(String payload) {
+ public void update(SHA256 sha256, byte[] foo, int start, int len) throws NoSuchAlgorithmException {
+ sha256.update(foo, start, len);
+ }
+
+ public void update2(SHA256 sha256, byte[] foo, int start, int len) throws NoSuchAlgorithmException {
+ sha256.update(foo, start, len);
+ }
+
+ // GOOD - Invoke a wrapper implementation with a salt.
+ public String getSHA256Hash4(String password) throws NoSuchAlgorithmException {
+ SHA256 sha256 = new SHA256();
+ byte[] salt = getSalt();
+ byte[] passBytes = password.getBytes();
+ sha256.update(passBytes, 0, passBytes.length);
+ sha256.update(salt, 0, salt.length);
+ return Base64.getEncoder().encodeToString(sha256.digest());
+ }
+
+ // GOOD - Invoke a wrapper implementation with a salt.
+ public String getSHA256Hash5(String password) throws NoSuchAlgorithmException {
+ SHA256 sha256 = new SHA256();
+ byte[] salt = getSalt();
+ byte[] passBytes = password.getBytes();
+ sha256.update(passBytes, 0, passBytes.length);
+ update(sha256, salt, 0, salt.length);
+ return Base64.getEncoder().encodeToString(sha256.digest());
+ }
+
+ // BAD - Invoke a wrapper implementation without a salt.
+ public String getSHA256Hash6(String password) throws NoSuchAlgorithmException {
+ SHA256 sha256 = new SHA256();
+ byte[] passBytes = password.getBytes();
+ sha256.update(passBytes, 0, passBytes.length);
+ return Base64.getEncoder().encodeToString(sha256.digest());
+ }
+
+ private String hash(String payload) throws NoSuchAlgorithmException {
MessageDigest alg = MessageDigest.getInstance("SHA-256");
return Base64.getEncoder().encodeToString(alg.digest(payload.getBytes(java.nio.charset.StandardCharsets.UTF_8)));
}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-759/SHA256.java b/java/ql/test/experimental/query-tests/security/CWE-759/SHA256.java
index 0d46f71d193..a0657af7112 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-759/SHA256.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-759/SHA256.java
@@ -1,20 +1,21 @@
import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
public class SHA256 {
MessageDigest md;
public int getBlockSize() {return 32;}
- public void init() throws Exception {
+ public void init() throws NoSuchAlgorithmException {
try { md = MessageDigest.getInstance("SHA-256"); }
catch (Exception e){
System.err.println(e);
}
}
- public void update(byte[] foo, int start, int len) throws Exception {
+ public void update(byte[] foo, int start, int len) throws NoSuchAlgorithmException {
md.update(foo, start, len);
}
- public byte[] digest() throws Exception {
+ public byte[] digest() throws NoSuchAlgorithmException {
return md.digest();
}
}
\ No newline at end of file
From b9809b071e5bc0f758770b1ccdeb0d945296ccaf Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Mon, 18 Jan 2021 19:22:34 +0000
Subject: [PATCH 022/725] Update the query to work with wrapper classes
---
.../Security/CWE/CWE-759/HashWithoutSalt.ql | 4 +++-
.../query-tests/security/CWE-759/HASH.java | 11 +++++++++++
.../security/CWE-759/HashWithoutSalt.expected | 8 ++++++++
.../security/CWE-759/HashWithoutSalt.java | 16 ++++++++++++++++
.../query-tests/security/CWE-759/SHA256.java | 2 +-
5 files changed, 39 insertions(+), 2 deletions(-)
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-759/HASH.java
diff --git a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
index 15361071610..b7c353de77a 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
@@ -89,13 +89,15 @@ class HashWithoutSaltConfiguration extends TaintTracking::Configuration {
ma.getArgument(0) = node.asExpr() and // Detect wrapper methods that invoke `md.update(salt)`
ma != mua and
(
+ ma.getQualifier().getType() instanceof Interface
+ or
mua.getQualifier().(VarAccess).getVariable().getAnAccess() = ma.getQualifier()
or
mua.getAnArgument().(VarAccess).getVariable().getAnAccess() = ma.getQualifier()
or
mua.getQualifier().(VarAccess).getVariable().getAnAccess() = ma.getAnArgument()
or
- mua.getAnArgument().(VarAccess).getVariable().getAnAccess() = ma.getAnArgument()
+ mua.getArgument(0).(VarAccess).getVariable().getAnAccess() = ma.getAnArgument()
) and
isMDUpdateCall(mua.getMethod())
)
diff --git a/java/ql/test/experimental/query-tests/security/CWE-759/HASH.java b/java/ql/test/experimental/query-tests/security/CWE-759/HASH.java
new file mode 100644
index 00000000000..ed4da313244
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-759/HASH.java
@@ -0,0 +1,11 @@
+import java.security.NoSuchAlgorithmException;
+
+public interface HASH {
+ void init() throws NoSuchAlgorithmException;
+
+ int getBlockSize();
+
+ void update(byte[] foo, int start, int len) throws NoSuchAlgorithmException;
+
+ byte[] digest() throws NoSuchAlgorithmException;
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.expected b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.expected
index 2077f2d6266..7fc5fe115e4 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.expected
@@ -1,11 +1,19 @@
edges
| HashWithoutSalt.java:10:36:10:43 | password : String | HashWithoutSalt.java:10:36:10:54 | getBytes(...) |
| HashWithoutSalt.java:17:13:17:20 | password : String | HashWithoutSalt.java:17:13:17:31 | getBytes(...) |
+| HashWithoutSalt.java:98:22:98:29 | password : String | HashWithoutSalt.java:99:17:99:25 | passBytes : byte[] |
+| HashWithoutSalt.java:99:17:99:25 | passBytes : byte[] | SHA256.java:14:22:14:31 | foo : byte[] |
+| SHA256.java:14:22:14:31 | foo : byte[] | SHA256.java:15:15:15:17 | foo |
nodes
| HashWithoutSalt.java:10:36:10:43 | password : String | semmle.label | password : String |
| HashWithoutSalt.java:10:36:10:54 | getBytes(...) | semmle.label | getBytes(...) |
| HashWithoutSalt.java:17:13:17:20 | password : String | semmle.label | password : String |
| HashWithoutSalt.java:17:13:17:31 | getBytes(...) | semmle.label | getBytes(...) |
+| HashWithoutSalt.java:98:22:98:29 | password : String | semmle.label | password : String |
+| HashWithoutSalt.java:99:17:99:25 | passBytes : byte[] | semmle.label | passBytes : byte[] |
+| SHA256.java:14:22:14:31 | foo : byte[] | semmle.label | foo : byte[] |
+| SHA256.java:15:15:15:17 | foo | semmle.label | foo |
#select
| HashWithoutSalt.java:10:36:10:54 | getBytes(...) | HashWithoutSalt.java:10:36:10:43 | password : String | HashWithoutSalt.java:10:36:10:54 | getBytes(...) | $@ is hashed without a salt. | HashWithoutSalt.java:10:36:10:43 | password | The password |
| HashWithoutSalt.java:17:13:17:31 | getBytes(...) | HashWithoutSalt.java:17:13:17:20 | password : String | HashWithoutSalt.java:17:13:17:31 | getBytes(...) | $@ is hashed without a salt. | HashWithoutSalt.java:17:13:17:20 | password | The password |
+| SHA256.java:15:15:15:17 | foo | HashWithoutSalt.java:98:22:98:29 | password : String | SHA256.java:15:15:15:17 | foo | $@ is hashed without a salt. | HashWithoutSalt.java:98:22:98:29 | password | The password |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java
index 852898f5481..9535e4d01e5 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java
@@ -100,6 +100,22 @@ public class HashWithoutSalt {
return Base64.getEncoder().encodeToString(sha256.digest());
}
+ // GOOD - Invoke a wrapper implementation with a salt, which is only detectable when a class type is declared (not interface).
+ public String getSHA256Hash7(byte[] passphrase) throws NoSuchAlgorithmException, ClassNotFoundException, IllegalAccessException, InstantiationException {
+ Class c = Class.forName("SHA256");
+ HASH sha256 = (HASH) (c.newInstance());
+ byte[] tmp = new byte[4];
+ byte[] key = new byte[32 * 2];
+ for (int i = 0; i < 2; i++) {
+ sha256.init();
+ tmp[3] = (byte) i;
+ sha256.update(tmp, 0, tmp.length);
+ sha256.update(passphrase, 0, passphrase.length);
+ System.arraycopy(sha256.digest(), 0, key, i * 32, 32);
+ }
+ return Base64.getEncoder().encodeToString(key);
+ }
+
private String hash(String payload) throws NoSuchAlgorithmException {
MessageDigest alg = MessageDigest.getInstance("SHA-256");
return Base64.getEncoder().encodeToString(alg.digest(payload.getBytes(java.nio.charset.StandardCharsets.UTF_8)));
diff --git a/java/ql/test/experimental/query-tests/security/CWE-759/SHA256.java b/java/ql/test/experimental/query-tests/security/CWE-759/SHA256.java
index a0657af7112..53532c66f8c 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-759/SHA256.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-759/SHA256.java
@@ -1,7 +1,7 @@
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
-public class SHA256 {
+public class SHA256 implements HASH {
MessageDigest md;
public int getBlockSize() {return 32;}
public void init() throws NoSuchAlgorithmException {
From a56dd60baa3e882d5e85a3479fecae8cc4c6ac19 Mon Sep 17 00:00:00 2001
From: haby0
Date: Thu, 21 Jan 2021 19:18:10 +0800
Subject: [PATCH 023/725] *)add CWE-652 XQueryInjection detection
---
.../Security/CWE/CWE-652/XQueryInjection.java | 35 +++++++++++++++++++
.../CWE/CWE-652/XQueryInjection.qhelp | 34 ++++++++++++++++++
.../Security/CWE/CWE-652/XQueryInjection.ql | 21 +++++++++++
.../CWE/CWE-652/XQueryInjectionLib.qll | 35 +++++++++++++++++++
4 files changed, 125 insertions(+)
create mode 100644 java/ql/src/Security/CWE/CWE-652/XQueryInjection.java
create mode 100644 java/ql/src/Security/CWE/CWE-652/XQueryInjection.qhelp
create mode 100644 java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
create mode 100644 java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll
diff --git a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.java b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.java
new file mode 100644
index 00000000000..a60adb37db2
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.java
@@ -0,0 +1,35 @@
+import javax.servlet.http.HttpServletRequest;
+import javax.xml.namespace.QName;
+import javax.xml.xquery.XQConnection;
+import javax.xml.xquery.XQDataSource;
+import javax.xml.xquery.XQException;
+import javax.xml.xquery.XQItemType;
+import javax.xml.xquery.XQPreparedExpression;
+import javax.xml.xquery.XQResultSequence;
+import net.sf.saxon.xqj.SaxonXQDataSource;
+
+public void bad(HttpServletRequest request) throws XQException {
+ String name = request.getParameter("name");
+ XQDataSource ds = new SaxonXQDataSource();
+ XQConnection conn = ds.getConnection();
+ String query = "for $user in doc(\"users.xml\")/Users/User[name='" + name + "'] return $user/password";
+ XQPreparedExpression xqpe = conn.prepareExpression(query);
+ XQResultSequence result = xqpe.executeQuery();
+ while (result.next()){
+ System.out.println(result.getItemAsString(null));
+ }
+}
+
+public void good(HttpServletRequest request) throws XQException {
+ String name = request.getParameter("name");
+ XQDataSource ds = new SaxonXQDataSource();
+ XQConnection conn = ds.getConnection();
+ String query = "declare variable $name as xs:string external;"
+ + " for $user in doc(\"users.xml\")/Users/User[name=$name] return $user/password";
+ XQPreparedExpression xqpe = conn.prepareExpression(query);
+ xqpe.bindString(new QName("name"), name, conn.createAtomicType(XQItemType.XQBASETYPE_STRING));
+ XQResultSequence result = xqpe.executeQuery();
+ while (result.next()){
+ System.out.println(result.getItemAsString(null));
+ }
+}
\ No newline at end of file
diff --git a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.qhelp b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.qhelp
new file mode 100644
index 00000000000..e05e24706cf
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.qhelp
@@ -0,0 +1,34 @@
+
+
+
+The software uses external input to dynamically construct an XQuery expression used to retrieve data from an XML database, but it does not neutralize or incorrectly neutralizes that input.
+This allows an attacker to control the structure of the query.
+
+
+
+
+Use parameterized queries. This will help ensure separation between data plane and control plane.
+
+
+
+
+This example is a comparison of unused parameterized query and using parameterized query.
+Parameterized query through bindString.
+
+
+
+
+
+
+cwe description:
+XQuery Injection.
+
+
+
+
+
+
+
diff --git a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
new file mode 100644
index 00000000000..01a29e047f5
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
@@ -0,0 +1,21 @@
+/**
+ * @name XQuery query built from user-controlled sources
+ * @description Building an XQuery query from user-controlled sources is vulnerable to insertion of
+ * malicious XQuery code by the user.
+ * @kind path-problem
+ * @problem.severity error
+ * @precision high
+ * @id java/XQuery-injection
+ * @tags security
+ * external/cwe/cwe-652
+ */
+
+import java
+import semmle.code.java.dataflow.FlowSources
+import XQueryInjectionLib
+import DataFlow::PathGraph
+
+from DataFlow::PathNode source, DataFlow::PathNode sink, XQueryInjectionConfig conf
+where conf.hasFlowPath(source, sink)
+select sink.getNode(), source, sink, "XQuery query might include code from $@.", source.getNode(),
+ "this user input"
diff --git a/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll b/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll
new file mode 100644
index 00000000000..06d04760986
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll
@@ -0,0 +1,35 @@
+import java
+import semmle.code.java.dataflow.FlowSources
+
+class XQueryInjectionConfig extends TaintTracking::Configuration {
+ XQueryInjectionConfig() { this = "XQueryInjectionConfig" }
+
+ override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
+
+ override predicate isSink(DataFlow::Node sink) { sink instanceof XQueryInjectionSink }
+}
+
+/*Find if the executeQuery method is finally called.*/
+predicate executeQuery(MethodAccess ma) {
+ exists(LocalVariableDeclExpr lvd, MethodAccess ma1, Method m | lvd.getAChildExpr() = ma |
+ m = ma1.getMethod() and
+ m.hasName("executeQuery") and
+ m.getDeclaringType()
+ .getASourceSupertype*()
+ .hasQualifiedName("javax.xml.xquery", "XQPreparedExpression") and
+ ma1.getQualifier() = lvd.getAnAccess()
+ )
+}
+
+class XQueryInjectionSink extends DataFlow::ExprNode {
+ XQueryInjectionSink() {
+ exists(MethodAccess ma, Method m | m = ma.getMethod() |
+ m.hasName("prepareExpression") and
+ m.getDeclaringType()
+ .getASourceSupertype*()
+ .hasQualifiedName("javax.xml.xquery", "XQConnection") and
+ executeQuery(ma) and
+ asExpr() = ma.getArgument(0)
+ )
+ }
+}
From ec4c155043198aba71bd42920f6c957cfd82643b Mon Sep 17 00:00:00 2001
From: haby0
Date: Sat, 23 Jan 2021 18:26:15 +0800
Subject: [PATCH 024/725] *)update XQueryInjection.qhelp
---
java/ql/src/Security/CWE/CWE-652/XQueryInjection.qhelp | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.qhelp b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.qhelp
index e05e24706cf..2b6433d6ef3 100644
--- a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.qhelp
+++ b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.qhelp
@@ -9,20 +9,19 @@ This allows an attacker to control the structure of the query.
-Use parameterized queries. This will help ensure separation between data plane and control plane.
+Use parameterized queries. This will help ensure the program retains control of the query structure.
-This example is a comparison of unused parameterized query and using parameterized query.
-Parameterized query through bindString.
+The following example compares building a query by string concatenation (bad) vs. using bindString to parameterize the query (good).
-cwe description:
+CWE description:
XQuery Injection.
From 44d99f8cd4a895c180ace4871450233d5609668b Mon Sep 17 00:00:00 2001
From: haby0
Date: Sat, 23 Jan 2021 18:26:58 +0800
Subject: [PATCH 025/725] *)update XQueryInjection.ql
---
.../Security/CWE/CWE-652/XQueryInjection.ql | 22 +++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
index 01a29e047f5..b4fcac80a43 100644
--- a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
+++ b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
@@ -15,6 +15,28 @@ import semmle.code.java.dataflow.FlowSources
import XQueryInjectionLib
import DataFlow::PathGraph
+class XQueryInjectionConfig extends DataFlow::Configuration {
+ XQueryInjectionConfig() { this = "XQueryInjectionConfig" }
+
+ override predicate isSource(DataFlow::Node source) { source instanceof XQueryInjectionSource }
+
+ override predicate isSink(DataFlow::Node sink) { sink instanceof XQueryInjectionSink }
+
+ override predicate isBarrier(DataFlow::Node node) {
+ exists(MethodAccess ma, Method m, BindParameterRemoteFlowConf conf, DataFlow::Node node1 |
+ m = ma.getMethod()
+ |
+ node.asExpr() = ma and
+ m.hasName("bindString") and
+ m.getDeclaringType()
+ .getASourceSupertype*()
+ .hasQualifiedName("javax.xml.xquery", "XQDynamicContext") and
+ ma.getArgument(1) = node1.asExpr() and
+ conf.hasFlowTo(node1)
+ )
+ }
+}
+
from DataFlow::PathNode source, DataFlow::PathNode sink, XQueryInjectionConfig conf
where conf.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "XQuery query might include code from $@.", source.getNode(),
From 0b326aae202c05d7b04f0999df0c72eb2034c2e8 Mon Sep 17 00:00:00 2001
From: haby0
Date: Sat, 23 Jan 2021 18:27:38 +0800
Subject: [PATCH 026/725] *)update XQueryInjectionLib.qll
---
.../CWE/CWE-652/XQueryInjectionLib.qll | 97 +++++++++++++++----
1 file changed, 76 insertions(+), 21 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll b/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll
index 06d04760986..808e5a984a5 100644
--- a/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll
+++ b/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll
@@ -1,35 +1,90 @@
import java
import semmle.code.java.dataflow.FlowSources
+import semmle.code.java.dataflow.TaintTracking2
+import DataFlow::PathGraph
-class XQueryInjectionConfig extends TaintTracking::Configuration {
- XQueryInjectionConfig() { this = "XQueryInjectionConfig" }
-
- override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
-
- override predicate isSink(DataFlow::Node sink) { sink instanceof XQueryInjectionSink }
+/** A call to `XQConnection.prepareExpression`. */
+class XQueryParserCall extends MethodAccess {
+ XQueryParserCall() {
+ exists(Method m |
+ this.getMethod() = m and
+ m.getDeclaringType()
+ .getASourceSupertype*()
+ .hasQualifiedName("javax.xml.xquery", "XQConnection") and
+ m.hasName("prepareExpression")
+ )
+ }
+ // return the first parameter of the `bindString` method and use it as a sink
+ Expr getSink() { result = this.getArgument(0) }
}
-/*Find if the executeQuery method is finally called.*/
-predicate executeQuery(MethodAccess ma) {
- exists(LocalVariableDeclExpr lvd, MethodAccess ma1, Method m | lvd.getAChildExpr() = ma |
- m = ma1.getMethod() and
- m.hasName("executeQuery") and
- m.getDeclaringType()
- .getASourceSupertype*()
- .hasQualifiedName("javax.xml.xquery", "XQPreparedExpression") and
- ma1.getQualifier() = lvd.getAnAccess()
- )
+/** A call to `XQDynamicContext.bindString`. */
+class XQueryBindStringCall extends MethodAccess {
+ XQueryBindStringCall() {
+ exists(Method m |
+ this.getMethod() = m and
+ m.getDeclaringType()
+ .getASourceSupertype*()
+ .hasQualifiedName("javax.xml.xquery", "XQDynamicContext") and
+ m.hasName("bindString")
+ )
+ }
+ // return the second parameter of the `bindString` method and use it as a sink
+ Expr getSink() { result = this.getArgument(1) }
}
-class XQueryInjectionSink extends DataFlow::ExprNode {
- XQueryInjectionSink() {
- exists(MethodAccess ma, Method m | m = ma.getMethod() |
+/** Used to determine whether to call the `prepareExpression` method, and the first parameter value can be remotely controlled. */
+class ParserParameterRemoteFlowConf extends TaintTracking2::Configuration {
+ ParserParameterRemoteFlowConf() { this = "ParserParameterRemoteFlowConf" }
+
+ override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(XQueryParserCall xqpc | xqpc.getSink() = sink.asExpr())
+ }
+}
+
+/** Used to determine whether to call the `bindString` method, and the second parameter value can be controlled remotely. */
+class BindParameterRemoteFlowConf extends TaintTracking2::Configuration {
+ BindParameterRemoteFlowConf() { this = "BindParameterRemoteFlowConf" }
+
+ override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(XQueryBindStringCall xqbsc | xqbsc.getSink() = sink.asExpr())
+ }
+}
+
+/**
+ * A data flow source for XQuery injection vulnerability.
+ * 1. `prepareExpression` call as sink.
+ * 2. Determine whether the `var1` parameter of `prepareExpression` method can be controlled remotely.
+ */
+class XQueryInjectionSource extends DataFlow::ExprNode {
+ XQueryInjectionSource() {
+ exists(MethodAccess ma, Method m, ParserParameterRemoteFlowConf conf, DataFlow::Node node |
+ m = ma.getMethod()
+ |
m.hasName("prepareExpression") and
m.getDeclaringType()
.getASourceSupertype*()
.hasQualifiedName("javax.xml.xquery", "XQConnection") and
- executeQuery(ma) and
- asExpr() = ma.getArgument(0)
+ asExpr() = ma and
+ node.asExpr() = ma.getArgument(0) and
+ conf.hasFlowTo(node)
+ )
+ }
+}
+
+/** A data flow sink for XQuery injection vulnerability. */
+class XQueryInjectionSink extends DataFlow::Node {
+ XQueryInjectionSink() {
+ exists(MethodAccess ma, Method m | m = ma.getMethod() |
+ m.hasName("executeQuery") and
+ m.getDeclaringType()
+ .getASourceSupertype*()
+ .hasQualifiedName("javax.xml.xquery", "XQPreparedExpression") and
+ asExpr() = ma.getQualifier()
)
}
}
From a64fc2b24ee7e084a0a402d11bb6795dabac6049 Mon Sep 17 00:00:00 2001
From: Francis Alexander
Date: Sun, 24 Jan 2021 18:58:39 +0530
Subject: [PATCH 027/725] Java: Queries to detect remote source flow to CORS
header
---
.../Security/CWE/CWE-346/UnvalidatedCors.java | 40 ++++++++++
.../CWE/CWE-346/UnvalidatedCors.qhelp | 75 +++++++++++++++++++
.../Security/CWE/CWE-346/UnvalidatedCors.ql | 55 ++++++++++++++
.../security/CWE-346/UnvalidatedCors.expected | 7 ++
.../security/CWE-346/UnvalidatedCors.java | 52 +++++++++++++
.../security/CWE-346/UnvalidatedCors.qlref | 1 +
.../test/query-tests/security/CWE-346/options | 1 +
.../org/apache/commons/lang3/StringUtils.java | 7 ++
.../servlet-api-2.4/javax/servlet/Filter.java | 11 +++
.../javax/servlet/FilterChain.java | 7 ++
.../javax/servlet/FilterConfig.java | 10 +++
.../servlet/http/HttpServletResponse.java | 6 ++
12 files changed, 272 insertions(+)
create mode 100644 java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.java
create mode 100644 java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.qhelp
create mode 100644 java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql
create mode 100644 java/ql/test/query-tests/security/CWE-346/UnvalidatedCors.expected
create mode 100644 java/ql/test/query-tests/security/CWE-346/UnvalidatedCors.java
create mode 100644 java/ql/test/query-tests/security/CWE-346/UnvalidatedCors.qlref
create mode 100644 java/ql/test/query-tests/security/CWE-346/options
create mode 100644 java/ql/test/stubs/apache-commons-lang3-3.7/org/apache/commons/lang3/StringUtils.java
create mode 100644 java/ql/test/stubs/servlet-api-2.4/javax/servlet/Filter.java
create mode 100644 java/ql/test/stubs/servlet-api-2.4/javax/servlet/FilterChain.java
create mode 100644 java/ql/test/stubs/servlet-api-2.4/javax/servlet/FilterConfig.java
diff --git a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.java b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.java
new file mode 100644
index 00000000000..8f388b13553
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.java
@@ -0,0 +1,40 @@
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang3.StringUtils;
+
+public class CorsFilter implements Filter {
+ public void init(FilterConfig filterConfig) throws ServletException {
+ // init
+ }
+
+ public void doFilter(ServletRequest req, ServletResponse res,
+ FilterChain chain) throws IOException, ServletException {
+ HttpServletRequest request = (HttpServletRequest) req;
+ HttpServletResponse response = (HttpServletResponse) res;
+ String url = request.getHeader("Origin");
+
+ if (!StringUtils.isEmpty(url)) {
+ String val = response.getHeader("Access-Control-Allow-Origin");
+
+ if (StringUtils.isEmpty(val)) {
+ response.addHeader("Access-Control-Allow-Origin", url);
+ response.addHeader("Access-Control-Allow-Credentials", "true");
+ }
+ }
+
+ chain.doFilter(req, res);
+ }
+
+ public void destroy() {
+ // destroy
+ }
+}
diff --git a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.qhelp b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.qhelp
new file mode 100644
index 00000000000..3293352c0d5
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.qhelp
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+ A server can send the
+ "Access-Control-Allow-Credentials" CORS header to control
+ when a browser may send user credentials in Cross-Origin HTTP
+ requests.
+
+
+
+
+ When the Access-Control-Allow-Credentials header
+ is "true", the Access-Control-Allow-Origin
+ header must have a value different from "*" in order to
+ make browsers accept the header. Therefore, to allow multiple origins
+ for Cross-Origin requests with credentials, the server must
+ dynamically compute the value of the
+ "Access-Control-Allow-Origin" header. Computing this
+ header value from information in the request to the server can
+ therefore potentially allow an attacker to control the origins that
+ the browser sends credentials to.
+
+
+
+
+
+
+
+
+
+
+ When the Access-Control-Allow-Credentials header
+ value is "true", a dynamic computation of the
+ Access-Control-Allow-Origin header must involve
+ sanitization if it relies on user-controlled input.
+
+
+
+
+
+ Since the "null" origin is easy to obtain for an
+ attacker, it is never safe to use "null" as the value of
+ the Access-Control-Allow-Origin header when the
+ Access-Control-Allow-Credentials header value is
+ "true".
+
+
+
+
+
+
+
+ In the example below, the server allows the browser to send
+ user credentials in a Cross-Origin request. The request header
+ origins controls the allowed origins for such a
+ Cross-Origin request.
+
+
+
+
+
+
+
+
+ Mozilla Developer Network: CORS, Access-Control-Allow-Origin.
+ Mozilla Developer Network: CORS, Access-Control-Allow-Credentials.
+ PortSwigger: Exploiting CORS Misconfigurations for Bitcoins and Bounties
+ W3C: CORS for developers, Advice for Resource Owners
+
+
diff --git a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql
new file mode 100644
index 00000000000..3685ec2209c
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql
@@ -0,0 +1,55 @@
+/**
+ * @name Cors header being set from remote source
+ * @description Cors header is being set from remote source, allowing to control the origin.
+ * @kind path-problem
+ * @problem.severity error
+ * @precision high
+ * @id java/unvalidated-cors-origin-set
+ * @tags security
+ * external/cwe/cwe-346
+ */
+
+import java
+import semmle.code.java.dataflow.FlowSources
+import semmle.code.java.frameworks.Servlets
+import semmle.code.java.dataflow.TaintTracking
+import DataFlow::PathGraph
+
+// Check for Access-Control-Allow-Credentials as well, this ensures fair chances of exploitability.
+predicate satisfyAllowCredentials(MethodAccess header, MethodAccess check) {
+ header.getArgument(0).(CompileTimeConstantExpr).getStringValue().toLowerCase() =
+ "access-control-allow-credentials" and
+ header.getArgument(1).(CompileTimeConstantExpr).getStringValue() = "true" and
+ header.getEnclosingCallable() = check.getEnclosingCallable()
+}
+
+predicate checkAccessControlAllowOriginHeader(Expr expr) {
+ expr.(CompileTimeConstantExpr).getStringValue().toLowerCase() = "access-control-allow-origin"
+}
+
+class CorsOriginConfig extends TaintTracking::Configuration {
+ CorsOriginConfig() { this = "CORSOriginConfig" }
+
+ override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(ResponseSetHeaderMethod h, MethodAccess m |
+ m = h.getAReference() and
+ checkAccessControlAllowOriginHeader(m.getArgument(0)) and
+ satisfyAllowCredentials(h.getAReference(), m) and
+ sink.asExpr() = m.getArgument(1)
+ )
+ or
+ exists(ResponseAddHeaderMethod a, MethodAccess m |
+ m = a.getAReference() and
+ checkAccessControlAllowOriginHeader(m.getArgument(0)) and
+ satisfyAllowCredentials(a.getAReference(), m) and
+ sink.asExpr() = m.getArgument(1)
+ )
+ }
+}
+
+from DataFlow::PathNode source, DataFlow::PathNode sink, CorsOriginConfig conf
+where conf.hasFlowPath(source, sink)
+select sink.getNode(), source, sink, "Cors header is being set using user controlled value $@.",
+ source.getNode(), "user-provided value"
diff --git a/java/ql/test/query-tests/security/CWE-346/UnvalidatedCors.expected b/java/ql/test/query-tests/security/CWE-346/UnvalidatedCors.expected
new file mode 100644
index 00000000000..8bb9202b582
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-346/UnvalidatedCors.expected
@@ -0,0 +1,7 @@
+edges
+| UnvalidatedCors.java:34:22:34:48 | getHeader(...) : String | UnvalidatedCors.java:40:67:40:69 | url |
+nodes
+| UnvalidatedCors.java:34:22:34:48 | getHeader(...) : String | semmle.label | getHeader(...) : String |
+| UnvalidatedCors.java:40:67:40:69 | url | semmle.label | url |
+#select
+| UnvalidatedCors.java:40:67:40:69 | url | UnvalidatedCors.java:34:22:34:48 | getHeader(...) : String | UnvalidatedCors.java:40:67:40:69 | url | Cors header is being set using user controlled value $@. | UnvalidatedCors.java:34:22:34:48 | getHeader(...) | user-provided value |
diff --git a/java/ql/test/query-tests/security/CWE-346/UnvalidatedCors.java b/java/ql/test/query-tests/security/CWE-346/UnvalidatedCors.java
new file mode 100644
index 00000000000..b05a88ab432
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-346/UnvalidatedCors.java
@@ -0,0 +1,52 @@
+package com.mossle.core.servlet;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ *
+ *
+ *
+ */
+public class UnvalidatedCors implements Filter {
+ public void init(FilterConfig filterConfig) throws ServletException {
+ // init
+ }
+
+ public void doFilter(ServletRequest req, ServletResponse res,
+ FilterChain chain) throws IOException, ServletException {
+ HttpServletRequest request = (HttpServletRequest) req;
+ HttpServletResponse response = (HttpServletResponse) res;
+ String url = request.getHeader("Origin");
+
+ if (!StringUtils.isEmpty(url)) {
+ String val = response.getHeader("Access-Control-Allow-Origin");
+
+ if (StringUtils.isEmpty(val)) {
+ response.addHeader("Access-Control-Allow-Origin", url);
+ response.addHeader("Access-Control-Allow-Credentials", "true");
+ }
+ }
+
+ chain.doFilter(req, res);
+ }
+
+ public void destroy() {
+ // destroy
+ }
+}
+
diff --git a/java/ql/test/query-tests/security/CWE-346/UnvalidatedCors.qlref b/java/ql/test/query-tests/security/CWE-346/UnvalidatedCors.qlref
new file mode 100644
index 00000000000..93df7860cb4
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-346/UnvalidatedCors.qlref
@@ -0,0 +1 @@
+Security/CWE/CWE-346/UnvalidatedCors.ql
diff --git a/java/ql/test/query-tests/security/CWE-346/options b/java/ql/test/query-tests/security/CWE-346/options
new file mode 100644
index 00000000000..4fd2033a3d4
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-346/options
@@ -0,0 +1 @@
+//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/servlet-api-2.4:${testdir}/../../../stubs/apache-commons-lang3-3.7
diff --git a/java/ql/test/stubs/apache-commons-lang3-3.7/org/apache/commons/lang3/StringUtils.java b/java/ql/test/stubs/apache-commons-lang3-3.7/org/apache/commons/lang3/StringUtils.java
new file mode 100644
index 00000000000..62b13e0e259
--- /dev/null
+++ b/java/ql/test/stubs/apache-commons-lang3-3.7/org/apache/commons/lang3/StringUtils.java
@@ -0,0 +1,7 @@
+package org.apache.commons.lang3;
+
+public class StringUtils {
+ public static boolean isEmpty(final CharSequence cs) {
+ return cs == null || cs.length() == 0;
+ }
+}
diff --git a/java/ql/test/stubs/servlet-api-2.4/javax/servlet/Filter.java b/java/ql/test/stubs/servlet-api-2.4/javax/servlet/Filter.java
new file mode 100644
index 00000000000..47fb902a3d6
--- /dev/null
+++ b/java/ql/test/stubs/servlet-api-2.4/javax/servlet/Filter.java
@@ -0,0 +1,11 @@
+package javax.servlet;
+
+import java.io.IOException;
+
+public interface Filter {
+ default public void init(FilterConfig filterConfig) throws ServletException {}
+ public void doFilter(ServletRequest request, ServletResponse response,
+ FilterChain chain)
+ throws IOException, ServletException;
+ default public void destroy() {}
+}
diff --git a/java/ql/test/stubs/servlet-api-2.4/javax/servlet/FilterChain.java b/java/ql/test/stubs/servlet-api-2.4/javax/servlet/FilterChain.java
new file mode 100644
index 00000000000..b9f9708be56
--- /dev/null
+++ b/java/ql/test/stubs/servlet-api-2.4/javax/servlet/FilterChain.java
@@ -0,0 +1,7 @@
+package javax.servlet;
+
+import java.io.IOException;
+
+public interface FilterChain {
+ public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException;
+}
diff --git a/java/ql/test/stubs/servlet-api-2.4/javax/servlet/FilterConfig.java b/java/ql/test/stubs/servlet-api-2.4/javax/servlet/FilterConfig.java
new file mode 100644
index 00000000000..85d172231e9
--- /dev/null
+++ b/java/ql/test/stubs/servlet-api-2.4/javax/servlet/FilterConfig.java
@@ -0,0 +1,10 @@
+package javax.servlet;
+
+import java.util.Enumeration;
+
+public interface FilterConfig {
+ public String getFilterName();
+ public ServletContext getServletContext();
+ public String getInitParameter(String name);
+ public Enumeration getInitParameterNames();
+}
diff --git a/java/ql/test/stubs/servlet-api-2.4/javax/servlet/http/HttpServletResponse.java b/java/ql/test/stubs/servlet-api-2.4/javax/servlet/http/HttpServletResponse.java
index 2971e023390..162ac0db3cc 100644
--- a/java/ql/test/stubs/servlet-api-2.4/javax/servlet/http/HttpServletResponse.java
+++ b/java/ql/test/stubs/servlet-api-2.4/javax/servlet/http/HttpServletResponse.java
@@ -24,6 +24,7 @@
package javax.servlet.http;
import java.io.IOException;
+import java.util.Collection;
import javax.servlet.ServletResponse;
public interface HttpServletResponse extends ServletResponse {
@@ -44,6 +45,11 @@ public interface HttpServletResponse extends ServletResponse {
public void addIntHeader(String name, int value);
public void setStatus(int sc);
public void setStatus(int sc, String sm);
+ public int getStatus();
+ public String getHeader(String name);
+ public Collection getHeaders(String name);
+ public Collection getHeaderNames();
+
public static final int SC_CONTINUE = 100;
public static final int SC_SWITCHING_PROTOCOLS = 101;
From 81e372d0782d21d30394e21912db6013c88503b8 Mon Sep 17 00:00:00 2001
From: Francis Alexander
Date: Sun, 24 Jan 2021 20:44:21 +0530
Subject: [PATCH 028/725] Formatting changes
---
.../Security/CWE/CWE-346/UnvalidatedCors.java | 10 +++-------
.../Security/CWE/CWE-346/UnvalidatedCors.ql | 2 +-
.../security/CWE-346/UnvalidatedCors.expected | 8 ++++----
.../security/CWE-346/UnvalidatedCors.java | 19 ++-----------------
4 files changed, 10 insertions(+), 29 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.java b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.java
index 8f388b13553..af07cdd1d8d 100644
--- a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.java
+++ b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.java
@@ -12,9 +12,7 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
public class CorsFilter implements Filter {
- public void init(FilterConfig filterConfig) throws ServletException {
- // init
- }
+ public void init(FilterConfig filterConfig) throws ServletException {}
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
@@ -23,7 +21,7 @@ public class CorsFilter implements Filter {
String url = request.getHeader("Origin");
if (!StringUtils.isEmpty(url)) {
- String val = response.getHeader("Access-Control-Allow-Origin");
+ String val = response.getHeader("Access-Control-Allow-Origin"); // BAD -> User controlled CORS header.
if (StringUtils.isEmpty(val)) {
response.addHeader("Access-Control-Allow-Origin", url);
@@ -34,7 +32,5 @@ public class CorsFilter implements Filter {
chain.doFilter(req, res);
}
- public void destroy() {
- // destroy
- }
+ public void destroy() {}
}
diff --git a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql
index 3685ec2209c..058bf535b82 100644
--- a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql
+++ b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql
@@ -28,7 +28,7 @@ predicate checkAccessControlAllowOriginHeader(Expr expr) {
}
class CorsOriginConfig extends TaintTracking::Configuration {
- CorsOriginConfig() { this = "CORSOriginConfig" }
+ CorsOriginConfig() { this = "CorsOriginConfig" }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
diff --git a/java/ql/test/query-tests/security/CWE-346/UnvalidatedCors.expected b/java/ql/test/query-tests/security/CWE-346/UnvalidatedCors.expected
index 8bb9202b582..b7954950deb 100644
--- a/java/ql/test/query-tests/security/CWE-346/UnvalidatedCors.expected
+++ b/java/ql/test/query-tests/security/CWE-346/UnvalidatedCors.expected
@@ -1,7 +1,7 @@
edges
-| UnvalidatedCors.java:34:22:34:48 | getHeader(...) : String | UnvalidatedCors.java:40:67:40:69 | url |
+| UnvalidatedCors.java:21:22:21:48 | getHeader(...) : String | UnvalidatedCors.java:27:67:27:69 | url |
nodes
-| UnvalidatedCors.java:34:22:34:48 | getHeader(...) : String | semmle.label | getHeader(...) : String |
-| UnvalidatedCors.java:40:67:40:69 | url | semmle.label | url |
+| UnvalidatedCors.java:21:22:21:48 | getHeader(...) : String | semmle.label | getHeader(...) : String |
+| UnvalidatedCors.java:27:67:27:69 | url | semmle.label | url |
#select
-| UnvalidatedCors.java:40:67:40:69 | url | UnvalidatedCors.java:34:22:34:48 | getHeader(...) : String | UnvalidatedCors.java:40:67:40:69 | url | Cors header is being set using user controlled value $@. | UnvalidatedCors.java:34:22:34:48 | getHeader(...) | user-provided value |
+| UnvalidatedCors.java:27:67:27:69 | url | UnvalidatedCors.java:21:22:21:48 | getHeader(...) : String | UnvalidatedCors.java:27:67:27:69 | url | Cors header is being set using user controlled value $@. | UnvalidatedCors.java:21:22:21:48 | getHeader(...) | user-provided value |
diff --git a/java/ql/test/query-tests/security/CWE-346/UnvalidatedCors.java b/java/ql/test/query-tests/security/CWE-346/UnvalidatedCors.java
index b05a88ab432..9ec3c8466be 100644
--- a/java/ql/test/query-tests/security/CWE-346/UnvalidatedCors.java
+++ b/java/ql/test/query-tests/security/CWE-346/UnvalidatedCors.java
@@ -1,5 +1,3 @@
-package com.mossle.core.servlet;
-
import java.io.IOException;
import javax.servlet.Filter;
@@ -13,19 +11,8 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
-/**
- *
- *
- *
- */
public class UnvalidatedCors implements Filter {
- public void init(FilterConfig filterConfig) throws ServletException {
- // init
- }
+ public void init(FilterConfig filterConfig) throws ServletException {}
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
@@ -45,8 +32,6 @@ public class UnvalidatedCors implements Filter {
chain.doFilter(req, res);
}
- public void destroy() {
- // destroy
- }
+ public void destroy() {}
}
From 75b79039a1c452de1b78736e8f319537e8fa1d5f Mon Sep 17 00:00:00 2001
From: Francis Alexander
Date: Sun, 24 Jan 2021 20:46:37 +0530
Subject: [PATCH 029/725] Example fixes
---
java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.java b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.java
index af07cdd1d8d..772a64969ea 100644
--- a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.java
+++ b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.java
@@ -21,10 +21,10 @@ public class CorsFilter implements Filter {
String url = request.getHeader("Origin");
if (!StringUtils.isEmpty(url)) {
- String val = response.getHeader("Access-Control-Allow-Origin"); // BAD -> User controlled CORS header.
+ String val = response.getHeader("Access-Control-Allow-Origin");
if (StringUtils.isEmpty(val)) {
- response.addHeader("Access-Control-Allow-Origin", url);
+ response.addHeader("Access-Control-Allow-Origin", url); // BAD -> User controlled CORS header being set here.
response.addHeader("Access-Control-Allow-Credentials", "true");
}
}
From 14a23eed4f09977b3ce217828b7a40581b78374e Mon Sep 17 00:00:00 2001
From: haby0
Date: Mon, 25 Jan 2021 19:15:59 +0800
Subject: [PATCH 030/725] Update
java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll
Co-authored-by: Chris Smowton
---
java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll b/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll
index 808e5a984a5..10235770b2a 100644
--- a/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll
+++ b/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll
@@ -14,8 +14,8 @@ class XQueryParserCall extends MethodAccess {
m.hasName("prepareExpression")
)
}
- // return the first parameter of the `bindString` method and use it as a sink
- Expr getSink() { result = this.getArgument(0) }
+ /** Returns the first parameter of the `bindString` method. */
+ Expr getInput() { result = this.getArgument(0) }
}
/** A call to `XQDynamicContext.bindString`. */
From 16308fe557bafc8079a89ed8435b00e27406d730 Mon Sep 17 00:00:00 2001
From: haby0
Date: Mon, 25 Jan 2021 19:16:18 +0800
Subject: [PATCH 031/725] Update
java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll
Co-authored-by: Chris Smowton
---
java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll b/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll
index 10235770b2a..3d5770b5be6 100644
--- a/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll
+++ b/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll
@@ -29,8 +29,8 @@ class XQueryBindStringCall extends MethodAccess {
m.hasName("bindString")
)
}
- // return the second parameter of the `bindString` method and use it as a sink
- Expr getSink() { result = this.getArgument(1) }
+ /** Returns the second parameter of the `bindString` method. */
+ Expr getInput() { result = this.getArgument(1) }
}
/** Used to determine whether to call the `prepareExpression` method, and the first parameter value can be remotely controlled. */
From d34233b44fd905989191563cd05e303ce3a0eaf6 Mon Sep 17 00:00:00 2001
From: Chris Smowton
Date: Mon, 25 Jan 2021 11:10:00 +0000
Subject: [PATCH 032/725] Rewrite XQuery injection to use an additional taint
step instead of multiple configurations.
Also remove a needless barrier -- the method in question doesn't conduct taint by default, so excluding particular instances of that call is not necessary.
---
.../Security/CWE/CWE-652/XQueryInjection.ql | 26 +++---
.../CWE/CWE-652/XQueryInjectionLib.qll | 87 ++++---------------
2 files changed, 27 insertions(+), 86 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
index b4fcac80a43..7163a1fd93d 100644
--- a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
+++ b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
@@ -15,25 +15,21 @@ import semmle.code.java.dataflow.FlowSources
import XQueryInjectionLib
import DataFlow::PathGraph
-class XQueryInjectionConfig extends DataFlow::Configuration {
+class XQueryInjectionConfig extends TaintTracking::Configuration {
XQueryInjectionConfig() { this = "XQueryInjectionConfig" }
- override predicate isSource(DataFlow::Node source) { source instanceof XQueryInjectionSource }
+ override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
- override predicate isSink(DataFlow::Node sink) { sink instanceof XQueryInjectionSink }
+ override predicate isSink(DataFlow::Node sink) {
+ sink.asExpr() = any(XQueryExecuteCall execute).getPreparedExpression()
+ }
- override predicate isBarrier(DataFlow::Node node) {
- exists(MethodAccess ma, Method m, BindParameterRemoteFlowConf conf, DataFlow::Node node1 |
- m = ma.getMethod()
- |
- node.asExpr() = ma and
- m.hasName("bindString") and
- m.getDeclaringType()
- .getASourceSupertype*()
- .hasQualifiedName("javax.xml.xquery", "XQDynamicContext") and
- ma.getArgument(1) = node1.asExpr() and
- conf.hasFlowTo(node1)
- )
+ /**
+ * Conveys taint from the input to a `prepareExpression` call to the returned prepared expression.
+ */
+ override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
+ exists(XQueryParserCall parser |
+ pred.asExpr() = parser.getInput() and succ.asExpr() = parser)
}
}
diff --git a/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll b/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll
index 3d5770b5be6..cfdbaaa70da 100644
--- a/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll
+++ b/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll
@@ -1,7 +1,4 @@
import java
-import semmle.code.java.dataflow.FlowSources
-import semmle.code.java.dataflow.TaintTracking2
-import DataFlow::PathGraph
/** A call to `XQConnection.prepareExpression`. */
class XQueryParserCall extends MethodAccess {
@@ -14,77 +11,25 @@ class XQueryParserCall extends MethodAccess {
m.hasName("prepareExpression")
)
}
- /** Returns the first parameter of the `bindString` method. */
+
+ /**
+ * Returns the first parameter of the `prepareExpression` method, which provides
+ * the string, stream or reader to be compiled into a prepared expression.
+ */
Expr getInput() { result = this.getArgument(0) }
}
-/** A call to `XQDynamicContext.bindString`. */
-class XQueryBindStringCall extends MethodAccess {
- XQueryBindStringCall() {
- exists(Method m |
- this.getMethod() = m and
- m.getDeclaringType()
- .getASourceSupertype*()
- .hasQualifiedName("javax.xml.xquery", "XQDynamicContext") and
- m.hasName("bindString")
- )
- }
- /** Returns the second parameter of the `bindString` method. */
- Expr getInput() { result = this.getArgument(1) }
-}
-
-/** Used to determine whether to call the `prepareExpression` method, and the first parameter value can be remotely controlled. */
-class ParserParameterRemoteFlowConf extends TaintTracking2::Configuration {
- ParserParameterRemoteFlowConf() { this = "ParserParameterRemoteFlowConf" }
-
- override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
-
- override predicate isSink(DataFlow::Node sink) {
- exists(XQueryParserCall xqpc | xqpc.getSink() = sink.asExpr())
- }
-}
-
-/** Used to determine whether to call the `bindString` method, and the second parameter value can be controlled remotely. */
-class BindParameterRemoteFlowConf extends TaintTracking2::Configuration {
- BindParameterRemoteFlowConf() { this = "BindParameterRemoteFlowConf" }
-
- override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
-
- override predicate isSink(DataFlow::Node sink) {
- exists(XQueryBindStringCall xqbsc | xqbsc.getSink() = sink.asExpr())
- }
-}
-
-/**
- * A data flow source for XQuery injection vulnerability.
- * 1. `prepareExpression` call as sink.
- * 2. Determine whether the `var1` parameter of `prepareExpression` method can be controlled remotely.
- */
-class XQueryInjectionSource extends DataFlow::ExprNode {
- XQueryInjectionSource() {
- exists(MethodAccess ma, Method m, ParserParameterRemoteFlowConf conf, DataFlow::Node node |
- m = ma.getMethod()
- |
- m.hasName("prepareExpression") and
- m.getDeclaringType()
- .getASourceSupertype*()
- .hasQualifiedName("javax.xml.xquery", "XQConnection") and
- asExpr() = ma and
- node.asExpr() = ma.getArgument(0) and
- conf.hasFlowTo(node)
- )
- }
-}
-
-/** A data flow sink for XQuery injection vulnerability. */
-class XQueryInjectionSink extends DataFlow::Node {
- XQueryInjectionSink() {
- exists(MethodAccess ma, Method m | m = ma.getMethod() |
- m.hasName("executeQuery") and
- m.getDeclaringType()
- .getASourceSupertype*()
- .hasQualifiedName("javax.xml.xquery", "XQPreparedExpression") and
- asExpr() = ma.getQualifier()
+/** A call to `XQPreparedExpression.executeQuery`. */
+class XQueryExecuteCall extends MethodAccess {
+ XQueryExecuteCall() {
+ exists(Method m | this.getMethod() = m and
+ m.hasName("executeQuery") and
+ m.getDeclaringType()
+ .getASourceSupertype*()
+ .hasQualifiedName("javax.xml.xquery", "XQPreparedExpression")
)
}
+
+ /** Return this prepared expression. */
+ Expr getPreparedExpression() { result = this.getQualifier() }
}
From 985d3d469aed73369d809befb847140b455a8010 Mon Sep 17 00:00:00 2001
From: Francis Alexander
Date: Mon, 25 Jan 2021 23:26:36 +0530
Subject: [PATCH 033/725] PR feedback integration
---
.../CWE/CWE-346/UnvalidatedCors.qhelp | 17 +++++----
.../Security/CWE/CWE-346/UnvalidatedCors.ql | 38 ++++++++++---------
2 files changed, 29 insertions(+), 26 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.qhelp b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.qhelp
index 3293352c0d5..b10d2a9150a 100644
--- a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.qhelp
+++ b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.qhelp
@@ -7,7 +7,7 @@
A server can send the
- "Access-Control-Allow-Credentials" CORS header to control
+ Access-Control-Allow-Credentials CORS header to control
when a browser may send user credentials in Cross-Origin HTTP
requests.
@@ -15,12 +15,12 @@
When the Access-Control-Allow-Credentials header
- is "true", the Access-Control-Allow-Origin
- header must have a value different from "*" in order to
+ is true, the Access-Control-Allow-Origin
+ header must have a value different from * in order to
make browsers accept the header. Therefore, to allow multiple origins
for Cross-Origin requests with credentials, the server must
dynamically compute the value of the
- "Access-Control-Allow-Origin" header. Computing this
+ Access-Control-Allow-Origin header. Computing this
header value from information in the request to the server can
therefore potentially allow an attacker to control the origins that
the browser sends credentials to.
@@ -35,7 +35,7 @@
When the Access-Control-Allow-Credentials header
- value is "true", a dynamic computation of the
+ value is true, a dynamic computation of the
Access-Control-Allow-Origin header must involve
sanitization if it relies on user-controlled input.
@@ -43,11 +43,12 @@
- Since the "null" origin is easy to obtain for an
- attacker, it is never safe to use "null" as the value of
+ Since the null origin is easy to obtain for an
+ attacker, it is never safe to use null as the value of
the Access-Control-Allow-Origin header when the
Access-Control-Allow-Credentials header value is
- "true".
+ true.This can be done using a sandboxed iframe. A more detailed
+ explanation is available in the portswigger blogpost.
diff --git a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql
index 058bf535b82..17e4cddb87e 100644
--- a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql
+++ b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql
@@ -15,16 +15,17 @@ import semmle.code.java.frameworks.Servlets
import semmle.code.java.dataflow.TaintTracking
import DataFlow::PathGraph
-// Check for Access-Control-Allow-Credentials as well, this ensures fair chances of exploitability.
-predicate satisfyAllowCredentials(MethodAccess header, MethodAccess check) {
+/**
+ * Holds if `header` sets `Access-Control-Allow-Credentials` to `true`. This ensures fair chances of exploitability.
+ */
+private predicate setsAllowCredentials(MethodAccess header) {
header.getArgument(0).(CompileTimeConstantExpr).getStringValue().toLowerCase() =
"access-control-allow-credentials" and
- header.getArgument(1).(CompileTimeConstantExpr).getStringValue() = "true" and
- header.getEnclosingCallable() = check.getEnclosingCallable()
+ header.getArgument(1).(CompileTimeConstantExpr).getStringValue() = "true"
}
-predicate checkAccessControlAllowOriginHeader(Expr expr) {
- expr.(CompileTimeConstantExpr).getStringValue().toLowerCase() = "access-control-allow-origin"
+private Expr getAccessControlAllowOriginHeaderName() {
+ result.(CompileTimeConstantExpr).getStringValue().toLowerCase() = "access-control-allow-origin"
}
class CorsOriginConfig extends TaintTracking::Configuration {
@@ -33,18 +34,19 @@ class CorsOriginConfig extends TaintTracking::Configuration {
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) {
- exists(ResponseSetHeaderMethod h, MethodAccess m |
- m = h.getAReference() and
- checkAccessControlAllowOriginHeader(m.getArgument(0)) and
- satisfyAllowCredentials(h.getAReference(), m) and
- sink.asExpr() = m.getArgument(1)
- )
- or
- exists(ResponseAddHeaderMethod a, MethodAccess m |
- m = a.getAReference() and
- checkAccessControlAllowOriginHeader(m.getArgument(0)) and
- satisfyAllowCredentials(a.getAReference(), m) and
- sink.asExpr() = m.getArgument(1)
+ exists(MethodAccess corsheader, MethodAccess allowcredentialsheader |
+ (
+ corsheader.getMethod() instanceof ResponseSetHeaderMethod or
+ corsheader.getMethod() instanceof ResponseAddHeaderMethod
+ ) and
+ (
+ allowcredentialsheader.getMethod() instanceof ResponseSetHeaderMethod or
+ allowcredentialsheader.getMethod() instanceof ResponseAddHeaderMethod
+ ) and
+ getAccessControlAllowOriginHeaderName() = corsheader.getArgument(0) and
+ setsAllowCredentials(allowcredentialsheader) and
+ corsheader.getEnclosingCallable() = allowcredentialsheader.getEnclosingCallable() and
+ sink.asExpr() = corsheader.getArgument(1)
)
}
}
From 19872e9aedb7892acf2be3c7f693a085c7cc6de6 Mon Sep 17 00:00:00 2001
From: Francis Alexander
Date: Tue, 26 Jan 2021 17:24:17 +0530
Subject: [PATCH 034/725] More Feedback integration
---
java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.qhelp | 2 +-
java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql | 8 ++++----
.../org/apache/commons/lang3/StringUtils.java | 2 +-
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.qhelp b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.qhelp
index b10d2a9150a..d01c27c23ca 100644
--- a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.qhelp
+++ b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.qhelp
@@ -48,7 +48,7 @@
the Access-Control-Allow-Origin header when the
Access-Control-Allow-Credentials header value is
true.This can be done using a sandboxed iframe. A more detailed
- explanation is available in the portswigger blogpost.
+ explanation is available in the portswigger blogpost referenced below.
diff --git a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql
index 17e4cddb87e..3d98237b104 100644
--- a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql
+++ b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql
@@ -19,6 +19,10 @@ import DataFlow::PathGraph
* Holds if `header` sets `Access-Control-Allow-Credentials` to `true`. This ensures fair chances of exploitability.
*/
private predicate setsAllowCredentials(MethodAccess header) {
+ (
+ header.getMethod() instanceof ResponseSetHeaderMethod or
+ header.getMethod() instanceof ResponseAddHeaderMethod
+ ) and
header.getArgument(0).(CompileTimeConstantExpr).getStringValue().toLowerCase() =
"access-control-allow-credentials" and
header.getArgument(1).(CompileTimeConstantExpr).getStringValue() = "true"
@@ -39,10 +43,6 @@ class CorsOriginConfig extends TaintTracking::Configuration {
corsheader.getMethod() instanceof ResponseSetHeaderMethod or
corsheader.getMethod() instanceof ResponseAddHeaderMethod
) and
- (
- allowcredentialsheader.getMethod() instanceof ResponseSetHeaderMethod or
- allowcredentialsheader.getMethod() instanceof ResponseAddHeaderMethod
- ) and
getAccessControlAllowOriginHeaderName() = corsheader.getArgument(0) and
setsAllowCredentials(allowcredentialsheader) and
corsheader.getEnclosingCallable() = allowcredentialsheader.getEnclosingCallable() and
diff --git a/java/ql/test/stubs/apache-commons-lang3-3.7/org/apache/commons/lang3/StringUtils.java b/java/ql/test/stubs/apache-commons-lang3-3.7/org/apache/commons/lang3/StringUtils.java
index 62b13e0e259..06517fa9615 100644
--- a/java/ql/test/stubs/apache-commons-lang3-3.7/org/apache/commons/lang3/StringUtils.java
+++ b/java/ql/test/stubs/apache-commons-lang3-3.7/org/apache/commons/lang3/StringUtils.java
@@ -2,6 +2,6 @@ package org.apache.commons.lang3;
public class StringUtils {
public static boolean isEmpty(final CharSequence cs) {
- return cs == null || cs.length() == 0;
+ return true;
}
}
From b76854a384cb7636e2a396fc399b12213dc2ff69 Mon Sep 17 00:00:00 2001
From: haby0
Date: Wed, 27 Jan 2021 10:14:33 +0800
Subject: [PATCH 035/725] *)add CWE-652 test case
---
.../security/CWE-652/XQueryInjection.expected | 19 ++++
.../security/CWE-652/XQueryInjection.java | 87 +++++++++++++++++++
.../security/CWE-652/XQueryInjection.qlref | 1 +
.../query-tests/security/CWE-652/options | 1 +
.../javax/xml/xquery/XQConnection.java | 19 ++++
.../javax/xml/xquery/XQDataFactory.java | 5 ++
.../javax/xml/xquery/XQDataSource.java | 5 ++
.../javax/xml/xquery/XQDynamicContext.java | 7 ++
.../javax/xml/xquery/XQException.java | 3 +
.../javax/xml/xquery/XQItemAccessor.java | 7 ++
.../javax/xml/xquery/XQItemType.java | 68 +++++++++++++++
.../xml/xquery/XQPreparedExpression.java | 5 ++
.../javax/xml/xquery/XQResultSequence.java | 3 +
.../javax/xml/xquery/XQSequence.java | 5 ++
.../javax/xml/xquery/XQSequenceType.java | 3 +
.../javax/xml/xquery/XQStaticContext.java | 3 +
.../net/sf/saxon/Configuration.java | 6 ++
.../net/sf/saxon/SourceResolver.java | 3 +
.../net/sf/saxon/xqj/Closable.java | 3 +
.../net/sf/saxon/xqj/SaxonXQConnection.java | 41 +++++++++
.../net/sf/saxon/xqj/SaxonXQDataFactory.java | 11 +++
.../net/sf/saxon/xqj/SaxonXQDataSource.java | 12 +++
.../sf/saxon/xqj/SaxonXQStaticContext.java | 5 ++
23 files changed, 322 insertions(+)
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.expected
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.java
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.qlref
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-652/options
create mode 100644 java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQConnection.java
create mode 100644 java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQDataFactory.java
create mode 100644 java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQDataSource.java
create mode 100644 java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQDynamicContext.java
create mode 100644 java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQException.java
create mode 100644 java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQItemAccessor.java
create mode 100644 java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQItemType.java
create mode 100644 java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQPreparedExpression.java
create mode 100644 java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQResultSequence.java
create mode 100644 java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQSequence.java
create mode 100644 java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQSequenceType.java
create mode 100644 java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQStaticContext.java
create mode 100644 java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/Configuration.java
create mode 100644 java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/SourceResolver.java
create mode 100644 java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/Closable.java
create mode 100644 java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQConnection.java
create mode 100644 java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQDataFactory.java
create mode 100644 java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQDataSource.java
create mode 100644 java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQStaticContext.java
diff --git a/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.expected
new file mode 100644
index 00000000000..1ac4289a99c
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.expected
@@ -0,0 +1,19 @@
+edges
+| XQueryInjection.java:26:37:26:65 | prepareExpression(...) : XQPreparedExpression | XQueryInjection.java:27:35:27:38 | xqpe |
+| XQueryInjection.java:41:37:41:65 | prepareExpression(...) : XQPreparedExpression | XQueryInjection.java:42:35:42:38 | xqpe |
+| XQueryInjection.java:53:37:53:64 | prepareExpression(...) : XQPreparedExpression | XQueryInjection.java:54:35:54:38 | xqpe |
+| XQueryInjection.java:66:37:66:62 | prepareExpression(...) : XQPreparedExpression | XQueryInjection.java:67:35:67:38 | xqpe |
+nodes
+| XQueryInjection.java:26:37:26:65 | prepareExpression(...) : XQPreparedExpression | semmle.label | prepareExpression(...) : XQPreparedExpression |
+| XQueryInjection.java:27:35:27:38 | xqpe | semmle.label | xqpe |
+| XQueryInjection.java:41:37:41:65 | prepareExpression(...) : XQPreparedExpression | semmle.label | prepareExpression(...) : XQPreparedExpression |
+| XQueryInjection.java:42:35:42:38 | xqpe | semmle.label | xqpe |
+| XQueryInjection.java:53:37:53:64 | prepareExpression(...) : XQPreparedExpression | semmle.label | prepareExpression(...) : XQPreparedExpression |
+| XQueryInjection.java:54:35:54:38 | xqpe | semmle.label | xqpe |
+| XQueryInjection.java:66:37:66:62 | prepareExpression(...) : XQPreparedExpression | semmle.label | prepareExpression(...) : XQPreparedExpression |
+| XQueryInjection.java:67:35:67:38 | xqpe | semmle.label | xqpe |
+#select
+| XQueryInjection.java:27:35:27:38 | xqpe | XQueryInjection.java:26:37:26:65 | prepareExpression(...) : XQPreparedExpression | XQueryInjection.java:27:35:27:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:26:37:26:65 | prepareExpression(...) | this user input |
+| XQueryInjection.java:42:35:42:38 | xqpe | XQueryInjection.java:41:37:41:65 | prepareExpression(...) : XQPreparedExpression | XQueryInjection.java:42:35:42:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:41:37:41:65 | prepareExpression(...) | this user input |
+| XQueryInjection.java:54:35:54:38 | xqpe | XQueryInjection.java:53:37:53:64 | prepareExpression(...) : XQPreparedExpression | XQueryInjection.java:54:35:54:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:53:37:53:64 | prepareExpression(...) | this user input |
+| XQueryInjection.java:67:35:67:38 | xqpe | XQueryInjection.java:66:37:66:62 | prepareExpression(...) : XQPreparedExpression | XQueryInjection.java:67:35:67:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:66:37:66:62 | prepareExpression(...) | this user input |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.java b/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.java
new file mode 100644
index 00000000000..48846ae1bca
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.java
@@ -0,0 +1,87 @@
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import javax.servlet.http.HttpServletRequest;
+import javax.xml.namespace.QName;
+import javax.xml.xquery.XQConnection;
+import javax.xml.xquery.XQDataSource;
+import javax.xml.xquery.XQException;
+import javax.xml.xquery.XQItemType;
+import javax.xml.xquery.XQPreparedExpression;
+import javax.xml.xquery.XQResultSequence;
+import net.sf.saxon.xqj.SaxonXQDataSource;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+@Controller
+public class XQueryInjection {
+
+ @RequestMapping
+ public void testRequestbad(HttpServletRequest request) throws Exception {
+ String name = request.getParameter("name");
+ XQDataSource ds = new SaxonXQDataSource();
+ XQConnection conn = ds.getConnection();
+ String query = "for $user in doc(\"users.xml\")/Users/User[name='" + name + "'] return $user/password";
+ XQPreparedExpression xqpe = conn.prepareExpression(query);
+ XQResultSequence result = xqpe.executeQuery();
+ while (result.next()){
+ System.out.println(result.getItemAsString(null));
+ }
+
+ }
+
+
+ @RequestMapping
+ public void testStringtbad(@RequestParam String nameStr) throws XQException {
+ String name = nameStr;
+ XQDataSource ds = new SaxonXQDataSource();
+ XQConnection conn = ds.getConnection();
+ String query = "for $user in doc(\"users.xml\")/Users/User[name='" + name + "'] return $user/password";
+ XQPreparedExpression xqpe = conn.prepareExpression(query);
+ XQResultSequence result = xqpe.executeQuery();
+ while (result.next()){
+ System.out.println(result.getItemAsString(null));
+ }
+ }
+
+ @RequestMapping
+ public void testInputStreambad(HttpServletRequest request) throws Exception {
+ InputStream name = request.getInputStream();
+ XQDataSource ds = new SaxonXQDataSource();
+ XQConnection conn = ds.getConnection();
+ XQPreparedExpression xqpe = conn.prepareExpression(name);
+ XQResultSequence result = xqpe.executeQuery();
+ while (result.next()){
+ System.out.println(result.getItemAsString(null));
+ }
+ }
+
+ @RequestMapping
+ public void testReaderbad(HttpServletRequest request) throws Exception {
+ InputStream name = request.getInputStream();
+ BufferedReader br = new BufferedReader(new InputStreamReader(name));
+ XQDataSource ds = new SaxonXQDataSource();
+ XQConnection conn = ds.getConnection();
+ XQPreparedExpression xqpe = conn.prepareExpression(br);
+ XQResultSequence result = xqpe.executeQuery();
+ while (result.next()){
+ System.out.println(result.getItemAsString(null));
+ }
+ }
+
+ @RequestMapping
+ public void good(HttpServletRequest request) throws XQException {
+ String name = request.getParameter("name");
+ XQDataSource ds = new SaxonXQDataSource();
+ XQConnection conn = ds.getConnection();
+ String query = "declare variable $name as xs:string external;"
+ + " for $user in doc(\"users.xml\")/Users/User[name=$name] return $user/password";
+ XQPreparedExpression xqpe = conn.prepareExpression(query);
+ xqpe.bindString(new QName("name"), name, conn.createAtomicType(XQItemType.XQBASETYPE_STRING));
+ XQResultSequence result = xqpe.executeQuery();
+ while (result.next()){
+ System.out.println(result.getItemAsString(null));
+ }
+ }
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.qlref b/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.qlref
new file mode 100644
index 00000000000..9bdeeeffa0f
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.qlref
@@ -0,0 +1 @@
+Security/CWE/CWE-652/XQueryInjection.ql
diff --git a/java/ql/test/experimental/query-tests/security/CWE-652/options b/java/ql/test/experimental/query-tests/security/CWE-652/options
new file mode 100644
index 00000000000..98e36c19267
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-652/options
@@ -0,0 +1 @@
+//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/apache-http-4.4.13/:${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/saxon-xqj-9.x/:${testdir}/../../../../stubs/springframework-5.2.3/
diff --git a/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQConnection.java b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQConnection.java
new file mode 100644
index 00000000000..3cff7592168
--- /dev/null
+++ b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQConnection.java
@@ -0,0 +1,19 @@
+package javax.xml.xquery;
+
+import java.io.InputStream;
+import java.io.Reader;
+
+public interface XQConnection extends XQDataFactory {
+
+ XQPreparedExpression prepareExpression(String var1) throws XQException;
+
+ XQPreparedExpression prepareExpression(String var1, XQStaticContext var2) throws XQException;
+
+ XQPreparedExpression prepareExpression(Reader var1) throws XQException;
+
+ XQPreparedExpression prepareExpression(Reader var1, XQStaticContext var2) throws XQException;
+
+ XQPreparedExpression prepareExpression(InputStream var1) throws XQException;
+
+ XQPreparedExpression prepareExpression(InputStream var1, XQStaticContext var2) throws XQException;
+}
diff --git a/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQDataFactory.java b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQDataFactory.java
new file mode 100644
index 00000000000..6bc2dbade6d
--- /dev/null
+++ b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQDataFactory.java
@@ -0,0 +1,5 @@
+package javax.xml.xquery;
+
+public interface XQDataFactory {
+ XQItemType createAtomicType(int var1) throws XQException;
+}
diff --git a/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQDataSource.java b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQDataSource.java
new file mode 100644
index 00000000000..7f981a1ff00
--- /dev/null
+++ b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQDataSource.java
@@ -0,0 +1,5 @@
+package javax.xml.xquery;
+
+public interface XQDataSource {
+ XQConnection getConnection() throws XQException;
+}
diff --git a/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQDynamicContext.java b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQDynamicContext.java
new file mode 100644
index 00000000000..c5058a2f866
--- /dev/null
+++ b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQDynamicContext.java
@@ -0,0 +1,7 @@
+package javax.xml.xquery;
+
+import javax.xml.namespace.QName;
+
+public interface XQDynamicContext {
+ void bindString(QName var1, String var2, XQItemType var3) throws XQException;
+}
diff --git a/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQException.java b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQException.java
new file mode 100644
index 00000000000..4d81c322327
--- /dev/null
+++ b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQException.java
@@ -0,0 +1,3 @@
+package javax.xml.xquery;
+
+public class XQException extends Exception {}
diff --git a/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQItemAccessor.java b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQItemAccessor.java
new file mode 100644
index 00000000000..b52f5c16461
--- /dev/null
+++ b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQItemAccessor.java
@@ -0,0 +1,7 @@
+package javax.xml.xquery;
+
+import java.util.Properties;
+
+public interface XQItemAccessor {
+ String getItemAsString(Properties var1) throws XQException;
+}
diff --git a/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQItemType.java b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQItemType.java
new file mode 100644
index 00000000000..b6c9cb896b0
--- /dev/null
+++ b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQItemType.java
@@ -0,0 +1,68 @@
+package javax.xml.xquery;
+
+public interface XQItemType extends XQSequenceType {
+ int XQITEMKIND_ATOMIC = 1;
+ int XQITEMKIND_ATTRIBUTE = 2;
+ int XQITEMKIND_COMMENT = 3;
+ int XQITEMKIND_DOCUMENT = 4;
+ int XQITEMKIND_DOCUMENT_ELEMENT = 5;
+ int XQITEMKIND_DOCUMENT_SCHEMA_ELEMENT = 6;
+ int XQITEMKIND_ELEMENT = 7;
+ int XQITEMKIND_ITEM = 8;
+ int XQITEMKIND_NODE = 9;
+ int XQITEMKIND_PI = 10;
+ int XQITEMKIND_TEXT = 11;
+ int XQITEMKIND_SCHEMA_ELEMENT = 12;
+ int XQITEMKIND_SCHEMA_ATTRIBUTE = 13;
+ int XQBASETYPE_UNTYPED = 1;
+ int XQBASETYPE_ANYTYPE = 2;
+ int XQBASETYPE_ANYSIMPLETYPE = 3;
+ int XQBASETYPE_ANYATOMICTYPE = 4;
+ int XQBASETYPE_UNTYPEDATOMIC = 5;
+ int XQBASETYPE_DAYTIMEDURATION = 6;
+ int XQBASETYPE_YEARMONTHDURATION = 7;
+ int XQBASETYPE_ANYURI = 8;
+ int XQBASETYPE_BASE64BINARY = 9;
+ int XQBASETYPE_BOOLEAN = 10;
+ int XQBASETYPE_DATE = 11;
+ int XQBASETYPE_INT = 12;
+ int XQBASETYPE_INTEGER = 13;
+ int XQBASETYPE_SHORT = 14;
+ int XQBASETYPE_LONG = 15;
+ int XQBASETYPE_DATETIME = 16;
+ int XQBASETYPE_DECIMAL = 17;
+ int XQBASETYPE_DOUBLE = 18;
+ int XQBASETYPE_DURATION = 19;
+ int XQBASETYPE_FLOAT = 20;
+ int XQBASETYPE_GDAY = 21;
+ int XQBASETYPE_GMONTH = 22;
+ int XQBASETYPE_GMONTHDAY = 23;
+ int XQBASETYPE_GYEAR = 24;
+ int XQBASETYPE_GYEARMONTH = 25;
+ int XQBASETYPE_HEXBINARY = 26;
+ int XQBASETYPE_NOTATION = 27;
+ int XQBASETYPE_QNAME = 28;
+ int XQBASETYPE_STRING = 29;
+ int XQBASETYPE_TIME = 30;
+ int XQBASETYPE_BYTE = 31;
+ int XQBASETYPE_NONPOSITIVE_INTEGER = 32;
+ int XQBASETYPE_NONNEGATIVE_INTEGER = 33;
+ int XQBASETYPE_NEGATIVE_INTEGER = 34;
+ int XQBASETYPE_POSITIVE_INTEGER = 35;
+ int XQBASETYPE_UNSIGNED_LONG = 36;
+ int XQBASETYPE_UNSIGNED_INT = 37;
+ int XQBASETYPE_UNSIGNED_SHORT = 38;
+ int XQBASETYPE_UNSIGNED_BYTE = 39;
+ int XQBASETYPE_NORMALIZED_STRING = 40;
+ int XQBASETYPE_TOKEN = 41;
+ int XQBASETYPE_LANGUAGE = 42;
+ int XQBASETYPE_NAME = 43;
+ int XQBASETYPE_NCNAME = 44;
+ int XQBASETYPE_NMTOKEN = 45;
+ int XQBASETYPE_ID = 46;
+ int XQBASETYPE_IDREF = 47;
+ int XQBASETYPE_ENTITY = 48;
+ int XQBASETYPE_IDREFS = 49;
+ int XQBASETYPE_ENTITIES = 50;
+ int XQBASETYPE_NMTOKENS = 51;
+}
diff --git a/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQPreparedExpression.java b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQPreparedExpression.java
new file mode 100644
index 00000000000..3ab5024c1e6
--- /dev/null
+++ b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQPreparedExpression.java
@@ -0,0 +1,5 @@
+package javax.xml.xquery;
+
+public interface XQPreparedExpression extends XQDynamicContext {
+ XQResultSequence executeQuery() throws XQException;
+}
diff --git a/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQResultSequence.java b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQResultSequence.java
new file mode 100644
index 00000000000..c7cd24e8561
--- /dev/null
+++ b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQResultSequence.java
@@ -0,0 +1,3 @@
+package javax.xml.xquery;
+
+public interface XQResultSequence extends XQSequence {}
diff --git a/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQSequence.java b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQSequence.java
new file mode 100644
index 00000000000..b872795a759
--- /dev/null
+++ b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQSequence.java
@@ -0,0 +1,5 @@
+package javax.xml.xquery;
+
+public interface XQSequence extends XQItemAccessor {
+ boolean next() throws XQException;
+}
diff --git a/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQSequenceType.java b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQSequenceType.java
new file mode 100644
index 00000000000..17d908f9e1c
--- /dev/null
+++ b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQSequenceType.java
@@ -0,0 +1,3 @@
+package javax.xml.xquery;
+
+public interface XQSequenceType {}
diff --git a/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQStaticContext.java b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQStaticContext.java
new file mode 100644
index 00000000000..60911d4fb72
--- /dev/null
+++ b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQStaticContext.java
@@ -0,0 +1,3 @@
+package javax.xml.xquery;
+
+public interface XQStaticContext {}
diff --git a/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/Configuration.java b/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/Configuration.java
new file mode 100644
index 00000000000..bfd8bf1ff58
--- /dev/null
+++ b/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/Configuration.java
@@ -0,0 +1,6 @@
+package net.sf.saxon;
+
+import java.io.Serializable;
+
+
+public class Configuration implements Serializable, SourceResolver {}
diff --git a/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/SourceResolver.java b/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/SourceResolver.java
new file mode 100644
index 00000000000..aa2e2b2fe4d
--- /dev/null
+++ b/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/SourceResolver.java
@@ -0,0 +1,3 @@
+package net.sf.saxon;
+
+public interface SourceResolver {}
diff --git a/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/Closable.java b/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/Closable.java
new file mode 100644
index 00000000000..91245a31fc9
--- /dev/null
+++ b/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/Closable.java
@@ -0,0 +1,3 @@
+package net.sf.saxon.xqj;
+
+public abstract class Closable {}
diff --git a/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQConnection.java b/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQConnection.java
new file mode 100644
index 00000000000..85bae6e7540
--- /dev/null
+++ b/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQConnection.java
@@ -0,0 +1,41 @@
+package net.sf.saxon.xqj;
+
+import java.io.Reader;
+import net.sf.saxon.Configuration;
+import javax.xml.xquery.XQConnection;
+import javax.xml.xquery.XQPreparedExpression;
+import javax.xml.xquery.XQException;
+import javax.xml.xquery.XQStaticContext;
+import java.io.InputStream;
+
+public class SaxonXQConnection extends SaxonXQDataFactory implements XQConnection {
+
+ private SaxonXQStaticContext staticContext;
+
+ SaxonXQConnection(SaxonXQDataSource dataSource) {
+ }
+
+ public XQPreparedExpression prepareExpression(InputStream xquery) throws XQException {
+ return null;
+ }
+
+ public XQPreparedExpression prepareExpression(InputStream xquery, XQStaticContext properties) throws XQException {
+ return null;
+ }
+
+ public XQPreparedExpression prepareExpression(Reader xquery) throws XQException {
+ return null;
+ }
+
+ public XQPreparedExpression prepareExpression(Reader xquery, XQStaticContext properties){
+ return null;
+ }
+
+ public XQPreparedExpression prepareExpression(String xquery) throws XQException {
+ return null;
+ }
+
+ public XQPreparedExpression prepareExpression(String xquery, XQStaticContext properties) throws XQException {
+ return null;
+ }
+}
diff --git a/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQDataFactory.java b/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQDataFactory.java
new file mode 100644
index 00000000000..2ea1a82541e
--- /dev/null
+++ b/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQDataFactory.java
@@ -0,0 +1,11 @@
+package net.sf.saxon.xqj;
+
+import javax.xml.xquery.XQException;
+import javax.xml.xquery.XQDataFactory;
+import javax.xml.xquery.XQItemType;
+
+public abstract class SaxonXQDataFactory extends Closable implements XQDataFactory {
+ public XQItemType createAtomicType(int baseType) throws XQException {
+ return null;
+ }
+}
diff --git a/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQDataSource.java b/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQDataSource.java
new file mode 100644
index 00000000000..7e2549b6fe8
--- /dev/null
+++ b/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQDataSource.java
@@ -0,0 +1,12 @@
+package net.sf.saxon.xqj;
+
+import javax.xml.xquery.XQDataSource;
+import javax.xml.xquery.XQException;
+import javax.xml.xquery.XQConnection;
+
+public class SaxonXQDataSource implements XQDataSource {
+
+ public XQConnection getConnection() throws XQException {
+ return new SaxonXQConnection(this);
+ }
+}
diff --git a/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQStaticContext.java b/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQStaticContext.java
new file mode 100644
index 00000000000..e5e19b6ae1f
--- /dev/null
+++ b/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQStaticContext.java
@@ -0,0 +1,5 @@
+package net.sf.saxon.xqj;
+
+import javax.xml.xquery.XQStaticContext;
+
+public class SaxonXQStaticContext implements XQStaticContext {}
From b5ae4178515d970dda6ae59931a4ae5b6b2fa2fa Mon Sep 17 00:00:00 2001
From: haby0
Date: Wed, 27 Jan 2021 10:19:04 +0800
Subject: [PATCH 036/725] *)update CWE-652 qhelp references
---
java/ql/src/Security/CWE/CWE-652/XQueryInjection.qhelp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.qhelp b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.qhelp
index 2b6433d6ef3..4e70553557c 100644
--- a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.qhelp
+++ b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.qhelp
@@ -21,8 +21,8 @@ This allows an attacker to control the structure of the query.
-CWE description:
-XQuery Injection.
+Introduction to XQuery Injection:
+Balisage Paper: XQuery Injection.
From ca2e6587fe9d14e1fd5a01970f93c2f630a8b5d2 Mon Sep 17 00:00:00 2001
From: haby0
Date: Wed, 27 Jan 2021 19:46:15 +0800
Subject: [PATCH 037/725] Update
java/ql/src/Security/CWE/CWE-652/XQueryInjection.qhelp
Co-authored-by: Chris Smowton
---
java/ql/src/Security/CWE/CWE-652/XQueryInjection.qhelp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.qhelp b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.qhelp
index 4e70553557c..7dfdc846c44 100644
--- a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.qhelp
+++ b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.qhelp
@@ -21,8 +21,8 @@ This allows an attacker to control the structure of the query.
-Introduction to XQuery Injection:
-Balisage Paper: XQuery Injection.
+Balisage:
+XQuery Injection.
From 31deca016fd545707e1c269efe96941decc9f0dd Mon Sep 17 00:00:00 2001
From: haby0
Date: Wed, 27 Jan 2021 19:46:45 +0800
Subject: [PATCH 038/725] Update
java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
Co-authored-by: Chris Smowton
---
java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
index 7163a1fd93d..bb18e613b94 100644
--- a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
+++ b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
@@ -5,7 +5,7 @@
* @kind path-problem
* @problem.severity error
* @precision high
- * @id java/XQuery-injection
+ * @id java/xquery-injection
* @tags security
* external/cwe/cwe-652
*/
From 81c56b9bed33f2cd0033f8bc1ba8a8d091db2133 Mon Sep 17 00:00:00 2001
From: haby0
Date: Wed, 27 Jan 2021 19:47:12 +0800
Subject: [PATCH 039/725] Update
java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
Co-authored-by: Chris Smowton
---
java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql | 3 +++
1 file changed, 3 insertions(+)
diff --git a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
index bb18e613b94..b1bca496e5c 100644
--- a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
+++ b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
@@ -15,6 +15,9 @@ import semmle.code.java.dataflow.FlowSources
import XQueryInjectionLib
import DataFlow::PathGraph
+/**
+ * Taint-tracking configuration tracing flow from remote sources, through an XQuery parser, to its eventual execution.
+ */
class XQueryInjectionConfig extends TaintTracking::Configuration {
XQueryInjectionConfig() { this = "XQueryInjectionConfig" }
From ff1ed3a012e720da3a04779ee0c177dd2ba7b4c0 Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Fri, 29 Jan 2021 03:39:02 +0000
Subject: [PATCH 040/725] Revamp the query to use three configurations to
detect password hash without salt
---
.../Security/CWE/CWE-759/HashWithoutSalt.ql | 110 ++++++++++-------
.../code/java/dataflow/TaintTracking3.qll | 7 ++
.../tainttracking3/TaintTrackingImpl.qll | 115 ++++++++++++++++++
.../tainttracking3/TaintTrackingParameter.qll | 5 +
4 files changed, 194 insertions(+), 43 deletions(-)
create mode 100644 java/ql/src/semmle/code/java/dataflow/TaintTracking3.qll
create mode 100644 java/ql/src/semmle/code/java/dataflow/internal/tainttracking3/TaintTrackingImpl.qll
create mode 100644 java/ql/src/semmle/code/java/dataflow/internal/tainttracking3/TaintTrackingParameter.qll
diff --git a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
index b7c353de77a..d9fcc22c02b 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
@@ -8,7 +8,10 @@
*/
import java
+import semmle.code.java.dataflow.DataFlow3
import semmle.code.java.dataflow.TaintTracking
+import semmle.code.java.dataflow.TaintTracking2
+import semmle.code.java.dataflow.TaintTracking3
import DataFlow::PathGraph
/** The Java class `java.security.MessageDigest`. */
@@ -16,6 +19,15 @@ class MessageDigest extends RefType {
MessageDigest() { this.hasQualifiedName("java.security", "MessageDigest") }
}
+class MDConstructor extends StaticMethodAccess {
+ MDConstructor() {
+ exists(Method m | m = this.getMethod() |
+ m.getDeclaringType() instanceof MessageDigest and
+ m.hasName("getInstance")
+ )
+ }
+}
+
/** The method `digest()` declared in `java.security.MessageDigest`. */
class MDDigestMethod extends Method {
MDDigestMethod() {
@@ -48,24 +60,20 @@ class PasswordVarExpr extends Expr {
}
/** Taint configuration tracking flow from an expression whose name suggests it holds password data to a method call that generates a hash without a salt. */
-class HashWithoutSaltConfiguration extends TaintTracking::Configuration {
- HashWithoutSaltConfiguration() { this = "HashWithoutSaltConfiguration" }
+class PasswordHashConfiguration extends TaintTracking3::Configuration {
+ PasswordHashConfiguration() { this = "PasswordHashConfiguration" }
- override predicate isSource(DataFlow::Node source) { source.asExpr() instanceof PasswordVarExpr }
+ override predicate isSource(DataFlow3::Node source) { source.asExpr() instanceof PasswordVarExpr }
- override predicate isSink(DataFlow::Node sink) {
+ override predicate isSink(DataFlow3::Node sink) {
exists(
- MethodAccess mua // invoke `md.update(password)` without the call of `md.update(digest)`
+ MethodAccess ma // invoke `md.update(password)` without the call of `md.update(digest)`
|
- sink.asExpr() = mua.getArgument(0) and
- mua.getMethod() instanceof MDUpdateMethod // md.update(password)
- )
- or
- // invoke `md.digest(password)` without another call of `md.update(salt)`
- exists(MethodAccess mda |
- sink.asExpr() = mda.getArgument(0) and
- mda.getMethod() instanceof MDDigestMethod and // md.digest(password)
- mda.getNumArgument() = 1
+ sink.asExpr() = ma.getArgument(0) and
+ (
+ ma.getMethod() instanceof MDUpdateMethod or // md.update(password)
+ ma.getMethod() instanceof MDDigestMethod // md.digest(password)
+ )
)
}
@@ -76,7 +84,7 @@ class HashWithoutSaltConfiguration extends TaintTracking::Configuration {
* `byte[] messageDigest = md.digest(allBytes);`
* Or the password is concatenated with a salt as a string.
*/
- override predicate isSanitizer(DataFlow::Node node) {
+ override predicate isSanitizer(DataFlow3::Node node) {
exists(MethodAccess ma |
ma.getMethod().getDeclaringType().hasQualifiedName("java.lang", "System") and
ma.getMethod().hasName("arraycopy") and
@@ -84,40 +92,56 @@ class HashWithoutSaltConfiguration extends TaintTracking::Configuration {
) // System.arraycopy(password.getBytes(), ...)
or
exists(AddExpr e | node.asExpr() = e.getAnOperand()) // password+salt
- or
- exists(MethodAccess mua, MethodAccess ma |
- ma.getArgument(0) = node.asExpr() and // Detect wrapper methods that invoke `md.update(salt)`
- ma != mua and
+ }
+}
+
+class PasswordDigestConfiguration extends TaintTracking2::Configuration {
+ PasswordDigestConfiguration() { this = "PasswordDigestConfiguration" }
+
+ override predicate isSource(DataFlow2::Node source) {
+ exists(MDConstructor mc | source.asExpr() = mc)
+ }
+
+ override predicate isSink(DataFlow2::Node sink) {
+ exists(MethodAccess ma |
(
- ma.getQualifier().getType() instanceof Interface
- or
- mua.getQualifier().(VarAccess).getVariable().getAnAccess() = ma.getQualifier()
- or
- mua.getAnArgument().(VarAccess).getVariable().getAnAccess() = ma.getQualifier()
- or
- mua.getQualifier().(VarAccess).getVariable().getAnAccess() = ma.getAnArgument()
- or
- mua.getArgument(0).(VarAccess).getVariable().getAnAccess() = ma.getAnArgument()
+ ma.getMethod() instanceof MDUpdateMethod or
+ ma.getMethod() instanceof MDDigestMethod
) and
- isMDUpdateCall(mua.getMethod())
+ exists(PasswordHashConfiguration cc | cc.hasFlowToExpr(ma.getAnArgument())) and
+ sink.asExpr() = ma.getQualifier()
)
}
}
-/** Holds if a method invokes `md.update(salt)`. */
-predicate isMDUpdateCall(Callable caller) {
- caller instanceof MDUpdateMethod
- or
- exists(Callable callee |
- caller.polyCalls(callee) and
- (
- callee instanceof MDUpdateMethod or
- isMDUpdateCall(callee)
+class HashWithoutSaltConfiguration extends TaintTracking::Configuration {
+ HashWithoutSaltConfiguration() { this = "HashWithoutSaltConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) {
+ exists(PasswordDigestConfiguration pc | pc.hasFlow(source, _))
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(MethodAccess ma |
+ ma.getMethod() instanceof MDDigestMethod and // md.digest(password)
+ sink.asExpr() = ma.getQualifier()
)
- )
+ }
+
+ /** Holds if `md.update` or `md.digest` calls integrate something other than the password, perhaps a salt. */
+ override predicate isSanitizer(DataFlow::Node node) {
+ exists(MethodAccess ma |
+ (
+ ma.getMethod() instanceof MDUpdateMethod
+ or
+ ma.getMethod() instanceof MDDigestMethod and ma.getNumArgument() != 0
+ ) and
+ node.asExpr() = ma.getQualifier() and
+ not exists(PasswordHashConfiguration cc | cc.hasFlowToExpr(ma.getAnArgument()))
+ )
+ }
}
-from DataFlow::PathNode source, DataFlow::PathNode sink, HashWithoutSaltConfiguration c
-where c.hasFlowPath(source, sink)
-select sink.getNode(), source, sink, "$@ is hashed without a salt.", source.getNode(),
- "The password"
+from DataFlow::PathNode source, DataFlow::PathNode sink, HashWithoutSaltConfiguration cc
+where cc.hasFlowPath(source, sink)
+select sink, source, sink, "$@ is hashed without a salt.", source, "The password"
diff --git a/java/ql/src/semmle/code/java/dataflow/TaintTracking3.qll b/java/ql/src/semmle/code/java/dataflow/TaintTracking3.qll
new file mode 100644
index 00000000000..49c43ed1418
--- /dev/null
+++ b/java/ql/src/semmle/code/java/dataflow/TaintTracking3.qll
@@ -0,0 +1,7 @@
+/**
+ * Provides classes for performing local (intra-procedural) and
+ * global (inter-procedural) taint-tracking analyses.
+ */
+module TaintTracking3 {
+ import semmle.code.java.dataflow.internal.tainttracking3.TaintTrackingImpl
+}
diff --git a/java/ql/src/semmle/code/java/dataflow/internal/tainttracking3/TaintTrackingImpl.qll b/java/ql/src/semmle/code/java/dataflow/internal/tainttracking3/TaintTrackingImpl.qll
new file mode 100644
index 00000000000..b509fad9cd2
--- /dev/null
+++ b/java/ql/src/semmle/code/java/dataflow/internal/tainttracking3/TaintTrackingImpl.qll
@@ -0,0 +1,115 @@
+/**
+ * Provides an implementation of global (interprocedural) taint tracking.
+ * This file re-exports the local (intraprocedural) taint-tracking analysis
+ * from `TaintTrackingParameter::Public` and adds a global analysis, mainly
+ * exposed through the `Configuration` class. For some languages, this file
+ * exists in several identical copies, allowing queries to use multiple
+ * `Configuration` classes that depend on each other without introducing
+ * mutual recursion among those configurations.
+ */
+
+import TaintTrackingParameter::Public
+private import TaintTrackingParameter::Private
+
+/**
+ * A configuration of interprocedural taint tracking analysis. This defines
+ * sources, sinks, and any other configurable aspect of the analysis. Each
+ * use of the taint tracking library must define its own unique extension of
+ * this abstract class.
+ *
+ * A taint-tracking configuration is a special data flow configuration
+ * (`DataFlow::Configuration`) that allows for flow through nodes that do not
+ * necessarily preserve values but are still relevant from a taint tracking
+ * perspective. (For example, string concatenation, where one of the operands
+ * is tainted.)
+ *
+ * To create a configuration, extend this class with a subclass whose
+ * characteristic predicate is a unique singleton string. For example, write
+ *
+ * ```ql
+ * class MyAnalysisConfiguration extends TaintTracking::Configuration {
+ * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
+ * // Override `isSource` and `isSink`.
+ * // Optionally override `isSanitizer`.
+ * // Optionally override `isSanitizerIn`.
+ * // Optionally override `isSanitizerOut`.
+ * // Optionally override `isSanitizerGuard`.
+ * // Optionally override `isAdditionalTaintStep`.
+ * }
+ * ```
+ *
+ * Then, to query whether there is flow between some `source` and `sink`,
+ * write
+ *
+ * ```ql
+ * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
+ * ```
+ *
+ * Multiple configurations can coexist, but it is unsupported to depend on
+ * another `TaintTracking::Configuration` or a `DataFlow::Configuration` in the
+ * overridden predicates that define sources, sinks, or additional steps.
+ * Instead, the dependency should go to a `TaintTracking2::Configuration` or a
+ * `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
+ */
+abstract class Configuration extends DataFlow::Configuration {
+ bindingset[this]
+ Configuration() { any() }
+
+ /**
+ * Holds if `source` is a relevant taint source.
+ *
+ * The smaller this predicate is, the faster `hasFlow()` will converge.
+ */
+ // overridden to provide taint-tracking specific qldoc
+ abstract override predicate isSource(DataFlow::Node source);
+
+ /**
+ * Holds if `sink` is a relevant taint sink.
+ *
+ * The smaller this predicate is, the faster `hasFlow()` will converge.
+ */
+ // overridden to provide taint-tracking specific qldoc
+ abstract override predicate isSink(DataFlow::Node sink);
+
+ /** Holds if the node `node` is a taint sanitizer. */
+ predicate isSanitizer(DataFlow::Node node) { none() }
+
+ final override predicate isBarrier(DataFlow::Node node) {
+ isSanitizer(node) or
+ defaultTaintSanitizer(node)
+ }
+
+ /** Holds if taint propagation into `node` is prohibited. */
+ predicate isSanitizerIn(DataFlow::Node node) { none() }
+
+ final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
+
+ /** Holds if taint propagation out of `node` is prohibited. */
+ predicate isSanitizerOut(DataFlow::Node node) { none() }
+
+ final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
+
+ /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
+ predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
+
+ final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
+
+ /**
+ * Holds if the additional taint propagation step from `node1` to `node2`
+ * must be taken into account in the analysis.
+ */
+ predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
+
+ final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
+ isAdditionalTaintStep(node1, node2) or
+ defaultAdditionalTaintStep(node1, node2)
+ }
+
+ /**
+ * Holds if taint may flow from `source` to `sink` for this configuration.
+ */
+ // overridden to provide taint-tracking specific qldoc
+ override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
+ super.hasFlow(source, sink)
+ }
+}
diff --git a/java/ql/src/semmle/code/java/dataflow/internal/tainttracking3/TaintTrackingParameter.qll b/java/ql/src/semmle/code/java/dataflow/internal/tainttracking3/TaintTrackingParameter.qll
new file mode 100644
index 00000000000..10fb6b09fa8
--- /dev/null
+++ b/java/ql/src/semmle/code/java/dataflow/internal/tainttracking3/TaintTrackingParameter.qll
@@ -0,0 +1,5 @@
+import semmle.code.java.dataflow.internal.TaintTrackingUtil as Public
+
+module Private {
+ import semmle.code.java.dataflow.DataFlow3::DataFlow3 as DataFlow
+}
From 97690b4eb7e66dac034d1482e3f7b4da50166357 Mon Sep 17 00:00:00 2001
From: haby0
Date: Mon, 8 Feb 2021 19:15:28 +0800
Subject: [PATCH 041/725] Update
java/ql/src/Security/CWE/CWE-652/XQueryInjection.qhelp
Co-authored-by: Felicity Chapman
---
java/ql/src/Security/CWE/CWE-652/XQueryInjection.qhelp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.qhelp b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.qhelp
index 7dfdc846c44..1ca6e780627 100644
--- a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.qhelp
+++ b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.qhelp
@@ -3,8 +3,8 @@
"qhelp.dtd">
-The software uses external input to dynamically construct an XQuery expression used to retrieve data from an XML database, but it does not neutralize or incorrectly neutralizes that input.
-This allows an attacker to control the structure of the query.
+The software uses external input to dynamically construct an XQuery expression which is then used to retrieve data from an XML database.
+However, the input is not neutralized, or is incorrectly neutralized, which allows an attacker to control the structure of the query.
From a6a0fa28c4d8ecddf1c058276c1d448d52722824 Mon Sep 17 00:00:00 2001
From: haby0
Date: Thu, 11 Feb 2021 16:05:48 +0800
Subject: [PATCH 042/725] *)add XQExpression.executeQuery(0) sink
---
.../Security/CWE/CWE-652/XQueryInjection.java | 26 ++++++
.../Security/CWE/CWE-652/XQueryInjection.ql | 6 +-
.../CWE/CWE-652/XQueryInjectionLib.qll | 31 +++++--
.../security/CWE-652/XQueryInjection.expected | 48 ++++++----
.../security/CWE-652/XQueryInjection.java | 89 ++++++++++++++++++-
.../javax/xml/xquery/XQConnection.java | 2 +
.../javax/xml/xquery/XQExpression.java | 25 ++++++
.../net/sf/saxon/xqj/SaxonXQConnection.java | 4 +
8 files changed, 202 insertions(+), 29 deletions(-)
create mode 100644 java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQExpression.java
diff --git a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.java b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.java
index a60adb37db2..f00df84968c 100644
--- a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.java
+++ b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.java
@@ -20,6 +20,18 @@ public void bad(HttpServletRequest request) throws XQException {
}
}
+public void bad1(HttpServletRequest request) throws XQException {
+ String name = request.getParameter("name");
+ XQDataSource xqds = new SaxonXQDataSource();
+ String query = "for $user in doc(\"users.xml\")/Users/User[name='" + name + "'] return $user/password";
+ XQConnection conn = xqds.getConnection();
+ XQExpression expr = conn.createExpression();
+ XQResultSequence result = expr.executeQuery(query);
+ while (result.next()){
+ System.out.println(result.getItemAsString(null));
+ }
+}
+
public void good(HttpServletRequest request) throws XQException {
String name = request.getParameter("name");
XQDataSource ds = new SaxonXQDataSource();
@@ -32,4 +44,18 @@ public void good(HttpServletRequest request) throws XQException {
while (result.next()){
System.out.println(result.getItemAsString(null));
}
+}
+
+public void good1(HttpServletRequest request) throws XQException {
+ String name = request.getParameter("name");
+ String query = "declare variable $name as xs:string external;"
+ + " for $user in doc(\"users.xml\")/Users/User[name=$name] return $user/password";
+ XQDataSource xqds = new SaxonXQDataSource();
+ XQConnection conn = xqds.getConnection();
+ XQExpression expr = conn.createExpression();
+ expr.bindString(new QName("name"), name, conn.createAtomicType(XQItemType.XQBASETYPE_STRING));
+ XQResultSequence result = expr.executeQuery(query);
+ while (result.next()){
+ System.out.println(result.getItemAsString(null));
+ }
}
\ No newline at end of file
diff --git a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
index b1bca496e5c..796df6b68da 100644
--- a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
+++ b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
@@ -24,15 +24,15 @@ class XQueryInjectionConfig extends TaintTracking::Configuration {
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) {
- sink.asExpr() = any(XQueryExecuteCall execute).getPreparedExpression()
+ sink.asExpr() = any(XQueryPreparedExecuteCall xpec).getPreparedExpression() or
+ sink.asExpr() = any(XQueryExecuteCall xec).getExecuteQueryArgument()
}
/**
* Conveys taint from the input to a `prepareExpression` call to the returned prepared expression.
*/
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
- exists(XQueryParserCall parser |
- pred.asExpr() = parser.getInput() and succ.asExpr() = parser)
+ exists(XQueryParserCall parser | pred.asExpr() = parser.getInput() and succ.asExpr() = parser)
}
}
diff --git a/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll b/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll
index cfdbaaa70da..756caf2cd75 100644
--- a/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll
+++ b/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll
@@ -20,16 +20,33 @@ class XQueryParserCall extends MethodAccess {
}
/** A call to `XQPreparedExpression.executeQuery`. */
-class XQueryExecuteCall extends MethodAccess {
- XQueryExecuteCall() {
- exists(Method m | this.getMethod() = m and
- m.hasName("executeQuery") and
- m.getDeclaringType()
- .getASourceSupertype*()
- .hasQualifiedName("javax.xml.xquery", "XQPreparedExpression")
+class XQueryPreparedExecuteCall extends MethodAccess {
+ XQueryPreparedExecuteCall() {
+ exists(Method m |
+ this.getMethod() = m and
+ m.hasName("executeQuery") and
+ m.getDeclaringType()
+ .getASourceSupertype*()
+ .hasQualifiedName("javax.xml.xquery", "XQPreparedExpression")
)
}
/** Return this prepared expression. */
Expr getPreparedExpression() { result = this.getQualifier() }
}
+
+/** A call to `XQExpression.executeQuery`. */
+class XQueryExecuteCall extends MethodAccess {
+ XQueryExecuteCall() {
+ exists(Method m |
+ this.getMethod() = m and
+ m.hasName("executeQuery") and
+ m.getDeclaringType()
+ .getASourceSupertype*()
+ .hasQualifiedName("javax.xml.xquery", "XQExpression")
+ )
+ }
+
+ /** Return this execute query argument. */
+ Expr getExecuteQueryArgument() { result = this.getArgument(0) }
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.expected
index 1ac4289a99c..e2b04e1e020 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.expected
@@ -1,19 +1,35 @@
edges
-| XQueryInjection.java:26:37:26:65 | prepareExpression(...) : XQPreparedExpression | XQueryInjection.java:27:35:27:38 | xqpe |
-| XQueryInjection.java:41:37:41:65 | prepareExpression(...) : XQPreparedExpression | XQueryInjection.java:42:35:42:38 | xqpe |
-| XQueryInjection.java:53:37:53:64 | prepareExpression(...) : XQPreparedExpression | XQueryInjection.java:54:35:54:38 | xqpe |
-| XQueryInjection.java:66:37:66:62 | prepareExpression(...) : XQPreparedExpression | XQueryInjection.java:67:35:67:38 | xqpe |
+| XQueryInjection.java:42:23:42:50 | getParameter(...) : String | XQueryInjection.java:47:35:47:38 | xqpe |
+| XQueryInjection.java:55:23:55:50 | getParameter(...) : String | XQueryInjection.java:60:53:60:57 | query |
+| XQueryInjection.java:68:32:68:59 | nameStr : String | XQueryInjection.java:73:35:73:38 | xqpe |
+| XQueryInjection.java:80:33:80:60 | nameStr : String | XQueryInjection.java:85:53:85:57 | query |
+| XQueryInjection.java:93:28:93:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:97:35:97:38 | xqpe |
+| XQueryInjection.java:105:28:105:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:109:53:109:56 | name |
+| XQueryInjection.java:117:28:117:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:122:35:122:38 | xqpe |
+| XQueryInjection.java:130:28:130:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:135:53:135:54 | br |
nodes
-| XQueryInjection.java:26:37:26:65 | prepareExpression(...) : XQPreparedExpression | semmle.label | prepareExpression(...) : XQPreparedExpression |
-| XQueryInjection.java:27:35:27:38 | xqpe | semmle.label | xqpe |
-| XQueryInjection.java:41:37:41:65 | prepareExpression(...) : XQPreparedExpression | semmle.label | prepareExpression(...) : XQPreparedExpression |
-| XQueryInjection.java:42:35:42:38 | xqpe | semmle.label | xqpe |
-| XQueryInjection.java:53:37:53:64 | prepareExpression(...) : XQPreparedExpression | semmle.label | prepareExpression(...) : XQPreparedExpression |
-| XQueryInjection.java:54:35:54:38 | xqpe | semmle.label | xqpe |
-| XQueryInjection.java:66:37:66:62 | prepareExpression(...) : XQPreparedExpression | semmle.label | prepareExpression(...) : XQPreparedExpression |
-| XQueryInjection.java:67:35:67:38 | xqpe | semmle.label | xqpe |
+| XQueryInjection.java:42:23:42:50 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| XQueryInjection.java:47:35:47:38 | xqpe | semmle.label | xqpe |
+| XQueryInjection.java:55:23:55:50 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| XQueryInjection.java:60:53:60:57 | query | semmle.label | query |
+| XQueryInjection.java:68:32:68:59 | nameStr : String | semmle.label | nameStr : String |
+| XQueryInjection.java:73:35:73:38 | xqpe | semmle.label | xqpe |
+| XQueryInjection.java:80:33:80:60 | nameStr : String | semmle.label | nameStr : String |
+| XQueryInjection.java:85:53:85:57 | query | semmle.label | query |
+| XQueryInjection.java:93:28:93:51 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
+| XQueryInjection.java:97:35:97:38 | xqpe | semmle.label | xqpe |
+| XQueryInjection.java:105:28:105:51 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
+| XQueryInjection.java:109:53:109:56 | name | semmle.label | name |
+| XQueryInjection.java:117:28:117:51 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
+| XQueryInjection.java:122:35:122:38 | xqpe | semmle.label | xqpe |
+| XQueryInjection.java:130:28:130:51 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
+| XQueryInjection.java:135:53:135:54 | br | semmle.label | br |
#select
-| XQueryInjection.java:27:35:27:38 | xqpe | XQueryInjection.java:26:37:26:65 | prepareExpression(...) : XQPreparedExpression | XQueryInjection.java:27:35:27:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:26:37:26:65 | prepareExpression(...) | this user input |
-| XQueryInjection.java:42:35:42:38 | xqpe | XQueryInjection.java:41:37:41:65 | prepareExpression(...) : XQPreparedExpression | XQueryInjection.java:42:35:42:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:41:37:41:65 | prepareExpression(...) | this user input |
-| XQueryInjection.java:54:35:54:38 | xqpe | XQueryInjection.java:53:37:53:64 | prepareExpression(...) : XQPreparedExpression | XQueryInjection.java:54:35:54:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:53:37:53:64 | prepareExpression(...) | this user input |
-| XQueryInjection.java:67:35:67:38 | xqpe | XQueryInjection.java:66:37:66:62 | prepareExpression(...) : XQPreparedExpression | XQueryInjection.java:67:35:67:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:66:37:66:62 | prepareExpression(...) | this user input |
+| XQueryInjection.java:47:35:47:38 | xqpe | XQueryInjection.java:42:23:42:50 | getParameter(...) : String | XQueryInjection.java:47:35:47:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:42:23:42:50 | getParameter(...) | this user input |
+| XQueryInjection.java:60:53:60:57 | query | XQueryInjection.java:55:23:55:50 | getParameter(...) : String | XQueryInjection.java:60:53:60:57 | query | XQuery query might include code from $@. | XQueryInjection.java:55:23:55:50 | getParameter(...) | this user input |
+| XQueryInjection.java:73:35:73:38 | xqpe | XQueryInjection.java:68:32:68:59 | nameStr : String | XQueryInjection.java:73:35:73:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:68:32:68:59 | nameStr | this user input |
+| XQueryInjection.java:85:53:85:57 | query | XQueryInjection.java:80:33:80:60 | nameStr : String | XQueryInjection.java:85:53:85:57 | query | XQuery query might include code from $@. | XQueryInjection.java:80:33:80:60 | nameStr | this user input |
+| XQueryInjection.java:97:35:97:38 | xqpe | XQueryInjection.java:93:28:93:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:97:35:97:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:93:28:93:51 | getInputStream(...) | this user input |
+| XQueryInjection.java:109:53:109:56 | name | XQueryInjection.java:105:28:105:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:109:53:109:56 | name | XQuery query might include code from $@. | XQueryInjection.java:105:28:105:51 | getInputStream(...) | this user input |
+| XQueryInjection.java:122:35:122:38 | xqpe | XQueryInjection.java:117:28:117:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:122:35:122:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:117:28:117:51 | getInputStream(...) | this user input |
+| XQueryInjection.java:135:53:135:54 | br | XQueryInjection.java:130:28:130:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:135:53:135:54 | br | XQuery query might include code from $@. | XQueryInjection.java:130:28:130:51 | getInputStream(...) | this user input |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.java b/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.java
index 48846ae1bca..36f829d81bd 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.java
@@ -6,6 +6,7 @@ import javax.xml.namespace.QName;
import javax.xml.xquery.XQConnection;
import javax.xml.xquery.XQDataSource;
import javax.xml.xquery.XQException;
+import javax.xml.xquery.XQExpression;
import javax.xml.xquery.XQItemType;
import javax.xml.xquery.XQPreparedExpression;
import javax.xml.xquery.XQResultSequence;
@@ -17,6 +18,25 @@ import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class XQueryInjection {
+ public static void main(String[] args) throws Exception {
+ XQDataSource xqds = new SaxonXQDataSource();
+ XQConnection conn;
+ try {
+ String name = "admin";
+ String query = "declare variable $name as xs:string external;"
+ + " for $user in doc(\"users.xml\")/Users/User[name=$name] return $user/password";
+ conn = xqds.getConnection();
+ XQExpression expr = conn.createExpression();
+ expr.bindString(new QName("name"), name, conn.createAtomicType(XQItemType.XQBASETYPE_STRING));
+ XQResultSequence result = expr.executeQuery(query);
+ while (result.next()){
+ System.out.println(result.getItemAsString(null));
+ }
+ } catch (XQException e) {
+ e.printStackTrace();
+ }
+ }
+
@RequestMapping
public void testRequestbad(HttpServletRequest request) throws Exception {
String name = request.getParameter("name");
@@ -28,16 +48,27 @@ public class XQueryInjection {
while (result.next()){
System.out.println(result.getItemAsString(null));
}
+ }
+ @RequestMapping
+ public void testRequestbad1(HttpServletRequest request) throws Exception {
+ String name = request.getParameter("name");
+ XQDataSource xqds = new SaxonXQDataSource();
+ String query = "for $user in doc(\"users.xml\")/Users/User[name='" + name + "'] return $user/password";
+ XQConnection conn = xqds.getConnection();
+ XQExpression expr = conn.createExpression();
+ XQResultSequence result = expr.executeQuery(query);
+ while (result.next()){
+ System.out.println(result.getItemAsString(null));
+ }
}
@RequestMapping
public void testStringtbad(@RequestParam String nameStr) throws XQException {
- String name = nameStr;
XQDataSource ds = new SaxonXQDataSource();
XQConnection conn = ds.getConnection();
- String query = "for $user in doc(\"users.xml\")/Users/User[name='" + name + "'] return $user/password";
+ String query = "for $user in doc(\"users.xml\")/Users/User[name='" + nameStr + "'] return $user/password";
XQPreparedExpression xqpe = conn.prepareExpression(query);
XQResultSequence result = xqpe.executeQuery();
while (result.next()){
@@ -45,6 +76,18 @@ public class XQueryInjection {
}
}
+ @RequestMapping
+ public void testStringtbad1(@RequestParam String nameStr) throws XQException {
+ XQDataSource xqds = new SaxonXQDataSource();
+ String query = "for $user in doc(\"users.xml\")/Users/User[name='" + nameStr + "'] return $user/password";
+ XQConnection conn = xqds.getConnection();
+ XQExpression expr = conn.createExpression();
+ XQResultSequence result = expr.executeQuery(query);
+ while (result.next()){
+ System.out.println(result.getItemAsString(null));
+ }
+ }
+
@RequestMapping
public void testInputStreambad(HttpServletRequest request) throws Exception {
InputStream name = request.getInputStream();
@@ -57,6 +100,18 @@ public class XQueryInjection {
}
}
+ @RequestMapping
+ public void testInputStreambad1(HttpServletRequest request) throws Exception {
+ InputStream name = request.getInputStream();
+ XQDataSource xqds = new SaxonXQDataSource();
+ XQConnection conn = xqds.getConnection();
+ XQExpression expr = conn.createExpression();
+ XQResultSequence result = expr.executeQuery(name);
+ while (result.next()){
+ System.out.println(result.getItemAsString(null));
+ }
+ }
+
@RequestMapping
public void testReaderbad(HttpServletRequest request) throws Exception {
InputStream name = request.getInputStream();
@@ -70,6 +125,19 @@ public class XQueryInjection {
}
}
+ @RequestMapping
+ public void testReaderbad1(HttpServletRequest request) throws Exception {
+ InputStream name = request.getInputStream();
+ BufferedReader br = new BufferedReader(new InputStreamReader(name));
+ XQDataSource xqds = new SaxonXQDataSource();
+ XQConnection conn = xqds.getConnection();
+ XQExpression expr = conn.createExpression();
+ XQResultSequence result = expr.executeQuery(br);
+ while (result.next()){
+ System.out.println(result.getItemAsString(null));
+ }
+ }
+
@RequestMapping
public void good(HttpServletRequest request) throws XQException {
String name = request.getParameter("name");
@@ -84,4 +152,19 @@ public class XQueryInjection {
System.out.println(result.getItemAsString(null));
}
}
-}
+
+ @RequestMapping
+ public void good1(HttpServletRequest request) throws XQException {
+ String name = request.getParameter("name");
+ String query = "declare variable $name as xs:string external;"
+ + " for $user in doc(\"users.xml\")/Users/User[name=$name] return $user/password";
+ XQDataSource xqds = new SaxonXQDataSource();
+ XQConnection conn = xqds.getConnection();
+ XQExpression expr = conn.createExpression();
+ expr.bindString(new QName("name"), name, conn.createAtomicType(XQItemType.XQBASETYPE_STRING));
+ XQResultSequence result = expr.executeQuery(query);
+ while (result.next()){
+ System.out.println(result.getItemAsString(null));
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQConnection.java b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQConnection.java
index 3cff7592168..dce756e9646 100644
--- a/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQConnection.java
+++ b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQConnection.java
@@ -4,6 +4,8 @@ import java.io.InputStream;
import java.io.Reader;
public interface XQConnection extends XQDataFactory {
+
+ XQExpression createExpression() throws XQException;
XQPreparedExpression prepareExpression(String var1) throws XQException;
diff --git a/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQExpression.java b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQExpression.java
new file mode 100644
index 00000000000..5f2e9235bc5
--- /dev/null
+++ b/java/ql/test/stubs/saxon-xqj-9.x/javax/xml/xquery/XQExpression.java
@@ -0,0 +1,25 @@
+package javax.xml.xquery;
+
+import java.io.InputStream;
+import java.io.Reader;
+
+public interface XQExpression extends XQDynamicContext {
+
+ void cancel() throws XQException;
+
+ boolean isClosed();
+
+ void close() throws XQException;
+
+ void executeCommand(String var1) throws XQException;
+
+ void executeCommand(Reader var1) throws XQException;
+
+ XQResultSequence executeQuery(String var1) throws XQException;
+
+ XQResultSequence executeQuery(Reader var1) throws XQException;
+
+ XQResultSequence executeQuery(InputStream var1) throws XQException;
+
+ XQStaticContext getStaticContext() throws XQException;
+}
\ No newline at end of file
diff --git a/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQConnection.java b/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQConnection.java
index 85bae6e7540..94bd38fa175 100644
--- a/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQConnection.java
+++ b/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQConnection.java
@@ -15,6 +15,10 @@ public class SaxonXQConnection extends SaxonXQDataFactory implements XQConnecti
SaxonXQConnection(SaxonXQDataSource dataSource) {
}
+ public XQExpression createExpression() throws XQException {
+ return null;
+ }
+
public XQPreparedExpression prepareExpression(InputStream xquery) throws XQException {
return null;
}
From dbb3d458f5488e3d5e71098325f0bfa933bd3d03 Mon Sep 17 00:00:00 2001
From: haby0
Date: Fri, 12 Feb 2021 10:47:41 +0800
Subject: [PATCH 043/725] *)add XQExpression.executeCommand(0) sink
---
.../Security/CWE/CWE-652/XQueryInjection.java | 9 +++
.../Security/CWE/CWE-652/XQueryInjection.ql | 3 +-
.../CWE/CWE-652/XQueryInjectionLib.qll | 16 +++++
.../security/CWE-652/XQueryInjection.expected | 72 ++++++++++---------
.../security/CWE-652/XQueryInjection.java | 63 +++++++++++-----
.../net/sf/saxon/xqj/SaxonXQConnection.java | 1 +
6 files changed, 112 insertions(+), 52 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.java b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.java
index f00df84968c..1b6be030249 100644
--- a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.java
+++ b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.java
@@ -32,6 +32,15 @@ public void bad1(HttpServletRequest request) throws XQException {
}
}
+public void bad2(HttpServletRequest request) throws XQException {
+ String name = request.getParameter("name");
+ XQDataSource xqds = new SaxonXQDataSource();
+ XQConnection conn = xqds.getConnection();
+ XQExpression expr = conn.createExpression();
+ //bad code
+ expr.executeCommand(name);
+}
+
public void good(HttpServletRequest request) throws XQException {
String name = request.getParameter("name");
XQDataSource ds = new SaxonXQDataSource();
diff --git a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
index 796df6b68da..69617cad629 100644
--- a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
+++ b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
@@ -25,7 +25,8 @@ class XQueryInjectionConfig extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) {
sink.asExpr() = any(XQueryPreparedExecuteCall xpec).getPreparedExpression() or
- sink.asExpr() = any(XQueryExecuteCall xec).getExecuteQueryArgument()
+ sink.asExpr() = any(XQueryExecuteCall xec).getExecuteQueryArgument() or
+ sink.asExpr() = any(XQueryExecuteCommandCall xecc).getExecuteCommandArgument()
}
/**
diff --git a/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll b/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll
index 756caf2cd75..2a4019f2c9a 100644
--- a/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll
+++ b/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll
@@ -50,3 +50,19 @@ class XQueryExecuteCall extends MethodAccess {
/** Return this execute query argument. */
Expr getExecuteQueryArgument() { result = this.getArgument(0) }
}
+
+/** A call to `XQExpression.executeCommand`. */
+class XQueryExecuteCommandCall extends MethodAccess {
+ XQueryExecuteCommandCall() {
+ exists(Method m |
+ this.getMethod() = m and
+ m.hasName("executeCommand") and
+ m.getDeclaringType()
+ .getASourceSupertype*()
+ .hasQualifiedName("javax.xml.xquery", "XQExpression")
+ )
+ }
+
+ /** Return this execute command argument. */
+ Expr getExecuteCommandArgument() { result = this.getArgument(0) }
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.expected
index e2b04e1e020..b3ae0ff90fb 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.expected
@@ -1,35 +1,43 @@
edges
-| XQueryInjection.java:42:23:42:50 | getParameter(...) : String | XQueryInjection.java:47:35:47:38 | xqpe |
-| XQueryInjection.java:55:23:55:50 | getParameter(...) : String | XQueryInjection.java:60:53:60:57 | query |
-| XQueryInjection.java:68:32:68:59 | nameStr : String | XQueryInjection.java:73:35:73:38 | xqpe |
-| XQueryInjection.java:80:33:80:60 | nameStr : String | XQueryInjection.java:85:53:85:57 | query |
-| XQueryInjection.java:93:28:93:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:97:35:97:38 | xqpe |
-| XQueryInjection.java:105:28:105:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:109:53:109:56 | name |
-| XQueryInjection.java:117:28:117:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:122:35:122:38 | xqpe |
-| XQueryInjection.java:130:28:130:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:135:53:135:54 | br |
+| XQueryInjection.java:45:23:45:50 | getParameter(...) : String | XQueryInjection.java:51:35:51:38 | xqpe |
+| XQueryInjection.java:59:23:59:50 | getParameter(...) : String | XQueryInjection.java:65:53:65:57 | query |
+| XQueryInjection.java:73:32:73:59 | nameStr : String | XQueryInjection.java:79:35:79:38 | xqpe |
+| XQueryInjection.java:86:33:86:60 | nameStr : String | XQueryInjection.java:92:53:92:57 | query |
+| XQueryInjection.java:100:28:100:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:104:35:104:38 | xqpe |
+| XQueryInjection.java:112:28:112:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:116:53:116:56 | name |
+| XQueryInjection.java:124:28:124:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:129:35:129:38 | xqpe |
+| XQueryInjection.java:137:28:137:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:142:53:142:54 | br |
+| XQueryInjection.java:150:23:150:50 | getParameter(...) : String | XQueryInjection.java:155:29:155:32 | name |
+| XQueryInjection.java:157:26:157:49 | getInputStream(...) : ServletInputStream | XQueryInjection.java:159:29:159:30 | br |
nodes
-| XQueryInjection.java:42:23:42:50 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| XQueryInjection.java:47:35:47:38 | xqpe | semmle.label | xqpe |
-| XQueryInjection.java:55:23:55:50 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| XQueryInjection.java:60:53:60:57 | query | semmle.label | query |
-| XQueryInjection.java:68:32:68:59 | nameStr : String | semmle.label | nameStr : String |
-| XQueryInjection.java:73:35:73:38 | xqpe | semmle.label | xqpe |
-| XQueryInjection.java:80:33:80:60 | nameStr : String | semmle.label | nameStr : String |
-| XQueryInjection.java:85:53:85:57 | query | semmle.label | query |
-| XQueryInjection.java:93:28:93:51 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
-| XQueryInjection.java:97:35:97:38 | xqpe | semmle.label | xqpe |
-| XQueryInjection.java:105:28:105:51 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
-| XQueryInjection.java:109:53:109:56 | name | semmle.label | name |
-| XQueryInjection.java:117:28:117:51 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
-| XQueryInjection.java:122:35:122:38 | xqpe | semmle.label | xqpe |
-| XQueryInjection.java:130:28:130:51 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
-| XQueryInjection.java:135:53:135:54 | br | semmle.label | br |
+| XQueryInjection.java:45:23:45:50 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| XQueryInjection.java:51:35:51:38 | xqpe | semmle.label | xqpe |
+| XQueryInjection.java:59:23:59:50 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| XQueryInjection.java:65:53:65:57 | query | semmle.label | query |
+| XQueryInjection.java:73:32:73:59 | nameStr : String | semmle.label | nameStr : String |
+| XQueryInjection.java:79:35:79:38 | xqpe | semmle.label | xqpe |
+| XQueryInjection.java:86:33:86:60 | nameStr : String | semmle.label | nameStr : String |
+| XQueryInjection.java:92:53:92:57 | query | semmle.label | query |
+| XQueryInjection.java:100:28:100:51 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
+| XQueryInjection.java:104:35:104:38 | xqpe | semmle.label | xqpe |
+| XQueryInjection.java:112:28:112:51 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
+| XQueryInjection.java:116:53:116:56 | name | semmle.label | name |
+| XQueryInjection.java:124:28:124:51 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
+| XQueryInjection.java:129:35:129:38 | xqpe | semmle.label | xqpe |
+| XQueryInjection.java:137:28:137:51 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
+| XQueryInjection.java:142:53:142:54 | br | semmle.label | br |
+| XQueryInjection.java:150:23:150:50 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| XQueryInjection.java:155:29:155:32 | name | semmle.label | name |
+| XQueryInjection.java:157:26:157:49 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
+| XQueryInjection.java:159:29:159:30 | br | semmle.label | br |
#select
-| XQueryInjection.java:47:35:47:38 | xqpe | XQueryInjection.java:42:23:42:50 | getParameter(...) : String | XQueryInjection.java:47:35:47:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:42:23:42:50 | getParameter(...) | this user input |
-| XQueryInjection.java:60:53:60:57 | query | XQueryInjection.java:55:23:55:50 | getParameter(...) : String | XQueryInjection.java:60:53:60:57 | query | XQuery query might include code from $@. | XQueryInjection.java:55:23:55:50 | getParameter(...) | this user input |
-| XQueryInjection.java:73:35:73:38 | xqpe | XQueryInjection.java:68:32:68:59 | nameStr : String | XQueryInjection.java:73:35:73:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:68:32:68:59 | nameStr | this user input |
-| XQueryInjection.java:85:53:85:57 | query | XQueryInjection.java:80:33:80:60 | nameStr : String | XQueryInjection.java:85:53:85:57 | query | XQuery query might include code from $@. | XQueryInjection.java:80:33:80:60 | nameStr | this user input |
-| XQueryInjection.java:97:35:97:38 | xqpe | XQueryInjection.java:93:28:93:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:97:35:97:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:93:28:93:51 | getInputStream(...) | this user input |
-| XQueryInjection.java:109:53:109:56 | name | XQueryInjection.java:105:28:105:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:109:53:109:56 | name | XQuery query might include code from $@. | XQueryInjection.java:105:28:105:51 | getInputStream(...) | this user input |
-| XQueryInjection.java:122:35:122:38 | xqpe | XQueryInjection.java:117:28:117:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:122:35:122:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:117:28:117:51 | getInputStream(...) | this user input |
-| XQueryInjection.java:135:53:135:54 | br | XQueryInjection.java:130:28:130:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:135:53:135:54 | br | XQuery query might include code from $@. | XQueryInjection.java:130:28:130:51 | getInputStream(...) | this user input |
+| XQueryInjection.java:51:35:51:38 | xqpe | XQueryInjection.java:45:23:45:50 | getParameter(...) : String | XQueryInjection.java:51:35:51:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:45:23:45:50 | getParameter(...) | this user input |
+| XQueryInjection.java:65:53:65:57 | query | XQueryInjection.java:59:23:59:50 | getParameter(...) : String | XQueryInjection.java:65:53:65:57 | query | XQuery query might include code from $@. | XQueryInjection.java:59:23:59:50 | getParameter(...) | this user input |
+| XQueryInjection.java:79:35:79:38 | xqpe | XQueryInjection.java:73:32:73:59 | nameStr : String | XQueryInjection.java:79:35:79:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:73:32:73:59 | nameStr | this user input |
+| XQueryInjection.java:92:53:92:57 | query | XQueryInjection.java:86:33:86:60 | nameStr : String | XQueryInjection.java:92:53:92:57 | query | XQuery query might include code from $@. | XQueryInjection.java:86:33:86:60 | nameStr | this user input |
+| XQueryInjection.java:104:35:104:38 | xqpe | XQueryInjection.java:100:28:100:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:104:35:104:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:100:28:100:51 | getInputStream(...) | this user input |
+| XQueryInjection.java:116:53:116:56 | name | XQueryInjection.java:112:28:112:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:116:53:116:56 | name | XQuery query might include code from $@. | XQueryInjection.java:112:28:112:51 | getInputStream(...) | this user input |
+| XQueryInjection.java:129:35:129:38 | xqpe | XQueryInjection.java:124:28:124:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:129:35:129:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:124:28:124:51 | getInputStream(...) | this user input |
+| XQueryInjection.java:142:53:142:54 | br | XQueryInjection.java:137:28:137:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:142:53:142:54 | br | XQuery query might include code from $@. | XQueryInjection.java:137:28:137:51 | getInputStream(...) | this user input |
+| XQueryInjection.java:155:29:155:32 | name | XQueryInjection.java:150:23:150:50 | getParameter(...) : String | XQueryInjection.java:155:29:155:32 | name | XQuery query might include code from $@. | XQueryInjection.java:150:23:150:50 | getParameter(...) | this user input |
+| XQueryInjection.java:159:29:159:30 | br | XQueryInjection.java:157:26:157:49 | getInputStream(...) : ServletInputStream | XQueryInjection.java:159:29:159:30 | br | XQuery query might include code from $@. | XQueryInjection.java:157:26:157:49 | getInputStream(...) | this user input |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.java b/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.java
index 36f829d81bd..d8df8057cc6 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.java
@@ -1,3 +1,5 @@
+package com.vuln.v2.controller;
+
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -27,9 +29,10 @@ public class XQueryInjection {
+ " for $user in doc(\"users.xml\")/Users/User[name=$name] return $user/password";
conn = xqds.getConnection();
XQExpression expr = conn.createExpression();
- expr.bindString(new QName("name"), name, conn.createAtomicType(XQItemType.XQBASETYPE_STRING));
+ expr.bindString(new QName("name"), name,
+ conn.createAtomicType(XQItemType.XQBASETYPE_STRING));
XQResultSequence result = expr.executeQuery(query);
- while (result.next()){
+ while (result.next()) {
System.out.println(result.getItemAsString(null));
}
} catch (XQException e) {
@@ -42,10 +45,11 @@ public class XQueryInjection {
String name = request.getParameter("name");
XQDataSource ds = new SaxonXQDataSource();
XQConnection conn = ds.getConnection();
- String query = "for $user in doc(\"users.xml\")/Users/User[name='" + name + "'] return $user/password";
+ String query = "for $user in doc(\"users.xml\")/Users/User[name='" + name
+ + "'] return $user/password";
XQPreparedExpression xqpe = conn.prepareExpression(query);
XQResultSequence result = xqpe.executeQuery();
- while (result.next()){
+ while (result.next()) {
System.out.println(result.getItemAsString(null));
}
}
@@ -54,11 +58,12 @@ public class XQueryInjection {
public void testRequestbad1(HttpServletRequest request) throws Exception {
String name = request.getParameter("name");
XQDataSource xqds = new SaxonXQDataSource();
- String query = "for $user in doc(\"users.xml\")/Users/User[name='" + name + "'] return $user/password";
+ String query = "for $user in doc(\"users.xml\")/Users/User[name='" + name
+ + "'] return $user/password";
XQConnection conn = xqds.getConnection();
XQExpression expr = conn.createExpression();
XQResultSequence result = expr.executeQuery(query);
- while (result.next()){
+ while (result.next()) {
System.out.println(result.getItemAsString(null));
}
}
@@ -68,10 +73,11 @@ public class XQueryInjection {
public void testStringtbad(@RequestParam String nameStr) throws XQException {
XQDataSource ds = new SaxonXQDataSource();
XQConnection conn = ds.getConnection();
- String query = "for $user in doc(\"users.xml\")/Users/User[name='" + nameStr + "'] return $user/password";
+ String query = "for $user in doc(\"users.xml\")/Users/User[name='" + nameStr
+ + "'] return $user/password";
XQPreparedExpression xqpe = conn.prepareExpression(query);
XQResultSequence result = xqpe.executeQuery();
- while (result.next()){
+ while (result.next()) {
System.out.println(result.getItemAsString(null));
}
}
@@ -79,11 +85,12 @@ public class XQueryInjection {
@RequestMapping
public void testStringtbad1(@RequestParam String nameStr) throws XQException {
XQDataSource xqds = new SaxonXQDataSource();
- String query = "for $user in doc(\"users.xml\")/Users/User[name='" + nameStr + "'] return $user/password";
+ String query = "for $user in doc(\"users.xml\")/Users/User[name='" + nameStr
+ + "'] return $user/password";
XQConnection conn = xqds.getConnection();
XQExpression expr = conn.createExpression();
XQResultSequence result = expr.executeQuery(query);
- while (result.next()){
+ while (result.next()) {
System.out.println(result.getItemAsString(null));
}
}
@@ -95,7 +102,7 @@ public class XQueryInjection {
XQConnection conn = ds.getConnection();
XQPreparedExpression xqpe = conn.prepareExpression(name);
XQResultSequence result = xqpe.executeQuery();
- while (result.next()){
+ while (result.next()) {
System.out.println(result.getItemAsString(null));
}
}
@@ -107,7 +114,7 @@ public class XQueryInjection {
XQConnection conn = xqds.getConnection();
XQExpression expr = conn.createExpression();
XQResultSequence result = expr.executeQuery(name);
- while (result.next()){
+ while (result.next()) {
System.out.println(result.getItemAsString(null));
}
}
@@ -120,7 +127,7 @@ public class XQueryInjection {
XQConnection conn = ds.getConnection();
XQPreparedExpression xqpe = conn.prepareExpression(br);
XQResultSequence result = xqpe.executeQuery();
- while (result.next()){
+ while (result.next()) {
System.out.println(result.getItemAsString(null));
}
}
@@ -133,11 +140,26 @@ public class XQueryInjection {
XQConnection conn = xqds.getConnection();
XQExpression expr = conn.createExpression();
XQResultSequence result = expr.executeQuery(br);
- while (result.next()){
+ while (result.next()) {
System.out.println(result.getItemAsString(null));
}
}
+ @RequestMapping
+ public void testExecuteCommandbad(HttpServletRequest request) throws Exception {
+ String name = request.getParameter("name");
+ XQDataSource xqds = new SaxonXQDataSource();
+ XQConnection conn = xqds.getConnection();
+ XQExpression expr = conn.createExpression();
+ //bad code
+ expr.executeCommand(name);
+ //bad code
+ InputStream is = request.getInputStream();
+ BufferedReader br = new BufferedReader(new InputStreamReader(is));
+ expr.executeCommand(br);
+ expr.close();
+ }
+
@RequestMapping
public void good(HttpServletRequest request) throws XQException {
String name = request.getParameter("name");
@@ -146,9 +168,10 @@ public class XQueryInjection {
String query = "declare variable $name as xs:string external;"
+ " for $user in doc(\"users.xml\")/Users/User[name=$name] return $user/password";
XQPreparedExpression xqpe = conn.prepareExpression(query);
- xqpe.bindString(new QName("name"), name, conn.createAtomicType(XQItemType.XQBASETYPE_STRING));
+ xqpe.bindString(new QName("name"), name,
+ conn.createAtomicType(XQItemType.XQBASETYPE_STRING));
XQResultSequence result = xqpe.executeQuery();
- while (result.next()){
+ while (result.next()) {
System.out.println(result.getItemAsString(null));
}
}
@@ -161,10 +184,12 @@ public class XQueryInjection {
XQDataSource xqds = new SaxonXQDataSource();
XQConnection conn = xqds.getConnection();
XQExpression expr = conn.createExpression();
- expr.bindString(new QName("name"), name, conn.createAtomicType(XQItemType.XQBASETYPE_STRING));
+ expr.bindString(new QName("name"), name,
+ conn.createAtomicType(XQItemType.XQBASETYPE_STRING));
XQResultSequence result = expr.executeQuery(query);
- while (result.next()){
+ while (result.next()) {
System.out.println(result.getItemAsString(null));
}
}
-}
\ No newline at end of file
+}
+
diff --git a/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQConnection.java b/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQConnection.java
index 94bd38fa175..0263588e8b4 100644
--- a/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQConnection.java
+++ b/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQConnection.java
@@ -5,6 +5,7 @@ import net.sf.saxon.Configuration;
import javax.xml.xquery.XQConnection;
import javax.xml.xquery.XQPreparedExpression;
import javax.xml.xquery.XQException;
+import javax.xml.xquery.XQExpression;
import javax.xml.xquery.XQStaticContext;
import java.io.InputStream;
From 22e741c7a3ae86332d48df3ba64f964e181a9569 Mon Sep 17 00:00:00 2001
From: haby0
Date: Fri, 12 Feb 2021 11:17:42 +0800
Subject: [PATCH 044/725] *)add XQExpression.executeCommand(0) sink
---
.../Security/CWE/CWE-652/XQueryInjection.java | 9 +++
.../Security/CWE/CWE-652/XQueryInjection.ql | 3 +-
.../CWE/CWE-652/XQueryInjectionLib.qll | 16 +++++
.../security/CWE-652/XQueryInjection.expected | 72 ++++++++++---------
.../security/CWE-652/XQueryInjection.java | 63 +++++++++++-----
.../net/sf/saxon/xqj/SaxonXQConnection.java | 1 +
6 files changed, 112 insertions(+), 52 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.java b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.java
index f00df84968c..1b6be030249 100644
--- a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.java
+++ b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.java
@@ -32,6 +32,15 @@ public void bad1(HttpServletRequest request) throws XQException {
}
}
+public void bad2(HttpServletRequest request) throws XQException {
+ String name = request.getParameter("name");
+ XQDataSource xqds = new SaxonXQDataSource();
+ XQConnection conn = xqds.getConnection();
+ XQExpression expr = conn.createExpression();
+ //bad code
+ expr.executeCommand(name);
+}
+
public void good(HttpServletRequest request) throws XQException {
String name = request.getParameter("name");
XQDataSource ds = new SaxonXQDataSource();
diff --git a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
index 796df6b68da..69617cad629 100644
--- a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
+++ b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
@@ -25,7 +25,8 @@ class XQueryInjectionConfig extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) {
sink.asExpr() = any(XQueryPreparedExecuteCall xpec).getPreparedExpression() or
- sink.asExpr() = any(XQueryExecuteCall xec).getExecuteQueryArgument()
+ sink.asExpr() = any(XQueryExecuteCall xec).getExecuteQueryArgument() or
+ sink.asExpr() = any(XQueryExecuteCommandCall xecc).getExecuteCommandArgument()
}
/**
diff --git a/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll b/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll
index 756caf2cd75..2a4019f2c9a 100644
--- a/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll
+++ b/java/ql/src/Security/CWE/CWE-652/XQueryInjectionLib.qll
@@ -50,3 +50,19 @@ class XQueryExecuteCall extends MethodAccess {
/** Return this execute query argument. */
Expr getExecuteQueryArgument() { result = this.getArgument(0) }
}
+
+/** A call to `XQExpression.executeCommand`. */
+class XQueryExecuteCommandCall extends MethodAccess {
+ XQueryExecuteCommandCall() {
+ exists(Method m |
+ this.getMethod() = m and
+ m.hasName("executeCommand") and
+ m.getDeclaringType()
+ .getASourceSupertype*()
+ .hasQualifiedName("javax.xml.xquery", "XQExpression")
+ )
+ }
+
+ /** Return this execute command argument. */
+ Expr getExecuteCommandArgument() { result = this.getArgument(0) }
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.expected
index e2b04e1e020..b3ae0ff90fb 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.expected
@@ -1,35 +1,43 @@
edges
-| XQueryInjection.java:42:23:42:50 | getParameter(...) : String | XQueryInjection.java:47:35:47:38 | xqpe |
-| XQueryInjection.java:55:23:55:50 | getParameter(...) : String | XQueryInjection.java:60:53:60:57 | query |
-| XQueryInjection.java:68:32:68:59 | nameStr : String | XQueryInjection.java:73:35:73:38 | xqpe |
-| XQueryInjection.java:80:33:80:60 | nameStr : String | XQueryInjection.java:85:53:85:57 | query |
-| XQueryInjection.java:93:28:93:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:97:35:97:38 | xqpe |
-| XQueryInjection.java:105:28:105:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:109:53:109:56 | name |
-| XQueryInjection.java:117:28:117:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:122:35:122:38 | xqpe |
-| XQueryInjection.java:130:28:130:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:135:53:135:54 | br |
+| XQueryInjection.java:45:23:45:50 | getParameter(...) : String | XQueryInjection.java:51:35:51:38 | xqpe |
+| XQueryInjection.java:59:23:59:50 | getParameter(...) : String | XQueryInjection.java:65:53:65:57 | query |
+| XQueryInjection.java:73:32:73:59 | nameStr : String | XQueryInjection.java:79:35:79:38 | xqpe |
+| XQueryInjection.java:86:33:86:60 | nameStr : String | XQueryInjection.java:92:53:92:57 | query |
+| XQueryInjection.java:100:28:100:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:104:35:104:38 | xqpe |
+| XQueryInjection.java:112:28:112:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:116:53:116:56 | name |
+| XQueryInjection.java:124:28:124:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:129:35:129:38 | xqpe |
+| XQueryInjection.java:137:28:137:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:142:53:142:54 | br |
+| XQueryInjection.java:150:23:150:50 | getParameter(...) : String | XQueryInjection.java:155:29:155:32 | name |
+| XQueryInjection.java:157:26:157:49 | getInputStream(...) : ServletInputStream | XQueryInjection.java:159:29:159:30 | br |
nodes
-| XQueryInjection.java:42:23:42:50 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| XQueryInjection.java:47:35:47:38 | xqpe | semmle.label | xqpe |
-| XQueryInjection.java:55:23:55:50 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| XQueryInjection.java:60:53:60:57 | query | semmle.label | query |
-| XQueryInjection.java:68:32:68:59 | nameStr : String | semmle.label | nameStr : String |
-| XQueryInjection.java:73:35:73:38 | xqpe | semmle.label | xqpe |
-| XQueryInjection.java:80:33:80:60 | nameStr : String | semmle.label | nameStr : String |
-| XQueryInjection.java:85:53:85:57 | query | semmle.label | query |
-| XQueryInjection.java:93:28:93:51 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
-| XQueryInjection.java:97:35:97:38 | xqpe | semmle.label | xqpe |
-| XQueryInjection.java:105:28:105:51 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
-| XQueryInjection.java:109:53:109:56 | name | semmle.label | name |
-| XQueryInjection.java:117:28:117:51 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
-| XQueryInjection.java:122:35:122:38 | xqpe | semmle.label | xqpe |
-| XQueryInjection.java:130:28:130:51 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
-| XQueryInjection.java:135:53:135:54 | br | semmle.label | br |
+| XQueryInjection.java:45:23:45:50 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| XQueryInjection.java:51:35:51:38 | xqpe | semmle.label | xqpe |
+| XQueryInjection.java:59:23:59:50 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| XQueryInjection.java:65:53:65:57 | query | semmle.label | query |
+| XQueryInjection.java:73:32:73:59 | nameStr : String | semmle.label | nameStr : String |
+| XQueryInjection.java:79:35:79:38 | xqpe | semmle.label | xqpe |
+| XQueryInjection.java:86:33:86:60 | nameStr : String | semmle.label | nameStr : String |
+| XQueryInjection.java:92:53:92:57 | query | semmle.label | query |
+| XQueryInjection.java:100:28:100:51 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
+| XQueryInjection.java:104:35:104:38 | xqpe | semmle.label | xqpe |
+| XQueryInjection.java:112:28:112:51 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
+| XQueryInjection.java:116:53:116:56 | name | semmle.label | name |
+| XQueryInjection.java:124:28:124:51 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
+| XQueryInjection.java:129:35:129:38 | xqpe | semmle.label | xqpe |
+| XQueryInjection.java:137:28:137:51 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
+| XQueryInjection.java:142:53:142:54 | br | semmle.label | br |
+| XQueryInjection.java:150:23:150:50 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| XQueryInjection.java:155:29:155:32 | name | semmle.label | name |
+| XQueryInjection.java:157:26:157:49 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
+| XQueryInjection.java:159:29:159:30 | br | semmle.label | br |
#select
-| XQueryInjection.java:47:35:47:38 | xqpe | XQueryInjection.java:42:23:42:50 | getParameter(...) : String | XQueryInjection.java:47:35:47:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:42:23:42:50 | getParameter(...) | this user input |
-| XQueryInjection.java:60:53:60:57 | query | XQueryInjection.java:55:23:55:50 | getParameter(...) : String | XQueryInjection.java:60:53:60:57 | query | XQuery query might include code from $@. | XQueryInjection.java:55:23:55:50 | getParameter(...) | this user input |
-| XQueryInjection.java:73:35:73:38 | xqpe | XQueryInjection.java:68:32:68:59 | nameStr : String | XQueryInjection.java:73:35:73:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:68:32:68:59 | nameStr | this user input |
-| XQueryInjection.java:85:53:85:57 | query | XQueryInjection.java:80:33:80:60 | nameStr : String | XQueryInjection.java:85:53:85:57 | query | XQuery query might include code from $@. | XQueryInjection.java:80:33:80:60 | nameStr | this user input |
-| XQueryInjection.java:97:35:97:38 | xqpe | XQueryInjection.java:93:28:93:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:97:35:97:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:93:28:93:51 | getInputStream(...) | this user input |
-| XQueryInjection.java:109:53:109:56 | name | XQueryInjection.java:105:28:105:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:109:53:109:56 | name | XQuery query might include code from $@. | XQueryInjection.java:105:28:105:51 | getInputStream(...) | this user input |
-| XQueryInjection.java:122:35:122:38 | xqpe | XQueryInjection.java:117:28:117:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:122:35:122:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:117:28:117:51 | getInputStream(...) | this user input |
-| XQueryInjection.java:135:53:135:54 | br | XQueryInjection.java:130:28:130:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:135:53:135:54 | br | XQuery query might include code from $@. | XQueryInjection.java:130:28:130:51 | getInputStream(...) | this user input |
+| XQueryInjection.java:51:35:51:38 | xqpe | XQueryInjection.java:45:23:45:50 | getParameter(...) : String | XQueryInjection.java:51:35:51:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:45:23:45:50 | getParameter(...) | this user input |
+| XQueryInjection.java:65:53:65:57 | query | XQueryInjection.java:59:23:59:50 | getParameter(...) : String | XQueryInjection.java:65:53:65:57 | query | XQuery query might include code from $@. | XQueryInjection.java:59:23:59:50 | getParameter(...) | this user input |
+| XQueryInjection.java:79:35:79:38 | xqpe | XQueryInjection.java:73:32:73:59 | nameStr : String | XQueryInjection.java:79:35:79:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:73:32:73:59 | nameStr | this user input |
+| XQueryInjection.java:92:53:92:57 | query | XQueryInjection.java:86:33:86:60 | nameStr : String | XQueryInjection.java:92:53:92:57 | query | XQuery query might include code from $@. | XQueryInjection.java:86:33:86:60 | nameStr | this user input |
+| XQueryInjection.java:104:35:104:38 | xqpe | XQueryInjection.java:100:28:100:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:104:35:104:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:100:28:100:51 | getInputStream(...) | this user input |
+| XQueryInjection.java:116:53:116:56 | name | XQueryInjection.java:112:28:112:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:116:53:116:56 | name | XQuery query might include code from $@. | XQueryInjection.java:112:28:112:51 | getInputStream(...) | this user input |
+| XQueryInjection.java:129:35:129:38 | xqpe | XQueryInjection.java:124:28:124:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:129:35:129:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:124:28:124:51 | getInputStream(...) | this user input |
+| XQueryInjection.java:142:53:142:54 | br | XQueryInjection.java:137:28:137:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:142:53:142:54 | br | XQuery query might include code from $@. | XQueryInjection.java:137:28:137:51 | getInputStream(...) | this user input |
+| XQueryInjection.java:155:29:155:32 | name | XQueryInjection.java:150:23:150:50 | getParameter(...) : String | XQueryInjection.java:155:29:155:32 | name | XQuery query might include code from $@. | XQueryInjection.java:150:23:150:50 | getParameter(...) | this user input |
+| XQueryInjection.java:159:29:159:30 | br | XQueryInjection.java:157:26:157:49 | getInputStream(...) : ServletInputStream | XQueryInjection.java:159:29:159:30 | br | XQuery query might include code from $@. | XQueryInjection.java:157:26:157:49 | getInputStream(...) | this user input |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.java b/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.java
index 36f829d81bd..d8df8057cc6 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-652/XQueryInjection.java
@@ -1,3 +1,5 @@
+package com.vuln.v2.controller;
+
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -27,9 +29,10 @@ public class XQueryInjection {
+ " for $user in doc(\"users.xml\")/Users/User[name=$name] return $user/password";
conn = xqds.getConnection();
XQExpression expr = conn.createExpression();
- expr.bindString(new QName("name"), name, conn.createAtomicType(XQItemType.XQBASETYPE_STRING));
+ expr.bindString(new QName("name"), name,
+ conn.createAtomicType(XQItemType.XQBASETYPE_STRING));
XQResultSequence result = expr.executeQuery(query);
- while (result.next()){
+ while (result.next()) {
System.out.println(result.getItemAsString(null));
}
} catch (XQException e) {
@@ -42,10 +45,11 @@ public class XQueryInjection {
String name = request.getParameter("name");
XQDataSource ds = new SaxonXQDataSource();
XQConnection conn = ds.getConnection();
- String query = "for $user in doc(\"users.xml\")/Users/User[name='" + name + "'] return $user/password";
+ String query = "for $user in doc(\"users.xml\")/Users/User[name='" + name
+ + "'] return $user/password";
XQPreparedExpression xqpe = conn.prepareExpression(query);
XQResultSequence result = xqpe.executeQuery();
- while (result.next()){
+ while (result.next()) {
System.out.println(result.getItemAsString(null));
}
}
@@ -54,11 +58,12 @@ public class XQueryInjection {
public void testRequestbad1(HttpServletRequest request) throws Exception {
String name = request.getParameter("name");
XQDataSource xqds = new SaxonXQDataSource();
- String query = "for $user in doc(\"users.xml\")/Users/User[name='" + name + "'] return $user/password";
+ String query = "for $user in doc(\"users.xml\")/Users/User[name='" + name
+ + "'] return $user/password";
XQConnection conn = xqds.getConnection();
XQExpression expr = conn.createExpression();
XQResultSequence result = expr.executeQuery(query);
- while (result.next()){
+ while (result.next()) {
System.out.println(result.getItemAsString(null));
}
}
@@ -68,10 +73,11 @@ public class XQueryInjection {
public void testStringtbad(@RequestParam String nameStr) throws XQException {
XQDataSource ds = new SaxonXQDataSource();
XQConnection conn = ds.getConnection();
- String query = "for $user in doc(\"users.xml\")/Users/User[name='" + nameStr + "'] return $user/password";
+ String query = "for $user in doc(\"users.xml\")/Users/User[name='" + nameStr
+ + "'] return $user/password";
XQPreparedExpression xqpe = conn.prepareExpression(query);
XQResultSequence result = xqpe.executeQuery();
- while (result.next()){
+ while (result.next()) {
System.out.println(result.getItemAsString(null));
}
}
@@ -79,11 +85,12 @@ public class XQueryInjection {
@RequestMapping
public void testStringtbad1(@RequestParam String nameStr) throws XQException {
XQDataSource xqds = new SaxonXQDataSource();
- String query = "for $user in doc(\"users.xml\")/Users/User[name='" + nameStr + "'] return $user/password";
+ String query = "for $user in doc(\"users.xml\")/Users/User[name='" + nameStr
+ + "'] return $user/password";
XQConnection conn = xqds.getConnection();
XQExpression expr = conn.createExpression();
XQResultSequence result = expr.executeQuery(query);
- while (result.next()){
+ while (result.next()) {
System.out.println(result.getItemAsString(null));
}
}
@@ -95,7 +102,7 @@ public class XQueryInjection {
XQConnection conn = ds.getConnection();
XQPreparedExpression xqpe = conn.prepareExpression(name);
XQResultSequence result = xqpe.executeQuery();
- while (result.next()){
+ while (result.next()) {
System.out.println(result.getItemAsString(null));
}
}
@@ -107,7 +114,7 @@ public class XQueryInjection {
XQConnection conn = xqds.getConnection();
XQExpression expr = conn.createExpression();
XQResultSequence result = expr.executeQuery(name);
- while (result.next()){
+ while (result.next()) {
System.out.println(result.getItemAsString(null));
}
}
@@ -120,7 +127,7 @@ public class XQueryInjection {
XQConnection conn = ds.getConnection();
XQPreparedExpression xqpe = conn.prepareExpression(br);
XQResultSequence result = xqpe.executeQuery();
- while (result.next()){
+ while (result.next()) {
System.out.println(result.getItemAsString(null));
}
}
@@ -133,11 +140,26 @@ public class XQueryInjection {
XQConnection conn = xqds.getConnection();
XQExpression expr = conn.createExpression();
XQResultSequence result = expr.executeQuery(br);
- while (result.next()){
+ while (result.next()) {
System.out.println(result.getItemAsString(null));
}
}
+ @RequestMapping
+ public void testExecuteCommandbad(HttpServletRequest request) throws Exception {
+ String name = request.getParameter("name");
+ XQDataSource xqds = new SaxonXQDataSource();
+ XQConnection conn = xqds.getConnection();
+ XQExpression expr = conn.createExpression();
+ //bad code
+ expr.executeCommand(name);
+ //bad code
+ InputStream is = request.getInputStream();
+ BufferedReader br = new BufferedReader(new InputStreamReader(is));
+ expr.executeCommand(br);
+ expr.close();
+ }
+
@RequestMapping
public void good(HttpServletRequest request) throws XQException {
String name = request.getParameter("name");
@@ -146,9 +168,10 @@ public class XQueryInjection {
String query = "declare variable $name as xs:string external;"
+ " for $user in doc(\"users.xml\")/Users/User[name=$name] return $user/password";
XQPreparedExpression xqpe = conn.prepareExpression(query);
- xqpe.bindString(new QName("name"), name, conn.createAtomicType(XQItemType.XQBASETYPE_STRING));
+ xqpe.bindString(new QName("name"), name,
+ conn.createAtomicType(XQItemType.XQBASETYPE_STRING));
XQResultSequence result = xqpe.executeQuery();
- while (result.next()){
+ while (result.next()) {
System.out.println(result.getItemAsString(null));
}
}
@@ -161,10 +184,12 @@ public class XQueryInjection {
XQDataSource xqds = new SaxonXQDataSource();
XQConnection conn = xqds.getConnection();
XQExpression expr = conn.createExpression();
- expr.bindString(new QName("name"), name, conn.createAtomicType(XQItemType.XQBASETYPE_STRING));
+ expr.bindString(new QName("name"), name,
+ conn.createAtomicType(XQItemType.XQBASETYPE_STRING));
XQResultSequence result = expr.executeQuery(query);
- while (result.next()){
+ while (result.next()) {
System.out.println(result.getItemAsString(null));
}
}
-}
\ No newline at end of file
+}
+
diff --git a/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQConnection.java b/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQConnection.java
index 94bd38fa175..0263588e8b4 100644
--- a/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQConnection.java
+++ b/java/ql/test/stubs/saxon-xqj-9.x/net/sf/saxon/xqj/SaxonXQConnection.java
@@ -5,6 +5,7 @@ import net.sf.saxon.Configuration;
import javax.xml.xquery.XQConnection;
import javax.xml.xquery.XQPreparedExpression;
import javax.xml.xquery.XQException;
+import javax.xml.xquery.XQExpression;
import javax.xml.xquery.XQStaticContext;
import java.io.InputStream;
From 6a6727fc80b67efd0c647eb8a9814e7799f63b83 Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Sun, 14 Feb 2021 14:59:53 +0000
Subject: [PATCH 045/725] Reduce the scope of the query to reduce FPs
---
.../Security/CWE/CWE-759/HashWithoutSalt.ql | 149 ++++++++++--------
.../security/CWE-759/HashWithoutSalt.expected | 12 +-
.../security/CWE-759/HashWithoutSalt.java | 30 ++--
.../query-tests/security/CWE-759/SHA512.java | 21 +++
4 files changed, 123 insertions(+), 89 deletions(-)
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-759/SHA512.java
diff --git a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
index d9fcc22c02b..c69c9739d64 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
@@ -8,10 +8,8 @@
*/
import java
-import semmle.code.java.dataflow.DataFlow3
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.TaintTracking2
-import semmle.code.java.dataflow.TaintTracking3
import DataFlow::PathGraph
/** The Java class `java.security.MessageDigest`. */
@@ -44,38 +42,91 @@ class MDUpdateMethod extends Method {
}
}
+/** The hashing method that could taint the input. */
+class MDHashMethodAccess extends MethodAccess {
+ MDHashMethodAccess() {
+ (
+ this.getMethod() instanceof MDDigestMethod or
+ this.getMethod() instanceof MDUpdateMethod
+ ) and
+ this.getNumArgument() != 0
+ }
+}
+
/** Gets a regular expression for matching common names of variables that indicate the value being held is a password. */
string getPasswordRegex() { result = "(?i).*pass(wd|word|code|phrase).*" }
/** Finds variables that hold password information judging by their names. */
class PasswordVarExpr extends Expr {
PasswordVarExpr() {
- exists(Variable v | this = v.getAnAccess() |
- (
- v.getName().toLowerCase().regexpMatch(getPasswordRegex()) and
- not v.getName().toLowerCase().matches("%hash%") // Exclude variable names such as `passwordHash` since their values were already hashed
- )
+ this.(VarAccess).getVariable().getName().toLowerCase().regexpMatch(getPasswordRegex()) and
+ not this.(VarAccess).getVariable().getName().toLowerCase().matches("%hash%") // Exclude variable names such as `passwordHash` since their values were already hashed
+ }
+}
+
+/** Holds if `Expr` e is an operand of `AddExpr`. */
+predicate hasAddExpr(AddExpr ae, Expr e) {
+ ae.getAnOperand() = e or
+ hasAddExpr(ae.getAnOperand(), e)
+}
+
+/** Holds if `MethodAccess` ma has a flow to another `MDHashMethodAccess` call. */
+predicate hasAnotherHashCall(MethodAccess ma) {
+ exists(MethodAccess ma2, DataFlow2::Node node1, DataFlow2::Node node2 |
+ ma2 instanceof MDHashMethodAccess and
+ ma2 != ma and
+ node1.asExpr() = ma.getAChildExpr() and
+ node2.asExpr() = ma2.getAChildExpr() and
+ (
+ TaintTracking2::localTaint(node1, node2) or
+ TaintTracking2::localTaint(node2, node1)
+ )
+ )
+}
+
+/** Holds if `MethodAccess` ma is a hashing call without a sibling node making another hashing call. */
+predicate isSingleHashMethodCall(MethodAccess ma) {
+ (
+ ma instanceof MDHashMethodAccess and
+ not hasAnotherHashCall(ma)
+ )
+}
+
+/** Holds if `MethodAccess` ma is invoked by `MethodAccess` ma2 either directly or indirectly. */
+predicate hasParentCall(MethodAccess ma2, MethodAccess ma) {
+ ma.getCaller() = ma2.getMethod() and
+ not ma2 instanceof MDHashMethodAccess
+ or
+ exists(MethodAccess ma3 |
+ ma.getCaller() = ma3.getMethod() and
+ not ma3 instanceof MDHashMethodAccess and
+ hasParentCall(ma2, ma3)
+ )
+}
+
+/** Holds if `MethodAccess` is a single hashing call. */
+predicate isSink(MethodAccess ma) {
+ isSingleHashMethodCall(ma) and
+ not exists(MethodAccess ma2 | hasParentCall(ma2, ma))
+}
+
+/** Sink of hashing calls. */
+class HashWithoutSaltSink extends DataFlow::ExprNode {
+ HashWithoutSaltSink() {
+ exists(MethodAccess ma |
+ this.asExpr() = ma.getAnArgument() and
+ isSink(ma)
)
}
}
/** Taint configuration tracking flow from an expression whose name suggests it holds password data to a method call that generates a hash without a salt. */
-class PasswordHashConfiguration extends TaintTracking3::Configuration {
- PasswordHashConfiguration() { this = "PasswordHashConfiguration" }
+class HashWithoutSaltConfiguration extends TaintTracking::Configuration {
+ HashWithoutSaltConfiguration() { this = "HashWithoutSaltConfiguration" }
- override predicate isSource(DataFlow3::Node source) { source.asExpr() instanceof PasswordVarExpr }
+ override predicate isSource(DataFlow::Node source) { source.asExpr() instanceof PasswordVarExpr }
- override predicate isSink(DataFlow3::Node sink) {
- exists(
- MethodAccess ma // invoke `md.update(password)` without the call of `md.update(digest)`
- |
- sink.asExpr() = ma.getArgument(0) and
- (
- ma.getMethod() instanceof MDUpdateMethod or // md.update(password)
- ma.getMethod() instanceof MDDigestMethod // md.digest(password)
- )
- )
- }
+ override predicate isSink(DataFlow::Node sink) { sink instanceof HashWithoutSaltSink }
/**
* Holds if a password is concatenated with a salt then hashed together through the call `System.arraycopy(password.getBytes(), ...)`, for example,
@@ -84,60 +135,26 @@ class PasswordHashConfiguration extends TaintTracking3::Configuration {
* `byte[] messageDigest = md.digest(allBytes);`
* Or the password is concatenated with a salt as a string.
*/
- override predicate isSanitizer(DataFlow3::Node node) {
+ override predicate isSanitizer(DataFlow::Node node) {
exists(MethodAccess ma |
ma.getMethod().getDeclaringType().hasQualifiedName("java.lang", "System") and
ma.getMethod().hasName("arraycopy") and
ma.getArgument(0) = node.asExpr()
) // System.arraycopy(password.getBytes(), ...)
or
- exists(AddExpr e | node.asExpr() = e.getAnOperand()) // password+salt
- }
-}
-
-class PasswordDigestConfiguration extends TaintTracking2::Configuration {
- PasswordDigestConfiguration() { this = "PasswordDigestConfiguration" }
-
- override predicate isSource(DataFlow2::Node source) {
- exists(MDConstructor mc | source.asExpr() = mc)
- }
-
- override predicate isSink(DataFlow2::Node sink) {
+ exists(AddExpr e | hasAddExpr(e, node.asExpr())) // password+salt
+ or
+ exists(ConditionalExpr ce | ce = node.asExpr()) // useSalt?password+":"+salt:password
+ or
exists(MethodAccess ma |
- (
- ma.getMethod() instanceof MDUpdateMethod or
- ma.getMethod() instanceof MDDigestMethod
- ) and
- exists(PasswordHashConfiguration cc | cc.hasFlowToExpr(ma.getAnArgument())) and
- sink.asExpr() = ma.getQualifier()
+ ma.getMethod().getDeclaringType().hasQualifiedName("java.lang", "StringBuilder") and
+ ma.getMethod().hasName("append") and
+ ma.getArgument(0) = node.asExpr() // stringBuilder.append(password).append(salt)
)
- }
-}
-
-class HashWithoutSaltConfiguration extends TaintTracking::Configuration {
- HashWithoutSaltConfiguration() { this = "HashWithoutSaltConfiguration" }
-
- override predicate isSource(DataFlow::Node source) {
- exists(PasswordDigestConfiguration pc | pc.hasFlow(source, _))
- }
-
- override predicate isSink(DataFlow::Node sink) {
+ or
exists(MethodAccess ma |
- ma.getMethod() instanceof MDDigestMethod and // md.digest(password)
- sink.asExpr() = ma.getQualifier()
- )
- }
-
- /** Holds if `md.update` or `md.digest` calls integrate something other than the password, perhaps a salt. */
- override predicate isSanitizer(DataFlow::Node node) {
- exists(MethodAccess ma |
- (
- ma.getMethod() instanceof MDUpdateMethod
- or
- ma.getMethod() instanceof MDDigestMethod and ma.getNumArgument() != 0
- ) and
- node.asExpr() = ma.getQualifier() and
- not exists(PasswordHashConfiguration cc | cc.hasFlowToExpr(ma.getAnArgument()))
+ ma.getQualifier().(VarAccess).getVariable().getType() instanceof Interface and
+ ma.getAnArgument() = node.asExpr() // Method access of interface type variables requires runtime determination
)
}
}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.expected b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.expected
index 7fc5fe115e4..fa36297a8a1 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.expected
@@ -1,19 +1,11 @@
edges
| HashWithoutSalt.java:10:36:10:43 | password : String | HashWithoutSalt.java:10:36:10:54 | getBytes(...) |
| HashWithoutSalt.java:17:13:17:20 | password : String | HashWithoutSalt.java:17:13:17:31 | getBytes(...) |
-| HashWithoutSalt.java:98:22:98:29 | password : String | HashWithoutSalt.java:99:17:99:25 | passBytes : byte[] |
-| HashWithoutSalt.java:99:17:99:25 | passBytes : byte[] | SHA256.java:14:22:14:31 | foo : byte[] |
-| SHA256.java:14:22:14:31 | foo : byte[] | SHA256.java:15:15:15:17 | foo |
nodes
| HashWithoutSalt.java:10:36:10:43 | password : String | semmle.label | password : String |
| HashWithoutSalt.java:10:36:10:54 | getBytes(...) | semmle.label | getBytes(...) |
| HashWithoutSalt.java:17:13:17:20 | password : String | semmle.label | password : String |
| HashWithoutSalt.java:17:13:17:31 | getBytes(...) | semmle.label | getBytes(...) |
-| HashWithoutSalt.java:98:22:98:29 | password : String | semmle.label | password : String |
-| HashWithoutSalt.java:99:17:99:25 | passBytes : byte[] | semmle.label | passBytes : byte[] |
-| SHA256.java:14:22:14:31 | foo : byte[] | semmle.label | foo : byte[] |
-| SHA256.java:15:15:15:17 | foo | semmle.label | foo |
#select
-| HashWithoutSalt.java:10:36:10:54 | getBytes(...) | HashWithoutSalt.java:10:36:10:43 | password : String | HashWithoutSalt.java:10:36:10:54 | getBytes(...) | $@ is hashed without a salt. | HashWithoutSalt.java:10:36:10:43 | password | The password |
-| HashWithoutSalt.java:17:13:17:31 | getBytes(...) | HashWithoutSalt.java:17:13:17:20 | password : String | HashWithoutSalt.java:17:13:17:31 | getBytes(...) | $@ is hashed without a salt. | HashWithoutSalt.java:17:13:17:20 | password | The password |
-| SHA256.java:15:15:15:17 | foo | HashWithoutSalt.java:98:22:98:29 | password : String | SHA256.java:15:15:15:17 | foo | $@ is hashed without a salt. | HashWithoutSalt.java:98:22:98:29 | password | The password |
+| HashWithoutSalt.java:10:36:10:54 | getBytes(...) | HashWithoutSalt.java:10:36:10:43 | password : String | HashWithoutSalt.java:10:36:10:54 | getBytes(...) | $@ is hashed without a salt. | HashWithoutSalt.java:10:36:10:43 | password : String | The password |
+| HashWithoutSalt.java:17:13:17:31 | getBytes(...) | HashWithoutSalt.java:17:13:17:20 | password : String | HashWithoutSalt.java:17:13:17:31 | getBytes(...) | $@ is hashed without a salt. | HashWithoutSalt.java:17:13:17:20 | password : String | The password |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java
index 9535e4d01e5..ef2fef8dd9c 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java
@@ -54,7 +54,12 @@ public class HashWithoutSalt {
// GOOD - Hash with a given salt stored somewhere else.
public String getSHA256Hash(String password, String salt) throws NoSuchAlgorithmException {
- return hash(password+salt);
+ return hash(password+":"+salt);
+ }
+
+ // GOOD - Hash with a given salt stored somewhere else.
+ public String getSHA256Hash2(String password, String salt, boolean useSalt) throws NoSuchAlgorithmException {
+ return hash(useSalt?password+":"+salt:password);
}
// GOOD - Hash with a salt for a variable named passwordHash, whose value is a hash used as an input for a hashing function.
@@ -71,9 +76,9 @@ public class HashWithoutSalt {
public void update2(SHA256 sha256, byte[] foo, int start, int len) throws NoSuchAlgorithmException {
sha256.update(foo, start, len);
}
-
- // GOOD - Invoke a wrapper implementation with a salt.
- public String getSHA256Hash4(String password) throws NoSuchAlgorithmException {
+
+ // BAD - Invoking a wrapper implementation without a salt is not detected.
+ public String getSHA256Hash4(String password) throws NoSuchAlgorithmException, ClassNotFoundException, IllegalAccessException, InstantiationException {
SHA256 sha256 = new SHA256();
byte[] salt = getSalt();
byte[] passBytes = password.getBytes();
@@ -82,7 +87,7 @@ public class HashWithoutSalt {
return Base64.getEncoder().encodeToString(sha256.digest());
}
- // GOOD - Invoke a wrapper implementation with a salt.
+ // BAD - Invoking a wrapper implementation without a salt is not detected.
public String getSHA256Hash5(String password) throws NoSuchAlgorithmException {
SHA256 sha256 = new SHA256();
byte[] salt = getSalt();
@@ -92,7 +97,7 @@ public class HashWithoutSalt {
return Base64.getEncoder().encodeToString(sha256.digest());
}
- // BAD - Invoke a wrapper implementation without a salt.
+ // BAD - Invoking a wrapper implementation without a salt is not detected.
public String getSHA256Hash6(String password) throws NoSuchAlgorithmException {
SHA256 sha256 = new SHA256();
byte[] passBytes = password.getBytes();
@@ -100,18 +105,17 @@ public class HashWithoutSalt {
return Base64.getEncoder().encodeToString(sha256.digest());
}
- // GOOD - Invoke a wrapper implementation with a salt, which is only detectable when a class type is declared (not interface).
+ // BAD - Invoke a wrapper implementation with a salt, which is not detected with an interface type variable.
public String getSHA256Hash7(byte[] passphrase) throws NoSuchAlgorithmException, ClassNotFoundException, IllegalAccessException, InstantiationException {
- Class c = Class.forName("SHA256");
- HASH sha256 = (HASH) (c.newInstance());
+ Class c = Class.forName("SHA512");
+ HASH sha512 = (HASH) (c.newInstance());
byte[] tmp = new byte[4];
byte[] key = new byte[32 * 2];
for (int i = 0; i < 2; i++) {
- sha256.init();
+ sha512.init();
tmp[3] = (byte) i;
- sha256.update(tmp, 0, tmp.length);
- sha256.update(passphrase, 0, passphrase.length);
- System.arraycopy(sha256.digest(), 0, key, i * 32, 32);
+ sha512.update(passphrase, 0, passphrase.length);
+ System.arraycopy(sha512.digest(), 0, key, i * 32, 32);
}
return Base64.getEncoder().encodeToString(key);
}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-759/SHA512.java b/java/ql/test/experimental/query-tests/security/CWE-759/SHA512.java
new file mode 100644
index 00000000000..ec41fbf2d1a
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-759/SHA512.java
@@ -0,0 +1,21 @@
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public class SHA512 implements HASH {
+ MessageDigest md;
+ public int getBlockSize() {return 32;}
+ public void init() throws NoSuchAlgorithmException {
+ try { md = MessageDigest.getInstance("SHA-512"); }
+ catch (Exception e){
+ System.err.println(e);
+ }
+ }
+
+ public void update(byte[] foo, int start, int len) throws NoSuchAlgorithmException {
+ md.update(foo, start, len);
+ }
+
+ public byte[] digest() throws NoSuchAlgorithmException {
+ return md.digest();
+ }
+}
\ No newline at end of file
From 23f620d255423ace5ede1c1cf7373ffe6018a8bd Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Mon, 15 Feb 2021 05:31:29 +0000
Subject: [PATCH 046/725] Query to detect insecure LDAP endpoint configuration
---
.../CWE/CWE-297/InsecureLdapEndpoint.java | 23 +++++++
.../CWE/CWE-297/InsecureLdapEndpoint.qhelp | 32 +++++++++
.../CWE/CWE-297/InsecureLdapEndpoint.ql | 65 +++++++++++++++++++
.../CWE-297/InsecureLdapEndpoint.expected | 2 +
.../CWE-297/InsecureLdapEndpoint.java | 52 +++++++++++++++
.../CWE-297/InsecureLdapEndpoint.qlref | 1 +
6 files changed, 175 insertions(+)
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.java
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.qhelp
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.ql
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-297/InsecureLdapEndpoint.expected
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-297/InsecureLdapEndpoint.java
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-297/InsecureLdapEndpoint.qlref
diff --git a/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.java b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.java
new file mode 100644
index 00000000000..0af5200a150
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.java
@@ -0,0 +1,23 @@
+public class InsecureLdapEndpoint {
+ public Hashtable createConnectionEnv() {
+ Hashtable env = new Hashtable();
+ env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+ env.put(Context.PROVIDER_URL, "ldaps://ad.your-server.com:636");
+
+ env.put(Context.SECURITY_AUTHENTICATION, "simple");
+ env.put(Context.SECURITY_PRINCIPAL, "username");
+ env.put(Context.SECURITY_CREDENTIALS, "secpassword");
+
+ // BAD - Test configuration with disabled SSL endpoint check.
+ {
+ System.setProperty("com.sun.jndi.ldap.object.disableEndpointIdentification", "true");
+ }
+
+ // GOOD - No configuration to disable SSL endpoint check since it is enabled by default.
+ {
+ }
+
+ return env;
+ }
+
+}
diff --git a/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.qhelp b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.qhelp
new file mode 100644
index 00000000000..3aee17f9777
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.qhelp
@@ -0,0 +1,32 @@
+
+
+
+
+Java versions 8u181 or greater have enabled LDAPS endpoint identification by default. Nowadays infrastructure services like LDAP are commonly deployed behind load balancers therefore the LDAP server name can be different from the FQDN of the LDAPS endpoint. If a service certificate does not properly contain a matching DNS name as part of the certificate, Java will reject it by default.
+Instead of addressing the issue properly by having a compliant certificate deployed, frequently developers simply disable SSL endpoint check.
+This query checks whether LDAPS endpoint check is disabled in system properties.
+
+
+
+Replace any non-conforming LDAP server certificates to include a DNS name in the subjectAltName field of the certificate that matches the FQDN of the service.
+
+
+
+The following two examples show two ways of configuring SSL endpoint. In the 'BAD' case,
+endpoint check is disabled. In the 'GOOD' case, endpoint check is left enabled through the default Java configuration.
+
+
+
+
+
+ Oracle Java 8 Update 181 (8u181):
+ Endpoint identification enabled on LDAPS connections
+
+
+ IBM:
+ Fix this LDAP SSL error
+
+
+
diff --git a/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.ql b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.ql
new file mode 100644
index 00000000000..266b2b999a8
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.ql
@@ -0,0 +1,65 @@
+/**
+ * @name Insecure LDAP Endpoint Configuration
+ * @description Java application configured to disable LDAP endpoint identification does not validate the SSL certificate to properly ensure that it is actually associated with that host.
+ * @kind problem
+ * @id java/insecure-ldap-endpoint
+ * @tags security
+ * external/cwe-297
+ */
+
+import java
+
+/**
+ * The method to set a system property.
+ */
+class SetSystemPropertyMethod extends Method {
+ SetSystemPropertyMethod() {
+ this.hasName("setProperty") and
+ this.getDeclaringType().hasQualifiedName("java.lang", "System")
+ }
+}
+
+/**
+ * The method to set Java properties.
+ */
+class SetPropertyMethod extends Method {
+ SetPropertyMethod() {
+ this.hasName("setProperty") and
+ this.getDeclaringType().hasQualifiedName("java.util", "Properties")
+ }
+}
+
+/**
+ * The method to set system properties.
+ */
+class SetSystemPropertiesMethod extends Method {
+ SetSystemPropertiesMethod() {
+ this.hasName("setProperties") and
+ this.getDeclaringType().hasQualifiedName("java.lang", "System")
+ }
+}
+
+/** Holds if `MethodAccess` ma disables SSL endpoint check. */
+predicate isInsecureSSLEndpoint(MethodAccess ma) {
+ (
+ ma.getMethod() instanceof SetSystemPropertyMethod and
+ (
+ ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() =
+ "com.sun.jndi.ldap.object.disableEndpointIdentification" and
+ ma.getArgument(1).(CompileTimeConstantExpr).getStringValue() = "true" //com.sun.jndi.ldap.object.disableEndpointIdentification=true
+ )
+ or
+ ma.getMethod() instanceof SetSystemPropertiesMethod and
+ exists(MethodAccess ma2 |
+ ma2.getMethod() instanceof SetPropertyMethod and
+ ma2.getArgument(0).(CompileTimeConstantExpr).getStringValue() =
+ "com.sun.jndi.ldap.object.disableEndpointIdentification" and
+ ma2.getArgument(1).(CompileTimeConstantExpr).getStringValue() = "true" and //com.sun.jndi.ldap.object.disableEndpointIdentification=true
+ ma2.getQualifier().(VarAccess).getVariable().getAnAccess() = ma.getArgument(0) // systemProps.setProperties(properties)
+ )
+ )
+}
+
+from MethodAccess ma
+where isInsecureSSLEndpoint(ma)
+select ma, "SSL configuration allows insecure endpoint configuration"
diff --git a/java/ql/test/experimental/query-tests/security/CWE-297/InsecureLdapEndpoint.expected b/java/ql/test/experimental/query-tests/security/CWE-297/InsecureLdapEndpoint.expected
new file mode 100644
index 00000000000..29eb44937ee
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-297/InsecureLdapEndpoint.expected
@@ -0,0 +1,2 @@
+| InsecureLdapEndpoint.java:17:9:17:92 | setProperty(...) | SSL configuration allows insecure endpoint configuration |
+| InsecureLdapEndpoint.java:48:3:48:34 | setProperties(...) | SSL configuration allows insecure endpoint configuration |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-297/InsecureLdapEndpoint.java b/java/ql/test/experimental/query-tests/security/CWE-297/InsecureLdapEndpoint.java
new file mode 100644
index 00000000000..44c37864af0
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-297/InsecureLdapEndpoint.java
@@ -0,0 +1,52 @@
+import java.util.Hashtable;
+import java.util.Properties;
+import javax.naming.Context;
+
+public class InsecureLdapEndpoint {
+ // BAD - Test configuration with disabled SSL endpoint check.
+ public Hashtable createConnectionEnv() {
+ Hashtable env = new Hashtable();
+ env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+ env.put(Context.PROVIDER_URL, "ldaps://ad.your-server.com:636");
+
+ env.put(Context.SECURITY_AUTHENTICATION, "simple");
+ env.put(Context.SECURITY_PRINCIPAL, "username");
+ env.put(Context.SECURITY_CREDENTIALS, "secpassword");
+
+ // Disable SSL endpoint check
+ System.setProperty("com.sun.jndi.ldap.object.disableEndpointIdentification", "true");
+
+ return env;
+ }
+
+ // GOOD - Test configuration without disabling SSL endpoint check.
+ public Hashtable createConnectionEnv2() {
+ Hashtable env = new Hashtable();
+ env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+ env.put(Context.PROVIDER_URL, "ldaps://ad.your-server.com:636");
+
+ env.put(Context.SECURITY_AUTHENTICATION, "simple");
+ env.put(Context.SECURITY_PRINCIPAL, "username");
+ env.put(Context.SECURITY_CREDENTIALS, "secpassword");
+
+ return env;
+ }
+
+ // BAD - Test configuration with disabled SSL endpoint check.
+ public Hashtable createConnectionEnv3() {
+ Hashtable env = new Hashtable();
+ env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+ env.put(Context.PROVIDER_URL, "ldaps://ad.your-server.com:636");
+
+ env.put(Context.SECURITY_AUTHENTICATION, "simple");
+ env.put(Context.SECURITY_PRINCIPAL, "username");
+ env.put(Context.SECURITY_CREDENTIALS, "secpassword");
+
+ // Disable SSL endpoint check
+ Properties properties = new Properties();
+ properties.setProperty("com.sun.jndi.ldap.object.disableEndpointIdentification", "true");
+ System.setProperties(properties);
+
+ return env;
+ }
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-297/InsecureLdapEndpoint.qlref b/java/ql/test/experimental/query-tests/security/CWE-297/InsecureLdapEndpoint.qlref
new file mode 100644
index 00000000000..1c4d99bb6a3
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-297/InsecureLdapEndpoint.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.ql
From 409d95c5222a35e35f9d383f6021fd3472c3f963 Mon Sep 17 00:00:00 2001
From: Francis Alexander
Date: Mon, 15 Feb 2021 14:01:14 +0530
Subject: [PATCH 047/725] Sanitizer checks to decrease FP
---
.../Security/CWE/CWE-346/UnvalidatedCors.ql | 23 +++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql
index 3d98237b104..17cf94d2c1e 100644
--- a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql
+++ b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql
@@ -28,6 +28,14 @@ private predicate setsAllowCredentials(MethodAccess header) {
header.getArgument(1).(CompileTimeConstantExpr).getStringValue() = "true"
}
+class CorsProbableCheckAccess extends MethodAccess {
+ CorsProbableCheckAccess() {
+ getMethod().getName() = ["contains", "equals"] and
+ getMethod().getDeclaringType().getQualifiedName() =
+ ["java.util.List", "java.util.ArrayList", "java.lang.String"]
+ }
+}
+
private Expr getAccessControlAllowOriginHeaderName() {
result.(CompileTimeConstantExpr).getStringValue().toLowerCase() = "access-control-allow-origin"
}
@@ -49,6 +57,21 @@ class CorsOriginConfig extends TaintTracking::Configuration {
sink.asExpr() = corsheader.getArgument(1)
)
}
+
+ /*
+ * This should ideally check, the origin being validated against a list/array-list.
+ * or function being used to validate the origin, which has a flow from its parameter to any of the CorsProbableCheckAccess functions
+ */
+
+ override predicate isSanitizer(DataFlow::Node node) {
+ node.asExpr() = any(CorsProbableCheckAccess ma).getAnArgument()
+ or
+ exists(MethodAccess ma, CorsProbableCheckAccess ca |
+ ma.getMethod().calls(ca.getMethod()) and
+ DataFlow::localExprFlow(ma.getMethod().getAParameter().getAnAccess(), ca.getAnArgument()) and
+ (node.asExpr() = ma.getAnArgument() or node.asExpr() = ma.getAnArgument().getAChildExpr())
+ )
+ }
}
from DataFlow::PathNode source, DataFlow::PathNode sink, CorsOriginConfig conf
From a03e6faf376b6c0a55a1f3d3ea7b79e43ede0ad0 Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Mon, 15 Feb 2021 14:10:17 +0000
Subject: [PATCH 048/725] Optimize the query and update qldoc
---
.../Security/CWE/CWE-759/HashWithoutSalt.ql | 25 ++--
.../code/java/dataflow/TaintTracking3.qll | 7 --
.../tainttracking3/TaintTrackingImpl.qll | 115 ------------------
.../tainttracking3/TaintTrackingParameter.qll | 5 -
4 files changed, 7 insertions(+), 145 deletions(-)
delete mode 100644 java/ql/src/semmle/code/java/dataflow/TaintTracking3.qll
delete mode 100644 java/ql/src/semmle/code/java/dataflow/internal/tainttracking3/TaintTrackingImpl.qll
delete mode 100644 java/ql/src/semmle/code/java/dataflow/internal/tainttracking3/TaintTrackingParameter.qll
diff --git a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
index c69c9739d64..e1ac21e9200 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
@@ -64,16 +64,12 @@ class PasswordVarExpr extends Expr {
}
}
-/** Holds if `Expr` e is an operand of `AddExpr`. */
-predicate hasAddExpr(AddExpr ae, Expr e) {
- ae.getAnOperand() = e or
- hasAddExpr(ae.getAnOperand(), e)
-}
+/** Holds if `Expr` e is a direct or indirect operand of `ae`. */
+predicate hasAddExpr(AddExpr ae, Expr e) { ae.getAnOperand+() = e }
/** Holds if `MethodAccess` ma has a flow to another `MDHashMethodAccess` call. */
predicate hasAnotherHashCall(MethodAccess ma) {
- exists(MethodAccess ma2, DataFlow2::Node node1, DataFlow2::Node node2 |
- ma2 instanceof MDHashMethodAccess and
+ exists(MDHashMethodAccess ma2, DataFlow::Node node1, DataFlow::Node node2 |
ma2 != ma and
node1.asExpr() = ma.getAChildExpr() and
node2.asExpr() = ma2.getAChildExpr() and
@@ -85,29 +81,22 @@ predicate hasAnotherHashCall(MethodAccess ma) {
}
/** Holds if `MethodAccess` ma is a hashing call without a sibling node making another hashing call. */
-predicate isSingleHashMethodCall(MethodAccess ma) {
- (
- ma instanceof MDHashMethodAccess and
- not hasAnotherHashCall(ma)
- )
-}
+predicate isSingleHashMethodCall(MDHashMethodAccess ma) { not hasAnotherHashCall(ma) }
/** Holds if `MethodAccess` ma is invoked by `MethodAccess` ma2 either directly or indirectly. */
predicate hasParentCall(MethodAccess ma2, MethodAccess ma) {
- ma.getCaller() = ma2.getMethod() and
- not ma2 instanceof MDHashMethodAccess
+ ma.getCaller() = ma2.getMethod()
or
exists(MethodAccess ma3 |
ma.getCaller() = ma3.getMethod() and
- not ma3 instanceof MDHashMethodAccess and
hasParentCall(ma2, ma3)
)
}
-/** Holds if `MethodAccess` is a single hashing call. */
+/** Holds if `MethodAccess` is a single hashing call that is not invoked by a wrapper method. */
predicate isSink(MethodAccess ma) {
isSingleHashMethodCall(ma) and
- not exists(MethodAccess ma2 | hasParentCall(ma2, ma))
+ not exists(MethodAccess ma2 | hasParentCall(ma2, ma)) // Not invoked by a wrapper method which could invoke MDHashMethod in another call stack to reduce FPs
}
/** Sink of hashing calls. */
diff --git a/java/ql/src/semmle/code/java/dataflow/TaintTracking3.qll b/java/ql/src/semmle/code/java/dataflow/TaintTracking3.qll
deleted file mode 100644
index 49c43ed1418..00000000000
--- a/java/ql/src/semmle/code/java/dataflow/TaintTracking3.qll
+++ /dev/null
@@ -1,7 +0,0 @@
-/**
- * Provides classes for performing local (intra-procedural) and
- * global (inter-procedural) taint-tracking analyses.
- */
-module TaintTracking3 {
- import semmle.code.java.dataflow.internal.tainttracking3.TaintTrackingImpl
-}
diff --git a/java/ql/src/semmle/code/java/dataflow/internal/tainttracking3/TaintTrackingImpl.qll b/java/ql/src/semmle/code/java/dataflow/internal/tainttracking3/TaintTrackingImpl.qll
deleted file mode 100644
index b509fad9cd2..00000000000
--- a/java/ql/src/semmle/code/java/dataflow/internal/tainttracking3/TaintTrackingImpl.qll
+++ /dev/null
@@ -1,115 +0,0 @@
-/**
- * Provides an implementation of global (interprocedural) taint tracking.
- * This file re-exports the local (intraprocedural) taint-tracking analysis
- * from `TaintTrackingParameter::Public` and adds a global analysis, mainly
- * exposed through the `Configuration` class. For some languages, this file
- * exists in several identical copies, allowing queries to use multiple
- * `Configuration` classes that depend on each other without introducing
- * mutual recursion among those configurations.
- */
-
-import TaintTrackingParameter::Public
-private import TaintTrackingParameter::Private
-
-/**
- * A configuration of interprocedural taint tracking analysis. This defines
- * sources, sinks, and any other configurable aspect of the analysis. Each
- * use of the taint tracking library must define its own unique extension of
- * this abstract class.
- *
- * A taint-tracking configuration is a special data flow configuration
- * (`DataFlow::Configuration`) that allows for flow through nodes that do not
- * necessarily preserve values but are still relevant from a taint tracking
- * perspective. (For example, string concatenation, where one of the operands
- * is tainted.)
- *
- * To create a configuration, extend this class with a subclass whose
- * characteristic predicate is a unique singleton string. For example, write
- *
- * ```ql
- * class MyAnalysisConfiguration extends TaintTracking::Configuration {
- * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
- * // Override `isSource` and `isSink`.
- * // Optionally override `isSanitizer`.
- * // Optionally override `isSanitizerIn`.
- * // Optionally override `isSanitizerOut`.
- * // Optionally override `isSanitizerGuard`.
- * // Optionally override `isAdditionalTaintStep`.
- * }
- * ```
- *
- * Then, to query whether there is flow between some `source` and `sink`,
- * write
- *
- * ```ql
- * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
- * ```
- *
- * Multiple configurations can coexist, but it is unsupported to depend on
- * another `TaintTracking::Configuration` or a `DataFlow::Configuration` in the
- * overridden predicates that define sources, sinks, or additional steps.
- * Instead, the dependency should go to a `TaintTracking2::Configuration` or a
- * `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
- */
-abstract class Configuration extends DataFlow::Configuration {
- bindingset[this]
- Configuration() { any() }
-
- /**
- * Holds if `source` is a relevant taint source.
- *
- * The smaller this predicate is, the faster `hasFlow()` will converge.
- */
- // overridden to provide taint-tracking specific qldoc
- abstract override predicate isSource(DataFlow::Node source);
-
- /**
- * Holds if `sink` is a relevant taint sink.
- *
- * The smaller this predicate is, the faster `hasFlow()` will converge.
- */
- // overridden to provide taint-tracking specific qldoc
- abstract override predicate isSink(DataFlow::Node sink);
-
- /** Holds if the node `node` is a taint sanitizer. */
- predicate isSanitizer(DataFlow::Node node) { none() }
-
- final override predicate isBarrier(DataFlow::Node node) {
- isSanitizer(node) or
- defaultTaintSanitizer(node)
- }
-
- /** Holds if taint propagation into `node` is prohibited. */
- predicate isSanitizerIn(DataFlow::Node node) { none() }
-
- final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
-
- /** Holds if taint propagation out of `node` is prohibited. */
- predicate isSanitizerOut(DataFlow::Node node) { none() }
-
- final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
-
- /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
- predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
-
- final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
-
- /**
- * Holds if the additional taint propagation step from `node1` to `node2`
- * must be taken into account in the analysis.
- */
- predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
-
- final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
- isAdditionalTaintStep(node1, node2) or
- defaultAdditionalTaintStep(node1, node2)
- }
-
- /**
- * Holds if taint may flow from `source` to `sink` for this configuration.
- */
- // overridden to provide taint-tracking specific qldoc
- override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
- super.hasFlow(source, sink)
- }
-}
diff --git a/java/ql/src/semmle/code/java/dataflow/internal/tainttracking3/TaintTrackingParameter.qll b/java/ql/src/semmle/code/java/dataflow/internal/tainttracking3/TaintTrackingParameter.qll
deleted file mode 100644
index 10fb6b09fa8..00000000000
--- a/java/ql/src/semmle/code/java/dataflow/internal/tainttracking3/TaintTrackingParameter.qll
+++ /dev/null
@@ -1,5 +0,0 @@
-import semmle.code.java.dataflow.internal.TaintTrackingUtil as Public
-
-module Private {
- import semmle.code.java.dataflow.DataFlow3::DataFlow3 as DataFlow
-}
From f1e44bce4abaa982976decbc669c2a327621eee3 Mon Sep 17 00:00:00 2001
From: haby0
Date: Tue, 16 Feb 2021 00:07:44 +0800
Subject: [PATCH 049/725] Update
java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
Co-authored-by: Chris Smowton
---
java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
index 69617cad629..dfa900f81ad 100644
--- a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
+++ b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
@@ -16,7 +16,7 @@ import XQueryInjectionLib
import DataFlow::PathGraph
/**
- * Taint-tracking configuration tracing flow from remote sources, through an XQuery parser, to its eventual execution.
+ * A taint-tracking configuration tracing flow from remote sources, through an XQuery parser, to its eventual execution.
*/
class XQueryInjectionConfig extends TaintTracking::Configuration {
XQueryInjectionConfig() { this = "XQueryInjectionConfig" }
From 92c00cb741ccdab82e7d0d8999e62a7243a989dd Mon Sep 17 00:00:00 2001
From: haby0
Date: Tue, 16 Feb 2021 00:09:21 +0800
Subject: [PATCH 050/725] Update
java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
Co-authored-by: Chris Smowton
---
java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
index dfa900f81ad..0bb85272f08 100644
--- a/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
+++ b/java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql
@@ -30,7 +30,7 @@ class XQueryInjectionConfig extends TaintTracking::Configuration {
}
/**
- * Conveys taint from the input to a `prepareExpression` call to the returned prepared expression.
+ * Holds if taint from the input `pred` to a `prepareExpression` call flows to the returned prepared expression `succ`.
*/
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(XQueryParserCall parser | pred.asExpr() = parser.getInput() and succ.asExpr() = parser)
From 5ce3af05910fefdbc8e07c793ded5593f1a0e706 Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Mon, 15 Feb 2021 21:38:54 +0000
Subject: [PATCH 051/725] Enhance the query and update qldoc
---
.../CWE/CWE-297/InsecureLdapEndpoint.java | 13 +--
.../CWE/CWE-297/InsecureLdapEndpoint.qhelp | 9 +-
.../CWE/CWE-297/InsecureLdapEndpoint.ql | 86 +++++++++++++------
.../CWE/CWE-297/InsecureLdapEndpoint2.java | 17 ++++
.../CWE-297/InsecureLdapEndpoint.expected | 7 +-
.../CWE-297/InsecureLdapEndpoint.java | 60 ++++++++++++-
6 files changed, 149 insertions(+), 43 deletions(-)
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint2.java
diff --git a/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.java b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.java
index 0af5200a150..6f7494f8811 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.java
+++ b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.java
@@ -8,16 +8,11 @@ public class InsecureLdapEndpoint {
env.put(Context.SECURITY_PRINCIPAL, "username");
env.put(Context.SECURITY_CREDENTIALS, "secpassword");
- // BAD - Test configuration with disabled SSL endpoint check.
- {
- System.setProperty("com.sun.jndi.ldap.object.disableEndpointIdentification", "true");
- }
-
- // GOOD - No configuration to disable SSL endpoint check since it is enabled by default.
- {
- }
+ // BAD - Test configuration with disabled SSL endpoint check.
+ {
+ System.setProperty("com.sun.jndi.ldap.object.disableEndpointIdentification", "true");
+ }
return env;
}
-
}
diff --git a/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.qhelp b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.qhelp
index 3aee17f9777..671f713ca0d 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.qhelp
+++ b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.qhelp
@@ -5,8 +5,8 @@
Java versions 8u181 or greater have enabled LDAPS endpoint identification by default. Nowadays infrastructure services like LDAP are commonly deployed behind load balancers therefore the LDAP server name can be different from the FQDN of the LDAPS endpoint. If a service certificate does not properly contain a matching DNS name as part of the certificate, Java will reject it by default.
-Instead of addressing the issue properly by having a compliant certificate deployed, frequently developers simply disable SSL endpoint check.
-This query checks whether LDAPS endpoint check is disabled in system properties.
+Instead of addressing the issue properly by having a compliant certificate deployed, frequently developers simply disable LDAPS endpoint check.
+Failing to validate the certificate makes the SSL session susceptible to a man-in-the-middle attack. This query checks whether LDAPS endpoint check is disabled in system properties.
@@ -14,9 +14,10 @@
-The following two examples show two ways of configuring SSL endpoint. In the 'BAD' case,
+
The following two examples show two ways of configuring LDAPS endpoint. In the 'BAD' case,
endpoint check is disabled. In the 'GOOD' case, endpoint check is left enabled through the default Java configuration.
-
+
+>
diff --git a/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.ql b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.ql
index 266b2b999a8..144f92875e7 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.ql
@@ -1,17 +1,15 @@
/**
- * @name Insecure LDAP Endpoint Configuration
- * @description Java application configured to disable LDAP endpoint identification does not validate the SSL certificate to properly ensure that it is actually associated with that host.
+ * @name Insecure LDAPS Endpoint Configuration
+ * @description Java application configured to disable LDAPS endpoint identification does not validate the SSL certificate to properly ensure that it is actually associated with that host.
* @kind problem
- * @id java/insecure-ldap-endpoint
+ * @id java/insecure-ldaps-endpoint
* @tags security
* external/cwe-297
*/
import java
-/**
- * The method to set a system property.
- */
+/** The method to set a system property. */
class SetSystemPropertyMethod extends Method {
SetSystemPropertyMethod() {
this.hasName("setProperty") and
@@ -19,19 +17,22 @@ class SetSystemPropertyMethod extends Method {
}
}
-/**
- * The method to set Java properties.
- */
-class SetPropertyMethod extends Method {
- SetPropertyMethod() {
- this.hasName("setProperty") and
- this.getDeclaringType().hasQualifiedName("java.util", "Properties")
- }
+/** The class `java.util.Hashtable`. */
+class TypeHashtable extends Class {
+ TypeHashtable() { this.getSourceDeclaration().hasQualifiedName("java.util", "Hashtable") }
}
/**
- * The method to set system properties.
+ * The method to set Java properties either through `setProperty` declared in the class `Properties` or `put` declared in its parent class `HashTable`.
*/
+class SetPropertyMethod extends Method {
+ SetPropertyMethod() {
+ this.getDeclaringType().getAnAncestor() instanceof TypeHashtable and
+ this.hasName(["put", "setProperty"])
+ }
+}
+
+/** The method to set system properties. */
class SetSystemPropertiesMethod extends Method {
SetSystemPropertiesMethod() {
this.hasName("setProperties") and
@@ -39,27 +40,62 @@ class SetSystemPropertiesMethod extends Method {
}
}
+/** Holds if an expression is evaluated to the string literal `com.sun.jndi.ldap.object.disableEndpointIdentification`. */
+predicate isPropertyDisableLdapEndpointId(Expr expr) {
+ expr.(CompileTimeConstantExpr).getStringValue() =
+ "com.sun.jndi.ldap.object.disableEndpointIdentification"
+ or
+ exists(Field f |
+ expr = f.getAnAccess() and
+ f.getAnAssignedValue().(StringLiteral).getValue() =
+ "com.sun.jndi.ldap.object.disableEndpointIdentification"
+ )
+}
+
+/** Holds if an expression is evaluated to the boolean value true. */
+predicate isBooleanTrue(Expr expr) {
+ expr.(CompileTimeConstantExpr).getStringValue() = "true" // "true"
+ or
+ expr.(BooleanLiteral).getBooleanValue() = true // true
+ or
+ exists(MethodAccess ma |
+ expr = ma and
+ ma.getMethod().hasName("toString") and
+ ma.getQualifier().(FieldAccess).getField().hasName("TRUE") and
+ ma.getQualifier()
+ .(FieldAccess)
+ .getField()
+ .getDeclaringType()
+ .hasQualifiedName("java.lang", "Boolean") // Boolean.TRUE.toString()
+ )
+}
+
+/** Holds if `ma` is in a test class or method. */
+predicate isTestMethod(MethodAccess ma) {
+ ma.getMethod() instanceof TestMethod or
+ ma.getEnclosingCallable().getDeclaringType().getPackage().getName().matches("%test%") or
+ ma.getEnclosingCallable().getDeclaringType().getName().toLowerCase().matches("%test%")
+}
+
/** Holds if `MethodAccess` ma disables SSL endpoint check. */
predicate isInsecureSSLEndpoint(MethodAccess ma) {
(
ma.getMethod() instanceof SetSystemPropertyMethod and
- (
- ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() =
- "com.sun.jndi.ldap.object.disableEndpointIdentification" and
- ma.getArgument(1).(CompileTimeConstantExpr).getStringValue() = "true" //com.sun.jndi.ldap.object.disableEndpointIdentification=true
- )
+ isPropertyDisableLdapEndpointId(ma.getArgument(0)) and
+ isBooleanTrue(ma.getArgument(1)) //com.sun.jndi.ldap.object.disableEndpointIdentification=true
or
ma.getMethod() instanceof SetSystemPropertiesMethod and
exists(MethodAccess ma2 |
ma2.getMethod() instanceof SetPropertyMethod and
- ma2.getArgument(0).(CompileTimeConstantExpr).getStringValue() =
- "com.sun.jndi.ldap.object.disableEndpointIdentification" and
- ma2.getArgument(1).(CompileTimeConstantExpr).getStringValue() = "true" and //com.sun.jndi.ldap.object.disableEndpointIdentification=true
+ isPropertyDisableLdapEndpointId(ma2.getArgument(0)) and
+ isBooleanTrue(ma2.getArgument(1)) and //com.sun.jndi.ldap.object.disableEndpointIdentification=true
ma2.getQualifier().(VarAccess).getVariable().getAnAccess() = ma.getArgument(0) // systemProps.setProperties(properties)
)
)
}
from MethodAccess ma
-where isInsecureSSLEndpoint(ma)
-select ma, "SSL configuration allows insecure endpoint configuration"
+where
+ isInsecureSSLEndpoint(ma) and
+ not isTestMethod(ma)
+select ma, "LDAPS configuration allows insecure endpoint identification"
diff --git a/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint2.java b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint2.java
new file mode 100644
index 00000000000..2a5c3c87bc7
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint2.java
@@ -0,0 +1,17 @@
+public class InsecureLdapEndpoint2 {
+ public Hashtable createConnectionEnv() {
+ Hashtable env = new Hashtable();
+ env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+ env.put(Context.PROVIDER_URL, "ldaps://ad.your-server.com:636");
+
+ env.put(Context.SECURITY_AUTHENTICATION, "simple");
+ env.put(Context.SECURITY_PRINCIPAL, "username");
+ env.put(Context.SECURITY_CREDENTIALS, "secpassword");
+
+ // GOOD - No configuration to disable SSL endpoint check since it is enabled by default.
+ {
+ }
+
+ return env;
+ }
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-297/InsecureLdapEndpoint.expected b/java/ql/test/experimental/query-tests/security/CWE-297/InsecureLdapEndpoint.expected
index 29eb44937ee..f37e49e30e0 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-297/InsecureLdapEndpoint.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-297/InsecureLdapEndpoint.expected
@@ -1,2 +1,5 @@
-| InsecureLdapEndpoint.java:17:9:17:92 | setProperty(...) | SSL configuration allows insecure endpoint configuration |
-| InsecureLdapEndpoint.java:48:3:48:34 | setProperties(...) | SSL configuration allows insecure endpoint configuration |
+| InsecureLdapEndpoint.java:19:9:19:92 | setProperty(...) | LDAPS configuration allows insecure endpoint identification |
+| InsecureLdapEndpoint.java:50:9:50:40 | setProperties(...) | LDAPS configuration allows insecure endpoint identification |
+| InsecureLdapEndpoint.java:68:9:68:40 | setProperties(...) | LDAPS configuration allows insecure endpoint identification |
+| InsecureLdapEndpoint.java:84:9:84:94 | setProperty(...) | LDAPS configuration allows insecure endpoint identification |
+| InsecureLdapEndpoint.java:102:9:102:40 | setProperties(...) | LDAPS configuration allows insecure endpoint identification |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-297/InsecureLdapEndpoint.java b/java/ql/test/experimental/query-tests/security/CWE-297/InsecureLdapEndpoint.java
index 44c37864af0..72f6bee118a 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-297/InsecureLdapEndpoint.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-297/InsecureLdapEndpoint.java
@@ -3,7 +3,9 @@ import java.util.Properties;
import javax.naming.Context;
public class InsecureLdapEndpoint {
- // BAD - Test configuration with disabled SSL endpoint check.
+ private static String PROP_DISABLE_LDAP_ENDPOINT_IDENTIFICATION = "com.sun.jndi.ldap.object.disableEndpointIdentification";
+
+ // BAD - Test configuration with disabled LDAPS endpoint check using `System.setProperty()`.
public Hashtable createConnectionEnv() {
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
@@ -19,7 +21,7 @@ public class InsecureLdapEndpoint {
return env;
}
- // GOOD - Test configuration without disabling SSL endpoint check.
+ // GOOD - Test configuration without disabling LDAPS endpoint check.
public Hashtable createConnectionEnv2() {
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
@@ -32,7 +34,7 @@ public class InsecureLdapEndpoint {
return env;
}
- // BAD - Test configuration with disabled SSL endpoint check.
+ // BAD - Test configuration with disabled LDAPS endpoint check using `System.setProperties()`.
public Hashtable createConnectionEnv3() {
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
@@ -49,4 +51,56 @@ public class InsecureLdapEndpoint {
return env;
}
+
+ // BAD - Test configuration with disabled LDAPS endpoint check using `HashTable.put()`.
+ public Hashtable createConnectionEnv4() {
+ Hashtable env = new Hashtable();
+ env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+ env.put(Context.PROVIDER_URL, "ldaps://ad.your-server.com:636");
+
+ env.put(Context.SECURITY_AUTHENTICATION, "simple");
+ env.put(Context.SECURITY_PRINCIPAL, "username");
+ env.put(Context.SECURITY_CREDENTIALS, "secpassword");
+
+ // Disable SSL endpoint check
+ Properties properties = new Properties();
+ properties.put("com.sun.jndi.ldap.object.disableEndpointIdentification", "true");
+ System.setProperties(properties);
+
+ return env;
+ }
+
+ // BAD - Test configuration with disabled LDAPS endpoint check using the `TRUE` boolean field.
+ public Hashtable createConnectionEnv5() {
+ Hashtable env = new Hashtable();
+ env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+ env.put(Context.PROVIDER_URL, "ldaps://ad.your-server.com:636");
+
+ env.put(Context.SECURITY_AUTHENTICATION, "simple");
+ env.put(Context.SECURITY_PRINCIPAL, "username");
+ env.put(Context.SECURITY_CREDENTIALS, "secpassword");
+
+ // Disable SSL endpoint check
+ System.setProperty(PROP_DISABLE_LDAP_ENDPOINT_IDENTIFICATION, Boolean.TRUE.toString());
+
+ return env;
+ }
+
+ // BAD - Test configuration with disabled LDAPS endpoint check using a boolean value.
+ public Hashtable createConnectionEnv6() {
+ Hashtable env = new Hashtable();
+ env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+ env.put(Context.PROVIDER_URL, "ldaps://ad.your-server.com:636");
+
+ env.put(Context.SECURITY_AUTHENTICATION, "simple");
+ env.put(Context.SECURITY_PRINCIPAL, "username");
+ env.put(Context.SECURITY_CREDENTIALS, "secpassword");
+
+ // Disable SSL endpoint check
+ Properties properties = new Properties();
+ properties.put("com.sun.jndi.ldap.object.disableEndpointIdentification", true);
+ System.setProperties(properties);
+
+ return env;
+ }
}
From e698ee77f7aa1ceb2034100429003ba0211681c6 Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Tue, 16 Feb 2021 14:11:39 +0000
Subject: [PATCH 052/725] Update qldoc and test method
---
.../Security/CWE/CWE-297/InsecureLdapEndpoint.qhelp | 4 ++--
.../Security/CWE/CWE-297/InsecureLdapEndpoint.ql | 7 ++++---
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.qhelp b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.qhelp
index 671f713ca0d..cb43f52515b 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.qhelp
+++ b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.qhelp
@@ -5,8 +5,8 @@
Java versions 8u181 or greater have enabled LDAPS endpoint identification by default. Nowadays infrastructure services like LDAP are commonly deployed behind load balancers therefore the LDAP server name can be different from the FQDN of the LDAPS endpoint. If a service certificate does not properly contain a matching DNS name as part of the certificate, Java will reject it by default.
-Instead of addressing the issue properly by having a compliant certificate deployed, frequently developers simply disable LDAPS endpoint check.
-Failing to validate the certificate makes the SSL session susceptible to a man-in-the-middle attack. This query checks whether LDAPS endpoint check is disabled in system properties.
+Instead of addressing the issue properly by having a compliant certificate deployed, frequently developers simply disable the LDAPS endpoint check.
+Failing to validate the certificate makes the SSL session susceptible to a man-in-the-middle attack. This query checks whether the LDAPS endpoint check is disabled in system properties.
diff --git a/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.ql b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.ql
index 144f92875e7..c83aeb4a6a5 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.ql
@@ -32,7 +32,7 @@ class SetPropertyMethod extends Method {
}
}
-/** The method to set system properties. */
+/** The `setProperties` method declared in `java.lang.System`. */
class SetSystemPropertiesMethod extends Method {
SetSystemPropertiesMethod() {
this.hasName("setProperties") and
@@ -40,7 +40,7 @@ class SetSystemPropertiesMethod extends Method {
}
}
-/** Holds if an expression is evaluated to the string literal `com.sun.jndi.ldap.object.disableEndpointIdentification`. */
+/** Holds if `expr` is evaluated to the string literal `com.sun.jndi.ldap.object.disableEndpointIdentification`. */
predicate isPropertyDisableLdapEndpointId(Expr expr) {
expr.(CompileTimeConstantExpr).getStringValue() =
"com.sun.jndi.ldap.object.disableEndpointIdentification"
@@ -72,7 +72,8 @@ predicate isBooleanTrue(Expr expr) {
/** Holds if `ma` is in a test class or method. */
predicate isTestMethod(MethodAccess ma) {
- ma.getMethod() instanceof TestMethod or
+ ma.getEnclosingCallable() instanceof TestMethod or
+ ma.getEnclosingCallable().getDeclaringType() instanceof TestClass or
ma.getEnclosingCallable().getDeclaringType().getPackage().getName().matches("%test%") or
ma.getEnclosingCallable().getDeclaringType().getName().toLowerCase().matches("%test%")
}
From 520ba472934962033fd1b11f222ca0c0cfc7e145 Mon Sep 17 00:00:00 2001
From: Francis Alexander
Date: Wed, 17 Feb 2021 08:35:50 +0530
Subject: [PATCH 053/725] Sanitizer improvements from code review
---
.../Security/CWE/CWE-346/UnvalidatedCors.ql | 36 ++++++++++++-------
1 file changed, 23 insertions(+), 13 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql
index 17cf94d2c1e..31de9821a96 100644
--- a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql
+++ b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql
@@ -30,9 +30,14 @@ private predicate setsAllowCredentials(MethodAccess header) {
class CorsProbableCheckAccess extends MethodAccess {
CorsProbableCheckAccess() {
- getMethod().getName() = ["contains", "equals"] and
- getMethod().getDeclaringType().getQualifiedName() =
- ["java.util.List", "java.util.ArrayList", "java.lang.String"]
+ getMethod().hasName("contains") and
+ getMethod()
+ .getDeclaringType()
+ .getASourceSupertype*()
+ .hasQualifiedName("java.util", "Collection")
+ or
+ getMethod().hasName("equals") and
+ getQualifier().getType() instanceof TypeString
}
}
@@ -58,18 +63,23 @@ class CorsOriginConfig extends TaintTracking::Configuration {
)
}
- /*
- * This should ideally check, the origin being validated against a list/array-list.
- * or function being used to validate the origin, which has a flow from its parameter to any of the CorsProbableCheckAccess functions
+ /**
+ * this sanitizer is oversimplistic:
+ * - it only considers local dataflows
+ * - it will consider any method calling `Collection.contains` or `String.equals` as a sanitizer
+ * no matter if that check is taken into account and its result reaches the
+ * return statement of the wrapper.
*/
-
override predicate isSanitizer(DataFlow::Node node) {
- node.asExpr() = any(CorsProbableCheckAccess ma).getAnArgument()
- or
- exists(MethodAccess ma, CorsProbableCheckAccess ca |
- ma.getMethod().calls(ca.getMethod()) and
- DataFlow::localExprFlow(ma.getMethod().getAParameter().getAnAccess(), ca.getAnArgument()) and
- (node.asExpr() = ma.getAnArgument() or node.asExpr() = ma.getAnArgument().getAChildExpr())
+ exists(CorsProbableCheckAccess check |
+ TaintTracking::localTaint(node, DataFlow::exprNode(check.getAnArgument()))
+ or
+ exists(MethodAccess wrapperAccess, Method wrapper, int i |
+ TaintTracking::localTaint(node, DataFlow::exprNode(wrapperAccess.getArgument(i))) and
+ wrapperAccess.getMethod() = wrapper and
+ TaintTracking::localTaint(DataFlow::parameterNode(wrapper.getParameter(i)),
+ DataFlow::exprNode(check.getAnArgument()))
+ )
)
}
}
From 58971f9f4e78789f546ce60fa7b5837d198cd288 Mon Sep 17 00:00:00 2001
From: Francis Alexander
Date: Wed, 17 Feb 2021 16:01:27 +0530
Subject: [PATCH 054/725] Switch qualified name to available CollectionType
---
java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql
index 31de9821a96..2a7152b1cad 100644
--- a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql
+++ b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql
@@ -31,10 +31,7 @@ private predicate setsAllowCredentials(MethodAccess header) {
class CorsProbableCheckAccess extends MethodAccess {
CorsProbableCheckAccess() {
getMethod().hasName("contains") and
- getMethod()
- .getDeclaringType()
- .getASourceSupertype*()
- .hasQualifiedName("java.util", "Collection")
+ getMethod().getDeclaringType().getASourceSupertype*() instanceof CollectionType
or
getMethod().hasName("equals") and
getQualifier().getType() instanceof TypeString
From 2baf2aa5c194e356dc563054a176c24d3ccb7311 Mon Sep 17 00:00:00 2001
From: Francis Alexander
Date: Wed, 17 Feb 2021 18:58:32 +0530
Subject: [PATCH 055/725] Apply suggestions from code review - improved
sanitizer checks.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Alvaro Muñoz
---
java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql | 3 +++
1 file changed, 3 insertions(+)
diff --git a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql
index 2a7152b1cad..9f9c12b2295 100644
--- a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql
+++ b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql
@@ -33,6 +33,9 @@ class CorsProbableCheckAccess extends MethodAccess {
getMethod().hasName("contains") and
getMethod().getDeclaringType().getASourceSupertype*() instanceof CollectionType
or
+ getMethod().hasName("containsKey") and
+ getMethod().getDeclaringType().getASourceSupertype*() instanceof MapType
+ or
getMethod().hasName("equals") and
getQualifier().getType() instanceof TypeString
}
From 8119fd2ad1e2d009991f9a12969ccd55054163c4 Mon Sep 17 00:00:00 2001
From: haby0
Date: Thu, 18 Feb 2021 18:11:10 +0800
Subject: [PATCH 056/725] *)add JsonHijacking ql query
---
.../Security/CWE/CWE-352/JsonHijacking.java | 119 ++++++++++++++++++
.../Security/CWE/CWE-352/JsonHijacking.qhelp | 35 ++++++
.../src/Security/CWE/CWE-352/JsonHijacking.ql | 32 +++++
.../Security/CWE/CWE-352/JsonHijackingLib.qll | 92 ++++++++++++++
.../Security/CWE/CWE-352/JsonStringLib.qll | 42 +++++++
.../security/CWE-352/JsonHijacking.expected | 48 +++++++
.../security/CWE-352/JsonHijacking.java | 119 ++++++++++++++++++
.../security/CWE-352/JsonHijacking.qlref | 1 +
.../query-tests/security/CWE-352/options | 1 +
.../com/alibaba/fastjson/JSON.java | 4 +
.../stereotype/Controller.java | 14 +++
.../core/annotation/AliasFor.class | Bin 0 -> 385 bytes
.../core/annotation/AliasFor.java | 10 ++
.../web/bind/annotation/GetMapping.class | Bin 0 -> 504 bytes
.../web/bind/annotation/GetMapping.java | 19 +++
.../web/bind/annotation/RequestMapping.java | 13 ++
.../web/bind/annotation/ResponseBody.class | Bin 0 -> 184 bytes
.../web/bind/annotation/ResponseBody.java | 4 +
18 files changed, 553 insertions(+)
create mode 100644 java/ql/src/Security/CWE/CWE-352/JsonHijacking.java
create mode 100644 java/ql/src/Security/CWE/CWE-352/JsonHijacking.qhelp
create mode 100644 java/ql/src/Security/CWE/CWE-352/JsonHijacking.ql
create mode 100644 java/ql/src/Security/CWE/CWE-352/JsonHijackingLib.qll
create mode 100644 java/ql/src/Security/CWE/CWE-352/JsonStringLib.qll
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonHijacking.expected
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonHijacking.java
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonHijacking.qlref
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/options
create mode 100644 java/ql/test/stubs/spring-context-5.3.2/org/springframework/stereotype/Controller.java
create mode 100644 java/ql/test/stubs/spring-core-5.3.2/org/springframework/core/annotation/AliasFor.class
create mode 100644 java/ql/test/stubs/spring-core-5.3.2/org/springframework/core/annotation/AliasFor.java
create mode 100644 java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/GetMapping.class
create mode 100644 java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/GetMapping.java
create mode 100644 java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/RequestMapping.java
create mode 100644 java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/ResponseBody.class
create mode 100644 java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/ResponseBody.java
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonHijacking.java b/java/ql/src/Security/CWE/CWE-352/JsonHijacking.java
new file mode 100644
index 00000000000..d08d436fa07
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-352/JsonHijacking.java
@@ -0,0 +1,119 @@
+import com.alibaba.fastjson.JSONObject;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.gson.Gson;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Random;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+@Controller
+public class JsonHijacking {
+
+ private static HashMap hashMap = new HashMap();
+
+ static {
+ hashMap.put("username","admin");
+ hashMap.put("password","123456");
+ }
+
+
+ @GetMapping(value = "jsonp1")
+ @ResponseBody
+ public String bad1(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+
+ Gson gson = new Gson();
+ String result = gson.toJson(hashMap);
+ resultStr = jsonpCallback + "(" + result + ")";
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp2")
+ @ResponseBody
+ public String bad2(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+
+ resultStr = jsonpCallback + "(" + JSONObject.toJSONString(hashMap) + ")";
+
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp3")
+ @ResponseBody
+ public String bad3(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String jsonStr = getJsonStr(hashMap);
+ resultStr = jsonpCallback + "(" + jsonStr + ")";
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp4")
+ @ResponseBody
+ public String bad4(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String restr = JSONObject.toJSONString(hashMap);
+ resultStr = jsonpCallback + "(" + restr + ");";
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp5")
+ @ResponseBody
+ public void bad5(HttpServletRequest request,
+ HttpServletResponse response) throws Exception {
+ response.setContentType("application/json");
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ PrintWriter pw = null;
+ Gson gson = new Gson();
+ String result = gson.toJson(hashMap);
+
+ String resultStr = null;
+ pw = response.getWriter();
+ resultStr = jsonpCallback + "(" + result + ")";
+ pw.println(resultStr);
+ }
+
+ @GetMapping(value = "jsonp6")
+ @ResponseBody
+ public void bad6(HttpServletRequest request,
+ HttpServletResponse response) throws Exception {
+ response.setContentType("application/json");
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ PrintWriter pw = null;
+ ObjectMapper mapper = new ObjectMapper();
+ String result = mapper.writeValueAsString(hashMap);
+ String resultStr = null;
+ pw = response.getWriter();
+ resultStr = jsonpCallback + "(" + result + ")";
+ pw.println(resultStr);
+ }
+
+ @GetMapping(value = "jsonp7")
+ @ResponseBody
+ public String good(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+
+ String val = "";
+ Random random = new Random();
+ for (int i = 0; i < 10; i++) {
+ val += String.valueOf(random.nextInt(10));
+ }
+ // good
+ jsonpCallback = jsonpCallback + "_" + val;
+ String jsonStr = getJsonStr(hashMap);
+ resultStr = jsonpCallback + "(" + jsonStr + ")";
+ return resultStr;
+ }
+
+ public static String getJsonStr(Object result) {
+ return JSONObject.toJSONString(result);
+ }
+}
\ No newline at end of file
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonHijacking.qhelp b/java/ql/src/Security/CWE/CWE-352/JsonHijacking.qhelp
new file mode 100644
index 00000000000..38e1845f992
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-352/JsonHijacking.qhelp
@@ -0,0 +1,35 @@
+
+
+
+The software uses external input as the function name to wrap JSON data and return it to the client as a request response. When there is a cross-domain problem,
+there is a problem of sensitive information leakage.
+
+
+
+
+The function name verification processing for external input can effectively prevent the leakage of sensitive information.
+
+
+
+
+The following example shows the case of no verification processing and verification processing for the external input function name.
+
+
+
+
+
+
+
+OWASPLondon20161124_JSON_Hijacking_Gareth_Heyes:
+JSON hijacking.
+
+
+Practical JSONP Injection:
+
+ Completely controllable from the URL (GET variable)
+.
+
+
+
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonHijacking.ql b/java/ql/src/Security/CWE/CWE-352/JsonHijacking.ql
new file mode 100644
index 00000000000..a6a6d2475f0
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-352/JsonHijacking.ql
@@ -0,0 +1,32 @@
+/**
+ * @name JSON Hijacking
+ * @description User-controlled callback function names that are not verified are vulnerable
+ * to json hijacking attacks.
+ * @kind path-problem
+ * @problem.severity error
+ * @precision high
+ * @id java/Json-hijacking
+ * @tags security
+ * external/cwe/cwe-352
+ */
+
+import java
+import JsonHijackingLib
+import semmle.code.java.dataflow.FlowSources
+import DataFlow::PathGraph
+
+/** Taint-tracking configuration tracing flow from remote sources to output jsonp data. */
+class JsonHijackingConfig extends TaintTracking::Configuration {
+ JsonHijackingConfig() { this = "JsonHijackingConfig" }
+
+ override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
+
+ override predicate isSink(DataFlow::Node sink) { sink instanceof JsonHijackingSink }
+}
+
+from DataFlow::PathNode source, DataFlow::PathNode sink, JsonHijackingConfig conf
+where
+ conf.hasFlowPath(source, sink) and
+ exists(JsonHijackingFlowConfig jhfc | jhfc.hasFlowTo(sink.getNode()))
+select sink.getNode(), source, sink, "Json Hijacking query might include code from $@.",
+ source.getNode(), "this user input"
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonHijackingLib.qll b/java/ql/src/Security/CWE/CWE-352/JsonHijackingLib.qll
new file mode 100644
index 00000000000..ba91a6670bf
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-352/JsonHijackingLib.qll
@@ -0,0 +1,92 @@
+import java
+import DataFlow
+import JsonStringLib
+import semmle.code.java.dataflow.DataFlow
+import semmle.code.java.dataflow.FlowSources
+import semmle.code.java.frameworks.spring.SpringController
+
+/** A data flow sink for unvalidated user input that is used to jsonp. */
+abstract class JsonHijackingSink extends DataFlow::Node { }
+
+/** Use ```print```, ```println```, ```write``` to output result. */
+private class WriterPrintln extends JsonHijackingSink {
+ WriterPrintln() {
+ exists(MethodAccess ma |
+ ma.getMethod().getName().regexpMatch("print|println|write") and
+ ma.getMethod()
+ .getDeclaringType()
+ .getASourceSupertype*()
+ .hasQualifiedName("java.io", "PrintWriter") and
+ ma.getArgument(0) = this.asExpr()
+ )
+ }
+}
+
+/** Spring Request Method return result. */
+private class SpringReturn extends JsonHijackingSink {
+ SpringReturn() {
+ exists(ReturnStmt rs, Method m | m = rs.getEnclosingCallable() |
+ m instanceof SpringRequestMappingMethod and
+ rs.getResult() = this.asExpr()
+ )
+ }
+}
+
+/** A concatenate expression using `(` and `)` or `);`. */
+class JsonHijackingExpr extends AddExpr {
+ JsonHijackingExpr() {
+ getRightOperand().toString().regexpMatch("\"\\)\"|\"\\);\"") and
+ getLeftOperand()
+ .(AddExpr)
+ .getLeftOperand()
+ .(AddExpr)
+ .getRightOperand()
+ .toString()
+ .regexpMatch("\"\\(\"")
+ }
+
+ /** Get the jsonp function name of this expression */
+ Expr getFunctionName() {
+ result = getLeftOperand().(AddExpr).getLeftOperand().(AddExpr).getLeftOperand()
+ }
+
+ /** Get the json data of this expression */
+ Expr getJsonExpr() { result = getLeftOperand().(AddExpr).getRightOperand() }
+}
+
+/** A data flow configuration tracing flow from remote sources to jsonp function name. */
+class RemoteFlowConfig extends DataFlow2::Configuration {
+ RemoteFlowConfig() { this = "RemoteFlowConfig" }
+
+ override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(JsonHijackingExpr jhe | jhe.getFunctionName() = sink.asExpr())
+ }
+}
+
+/** A data flow configuration tracing flow from json data to splicing jsonp data. */
+class JsonDataFlowConfig extends DataFlow2::Configuration {
+ JsonDataFlowConfig() { this = "JsonDataFlowConfig" }
+
+ override predicate isSource(DataFlow::Node src) { src instanceof JsonpStringSource }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(JsonHijackingExpr jhe | jhe.getJsonExpr() = sink.asExpr())
+ }
+}
+
+/** Taint-tracking configuration tracing flow from user-controllable function name jsonp data to output jsonp data. */
+class JsonHijackingFlowConfig extends TaintTracking::Configuration {
+ JsonHijackingFlowConfig() { this = "JsonHijackingFlowConfig" }
+
+ override predicate isSource(DataFlow::Node src) {
+ exists(JsonHijackingExpr jhe, JsonDataFlowConfig jdfc, RemoteFlowConfig rfc |
+ jhe = src.asExpr() and
+ jdfc.hasFlowTo(DataFlow::exprNode(jhe.getJsonExpr())) and
+ rfc.hasFlowTo(DataFlow::exprNode(jhe.getFunctionName()))
+ )
+ }
+
+ override predicate isSink(DataFlow::Node sink) { sink instanceof JsonHijackingSink }
+}
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonStringLib.qll b/java/ql/src/Security/CWE/CWE-352/JsonStringLib.qll
new file mode 100644
index 00000000000..0da8bc860d1
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-352/JsonStringLib.qll
@@ -0,0 +1,42 @@
+import java
+import semmle.code.java.dataflow.DataFlow
+import semmle.code.java.dataflow.FlowSources
+import DataFlow::PathGraph
+
+/** Json string type data */
+abstract class JsonpStringSource extends DataFlow::Node { }
+
+/** Convert to String using Gson library. */
+private class GsonString extends JsonpStringSource {
+ GsonString() {
+ exists(MethodAccess ma, Method m | ma.getMethod() = m |
+ m.hasName("toJson") and
+ m.getDeclaringType().getASupertype*().hasQualifiedName("com.google.gson", "Gson") and
+ this.asExpr() = ma
+ )
+ }
+}
+
+/** Convert to String using Fastjson library. */
+private class FastjsonString extends JsonpStringSource {
+ FastjsonString() {
+ exists(MethodAccess ma, Method m | ma.getMethod() = m |
+ m.hasName("toJSONString") and
+ m.getDeclaringType().getASupertype*().hasQualifiedName("com.alibaba.fastjson", "JSON") and
+ this.asExpr() = ma
+ )
+ }
+}
+
+/** Convert to String using Jackson library. */
+private class JacksonString extends JsonpStringSource {
+ JacksonString() {
+ exists(MethodAccess ma, Method m | ma.getMethod() = m |
+ m.hasName("writeValueAsString") and
+ m.getDeclaringType()
+ .getASupertype*()
+ .hasQualifiedName("com.fasterxml.jackson.databind", "ObjectMapper") and
+ this.asExpr() = ma
+ )
+ }
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonHijacking.expected b/java/ql/test/experimental/query-tests/security/CWE-352/JsonHijacking.expected
new file mode 100644
index 00000000000..8efc3be1673
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonHijacking.expected
@@ -0,0 +1,48 @@
+edges
+| JsonHijacking.java:28:32:28:68 | getParameter(...) : String | JsonHijacking.java:33:16:33:24 | resultStr |
+| JsonHijacking.java:32:21:32:54 | ... + ... : String | JsonHijacking.java:33:16:33:24 | resultStr |
+| JsonHijacking.java:40:32:40:68 | getParameter(...) : String | JsonHijacking.java:44:16:44:24 | resultStr |
+| JsonHijacking.java:42:21:42:80 | ... + ... : String | JsonHijacking.java:44:16:44:24 | resultStr |
+| JsonHijacking.java:51:32:51:68 | getParameter(...) : String | JsonHijacking.java:54:16:54:24 | resultStr |
+| JsonHijacking.java:53:21:53:55 | ... + ... : String | JsonHijacking.java:54:16:54:24 | resultStr |
+| JsonHijacking.java:61:32:61:68 | getParameter(...) : String | JsonHijacking.java:64:16:64:24 | resultStr |
+| JsonHijacking.java:63:21:63:54 | ... + ... : String | JsonHijacking.java:64:16:64:24 | resultStr |
+| JsonHijacking.java:72:32:72:68 | getParameter(...) : String | JsonHijacking.java:80:20:80:28 | resultStr |
+| JsonHijacking.java:79:21:79:54 | ... + ... : String | JsonHijacking.java:80:20:80:28 | resultStr |
+| JsonHijacking.java:88:32:88:68 | getParameter(...) : String | JsonHijacking.java:95:20:95:28 | resultStr |
+| JsonHijacking.java:94:21:94:54 | ... + ... : String | JsonHijacking.java:95:20:95:28 | resultStr |
+| JsonHijacking.java:102:32:102:68 | getParameter(...) : String | JsonHijacking.java:113:16:113:24 | resultStr |
+nodes
+| JsonHijacking.java:28:32:28:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonHijacking.java:32:21:32:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonHijacking.java:33:16:33:24 | resultStr | semmle.label | resultStr |
+| JsonHijacking.java:33:16:33:24 | resultStr | semmle.label | resultStr |
+| JsonHijacking.java:40:32:40:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonHijacking.java:42:21:42:80 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonHijacking.java:44:16:44:24 | resultStr | semmle.label | resultStr |
+| JsonHijacking.java:44:16:44:24 | resultStr | semmle.label | resultStr |
+| JsonHijacking.java:51:32:51:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonHijacking.java:53:21:53:55 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonHijacking.java:54:16:54:24 | resultStr | semmle.label | resultStr |
+| JsonHijacking.java:54:16:54:24 | resultStr | semmle.label | resultStr |
+| JsonHijacking.java:61:32:61:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonHijacking.java:63:21:63:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonHijacking.java:64:16:64:24 | resultStr | semmle.label | resultStr |
+| JsonHijacking.java:64:16:64:24 | resultStr | semmle.label | resultStr |
+| JsonHijacking.java:72:32:72:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonHijacking.java:79:21:79:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonHijacking.java:80:20:80:28 | resultStr | semmle.label | resultStr |
+| JsonHijacking.java:80:20:80:28 | resultStr | semmle.label | resultStr |
+| JsonHijacking.java:88:32:88:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonHijacking.java:94:21:94:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonHijacking.java:95:20:95:28 | resultStr | semmle.label | resultStr |
+| JsonHijacking.java:95:20:95:28 | resultStr | semmle.label | resultStr |
+| JsonHijacking.java:102:32:102:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonHijacking.java:113:16:113:24 | resultStr | semmle.label | resultStr |
+#select
+| JsonHijacking.java:33:16:33:24 | resultStr | JsonHijacking.java:28:32:28:68 | getParameter(...) : String | JsonHijacking.java:33:16:33:24 | resultStr | Json Hijacking query might include code from $@. | JsonHijacking.java:28:32:28:68 | getParameter(...) | this user input |
+| JsonHijacking.java:44:16:44:24 | resultStr | JsonHijacking.java:40:32:40:68 | getParameter(...) : String | JsonHijacking.java:44:16:44:24 | resultStr | Json Hijacking query might include code from $@. | JsonHijacking.java:40:32:40:68 | getParameter(...) | this user input |
+| JsonHijacking.java:54:16:54:24 | resultStr | JsonHijacking.java:51:32:51:68 | getParameter(...) : String | JsonHijacking.java:54:16:54:24 | resultStr | Json Hijacking query might include code from $@. | JsonHijacking.java:51:32:51:68 | getParameter(...) | this user input |
+| JsonHijacking.java:64:16:64:24 | resultStr | JsonHijacking.java:61:32:61:68 | getParameter(...) : String | JsonHijacking.java:64:16:64:24 | resultStr | Json Hijacking query might include code from $@. | JsonHijacking.java:61:32:61:68 | getParameter(...) | this user input |
+| JsonHijacking.java:80:20:80:28 | resultStr | JsonHijacking.java:72:32:72:68 | getParameter(...) : String | JsonHijacking.java:80:20:80:28 | resultStr | Json Hijacking query might include code from $@. | JsonHijacking.java:72:32:72:68 | getParameter(...) | this user input |
+| JsonHijacking.java:95:20:95:28 | resultStr | JsonHijacking.java:88:32:88:68 | getParameter(...) : String | JsonHijacking.java:95:20:95:28 | resultStr | Json Hijacking query might include code from $@. | JsonHijacking.java:88:32:88:68 | getParameter(...) | this user input |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonHijacking.java b/java/ql/test/experimental/query-tests/security/CWE-352/JsonHijacking.java
new file mode 100644
index 00000000000..9b473e0610c
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonHijacking.java
@@ -0,0 +1,119 @@
+import com.alibaba.fastjson.JSONObject;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.gson.Gson;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Random;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+@Controller
+public class JsonHijacking {
+
+ private static HashMap hashMap = new HashMap();
+
+ static {
+ hashMap.put("username","admin");
+ hashMap.put("password","123456");
+ }
+
+
+ @GetMapping(value = "jsonp1")
+ @ResponseBody
+ public String bad1(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+
+ Gson gson = new Gson();
+ String result = gson.toJson(hashMap);
+ resultStr = jsonpCallback + "(" + result + ")";
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp2")
+ @ResponseBody
+ public String bad2(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+
+ resultStr = jsonpCallback + "(" + JSONObject.toJSONString(hashMap) + ")";
+
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp3")
+ @ResponseBody
+ public String bad3(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String jsonStr = getJsonStr(hashMap);
+ resultStr = jsonpCallback + "(" + jsonStr + ")";
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp4")
+ @ResponseBody
+ public String bad4(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String restr = JSONObject.toJSONString(hashMap);
+ resultStr = jsonpCallback + "(" + restr + ");";
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp5")
+ @ResponseBody
+ public void bad5(HttpServletRequest request,
+ HttpServletResponse response) throws Exception {
+ response.setContentType("application/json");
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ PrintWriter pw = null;
+ Gson gson = new Gson();
+ String result = gson.toJson(hashMap);
+
+ String resultStr = null;
+ pw = response.getWriter();
+ resultStr = jsonpCallback + "(" + result + ")";
+ pw.println(resultStr);
+ }
+
+ @GetMapping(value = "jsonp6")
+ @ResponseBody
+ public void bad6(HttpServletRequest request,
+ HttpServletResponse response) throws Exception {
+ response.setContentType("application/json");
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ PrintWriter pw = null;
+ ObjectMapper mapper = new ObjectMapper();
+ String result = mapper.writeValueAsString(hashMap);
+ String resultStr = null;
+ pw = response.getWriter();
+ resultStr = jsonpCallback + "(" + result + ")";
+ pw.println(resultStr);
+ }
+
+ @GetMapping(value = "jsonp7")
+ @ResponseBody
+ public String good(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+
+ String val = "";
+ Random random = new Random();
+ for (int i = 0; i < 10; i++) {
+ val += String.valueOf(random.nextInt(10));
+ }
+ // good
+ jsonpCallback = jsonpCallback + "_" + val;
+ String jsonStr = getJsonStr(hashMap);
+ resultStr = jsonpCallback + "(" + jsonStr + ")";
+ return resultStr;
+ }
+
+ public static String getJsonStr(Object result) {
+ return JSONObject.toJSONString(result);
+ }
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonHijacking.qlref b/java/ql/test/experimental/query-tests/security/CWE-352/JsonHijacking.qlref
new file mode 100644
index 00000000000..e79471b3c1e
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonHijacking.qlref
@@ -0,0 +1 @@
+Security/CWE/CWE-352/JsonHijacking.ql
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/options b/java/ql/test/experimental/query-tests/security/CWE-352/options
new file mode 100644
index 00000000000..3676b8e38b6
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/options
@@ -0,0 +1 @@
+//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/apache-http-4.4.13/:${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/fastjson-1.2.74/:${testdir}/../../../../stubs/gson-2.8.6/:${testdir}/../../../../stubs/jackson-databind-2.10/:${testdir}/../../../../stubs/springframework-5.2.3/:${testdir}/../../../../stubs/spring-context-5.3.2/:${testdir}/../../../../stubs/spring-web-5.3.2/:${testdir}/../../../../stubs/spring-core-5.3.2/
diff --git a/java/ql/test/stubs/fastjson-1.2.74/com/alibaba/fastjson/JSON.java b/java/ql/test/stubs/fastjson-1.2.74/com/alibaba/fastjson/JSON.java
index b71e890e9b7..99e2873d375 100644
--- a/java/ql/test/stubs/fastjson-1.2.74/com/alibaba/fastjson/JSON.java
+++ b/java/ql/test/stubs/fastjson-1.2.74/com/alibaba/fastjson/JSON.java
@@ -26,6 +26,10 @@ import com.alibaba.fastjson.parser.*;
import com.alibaba.fastjson.parser.deserializer.ParseProcess;
public abstract class JSON {
+ public static String toJSONString(Object object) {
+ return null;
+ }
+
public static Object parse(String text) {
return null;
}
diff --git a/java/ql/test/stubs/spring-context-5.3.2/org/springframework/stereotype/Controller.java b/java/ql/test/stubs/spring-context-5.3.2/org/springframework/stereotype/Controller.java
new file mode 100644
index 00000000000..9b1751fa2ae
--- /dev/null
+++ b/java/ql/test/stubs/spring-context-5.3.2/org/springframework/stereotype/Controller.java
@@ -0,0 +1,14 @@
+package org.springframework.stereotype;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Controller {
+ String value() default "";
+}
diff --git a/java/ql/test/stubs/spring-core-5.3.2/org/springframework/core/annotation/AliasFor.class b/java/ql/test/stubs/spring-core-5.3.2/org/springframework/core/annotation/AliasFor.class
new file mode 100644
index 0000000000000000000000000000000000000000..438065489278b92503abc2ad8d75716c5e56ac08
GIT binary patch
literal 385
zcmb7=!Ab)$5QhJ0x31kDdrH&Kx0=)qG#3PM4!PcXZrOKO@(l3m};gAd?CiL-}x
zJqaEL=Fj~6^G&|KKRyB6W0qr@<21(^Vbrp1G~wdrcD3b}m1S3}bqdDS4}|lDb3So0
z-aYCKH#QMKxO{0`GCTd`S`$rab#IG=`O1e{#kVeF6L_cJeRx%s4_fgdPA#nAxb#7`
zj5*1|vPl9`tbG$Iy);(DbZ?q>Y=pc21QTZcMbG6{R|0?4KmBGoU|o}(H;@|2R}C^k
ghLPwaQNxHF$I?t>JeJBL3UL&FIx;a%x-6Xh0OC_*bpQYW
literal 0
HcmV?d00001
diff --git a/java/ql/test/stubs/spring-core-5.3.2/org/springframework/core/annotation/AliasFor.java b/java/ql/test/stubs/spring-core-5.3.2/org/springframework/core/annotation/AliasFor.java
new file mode 100644
index 00000000000..3a823fade5b
--- /dev/null
+++ b/java/ql/test/stubs/spring-core-5.3.2/org/springframework/core/annotation/AliasFor.java
@@ -0,0 +1,10 @@
+package org.springframework.core.annotation;
+
+public @interface AliasFor {
+ @AliasFor("attribute")
+ String value() default "";
+
+ @AliasFor("value")
+ String attribute() default "";
+
+}
diff --git a/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/GetMapping.class b/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/GetMapping.class
new file mode 100644
index 0000000000000000000000000000000000000000..5392ca0ebc1593d6bfa2f7d765ee4ca169fb260f
GIT binary patch
literal 504
zcma)&y-ve06orr5G%4k$Ewlp@8)_FUu~3N#3Bgi?)JiO!oYW02i5(KVeK!UkfQLfd
z3?&dTFj%_xlg>TI=i~G39l#Za0geNl1Q;-QTBMR;Fd9$SVk3AWbj;^AS316C=-+5<
ztgy=HTe%W0u?%2nZA9WoH5`o>f62T|*k=Ym6S+tWhIV9h;Zj+SS#FjtD#y;;xIB_~
zDxp)|dubm;mXYs88HC|<=CoC*d{Tu96Imr8>11m1m={?Yb44Ckk!ycGS!yuAF9#FEVXJbh%nj0^%G
u-TFC+dFlH8Nm;4MC5#O62q7eGj&Kvy7#SEDn1GlW=t>44%>pEu7+3+XjWw+R
literal 0
HcmV?d00001
diff --git a/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/ResponseBody.java b/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/ResponseBody.java
new file mode 100644
index 00000000000..b2134009968
--- /dev/null
+++ b/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/ResponseBody.java
@@ -0,0 +1,4 @@
+package org.springframework.web.bind.annotation;
+
+public @interface ResponseBody {
+}
From 45bdb22db840589f232f46c31717b96c91053ee9 Mon Sep 17 00:00:00 2001
From: Francis Alexander
Date: Sun, 21 Feb 2021 16:45:48 +0530
Subject: [PATCH 057/725] Switch from sanitizer to tainttracking, formatting
and qldoc changes
---
.../Security/CWE/CWE-346/UnvalidatedCors.java | 9 +++
.../CWE/CWE-346/UnvalidatedCors.qhelp | 12 ++--
.../Security/CWE/CWE-346/UnvalidatedCors.ql | 64 +++++++++----------
.../security/CWE-346/UnvalidatedCors.expected | 2 +-
4 files changed, 46 insertions(+), 41 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.java b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.java
index 772a64969ea..fd562d44f30 100644
--- a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.java
+++ b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.java
@@ -29,6 +29,15 @@ public class CorsFilter implements Filter {
}
}
+ if (!StringUtils.isEmpty(url)) {
+ List checkorigins = Arrays.asList("www.example.com", "www.sub.example.com");
+
+ if (checkorigins.contains(url)) { // GOOD -> Origin is validated here.
+ response.addHeader("Access-Control-Allow-Origin", url);
+ response.addHeader("Access-Control-Allow-Credentials", "true");
+ }
+ }
+
chain.doFilter(req, res);
}
diff --git a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.qhelp b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.qhelp
index d01c27c23ca..da98e896a60 100644
--- a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.qhelp
+++ b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.qhelp
@@ -16,9 +16,9 @@
When the Access-Control-Allow-Credentials header
is true, the Access-Control-Allow-Origin
- header must have a value different from * in order to
- make browsers accept the header. Therefore, to allow multiple origins
- for Cross-Origin requests with credentials, the server must
+ header must have a value different from * in order
+ for browsers to accept the header. Therefore, to allow multiple origins
+ for cross-origin requests with credentials, the server must
dynamically compute the value of the
Access-Control-Allow-Origin header. Computing this
header value from information in the request to the server can
@@ -47,8 +47,8 @@
attacker, it is never safe to use null as the value of
the Access-Control-Allow-Origin header when the
Access-Control-Allow-Credentials header value is
- true.This can be done using a sandboxed iframe. A more detailed
- explanation is available in the portswigger blogpost referenced below.
+ true.A null origin can be set by an attacker using a sandboxed iframe.
+ A more detailed explanation is available in the portswigger blogpost referenced below.
@@ -57,7 +57,7 @@
In the example below, the server allows the browser to send
- user credentials in a Cross-Origin request. The request header
+ user credentials in a cross-origin request. The request header
origins controls the allowed origins for such a
Cross-Origin request.
diff --git a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql
index 9f9c12b2295..0cd99aea68a 100644
--- a/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql
+++ b/java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql
@@ -1,6 +1,6 @@
/**
- * @name Cors header being set from remote source
- * @description Cors header is being set from remote source, allowing to control the origin.
+ * @name CORS is derived from untrusted input
+ * @description CORS header is derived from untrusted input, allowing a remote user to control which origins are trusted.
* @kind path-problem
* @problem.severity error
* @precision high
@@ -13,6 +13,7 @@ import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.frameworks.Servlets
import semmle.code.java.dataflow.TaintTracking
+import semmle.code.java.dataflow.TaintTracking2
import DataFlow::PathGraph
/**
@@ -25,10 +26,10 @@ private predicate setsAllowCredentials(MethodAccess header) {
) and
header.getArgument(0).(CompileTimeConstantExpr).getStringValue().toLowerCase() =
"access-control-allow-credentials" and
- header.getArgument(1).(CompileTimeConstantExpr).getStringValue() = "true"
+ header.getArgument(1).(CompileTimeConstantExpr).getStringValue().toLowerCase() = "true"
}
-class CorsProbableCheckAccess extends MethodAccess {
+private class CorsProbableCheckAccess extends MethodAccess {
CorsProbableCheckAccess() {
getMethod().hasName("contains") and
getMethod().getDeclaringType().getASourceSupertype*() instanceof CollectionType
@@ -45,46 +46,41 @@ private Expr getAccessControlAllowOriginHeaderName() {
result.(CompileTimeConstantExpr).getStringValue().toLowerCase() = "access-control-allow-origin"
}
+/**
+ * This taintflow2 configuration checks if there is a flow from source node towards CorsProbableCheckAccess methods.
+ */
+class CorsSourceReachesCheckConfig extends TaintTracking2::Configuration {
+ CorsSourceReachesCheckConfig() { this = "CorsOriginConfig" }
+
+ override predicate isSource(DataFlow::Node source) { any(CorsOriginConfig c).hasFlow(source, _) }
+
+ override predicate isSink(DataFlow::Node sink) {
+ sink.asExpr() = any(CorsProbableCheckAccess check).getAnArgument()
+ }
+}
+
class CorsOriginConfig extends TaintTracking::Configuration {
CorsOriginConfig() { this = "CorsOriginConfig" }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess corsheader, MethodAccess allowcredentialsheader |
+ exists(MethodAccess corsHeader, MethodAccess allowCredentialsHeader |
(
- corsheader.getMethod() instanceof ResponseSetHeaderMethod or
- corsheader.getMethod() instanceof ResponseAddHeaderMethod
+ corsHeader.getMethod() instanceof ResponseSetHeaderMethod or
+ corsHeader.getMethod() instanceof ResponseAddHeaderMethod
) and
- getAccessControlAllowOriginHeaderName() = corsheader.getArgument(0) and
- setsAllowCredentials(allowcredentialsheader) and
- corsheader.getEnclosingCallable() = allowcredentialsheader.getEnclosingCallable() and
- sink.asExpr() = corsheader.getArgument(1)
- )
- }
-
- /**
- * this sanitizer is oversimplistic:
- * - it only considers local dataflows
- * - it will consider any method calling `Collection.contains` or `String.equals` as a sanitizer
- * no matter if that check is taken into account and its result reaches the
- * return statement of the wrapper.
- */
- override predicate isSanitizer(DataFlow::Node node) {
- exists(CorsProbableCheckAccess check |
- TaintTracking::localTaint(node, DataFlow::exprNode(check.getAnArgument()))
- or
- exists(MethodAccess wrapperAccess, Method wrapper, int i |
- TaintTracking::localTaint(node, DataFlow::exprNode(wrapperAccess.getArgument(i))) and
- wrapperAccess.getMethod() = wrapper and
- TaintTracking::localTaint(DataFlow::parameterNode(wrapper.getParameter(i)),
- DataFlow::exprNode(check.getAnArgument()))
- )
+ getAccessControlAllowOriginHeaderName() = corsHeader.getArgument(0) and
+ setsAllowCredentials(allowCredentialsHeader) and
+ corsHeader.getEnclosingCallable() = allowCredentialsHeader.getEnclosingCallable() and
+ sink.asExpr() = corsHeader.getArgument(1)
)
}
}
-from DataFlow::PathNode source, DataFlow::PathNode sink, CorsOriginConfig conf
-where conf.hasFlowPath(source, sink)
-select sink.getNode(), source, sink, "Cors header is being set using user controlled value $@.",
+from
+ DataFlow::PathNode source, DataFlow::PathNode sink, CorsOriginConfig conf,
+ CorsSourceReachesCheckConfig sanconf
+where conf.hasFlowPath(source, sink) and not sanconf.hasFlow(source.getNode(), _)
+select sink.getNode(), source, sink, "CORS header is being set using user controlled value $@.",
source.getNode(), "user-provided value"
diff --git a/java/ql/test/query-tests/security/CWE-346/UnvalidatedCors.expected b/java/ql/test/query-tests/security/CWE-346/UnvalidatedCors.expected
index b7954950deb..d215f034fd2 100644
--- a/java/ql/test/query-tests/security/CWE-346/UnvalidatedCors.expected
+++ b/java/ql/test/query-tests/security/CWE-346/UnvalidatedCors.expected
@@ -4,4 +4,4 @@ nodes
| UnvalidatedCors.java:21:22:21:48 | getHeader(...) : String | semmle.label | getHeader(...) : String |
| UnvalidatedCors.java:27:67:27:69 | url | semmle.label | url |
#select
-| UnvalidatedCors.java:27:67:27:69 | url | UnvalidatedCors.java:21:22:21:48 | getHeader(...) : String | UnvalidatedCors.java:27:67:27:69 | url | Cors header is being set using user controlled value $@. | UnvalidatedCors.java:21:22:21:48 | getHeader(...) | user-provided value |
+| UnvalidatedCors.java:27:67:27:69 | url | UnvalidatedCors.java:21:22:21:48 | getHeader(...) : String | UnvalidatedCors.java:27:67:27:69 | url | CORS header is being set using user controlled value $@. | UnvalidatedCors.java:21:22:21:48 | getHeader(...) | user-provided value |
From 872a000a33e1b0d6fb2efeb926c7ed9e18725bd5 Mon Sep 17 00:00:00 2001
From: haby0
Date: Wed, 24 Feb 2021 20:36:12 +0800
Subject: [PATCH 058/725] *)update to JSONP injection
---
.../Security/CWE/CWE-352/JsonpInjection.java | 170 +++++++++++++++++
.../Security/CWE/CWE-352/JsonpInjection.qhelp | 35 ++++
.../Security/CWE/CWE-352/JsonpInjection.ql | 55 ++++++
.../CWE/CWE-352/JsonpInjectionLib.qll | 92 ++++++++++
.../security/CWE-352/JsonpInjection.expected | 60 ++++++
.../security/CWE-352/JsonpInjection.java | 171 ++++++++++++++++++
.../security/CWE-352/JsonpInjection.qlref | 1 +
7 files changed, 584 insertions(+)
create mode 100644 java/ql/src/Security/CWE/CWE-352/JsonpInjection.java
create mode 100644 java/ql/src/Security/CWE/CWE-352/JsonpInjection.qhelp
create mode 100644 java/ql/src/Security/CWE/CWE-352/JsonpInjection.ql
create mode 100644 java/ql/src/Security/CWE/CWE-352/JsonpInjectionLib.qll
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.expected
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.java
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.qlref
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonpInjection.java b/java/ql/src/Security/CWE/CWE-352/JsonpInjection.java
new file mode 100644
index 00000000000..8b4e7cc005e
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-352/JsonpInjection.java
@@ -0,0 +1,170 @@
+import com.alibaba.fastjson.JSONObject;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.gson.Gson;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Random;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+@Controller
+public class JsonpInjection {
+private static HashMap hashMap = new HashMap();
+
+ static {
+ hashMap.put("username","admin");
+ hashMap.put("password","123456");
+ }
+
+
+ @GetMapping(value = "jsonp1")
+ @ResponseBody
+ public String bad1(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+
+ Gson gson = new Gson();
+ String result = gson.toJson(hashMap);
+ resultStr = jsonpCallback + "(" + result + ")";
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp2")
+ @ResponseBody
+ public String bad2(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+
+ resultStr = jsonpCallback + "(" + JSONObject.toJSONString(hashMap) + ")";
+
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp3")
+ @ResponseBody
+ public String bad3(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String jsonStr = getJsonStr(hashMap);
+ resultStr = jsonpCallback + "(" + jsonStr + ")";
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp4")
+ @ResponseBody
+ public String bad4(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String restr = JSONObject.toJSONString(hashMap);
+ resultStr = jsonpCallback + "(" + restr + ");";
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp5")
+ @ResponseBody
+ public void bad5(HttpServletRequest request,
+ HttpServletResponse response) throws Exception {
+ response.setContentType("application/json");
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ PrintWriter pw = null;
+ Gson gson = new Gson();
+ String result = gson.toJson(hashMap);
+
+ String resultStr = null;
+ pw = response.getWriter();
+ resultStr = jsonpCallback + "(" + result + ")";
+ pw.println(resultStr);
+ }
+
+ @GetMapping(value = "jsonp6")
+ @ResponseBody
+ public void bad6(HttpServletRequest request,
+ HttpServletResponse response) throws Exception {
+ response.setContentType("application/json");
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ PrintWriter pw = null;
+ ObjectMapper mapper = new ObjectMapper();
+ String result = mapper.writeValueAsString(hashMap);
+ String resultStr = null;
+ pw = response.getWriter();
+ resultStr = jsonpCallback + "(" + result + ")";
+ pw.println(resultStr);
+ }
+
+ @GetMapping(value = "jsonp7")
+ @ResponseBody
+ public String good(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+
+ String val = "";
+ Random random = new Random();
+ for (int i = 0; i < 10; i++) {
+ val += String.valueOf(random.nextInt(10));
+ }
+ // good
+ jsonpCallback = jsonpCallback + "_" + val;
+ String jsonStr = getJsonStr(hashMap);
+ resultStr = jsonpCallback + "(" + jsonStr + ")";
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp8")
+ @ResponseBody
+ public String good1(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+
+ String token = request.getParameter("token");
+
+ // good
+ if (verifToken(token)){
+ System.out.println(token);
+ String jsonStr = getJsonStr(hashMap);
+ resultStr = jsonpCallback + "(" + jsonStr + ")";
+ return resultStr;
+ }
+
+ return "error";
+ }
+
+ @GetMapping(value = "jsonp9")
+ @ResponseBody
+ public String good2(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+
+ String referer = request.getHeader("Referer");
+
+ boolean result = verifReferer(referer);
+ // good
+ if (result){
+ String jsonStr = getJsonStr(hashMap);
+ resultStr = jsonpCallback + "(" + jsonStr + ")";
+ return resultStr;
+ }
+
+ return "error";
+ }
+
+ public static String getJsonStr(Object result) {
+ return JSONObject.toJSONString(result);
+ }
+
+ public static boolean verifToken(String token){
+ if (token != "xxxx"){
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean verifReferer(String referer){
+ if (!referer.startsWith("http://test.com/")){
+ return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonpInjection.qhelp b/java/ql/src/Security/CWE/CWE-352/JsonpInjection.qhelp
new file mode 100644
index 00000000000..b063b409d3a
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-352/JsonpInjection.qhelp
@@ -0,0 +1,35 @@
+
+
+
+The software uses external input as the function name to wrap JSON data and return it to the client as a request response. When there is a cross-domain problem,
+there is a problem of sensitive information leakage.
+
+
+
+
+Adding `Referer` or random `token` verification processing can effectively prevent the leakage of sensitive information.
+
+
+
+
+The following example shows the case of no verification processing and verification processing for the external input function name.
+
+
+
+
+
+
+
+OWASPLondon20161124_JSON_Hijacking_Gareth_Heyes:
+JSON hijacking.
+
+
+Practical JSONP Injection:
+
+ Completely controllable from the URL (GET variable)
+.
+
+
+
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonpInjection.ql b/java/ql/src/Security/CWE/CWE-352/JsonpInjection.ql
new file mode 100644
index 00000000000..e7ca2d41e34
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-352/JsonpInjection.ql
@@ -0,0 +1,55 @@
+/**
+ * @name JSON Hijacking
+ * @description User-controlled callback function names that are not verified are vulnerable
+ * to json hijacking attacks.
+ * @kind path-problem
+ * @problem.severity error
+ * @precision high
+ * @id java/Json-hijacking
+ * @tags security
+ * external/cwe/cwe-352
+ */
+
+import java
+import JsonpInjectionLib
+import semmle.code.java.dataflow.FlowSources
+import semmle.code.java.deadcode.WebEntryPoints
+import DataFlow::PathGraph
+
+class VerifAuth extends DataFlow::BarrierGuard {
+ VerifAuth() {
+ exists(MethodAccess ma, Node prod, Node succ |
+ this = ma and
+ ma.getMethod().getName().regexpMatch("(?i).*(token|auth|referer).*") and
+ prod instanceof RemoteFlowSource and
+ succ.asExpr() = ma.getAnArgument() and
+ ma.getMethod().getAParameter().getName().regexpMatch("(?i).*(token|auth|referer).*") and
+ localFlowStep*(prod, succ)
+ )
+ }
+
+ override predicate checks(Expr e, boolean branch) {
+ exists(ReturnStmt rs |
+ e = rs.getResult() and
+ branch = true
+ )
+ }
+}
+
+/** Taint-tracking configuration tracing flow from remote sources to output jsonp data. */
+class JsonpInjectionConfig extends TaintTracking::Configuration {
+ JsonpInjectionConfig() { this = "JsonpInjectionConfig" }
+
+ override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
+
+ override predicate isSink(DataFlow::Node sink) { sink instanceof JsonpInjectionSink }
+
+ override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { guard instanceof VerifAuth }
+}
+
+from DataFlow::PathNode source, DataFlow::PathNode sink, JsonpInjectionConfig conf
+where
+ conf.hasFlowPath(source, sink) and
+ exists(JsonpInjectionFlowConfig jhfc | jhfc.hasFlowTo(sink.getNode()))
+select sink.getNode(), source, sink, "Json Hijacking query might include code from $@.",
+ source.getNode(), "this user input"
\ No newline at end of file
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonpInjectionLib.qll b/java/ql/src/Security/CWE/CWE-352/JsonpInjectionLib.qll
new file mode 100644
index 00000000000..4294a5e8c8f
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-352/JsonpInjectionLib.qll
@@ -0,0 +1,92 @@
+import java
+import DataFlow
+import JsonStringLib
+import semmle.code.java.dataflow.DataFlow
+import semmle.code.java.dataflow.FlowSources
+import semmle.code.java.frameworks.spring.SpringController
+
+/** A data flow sink for unvalidated user input that is used to jsonp. */
+abstract class JsonpInjectionSink extends DataFlow::Node { }
+
+/** Use ```print```, ```println```, ```write``` to output result. */
+private class WriterPrintln extends JsonpInjectionSink {
+ WriterPrintln() {
+ exists(MethodAccess ma |
+ ma.getMethod().getName().regexpMatch("print|println|write") and
+ ma.getMethod()
+ .getDeclaringType()
+ .getASourceSupertype*()
+ .hasQualifiedName("java.io", "PrintWriter") and
+ ma.getArgument(0) = this.asExpr()
+ )
+ }
+}
+
+/** Spring Request Method return result. */
+private class SpringReturn extends JsonpInjectionSink {
+ SpringReturn() {
+ exists(ReturnStmt rs, Method m | m = rs.getEnclosingCallable() |
+ m instanceof SpringRequestMappingMethod and
+ rs.getResult() = this.asExpr()
+ )
+ }
+}
+
+/** A concatenate expression using `(` and `)` or `);`. */
+class JsonpInjectionExpr extends AddExpr {
+ JsonpInjectionExpr() {
+ getRightOperand().toString().regexpMatch("\"\\)\"|\"\\);\"") and
+ getLeftOperand()
+ .(AddExpr)
+ .getLeftOperand()
+ .(AddExpr)
+ .getRightOperand()
+ .toString()
+ .regexpMatch("\"\\(\"")
+ }
+
+ /** Get the jsonp function name of this expression */
+ Expr getFunctionName() {
+ result = getLeftOperand().(AddExpr).getLeftOperand().(AddExpr).getLeftOperand()
+ }
+
+ /** Get the json data of this expression */
+ Expr getJsonExpr() { result = getLeftOperand().(AddExpr).getRightOperand() }
+}
+
+/** A data flow configuration tracing flow from remote sources to jsonp function name. */
+class RemoteFlowConfig extends DataFlow2::Configuration {
+ RemoteFlowConfig() { this = "RemoteFlowConfig" }
+
+ override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(JsonpInjectionExpr jhe | jhe.getFunctionName() = sink.asExpr())
+ }
+}
+
+/** A data flow configuration tracing flow from json data to splicing jsonp data. */
+class JsonDataFlowConfig extends DataFlow2::Configuration {
+ JsonDataFlowConfig() { this = "JsonDataFlowConfig" }
+
+ override predicate isSource(DataFlow::Node src) { src instanceof JsonpStringSource }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(JsonpInjectionExpr jhe | jhe.getJsonExpr() = sink.asExpr())
+ }
+}
+
+/** Taint-tracking configuration tracing flow from user-controllable function name jsonp data to output jsonp data. */
+class JsonpInjectionFlowConfig extends DataFlow::Configuration {
+ JsonpInjectionFlowConfig() { this = "JsonpInjectionFlowConfig" }
+
+ override predicate isSource(DataFlow::Node src) {
+ exists(JsonpInjectionExpr jhe, JsonDataFlowConfig jdfc, RemoteFlowConfig rfc |
+ jhe = src.asExpr() and
+ jdfc.hasFlowTo(DataFlow::exprNode(jhe.getJsonExpr())) and
+ rfc.hasFlowTo(DataFlow::exprNode(jhe.getFunctionName()))
+ )
+ }
+
+ override predicate isSink(DataFlow::Node sink) { sink instanceof JsonpInjectionSink }
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.expected
new file mode 100644
index 00000000000..019af8f5c05
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.expected
@@ -0,0 +1,60 @@
+edges
+| JsonpInjection.java:28:32:28:68 | getParameter(...) : String | JsonpInjection.java:33:16:33:24 | resultStr |
+| JsonpInjection.java:32:21:32:54 | ... + ... : String | JsonpInjection.java:33:16:33:24 | resultStr |
+| JsonpInjection.java:40:32:40:68 | getParameter(...) : String | JsonpInjection.java:44:16:44:24 | resultStr |
+| JsonpInjection.java:42:21:42:80 | ... + ... : String | JsonpInjection.java:44:16:44:24 | resultStr |
+| JsonpInjection.java:51:32:51:68 | getParameter(...) : String | JsonpInjection.java:54:16:54:24 | resultStr |
+| JsonpInjection.java:53:21:53:55 | ... + ... : String | JsonpInjection.java:54:16:54:24 | resultStr |
+| JsonpInjection.java:61:32:61:68 | getParameter(...) : String | JsonpInjection.java:64:16:64:24 | resultStr |
+| JsonpInjection.java:63:21:63:54 | ... + ... : String | JsonpInjection.java:64:16:64:24 | resultStr |
+| JsonpInjection.java:72:32:72:68 | getParameter(...) : String | JsonpInjection.java:80:20:80:28 | resultStr |
+| JsonpInjection.java:79:21:79:54 | ... + ... : String | JsonpInjection.java:80:20:80:28 | resultStr |
+| JsonpInjection.java:88:32:88:68 | getParameter(...) : String | JsonpInjection.java:95:20:95:28 | resultStr |
+| JsonpInjection.java:94:21:94:54 | ... + ... : String | JsonpInjection.java:95:20:95:28 | resultStr |
+| JsonpInjection.java:102:32:102:68 | getParameter(...) : String | JsonpInjection.java:113:16:113:24 | resultStr |
+| JsonpInjection.java:128:25:128:59 | ... + ... : String | JsonpInjection.java:129:20:129:28 | resultStr |
+| JsonpInjection.java:147:25:147:59 | ... + ... : String | JsonpInjection.java:148:20:148:28 | resultStr |
+nodes
+| JsonpInjection.java:28:32:28:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpInjection.java:32:21:32:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpInjection.java:33:16:33:24 | resultStr | semmle.label | resultStr |
+| JsonpInjection.java:33:16:33:24 | resultStr | semmle.label | resultStr |
+| JsonpInjection.java:40:32:40:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpInjection.java:42:21:42:80 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpInjection.java:44:16:44:24 | resultStr | semmle.label | resultStr |
+| JsonpInjection.java:44:16:44:24 | resultStr | semmle.label | resultStr |
+| JsonpInjection.java:51:32:51:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpInjection.java:53:21:53:55 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpInjection.java:54:16:54:24 | resultStr | semmle.label | resultStr |
+| JsonpInjection.java:54:16:54:24 | resultStr | semmle.label | resultStr |
+| JsonpInjection.java:61:32:61:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpInjection.java:63:21:63:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpInjection.java:64:16:64:24 | resultStr | semmle.label | resultStr |
+| JsonpInjection.java:64:16:64:24 | resultStr | semmle.label | resultStr |
+| JsonpInjection.java:72:32:72:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpInjection.java:79:21:79:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpInjection.java:80:20:80:28 | resultStr | semmle.label | resultStr |
+| JsonpInjection.java:80:20:80:28 | resultStr | semmle.label | resultStr |
+| JsonpInjection.java:88:32:88:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpInjection.java:94:21:94:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpInjection.java:95:20:95:28 | resultStr | semmle.label | resultStr |
+| JsonpInjection.java:95:20:95:28 | resultStr | semmle.label | resultStr |
+| JsonpInjection.java:102:32:102:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpInjection.java:113:16:113:24 | resultStr | semmle.label | resultStr |
+| JsonpInjection.java:128:25:128:59 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpInjection.java:129:20:129:28 | resultStr | semmle.label | resultStr |
+| JsonpInjection.java:147:25:147:59 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpInjection.java:148:20:148:28 | resultStr | semmle.label | resultStr |
+#select
+| JsonpInjection.java:33:16:33:24 | resultStr | JsonpInjection.java:28:32:28:68 | getParameter(...) : String | JsonpInjection.java:33:16:33:24 | resultStr | Json Hijacking query
+might include code from $@. | JsonpInjection.java:28:32:28:68 | getParameter(...) | this user input |
+| JsonpInjection.java:44:16:44:24 | resultStr | JsonpInjection.java:40:32:40:68 | getParameter(...) : String | JsonpInjection.java:44:16:44:24 | resultStr | Json Hijacking query
+might include code from $@. | JsonpInjection.java:40:32:40:68 | getParameter(...) | this user input |
+| JsonpInjection.java:54:16:54:24 | resultStr | JsonpInjection.java:51:32:51:68 | getParameter(...) : String | JsonpInjection.java:54:16:54:24 | resultStr | Json Hijacking query
+might include code from $@. | JsonpInjection.java:51:32:51:68 | getParameter(...) | this user input |
+| JsonpInjection.java:64:16:64:24 | resultStr | JsonpInjection.java:61:32:61:68 | getParameter(...) : String | JsonpInjection.java:64:16:64:24 | resultStr | Json Hijacking query
+might include code from $@. | JsonpInjection.java:61:32:61:68 | getParameter(...) | this user input |
+| JsonpInjection.java:80:20:80:28 | resultStr | JsonpInjection.java:72:32:72:68 | getParameter(...) : String | JsonpInjection.java:80:20:80:28 | resultStr | Json Hijacking query
+might include code from $@. | JsonpInjection.java:72:32:72:68 | getParameter(...) | this user input |
+| JsonpInjection.java:95:20:95:28 | resultStr | JsonpInjection.java:88:32:88:68 | getParameter(...) : String | JsonpInjection.java:95:20:95:28 | resultStr | Json Hijacking query
+might include code from $@. | JsonpInjection.java:88:32:88:68 | getParameter(...) | this user input |
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.java b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.java
new file mode 100644
index 00000000000..df3aa2c02fe
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.java
@@ -0,0 +1,171 @@
+import com.alibaba.fastjson.JSONObject;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.gson.Gson;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Random;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+@Controller
+public class JsonpInjection {
+
+ private static HashMap hashMap = new HashMap();
+
+ static {
+ hashMap.put("username","admin");
+ hashMap.put("password","123456");
+ }
+
+
+ @GetMapping(value = "jsonp1")
+ @ResponseBody
+ public String bad1(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+
+ Gson gson = new Gson();
+ String result = gson.toJson(hashMap);
+ resultStr = jsonpCallback + "(" + result + ")";
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp2")
+ @ResponseBody
+ public String bad2(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+
+ resultStr = jsonpCallback + "(" + JSONObject.toJSONString(hashMap) + ")";
+
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp3")
+ @ResponseBody
+ public String bad3(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String jsonStr = getJsonStr(hashMap);
+ resultStr = jsonpCallback + "(" + jsonStr + ")";
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp4")
+ @ResponseBody
+ public String bad4(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String restr = JSONObject.toJSONString(hashMap);
+ resultStr = jsonpCallback + "(" + restr + ");";
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp5")
+ @ResponseBody
+ public void bad5(HttpServletRequest request,
+ HttpServletResponse response) throws Exception {
+ response.setContentType("application/json");
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ PrintWriter pw = null;
+ Gson gson = new Gson();
+ String result = gson.toJson(hashMap);
+
+ String resultStr = null;
+ pw = response.getWriter();
+ resultStr = jsonpCallback + "(" + result + ")";
+ pw.println(resultStr);
+ }
+
+ @GetMapping(value = "jsonp6")
+ @ResponseBody
+ public void bad6(HttpServletRequest request,
+ HttpServletResponse response) throws Exception {
+ response.setContentType("application/json");
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ PrintWriter pw = null;
+ ObjectMapper mapper = new ObjectMapper();
+ String result = mapper.writeValueAsString(hashMap);
+ String resultStr = null;
+ pw = response.getWriter();
+ resultStr = jsonpCallback + "(" + result + ")";
+ pw.println(resultStr);
+ }
+
+ @GetMapping(value = "jsonp7")
+ @ResponseBody
+ public String good(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+
+ String val = "";
+ Random random = new Random();
+ for (int i = 0; i < 10; i++) {
+ val += String.valueOf(random.nextInt(10));
+ }
+ // good
+ jsonpCallback = jsonpCallback + "_" + val;
+ String jsonStr = getJsonStr(hashMap);
+ resultStr = jsonpCallback + "(" + jsonStr + ")";
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp8")
+ @ResponseBody
+ public String good1(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+
+ String token = request.getParameter("token");
+
+ // good
+ if (verifToken(token)){
+ System.out.println(token);
+ String jsonStr = getJsonStr(hashMap);
+ resultStr = jsonpCallback + "(" + jsonStr + ")";
+ return resultStr;
+ }
+
+ return "error";
+ }
+
+ @GetMapping(value = "jsonp9")
+ @ResponseBody
+ public String good2(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+
+ String referer = request.getHeader("Referer");
+
+ boolean result = verifReferer(referer);
+ // good
+ if (result){
+ String jsonStr = getJsonStr(hashMap);
+ resultStr = jsonpCallback + "(" + jsonStr + ")";
+ return resultStr;
+ }
+
+ return "error";
+ }
+
+ public static String getJsonStr(Object result) {
+ return JSONObject.toJSONString(result);
+ }
+
+ public static boolean verifToken(String token){
+ if (token != "xxxx"){
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean verifReferer(String referer){
+ if (!referer.startsWith("http://test.com/")){
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.qlref b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.qlref
new file mode 100644
index 00000000000..6ad4b8acda7
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.qlref
@@ -0,0 +1 @@
+Security/CWE/CWE-352/JsonpInjection.ql
From 6fe8bafc7d35f77d5db38d0802b192b6abb45dd1 Mon Sep 17 00:00:00 2001
From: haby0
Date: Wed, 24 Feb 2021 20:59:51 +0800
Subject: [PATCH 059/725] *)update
---
.../Security/CWE/CWE-352/JsonHijacking.java | 119 ------------------
.../Security/CWE/CWE-352/JsonHijacking.qhelp | 35 ------
.../src/Security/CWE/CWE-352/JsonHijacking.ql | 32 -----
.../Security/CWE/CWE-352/JsonHijackingLib.qll | 92 --------------
.../security/CWE-352/JsonHijacking.expected | 48 -------
.../security/CWE-352/JsonHijacking.java | 119 ------------------
.../security/CWE-352/JsonHijacking.qlref | 1 -
7 files changed, 446 deletions(-)
delete mode 100644 java/ql/src/Security/CWE/CWE-352/JsonHijacking.java
delete mode 100644 java/ql/src/Security/CWE/CWE-352/JsonHijacking.qhelp
delete mode 100644 java/ql/src/Security/CWE/CWE-352/JsonHijacking.ql
delete mode 100644 java/ql/src/Security/CWE/CWE-352/JsonHijackingLib.qll
delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonHijacking.expected
delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonHijacking.java
delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonHijacking.qlref
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonHijacking.java b/java/ql/src/Security/CWE/CWE-352/JsonHijacking.java
deleted file mode 100644
index d08d436fa07..00000000000
--- a/java/ql/src/Security/CWE/CWE-352/JsonHijacking.java
+++ /dev/null
@@ -1,119 +0,0 @@
-import com.alibaba.fastjson.JSONObject;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.gson.Gson;
-import java.io.PrintWriter;
-import java.util.HashMap;
-import java.util.Random;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.ResponseBody;
-
-@Controller
-public class JsonHijacking {
-
- private static HashMap hashMap = new HashMap();
-
- static {
- hashMap.put("username","admin");
- hashMap.put("password","123456");
- }
-
-
- @GetMapping(value = "jsonp1")
- @ResponseBody
- public String bad1(HttpServletRequest request) {
- String resultStr = null;
- String jsonpCallback = request.getParameter("jsonpCallback");
-
- Gson gson = new Gson();
- String result = gson.toJson(hashMap);
- resultStr = jsonpCallback + "(" + result + ")";
- return resultStr;
- }
-
- @GetMapping(value = "jsonp2")
- @ResponseBody
- public String bad2(HttpServletRequest request) {
- String resultStr = null;
- String jsonpCallback = request.getParameter("jsonpCallback");
-
- resultStr = jsonpCallback + "(" + JSONObject.toJSONString(hashMap) + ")";
-
- return resultStr;
- }
-
- @GetMapping(value = "jsonp3")
- @ResponseBody
- public String bad3(HttpServletRequest request) {
- String resultStr = null;
- String jsonpCallback = request.getParameter("jsonpCallback");
- String jsonStr = getJsonStr(hashMap);
- resultStr = jsonpCallback + "(" + jsonStr + ")";
- return resultStr;
- }
-
- @GetMapping(value = "jsonp4")
- @ResponseBody
- public String bad4(HttpServletRequest request) {
- String resultStr = null;
- String jsonpCallback = request.getParameter("jsonpCallback");
- String restr = JSONObject.toJSONString(hashMap);
- resultStr = jsonpCallback + "(" + restr + ");";
- return resultStr;
- }
-
- @GetMapping(value = "jsonp5")
- @ResponseBody
- public void bad5(HttpServletRequest request,
- HttpServletResponse response) throws Exception {
- response.setContentType("application/json");
- String jsonpCallback = request.getParameter("jsonpCallback");
- PrintWriter pw = null;
- Gson gson = new Gson();
- String result = gson.toJson(hashMap);
-
- String resultStr = null;
- pw = response.getWriter();
- resultStr = jsonpCallback + "(" + result + ")";
- pw.println(resultStr);
- }
-
- @GetMapping(value = "jsonp6")
- @ResponseBody
- public void bad6(HttpServletRequest request,
- HttpServletResponse response) throws Exception {
- response.setContentType("application/json");
- String jsonpCallback = request.getParameter("jsonpCallback");
- PrintWriter pw = null;
- ObjectMapper mapper = new ObjectMapper();
- String result = mapper.writeValueAsString(hashMap);
- String resultStr = null;
- pw = response.getWriter();
- resultStr = jsonpCallback + "(" + result + ")";
- pw.println(resultStr);
- }
-
- @GetMapping(value = "jsonp7")
- @ResponseBody
- public String good(HttpServletRequest request) {
- String resultStr = null;
- String jsonpCallback = request.getParameter("jsonpCallback");
-
- String val = "";
- Random random = new Random();
- for (int i = 0; i < 10; i++) {
- val += String.valueOf(random.nextInt(10));
- }
- // good
- jsonpCallback = jsonpCallback + "_" + val;
- String jsonStr = getJsonStr(hashMap);
- resultStr = jsonpCallback + "(" + jsonStr + ")";
- return resultStr;
- }
-
- public static String getJsonStr(Object result) {
- return JSONObject.toJSONString(result);
- }
-}
\ No newline at end of file
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonHijacking.qhelp b/java/ql/src/Security/CWE/CWE-352/JsonHijacking.qhelp
deleted file mode 100644
index 38e1845f992..00000000000
--- a/java/ql/src/Security/CWE/CWE-352/JsonHijacking.qhelp
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-The software uses external input as the function name to wrap JSON data and return it to the client as a request response. When there is a cross-domain problem,
-there is a problem of sensitive information leakage.
-
-
-
-
-The function name verification processing for external input can effectively prevent the leakage of sensitive information.
-
-
-
-
-The following example shows the case of no verification processing and verification processing for the external input function name.
-
-
-
-
-
-
-
-OWASPLondon20161124_JSON_Hijacking_Gareth_Heyes:
-JSON hijacking.
-
-
-Practical JSONP Injection:
-
- Completely controllable from the URL (GET variable)
-.
-
-
-
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonHijacking.ql b/java/ql/src/Security/CWE/CWE-352/JsonHijacking.ql
deleted file mode 100644
index a6a6d2475f0..00000000000
--- a/java/ql/src/Security/CWE/CWE-352/JsonHijacking.ql
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * @name JSON Hijacking
- * @description User-controlled callback function names that are not verified are vulnerable
- * to json hijacking attacks.
- * @kind path-problem
- * @problem.severity error
- * @precision high
- * @id java/Json-hijacking
- * @tags security
- * external/cwe/cwe-352
- */
-
-import java
-import JsonHijackingLib
-import semmle.code.java.dataflow.FlowSources
-import DataFlow::PathGraph
-
-/** Taint-tracking configuration tracing flow from remote sources to output jsonp data. */
-class JsonHijackingConfig extends TaintTracking::Configuration {
- JsonHijackingConfig() { this = "JsonHijackingConfig" }
-
- override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
-
- override predicate isSink(DataFlow::Node sink) { sink instanceof JsonHijackingSink }
-}
-
-from DataFlow::PathNode source, DataFlow::PathNode sink, JsonHijackingConfig conf
-where
- conf.hasFlowPath(source, sink) and
- exists(JsonHijackingFlowConfig jhfc | jhfc.hasFlowTo(sink.getNode()))
-select sink.getNode(), source, sink, "Json Hijacking query might include code from $@.",
- source.getNode(), "this user input"
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonHijackingLib.qll b/java/ql/src/Security/CWE/CWE-352/JsonHijackingLib.qll
deleted file mode 100644
index ba91a6670bf..00000000000
--- a/java/ql/src/Security/CWE/CWE-352/JsonHijackingLib.qll
+++ /dev/null
@@ -1,92 +0,0 @@
-import java
-import DataFlow
-import JsonStringLib
-import semmle.code.java.dataflow.DataFlow
-import semmle.code.java.dataflow.FlowSources
-import semmle.code.java.frameworks.spring.SpringController
-
-/** A data flow sink for unvalidated user input that is used to jsonp. */
-abstract class JsonHijackingSink extends DataFlow::Node { }
-
-/** Use ```print```, ```println```, ```write``` to output result. */
-private class WriterPrintln extends JsonHijackingSink {
- WriterPrintln() {
- exists(MethodAccess ma |
- ma.getMethod().getName().regexpMatch("print|println|write") and
- ma.getMethod()
- .getDeclaringType()
- .getASourceSupertype*()
- .hasQualifiedName("java.io", "PrintWriter") and
- ma.getArgument(0) = this.asExpr()
- )
- }
-}
-
-/** Spring Request Method return result. */
-private class SpringReturn extends JsonHijackingSink {
- SpringReturn() {
- exists(ReturnStmt rs, Method m | m = rs.getEnclosingCallable() |
- m instanceof SpringRequestMappingMethod and
- rs.getResult() = this.asExpr()
- )
- }
-}
-
-/** A concatenate expression using `(` and `)` or `);`. */
-class JsonHijackingExpr extends AddExpr {
- JsonHijackingExpr() {
- getRightOperand().toString().regexpMatch("\"\\)\"|\"\\);\"") and
- getLeftOperand()
- .(AddExpr)
- .getLeftOperand()
- .(AddExpr)
- .getRightOperand()
- .toString()
- .regexpMatch("\"\\(\"")
- }
-
- /** Get the jsonp function name of this expression */
- Expr getFunctionName() {
- result = getLeftOperand().(AddExpr).getLeftOperand().(AddExpr).getLeftOperand()
- }
-
- /** Get the json data of this expression */
- Expr getJsonExpr() { result = getLeftOperand().(AddExpr).getRightOperand() }
-}
-
-/** A data flow configuration tracing flow from remote sources to jsonp function name. */
-class RemoteFlowConfig extends DataFlow2::Configuration {
- RemoteFlowConfig() { this = "RemoteFlowConfig" }
-
- override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
-
- override predicate isSink(DataFlow::Node sink) {
- exists(JsonHijackingExpr jhe | jhe.getFunctionName() = sink.asExpr())
- }
-}
-
-/** A data flow configuration tracing flow from json data to splicing jsonp data. */
-class JsonDataFlowConfig extends DataFlow2::Configuration {
- JsonDataFlowConfig() { this = "JsonDataFlowConfig" }
-
- override predicate isSource(DataFlow::Node src) { src instanceof JsonpStringSource }
-
- override predicate isSink(DataFlow::Node sink) {
- exists(JsonHijackingExpr jhe | jhe.getJsonExpr() = sink.asExpr())
- }
-}
-
-/** Taint-tracking configuration tracing flow from user-controllable function name jsonp data to output jsonp data. */
-class JsonHijackingFlowConfig extends TaintTracking::Configuration {
- JsonHijackingFlowConfig() { this = "JsonHijackingFlowConfig" }
-
- override predicate isSource(DataFlow::Node src) {
- exists(JsonHijackingExpr jhe, JsonDataFlowConfig jdfc, RemoteFlowConfig rfc |
- jhe = src.asExpr() and
- jdfc.hasFlowTo(DataFlow::exprNode(jhe.getJsonExpr())) and
- rfc.hasFlowTo(DataFlow::exprNode(jhe.getFunctionName()))
- )
- }
-
- override predicate isSink(DataFlow::Node sink) { sink instanceof JsonHijackingSink }
-}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonHijacking.expected b/java/ql/test/experimental/query-tests/security/CWE-352/JsonHijacking.expected
deleted file mode 100644
index 8efc3be1673..00000000000
--- a/java/ql/test/experimental/query-tests/security/CWE-352/JsonHijacking.expected
+++ /dev/null
@@ -1,48 +0,0 @@
-edges
-| JsonHijacking.java:28:32:28:68 | getParameter(...) : String | JsonHijacking.java:33:16:33:24 | resultStr |
-| JsonHijacking.java:32:21:32:54 | ... + ... : String | JsonHijacking.java:33:16:33:24 | resultStr |
-| JsonHijacking.java:40:32:40:68 | getParameter(...) : String | JsonHijacking.java:44:16:44:24 | resultStr |
-| JsonHijacking.java:42:21:42:80 | ... + ... : String | JsonHijacking.java:44:16:44:24 | resultStr |
-| JsonHijacking.java:51:32:51:68 | getParameter(...) : String | JsonHijacking.java:54:16:54:24 | resultStr |
-| JsonHijacking.java:53:21:53:55 | ... + ... : String | JsonHijacking.java:54:16:54:24 | resultStr |
-| JsonHijacking.java:61:32:61:68 | getParameter(...) : String | JsonHijacking.java:64:16:64:24 | resultStr |
-| JsonHijacking.java:63:21:63:54 | ... + ... : String | JsonHijacking.java:64:16:64:24 | resultStr |
-| JsonHijacking.java:72:32:72:68 | getParameter(...) : String | JsonHijacking.java:80:20:80:28 | resultStr |
-| JsonHijacking.java:79:21:79:54 | ... + ... : String | JsonHijacking.java:80:20:80:28 | resultStr |
-| JsonHijacking.java:88:32:88:68 | getParameter(...) : String | JsonHijacking.java:95:20:95:28 | resultStr |
-| JsonHijacking.java:94:21:94:54 | ... + ... : String | JsonHijacking.java:95:20:95:28 | resultStr |
-| JsonHijacking.java:102:32:102:68 | getParameter(...) : String | JsonHijacking.java:113:16:113:24 | resultStr |
-nodes
-| JsonHijacking.java:28:32:28:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonHijacking.java:32:21:32:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonHijacking.java:33:16:33:24 | resultStr | semmle.label | resultStr |
-| JsonHijacking.java:33:16:33:24 | resultStr | semmle.label | resultStr |
-| JsonHijacking.java:40:32:40:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonHijacking.java:42:21:42:80 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonHijacking.java:44:16:44:24 | resultStr | semmle.label | resultStr |
-| JsonHijacking.java:44:16:44:24 | resultStr | semmle.label | resultStr |
-| JsonHijacking.java:51:32:51:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonHijacking.java:53:21:53:55 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonHijacking.java:54:16:54:24 | resultStr | semmle.label | resultStr |
-| JsonHijacking.java:54:16:54:24 | resultStr | semmle.label | resultStr |
-| JsonHijacking.java:61:32:61:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonHijacking.java:63:21:63:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonHijacking.java:64:16:64:24 | resultStr | semmle.label | resultStr |
-| JsonHijacking.java:64:16:64:24 | resultStr | semmle.label | resultStr |
-| JsonHijacking.java:72:32:72:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonHijacking.java:79:21:79:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonHijacking.java:80:20:80:28 | resultStr | semmle.label | resultStr |
-| JsonHijacking.java:80:20:80:28 | resultStr | semmle.label | resultStr |
-| JsonHijacking.java:88:32:88:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonHijacking.java:94:21:94:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonHijacking.java:95:20:95:28 | resultStr | semmle.label | resultStr |
-| JsonHijacking.java:95:20:95:28 | resultStr | semmle.label | resultStr |
-| JsonHijacking.java:102:32:102:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonHijacking.java:113:16:113:24 | resultStr | semmle.label | resultStr |
-#select
-| JsonHijacking.java:33:16:33:24 | resultStr | JsonHijacking.java:28:32:28:68 | getParameter(...) : String | JsonHijacking.java:33:16:33:24 | resultStr | Json Hijacking query might include code from $@. | JsonHijacking.java:28:32:28:68 | getParameter(...) | this user input |
-| JsonHijacking.java:44:16:44:24 | resultStr | JsonHijacking.java:40:32:40:68 | getParameter(...) : String | JsonHijacking.java:44:16:44:24 | resultStr | Json Hijacking query might include code from $@. | JsonHijacking.java:40:32:40:68 | getParameter(...) | this user input |
-| JsonHijacking.java:54:16:54:24 | resultStr | JsonHijacking.java:51:32:51:68 | getParameter(...) : String | JsonHijacking.java:54:16:54:24 | resultStr | Json Hijacking query might include code from $@. | JsonHijacking.java:51:32:51:68 | getParameter(...) | this user input |
-| JsonHijacking.java:64:16:64:24 | resultStr | JsonHijacking.java:61:32:61:68 | getParameter(...) : String | JsonHijacking.java:64:16:64:24 | resultStr | Json Hijacking query might include code from $@. | JsonHijacking.java:61:32:61:68 | getParameter(...) | this user input |
-| JsonHijacking.java:80:20:80:28 | resultStr | JsonHijacking.java:72:32:72:68 | getParameter(...) : String | JsonHijacking.java:80:20:80:28 | resultStr | Json Hijacking query might include code from $@. | JsonHijacking.java:72:32:72:68 | getParameter(...) | this user input |
-| JsonHijacking.java:95:20:95:28 | resultStr | JsonHijacking.java:88:32:88:68 | getParameter(...) : String | JsonHijacking.java:95:20:95:28 | resultStr | Json Hijacking query might include code from $@. | JsonHijacking.java:88:32:88:68 | getParameter(...) | this user input |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonHijacking.java b/java/ql/test/experimental/query-tests/security/CWE-352/JsonHijacking.java
deleted file mode 100644
index 9b473e0610c..00000000000
--- a/java/ql/test/experimental/query-tests/security/CWE-352/JsonHijacking.java
+++ /dev/null
@@ -1,119 +0,0 @@
-import com.alibaba.fastjson.JSONObject;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.gson.Gson;
-import java.io.PrintWriter;
-import java.util.HashMap;
-import java.util.Random;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.ResponseBody;
-
-@Controller
-public class JsonHijacking {
-
- private static HashMap hashMap = new HashMap();
-
- static {
- hashMap.put("username","admin");
- hashMap.put("password","123456");
- }
-
-
- @GetMapping(value = "jsonp1")
- @ResponseBody
- public String bad1(HttpServletRequest request) {
- String resultStr = null;
- String jsonpCallback = request.getParameter("jsonpCallback");
-
- Gson gson = new Gson();
- String result = gson.toJson(hashMap);
- resultStr = jsonpCallback + "(" + result + ")";
- return resultStr;
- }
-
- @GetMapping(value = "jsonp2")
- @ResponseBody
- public String bad2(HttpServletRequest request) {
- String resultStr = null;
- String jsonpCallback = request.getParameter("jsonpCallback");
-
- resultStr = jsonpCallback + "(" + JSONObject.toJSONString(hashMap) + ")";
-
- return resultStr;
- }
-
- @GetMapping(value = "jsonp3")
- @ResponseBody
- public String bad3(HttpServletRequest request) {
- String resultStr = null;
- String jsonpCallback = request.getParameter("jsonpCallback");
- String jsonStr = getJsonStr(hashMap);
- resultStr = jsonpCallback + "(" + jsonStr + ")";
- return resultStr;
- }
-
- @GetMapping(value = "jsonp4")
- @ResponseBody
- public String bad4(HttpServletRequest request) {
- String resultStr = null;
- String jsonpCallback = request.getParameter("jsonpCallback");
- String restr = JSONObject.toJSONString(hashMap);
- resultStr = jsonpCallback + "(" + restr + ");";
- return resultStr;
- }
-
- @GetMapping(value = "jsonp5")
- @ResponseBody
- public void bad5(HttpServletRequest request,
- HttpServletResponse response) throws Exception {
- response.setContentType("application/json");
- String jsonpCallback = request.getParameter("jsonpCallback");
- PrintWriter pw = null;
- Gson gson = new Gson();
- String result = gson.toJson(hashMap);
-
- String resultStr = null;
- pw = response.getWriter();
- resultStr = jsonpCallback + "(" + result + ")";
- pw.println(resultStr);
- }
-
- @GetMapping(value = "jsonp6")
- @ResponseBody
- public void bad6(HttpServletRequest request,
- HttpServletResponse response) throws Exception {
- response.setContentType("application/json");
- String jsonpCallback = request.getParameter("jsonpCallback");
- PrintWriter pw = null;
- ObjectMapper mapper = new ObjectMapper();
- String result = mapper.writeValueAsString(hashMap);
- String resultStr = null;
- pw = response.getWriter();
- resultStr = jsonpCallback + "(" + result + ")";
- pw.println(resultStr);
- }
-
- @GetMapping(value = "jsonp7")
- @ResponseBody
- public String good(HttpServletRequest request) {
- String resultStr = null;
- String jsonpCallback = request.getParameter("jsonpCallback");
-
- String val = "";
- Random random = new Random();
- for (int i = 0; i < 10; i++) {
- val += String.valueOf(random.nextInt(10));
- }
- // good
- jsonpCallback = jsonpCallback + "_" + val;
- String jsonStr = getJsonStr(hashMap);
- resultStr = jsonpCallback + "(" + jsonStr + ")";
- return resultStr;
- }
-
- public static String getJsonStr(Object result) {
- return JSONObject.toJSONString(result);
- }
-}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonHijacking.qlref b/java/ql/test/experimental/query-tests/security/CWE-352/JsonHijacking.qlref
deleted file mode 100644
index e79471b3c1e..00000000000
--- a/java/ql/test/experimental/query-tests/security/CWE-352/JsonHijacking.qlref
+++ /dev/null
@@ -1 +0,0 @@
-Security/CWE/CWE-352/JsonHijacking.ql
From 476309af6dc21295312598a6cd0fb406539dc2a6 Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Sun, 21 Feb 2021 19:45:12 +0100
Subject: [PATCH 060/725] Added SpringHttpInvokerUnsafeDeserialization.ql
---
.../SpringHttpInvokerUnsafeDeserialization.ql | 56 +++++++++++++++++++
1 file changed, 56 insertions(+)
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-502/SpringHttpInvokerUnsafeDeserialization.ql
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/SpringHttpInvokerUnsafeDeserialization.ql b/java/ql/src/experimental/Security/CWE/CWE-502/SpringHttpInvokerUnsafeDeserialization.ql
new file mode 100644
index 00000000000..73c67582692
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/SpringHttpInvokerUnsafeDeserialization.ql
@@ -0,0 +1,56 @@
+/**
+ * @name Unsafe deserialization with spring's remote service exporters.
+ * @description Creating a bean based on RemoteInvocationSerializingExporter
+ * may lead to arbitrary code execution.
+ * @kind problem
+ * @problem.severity error
+ * @precision high
+ * @id java/spring-exporter-unsafe-deserialization
+ * @tags security
+ * external/cwe/cwe-502
+ */
+
+import java
+
+/**
+ * Holds if `method` initializes a bean.
+ */
+private predicate createsBean(Method method) {
+ method.hasAnnotation("org.springframework.context.annotation", "Bean")
+}
+
+/**
+ * Holds if `type` is `RemoteInvocationSerializingExporter`.
+ */
+private predicate isRemoteInvocationSerializingExporter(RefType type) {
+ type.hasQualifiedName("org.springframework.remoting.rmi", "RemoteInvocationSerializingExporter")
+}
+
+/**
+ * Holds if `method` returns an object that extends `RemoteInvocationSerializingExporter`.
+ */
+private predicate returnsRemoteInvocationSerializingExporter(Method method) {
+ isRemoteInvocationSerializingExporter(method.getReturnType()) or
+ isRemoteInvocationSerializingExporter(method.getReturnType().(RefType).getASupertype*())
+}
+
+/**
+ * Holds if `method` belongs to a Spring configuration.
+ */
+private predicate isInConfiguration(Method method) {
+ method.getDeclaringType().hasAnnotation("org.springframework.context.annotation", "Configuration")
+}
+
+/**
+ * Holds if `method` initializes a bean that is based on `RemoteInvocationSerializingExporter`.
+ */
+private predicate createsRemoteInvocationSerializingExporterBean(Method method) {
+ isInConfiguration(method) and
+ createsBean(method) and
+ returnsRemoteInvocationSerializingExporter(method)
+}
+
+from Method method
+where createsRemoteInvocationSerializingExporterBean(method)
+select method,
+ "Unasafe deserialization in a remote service exporter in '" + method.getName() + "' method"
From 95284ad71ddc87e91baaf1dc4edd5d1494c8d806 Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Sun, 21 Feb 2021 21:43:17 +0100
Subject: [PATCH 061/725] Added SpringHttpInvokerUnsafeDeserialization.qhelp
and example
---
...ringHttpInvokerUnsafeDeserialization.qhelp | 69 +++++++++++++++++++
.../CWE-502/UnsafeHttpInvokerEndpoint.java | 24 +++++++
2 files changed, 93 insertions(+)
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-502/SpringHttpInvokerUnsafeDeserialization.qhelp
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-502/UnsafeHttpInvokerEndpoint.java
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/SpringHttpInvokerUnsafeDeserialization.qhelp b/java/ql/src/experimental/Security/CWE/CWE-502/SpringHttpInvokerUnsafeDeserialization.qhelp
new file mode 100644
index 00000000000..11a3333007a
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/SpringHttpInvokerUnsafeDeserialization.qhelp
@@ -0,0 +1,69 @@
+
+
+
+
+
+Spring Framework provides an abstract base class RemoteInvocationSerializingExporter
+for defining remote service exporters.
+A Spring exporter, which is based on this class, deserializes incoming data using ObjectInputStream.
+Deserializing untrusted data is easily exploitable and in many cases allows an attacker
+to execute arbitrary code.
+
+
+Spring Framework also provides two classes that extend RemoteInvocationSerializingExporter:
+
+HttpInvokerServiceExporter
+
+
+SimpleHttpInvokerServiceExporter
+
+
+
+These classes export specified beans as HTTP endpoints that deserialize data from an HTTP request
+using unsafe ObjectInputStream. If a remote attacker can reach such endpoints,
+it results in remote code execution.
+
+
+
+
+
+Avoid using HttpInvokerServiceExporter, SimpleHttpInvokerServiceExporter
+and other exporters that are based on RemoteInvocationSerializingExporter.
+Instead, use other message formats for API endpoints (for example, JSON),
+but make sure that the underlying deserialization mechanism is properly configured
+so that deserialization attacks are not possible. If the vulnerable exporters can not be replaced,
+consider using global deserialization filters introduced by JEP 290.
+In general, avoid deserialization of untrusted data.
+
+
+
+
+
+The following example defines a vulnerable HTTP endpoint:
+
+
+
+
+
+
+OWASP:
+Deserialization of untrusted data.
+
+
+National Vulnerability Database:
+CVE-2016-1000027
+
+
+Tenable Research Advisory:
+[R2] Pivotal Spring Framework HttpInvokerServiceExporter readRemoteInvocation Method Untrusted Java Deserialization
+
+
+Spring Framework bug tracker:
+Sonatype vulnerability CVE-2016-1000027 in Spring-web project
+
+
+OpenJDK:
+JEP 290: Filter Incoming Serialization Data
+
+
+
\ No newline at end of file
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeHttpInvokerEndpoint.java b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeHttpInvokerEndpoint.java
new file mode 100644
index 00000000000..a4c6dadf17c
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeHttpInvokerEndpoint.java
@@ -0,0 +1,24 @@
+@Configuration
+public class Server {
+
+ @Bean(name = "/account")
+ HttpInvokerServiceExporter accountService() {
+ HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter();
+ exporter.setService(new AccountServiceImpl());
+ exporter.setServiceInterface(AccountService.class);
+ return exporter;
+ }
+
+}
+
+class AccountServiceImpl implements AccountService {
+
+ @Override
+ public String echo(String data) {
+ return data;
+ }
+}
+
+interface AccountService {
+ String echo(String data);
+}
\ No newline at end of file
From aac0c27dcd25fc37b9a56b5235f2e7ea09404009 Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Sun, 21 Feb 2021 22:19:53 +0100
Subject: [PATCH 062/725] Added tests for
SpringHttpInvokerUnsafeDeserialization.ql
---
...gHttpInvokerUnsafeDeserialization.expected | 1 +
...pringHttpInvokerUnsafeDeserialization.java | 45 +++++++++++++++++++
...ringHttpInvokerUnsafeDeserialization.qlref | 1 +
.../query-tests/security/CWE-502/options | 1 +
.../context/annotation/Bean.java | 10 +++++
.../context/annotation/Configuration.java | 7 +++
.../HttpInvokerServiceExporter.java | 8 ++++
.../RemoteInvocationSerializingExporter.java | 5 +++
8 files changed, 78 insertions(+)
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-502/SpringHttpInvokerUnsafeDeserialization.expected
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-502/SpringHttpInvokerUnsafeDeserialization.java
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-502/SpringHttpInvokerUnsafeDeserialization.qlref
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-502/options
create mode 100644 java/ql/test/stubs/springframework-5.2.3/org/springframework/context/annotation/Bean.java
create mode 100644 java/ql/test/stubs/springframework-5.2.3/org/springframework/context/annotation/Configuration.java
create mode 100644 java/ql/test/stubs/springframework-5.2.3/org/springframework/remoting/httpinvoker/HttpInvokerServiceExporter.java
create mode 100644 java/ql/test/stubs/springframework-5.2.3/org/springframework/remoting/rmi/RemoteInvocationSerializingExporter.java
diff --git a/java/ql/test/experimental/query-tests/security/CWE-502/SpringHttpInvokerUnsafeDeserialization.expected b/java/ql/test/experimental/query-tests/security/CWE-502/SpringHttpInvokerUnsafeDeserialization.expected
new file mode 100644
index 00000000000..0ce0efca838
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-502/SpringHttpInvokerUnsafeDeserialization.expected
@@ -0,0 +1 @@
+| SpringHttpInvokerUnsafeDeserialization.java:9:32:9:37 | unsafe | Unasafe deserialization in a remote service exporter in 'unsafe' method |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-502/SpringHttpInvokerUnsafeDeserialization.java b/java/ql/test/experimental/query-tests/security/CWE-502/SpringHttpInvokerUnsafeDeserialization.java
new file mode 100644
index 00000000000..d678c8f0191
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-502/SpringHttpInvokerUnsafeDeserialization.java
@@ -0,0 +1,45 @@
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter;
+
+@Configuration
+public class SpringHttpInvokerUnsafeDeserialization {
+
+ @Bean(name = "/unsafe")
+ HttpInvokerServiceExporter unsafe() {
+ HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter();
+ exporter.setService(new AccountServiceImpl());
+ exporter.setServiceInterface(AccountService.class);
+ return exporter;
+ }
+
+ HttpInvokerServiceExporter notABean() {
+ HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter();
+ exporter.setService(new AccountServiceImpl());
+ exporter.setServiceInterface(AccountService.class);
+ return exporter;
+ }
+}
+
+class NotAConfiguration {
+
+ @Bean(name = "/notAnEndpoint")
+ HttpInvokerServiceExporter notAnEndpoint() {
+ HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter();
+ exporter.setService(new AccountServiceImpl());
+ exporter.setServiceInterface(AccountService.class);
+ return exporter;
+ }
+}
+
+class AccountServiceImpl implements AccountService {
+
+ @Override
+ public String echo(String data) {
+ return data;
+ }
+}
+
+interface AccountService {
+ String echo(String data);
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-502/SpringHttpInvokerUnsafeDeserialization.qlref b/java/ql/test/experimental/query-tests/security/CWE-502/SpringHttpInvokerUnsafeDeserialization.qlref
new file mode 100644
index 00000000000..014b0872ea4
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-502/SpringHttpInvokerUnsafeDeserialization.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-502/SpringHttpInvokerUnsafeDeserialization.ql
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-502/options b/java/ql/test/experimental/query-tests/security/CWE-502/options
new file mode 100644
index 00000000000..31b8e3f6935
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-502/options
@@ -0,0 +1 @@
+//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3
\ No newline at end of file
diff --git a/java/ql/test/stubs/springframework-5.2.3/org/springframework/context/annotation/Bean.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/context/annotation/Bean.java
new file mode 100644
index 00000000000..24d7be0f817
--- /dev/null
+++ b/java/ql/test/stubs/springframework-5.2.3/org/springframework/context/annotation/Bean.java
@@ -0,0 +1,10 @@
+package org.springframework.context.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
+public @interface Bean {
+
+ String[] name() default {};
+}
\ No newline at end of file
diff --git a/java/ql/test/stubs/springframework-5.2.3/org/springframework/context/annotation/Configuration.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/context/annotation/Configuration.java
new file mode 100644
index 00000000000..0024b3ba833
--- /dev/null
+++ b/java/ql/test/stubs/springframework-5.2.3/org/springframework/context/annotation/Configuration.java
@@ -0,0 +1,7 @@
+package org.springframework.context.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target(ElementType.TYPE)
+public @interface Configuration {}
\ No newline at end of file
diff --git a/java/ql/test/stubs/springframework-5.2.3/org/springframework/remoting/httpinvoker/HttpInvokerServiceExporter.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/remoting/httpinvoker/HttpInvokerServiceExporter.java
new file mode 100644
index 00000000000..5eb56c0897b
--- /dev/null
+++ b/java/ql/test/stubs/springframework-5.2.3/org/springframework/remoting/httpinvoker/HttpInvokerServiceExporter.java
@@ -0,0 +1,8 @@
+package org.springframework.remoting.httpinvoker;
+
+public class HttpInvokerServiceExporter extends org.springframework.remoting.rmi.RemoteInvocationSerializingExporter {
+
+ public void setService(Object service) {}
+
+ public void setServiceInterface(Class clazz) {}
+}
\ No newline at end of file
diff --git a/java/ql/test/stubs/springframework-5.2.3/org/springframework/remoting/rmi/RemoteInvocationSerializingExporter.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/remoting/rmi/RemoteInvocationSerializingExporter.java
new file mode 100644
index 00000000000..a6f6fec194f
--- /dev/null
+++ b/java/ql/test/stubs/springframework-5.2.3/org/springframework/remoting/rmi/RemoteInvocationSerializingExporter.java
@@ -0,0 +1,5 @@
+package org.springframework.remoting.rmi;
+
+public abstract class RemoteInvocationSerializingExporter {
+
+}
From e02b51f42bad96acbdf29a22c3768af894a942af Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Wed, 24 Feb 2021 22:23:11 +0100
Subject: [PATCH 063/725] Improved SpringHttpInvokerUnsafeDeserialization.qhelp
---
.../SpringHttpInvokerUnsafeDeserialization.qhelp | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/SpringHttpInvokerUnsafeDeserialization.qhelp b/java/ql/src/experimental/Security/CWE/CWE-502/SpringHttpInvokerUnsafeDeserialization.qhelp
index 11a3333007a..49237a8500e 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-502/SpringHttpInvokerUnsafeDeserialization.qhelp
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/SpringHttpInvokerUnsafeDeserialization.qhelp
@@ -21,14 +21,17 @@ Spring Framework also provides two classes that extend RemoteInvocationSer
These classes export specified beans as HTTP endpoints that deserialize data from an HTTP request
using unsafe ObjectInputStream. If a remote attacker can reach such endpoints,
-it results in remote code execution.
+it results in remote code execution in the worst case.
+
+
+CVE-2016-1000027 has been assigned to this issue in Spring Framework. There is no fix for that.
Avoid using HttpInvokerServiceExporter, SimpleHttpInvokerServiceExporter
-and other exporters that are based on RemoteInvocationSerializingExporter.
+and any other exporter that is based on RemoteInvocationSerializingExporter.
Instead, use other message formats for API endpoints (for example, JSON),
but make sure that the underlying deserialization mechanism is properly configured
so that deserialization attacks are not possible. If the vulnerable exporters can not be replaced,
@@ -50,6 +53,14 @@ OWASP:
Deserialization of untrusted data.
+Spring Framework API documentation:
+RemoteInvocationSerializingExporter class
+
+
+Spring Framework API documentation:
+HttpInvokerServiceExporter class
+
+
National Vulnerability Database:
CVE-2016-1000027
@@ -66,4 +77,5 @@ OpenJDK:
JEP 290: Filter Incoming Serialization Data
+
\ No newline at end of file
From 2e02625f22aeebb0c9ef3dfc02e71c714196b3d8 Mon Sep 17 00:00:00 2001
From: Dave Bartolomeo
Date: Thu, 25 Feb 2021 12:53:39 -0500
Subject: [PATCH 064/725] C++: Summary metrics queries
This is a first attempt at implementing, for C++, the set of summary queries that we expect all languages to implement to help diagnose extraction failures and build configuration problems. See the spec in [this document](https://docs.google.com/document/d/1V3zpkj0OGh8GEUVwACRx7fiafE5zklujAftZaYUyf9s/edit?usp=sharing). The five queries are:
- Total number of source files (including .c/.cpp and header files)
- Total number of lines of text across all text files
- Total number of lines of code across all text files
- Number of lines of text in each source file
- Number of lines of code in each source file
I've added some simple unit tests that cover all five of these.
---
cpp/ql/src/Diagnostics/Files.ql | 10 ++
cpp/ql/src/Diagnostics/Lines.ql | 10 ++
cpp/ql/src/Diagnostics/LinesOfCode.ql | 10 ++
cpp/ql/src/Diagnostics/LinesOfCodePerFile.ql | 12 ++
cpp/ql/src/Diagnostics/LinesPerFile.ql | 12 ++
.../query-tests/Diagnostics/Files.expected | 1 +
.../test/query-tests/Diagnostics/Files.qlref | 1 +
.../query-tests/Diagnostics/Lines.expected | 1 +
.../test/query-tests/Diagnostics/Lines.qlref | 1 +
.../Diagnostics/LinesOfCode.expected | 1 +
.../query-tests/Diagnostics/LinesOfCode.qlref | 1 +
.../Diagnostics/LinesOfCodePerFile.expected | 3 +
.../Diagnostics/LinesOfCodePerFile.qlref | 1 +
.../Diagnostics/LinesPerFile.expected | 3 +
.../Diagnostics/LinesPerFile.qlref | 1 +
.../query-tests/Diagnostics/empty-file.cpp | 0
.../query-tests/Diagnostics/large-file.cpp | 119 ++++++++++++++++++
.../query-tests/Diagnostics/short-file.cpp | 3 +
18 files changed, 190 insertions(+)
create mode 100644 cpp/ql/src/Diagnostics/Files.ql
create mode 100644 cpp/ql/src/Diagnostics/Lines.ql
create mode 100644 cpp/ql/src/Diagnostics/LinesOfCode.ql
create mode 100644 cpp/ql/src/Diagnostics/LinesOfCodePerFile.ql
create mode 100644 cpp/ql/src/Diagnostics/LinesPerFile.ql
create mode 100644 cpp/ql/test/query-tests/Diagnostics/Files.expected
create mode 100644 cpp/ql/test/query-tests/Diagnostics/Files.qlref
create mode 100644 cpp/ql/test/query-tests/Diagnostics/Lines.expected
create mode 100644 cpp/ql/test/query-tests/Diagnostics/Lines.qlref
create mode 100644 cpp/ql/test/query-tests/Diagnostics/LinesOfCode.expected
create mode 100644 cpp/ql/test/query-tests/Diagnostics/LinesOfCode.qlref
create mode 100644 cpp/ql/test/query-tests/Diagnostics/LinesOfCodePerFile.expected
create mode 100644 cpp/ql/test/query-tests/Diagnostics/LinesOfCodePerFile.qlref
create mode 100644 cpp/ql/test/query-tests/Diagnostics/LinesPerFile.expected
create mode 100644 cpp/ql/test/query-tests/Diagnostics/LinesPerFile.qlref
create mode 100644 cpp/ql/test/query-tests/Diagnostics/empty-file.cpp
create mode 100644 cpp/ql/test/query-tests/Diagnostics/large-file.cpp
create mode 100644 cpp/ql/test/query-tests/Diagnostics/short-file.cpp
diff --git a/cpp/ql/src/Diagnostics/Files.ql b/cpp/ql/src/Diagnostics/Files.ql
new file mode 100644
index 00000000000..0759284541f
--- /dev/null
+++ b/cpp/ql/src/Diagnostics/Files.ql
@@ -0,0 +1,10 @@
+/**
+ * @name Total source files
+ * @description The total number of source files.
+ * @kind metric
+ * @id cpp/metrics/files
+ */
+
+import cpp
+
+select count(File f | f.fromSource())
diff --git a/cpp/ql/src/Diagnostics/Lines.ql b/cpp/ql/src/Diagnostics/Lines.ql
new file mode 100644
index 00000000000..cce3e42c00f
--- /dev/null
+++ b/cpp/ql/src/Diagnostics/Lines.ql
@@ -0,0 +1,10 @@
+/**
+ * @name Total lines of text
+ * @description The total number of lines of text across all source files.
+ * @kind metric
+ * @id cpp/metrics/lines
+ */
+
+import cpp
+
+select sum(File f | f.fromSource() | f.getMetrics().getNumberOfLines())
diff --git a/cpp/ql/src/Diagnostics/LinesOfCode.ql b/cpp/ql/src/Diagnostics/LinesOfCode.ql
new file mode 100644
index 00000000000..9553e39b3e4
--- /dev/null
+++ b/cpp/ql/src/Diagnostics/LinesOfCode.ql
@@ -0,0 +1,10 @@
+/**
+ * @name Total lines of code
+ * @description The total number of lines of code across all source files.
+ * @kind metric
+ * @id cpp/metrics/lines-of-code
+ */
+
+import cpp
+
+select sum(File f | f.fromSource() | f.getMetrics().getNumberOfLinesOfCode())
diff --git a/cpp/ql/src/Diagnostics/LinesOfCodePerFile.ql b/cpp/ql/src/Diagnostics/LinesOfCodePerFile.ql
new file mode 100644
index 00000000000..ebb9a19c447
--- /dev/null
+++ b/cpp/ql/src/Diagnostics/LinesOfCodePerFile.ql
@@ -0,0 +1,12 @@
+/**
+ * @name Lines of code per source file
+ * @description The number of lines of code for each source file.
+ * @kind metric
+ * @id cpp/metrics/lines-of-code-per-file
+ */
+
+import cpp
+
+from File f
+where f.fromSource()
+select f, f.getMetrics().getNumberOfLinesOfCode()
diff --git a/cpp/ql/src/Diagnostics/LinesPerFile.ql b/cpp/ql/src/Diagnostics/LinesPerFile.ql
new file mode 100644
index 00000000000..4fa3608d293
--- /dev/null
+++ b/cpp/ql/src/Diagnostics/LinesPerFile.ql
@@ -0,0 +1,12 @@
+/**
+ * @name Lines of text per source file
+ * @description The number of lines of text for each source file.
+ * @kind metric
+ * @id cpp/metrics/lines-per-file
+ */
+
+import cpp
+
+from File f
+where f.fromSource()
+select f, f.getMetrics().getNumberOfLines()
diff --git a/cpp/ql/test/query-tests/Diagnostics/Files.expected b/cpp/ql/test/query-tests/Diagnostics/Files.expected
new file mode 100644
index 00000000000..7621cebdd5f
--- /dev/null
+++ b/cpp/ql/test/query-tests/Diagnostics/Files.expected
@@ -0,0 +1 @@
+| 3 |
diff --git a/cpp/ql/test/query-tests/Diagnostics/Files.qlref b/cpp/ql/test/query-tests/Diagnostics/Files.qlref
new file mode 100644
index 00000000000..f29d0010003
--- /dev/null
+++ b/cpp/ql/test/query-tests/Diagnostics/Files.qlref
@@ -0,0 +1 @@
+Diagnostics/Files.ql
\ No newline at end of file
diff --git a/cpp/ql/test/query-tests/Diagnostics/Lines.expected b/cpp/ql/test/query-tests/Diagnostics/Lines.expected
new file mode 100644
index 00000000000..d847b050658
--- /dev/null
+++ b/cpp/ql/test/query-tests/Diagnostics/Lines.expected
@@ -0,0 +1 @@
+| 122 |
diff --git a/cpp/ql/test/query-tests/Diagnostics/Lines.qlref b/cpp/ql/test/query-tests/Diagnostics/Lines.qlref
new file mode 100644
index 00000000000..0b62ca77ec6
--- /dev/null
+++ b/cpp/ql/test/query-tests/Diagnostics/Lines.qlref
@@ -0,0 +1 @@
+Diagnostics/Lines.ql
diff --git a/cpp/ql/test/query-tests/Diagnostics/LinesOfCode.expected b/cpp/ql/test/query-tests/Diagnostics/LinesOfCode.expected
new file mode 100644
index 00000000000..a75c288c151
--- /dev/null
+++ b/cpp/ql/test/query-tests/Diagnostics/LinesOfCode.expected
@@ -0,0 +1 @@
+| 93 |
diff --git a/cpp/ql/test/query-tests/Diagnostics/LinesOfCode.qlref b/cpp/ql/test/query-tests/Diagnostics/LinesOfCode.qlref
new file mode 100644
index 00000000000..b07f3413688
--- /dev/null
+++ b/cpp/ql/test/query-tests/Diagnostics/LinesOfCode.qlref
@@ -0,0 +1 @@
+Diagnostics/LinesOfCode.ql
\ No newline at end of file
diff --git a/cpp/ql/test/query-tests/Diagnostics/LinesOfCodePerFile.expected b/cpp/ql/test/query-tests/Diagnostics/LinesOfCodePerFile.expected
new file mode 100644
index 00000000000..076ee4121a4
--- /dev/null
+++ b/cpp/ql/test/query-tests/Diagnostics/LinesOfCodePerFile.expected
@@ -0,0 +1,3 @@
+| empty-file.cpp:0:0:0:0 | empty-file.cpp | 0 |
+| large-file.cpp:0:0:0:0 | large-file.cpp | 90 |
+| short-file.cpp:0:0:0:0 | short-file.cpp | 3 |
diff --git a/cpp/ql/test/query-tests/Diagnostics/LinesOfCodePerFile.qlref b/cpp/ql/test/query-tests/Diagnostics/LinesOfCodePerFile.qlref
new file mode 100644
index 00000000000..ea50143078c
--- /dev/null
+++ b/cpp/ql/test/query-tests/Diagnostics/LinesOfCodePerFile.qlref
@@ -0,0 +1 @@
+Diagnostics/LinesOfCodePerFile.ql
\ No newline at end of file
diff --git a/cpp/ql/test/query-tests/Diagnostics/LinesPerFile.expected b/cpp/ql/test/query-tests/Diagnostics/LinesPerFile.expected
new file mode 100644
index 00000000000..e02d0591ab2
--- /dev/null
+++ b/cpp/ql/test/query-tests/Diagnostics/LinesPerFile.expected
@@ -0,0 +1,3 @@
+| empty-file.cpp:0:0:0:0 | empty-file.cpp | 0 |
+| large-file.cpp:0:0:0:0 | large-file.cpp | 119 |
+| short-file.cpp:0:0:0:0 | short-file.cpp | 3 |
diff --git a/cpp/ql/test/query-tests/Diagnostics/LinesPerFile.qlref b/cpp/ql/test/query-tests/Diagnostics/LinesPerFile.qlref
new file mode 100644
index 00000000000..768a83875e6
--- /dev/null
+++ b/cpp/ql/test/query-tests/Diagnostics/LinesPerFile.qlref
@@ -0,0 +1 @@
+Diagnostics/LinesPerFile.ql
\ No newline at end of file
diff --git a/cpp/ql/test/query-tests/Diagnostics/empty-file.cpp b/cpp/ql/test/query-tests/Diagnostics/empty-file.cpp
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/cpp/ql/test/query-tests/Diagnostics/large-file.cpp b/cpp/ql/test/query-tests/Diagnostics/large-file.cpp
new file mode 100644
index 00000000000..d6d06518b66
--- /dev/null
+++ b/cpp/ql/test/query-tests/Diagnostics/large-file.cpp
@@ -0,0 +1,119 @@
+int a00(float x) {
+ return (int)x;
+}
+
+int a01(float x) {
+ return (int)x;
+}
+
+int a02(float x) {
+ return (int)x;
+}
+
+int a03(float x) {
+ return (int)x;
+}
+
+int a04(float x) {
+ return (int)x;
+}
+
+int a05(float x) {
+ return (int)x;
+}
+
+int a06(float x) {
+ return (int)x;
+}
+
+int a07(float x) {
+ return (int)x;
+}
+
+int a08(float x) {
+ return (int)x;
+}
+
+int a09(float x) {
+ return (int)x;
+}
+
+int a10(float x) {
+ return (int)x;
+}
+
+int a11(float x) {
+ return (int)x;
+}
+
+int a12(float x) {
+ return (int)x;
+}
+
+int a13(float x) {
+ return (int)x;
+}
+
+int a14(float x) {
+ return (int)x;
+}
+
+int a15(float x) {
+ return (int)x;
+}
+
+int a16(float x) {
+ return (int)x;
+}
+
+int a17(float x) {
+ return (int)x;
+}
+
+int a18(float x) {
+ return (int)x;
+}
+
+int a19(float x) {
+ return (int)x;
+}
+
+int a20(float x) {
+ return (int)x;
+}
+
+int a21(float x) {
+ return (int)x;
+}
+
+int a22(float x) {
+ return (int)x;
+}
+
+int a23(float x) {
+ return (int)x;
+}
+
+int a24(float x) {
+ return (int)x;
+}
+
+int a25(float x) {
+ return (int)x;
+}
+
+int a26(float x) {
+ return (int)x;
+}
+
+int a27(float x) {
+ return (int)x;
+}
+
+int a28(float x) {
+ return (int)x;
+}
+
+int a29(float x) {
+ return (int)x;
+}
diff --git a/cpp/ql/test/query-tests/Diagnostics/short-file.cpp b/cpp/ql/test/query-tests/Diagnostics/short-file.cpp
new file mode 100644
index 00000000000..e4055360d37
--- /dev/null
+++ b/cpp/ql/test/query-tests/Diagnostics/short-file.cpp
@@ -0,0 +1,3 @@
+int g(float x) {
+ return (int)x;
+}
From f795d5e0d3b4f1a7fc5446755020786c3faecae1 Mon Sep 17 00:00:00 2001
From: haby0
Date: Sat, 27 Feb 2021 16:25:17 +0800
Subject: [PATCH 065/725] update JSONP Injection ql
---
.../Security/CWE/CWE-352/JsonpInjection.ql | 45 +++++---
.../CWE/CWE-352/JsonpInjectionFilterLib.qll | 77 +++++++++++++
.../CWE/CWE-352/JsonpInjectionLib.qll | 65 ++++++++++-
.../CWE/CWE-352/JsonpInjectionServlet.java | 60 ++++++++++
.../CWE/CWE-352/JsonpInjectionServlet1.java | 64 +++++++++++
.../CWE/CWE-352/JsonpInjectionServlet2.java | 50 +++++++++
.../semmle/code/java/frameworks/Servlets.qll | 27 +++++
.../security/CWE-352/JsonpInjection.expected | 106 +++++++++---------
.../security/CWE-352/JsonpInjection.java | 13 ++-
.../core/annotation/AliasFor.class | Bin 385 -> 0 bytes
.../web/bind/annotation/GetMapping.class | Bin 504 -> 0 bytes
.../web/bind/annotation/RequestMapping.java | 2 +
.../web/bind/annotation/RequestMethod.java | 15 +++
.../web/bind/annotation/ResponseBody.class | Bin 184 -> 0 bytes
14 files changed, 448 insertions(+), 76 deletions(-)
create mode 100644 java/ql/src/Security/CWE/CWE-352/JsonpInjectionFilterLib.qll
create mode 100644 java/ql/src/Security/CWE/CWE-352/JsonpInjectionServlet.java
create mode 100644 java/ql/src/Security/CWE/CWE-352/JsonpInjectionServlet1.java
create mode 100644 java/ql/src/Security/CWE/CWE-352/JsonpInjectionServlet2.java
delete mode 100644 java/ql/test/stubs/spring-core-5.3.2/org/springframework/core/annotation/AliasFor.class
delete mode 100644 java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/GetMapping.class
create mode 100644 java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/RequestMethod.java
delete mode 100644 java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/ResponseBody.class
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonpInjection.ql b/java/ql/src/Security/CWE/CWE-352/JsonpInjection.ql
index e7ca2d41e34..53ee6182511 100644
--- a/java/ql/src/Security/CWE/CWE-352/JsonpInjection.ql
+++ b/java/ql/src/Security/CWE/CWE-352/JsonpInjection.ql
@@ -1,55 +1,68 @@
/**
- * @name JSON Hijacking
+ * @name JSONP Injection
* @description User-controlled callback function names that are not verified are vulnerable
- * to json hijacking attacks.
+ * to jsonp injection attacks.
* @kind path-problem
* @problem.severity error
* @precision high
- * @id java/Json-hijacking
+ * @id java/JSONP-Injection
* @tags security
* external/cwe/cwe-352
*/
import java
import JsonpInjectionLib
+import JsonpInjectionFilterLib
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.deadcode.WebEntryPoints
import DataFlow::PathGraph
-class VerifAuth extends DataFlow::BarrierGuard {
- VerifAuth() {
+
+/** If there is a method to verify `token`, `auth`, `referer`, and `origin`, it will not pass. */
+class ServletVerifAuth extends DataFlow::BarrierGuard {
+ ServletVerifAuth() {
exists(MethodAccess ma, Node prod, Node succ |
- this = ma and
- ma.getMethod().getName().regexpMatch("(?i).*(token|auth|referer).*") and
+ ma.getMethod().getName().regexpMatch("(?i).*(token|auth|referer|origin).*") and
prod instanceof RemoteFlowSource and
succ.asExpr() = ma.getAnArgument() and
- ma.getMethod().getAParameter().getName().regexpMatch("(?i).*(token|auth|referer).*") and
- localFlowStep*(prod, succ)
+ ma.getMethod().getAParameter().getName().regexpMatch("(?i).*(token|auth|referer|origin).*") and
+ localFlowStep*(prod, succ) and
+ this = ma
)
}
override predicate checks(Expr e, boolean branch) {
- exists(ReturnStmt rs |
- e = rs.getResult() and
+ exists(Node node |
+ node instanceof JsonpInjectionSink and
+ e = node.asExpr() and
branch = true
)
}
}
-/** Taint-tracking configuration tracing flow from remote sources to output jsonp data. */
+/** Taint-tracking configuration tracing flow from get method request sources to output jsonp data. */
class JsonpInjectionConfig extends TaintTracking::Configuration {
JsonpInjectionConfig() { this = "JsonpInjectionConfig" }
- override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
+ override predicate isSource(DataFlow::Node source) { source instanceof GetHttpRequestSource }
override predicate isSink(DataFlow::Node sink) { sink instanceof JsonpInjectionSink }
- override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { guard instanceof VerifAuth }
+ override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
+ guard instanceof ServletVerifAuth
+ }
+
+ override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
+ exists(MethodAccess ma |
+ isRequestGetParamMethod(ma) and pred.asExpr() = ma.getQualifier() and succ.asExpr() = ma
+ )
+ }
}
from DataFlow::PathNode source, DataFlow::PathNode sink, JsonpInjectionConfig conf
where
+ not checks() = false and
conf.hasFlowPath(source, sink) and
exists(JsonpInjectionFlowConfig jhfc | jhfc.hasFlowTo(sink.getNode()))
-select sink.getNode(), source, sink, "Json Hijacking query might include code from $@.",
- source.getNode(), "this user input"
\ No newline at end of file
+select sink.getNode(), source, sink, "Jsonp Injection query might include code from $@.",
+ source.getNode(), "this user input"
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonpInjectionFilterLib.qll b/java/ql/src/Security/CWE/CWE-352/JsonpInjectionFilterLib.qll
new file mode 100644
index 00000000000..b349bed2641
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-352/JsonpInjectionFilterLib.qll
@@ -0,0 +1,77 @@
+/**
+ * @name JSONP Injection
+ * @description User-controlled callback function names that are not verified are vulnerable
+ * to json hijacking attacks.
+ * @kind path-problem
+ */
+
+import java
+import DataFlow
+import semmle.code.java.dataflow.FlowSources
+import semmle.code.java.dataflow.TaintTracking2
+import DataFlow::PathGraph
+
+class FilterVerifAuth extends DataFlow::BarrierGuard {
+ FilterVerifAuth() {
+ exists(MethodAccess ma, Node prod, Node succ |
+ ma.getMethod().getName().regexpMatch("(?i).*(token|auth|referer|origin).*") and
+ prod instanceof RemoteFlowSource and
+ succ.asExpr() = ma.getAnArgument() and
+ ma.getMethod().getAParameter().getName().regexpMatch("(?i).*(token|auth|referer|origin).*") and
+ localFlowStep*(prod, succ) and
+ this = ma
+ )
+ }
+
+ override predicate checks(Expr e, boolean branch) {
+ exists(Node node |
+ node instanceof DoFilterMethodSink and
+ e = node.asExpr() and
+ branch = true
+ )
+ }
+}
+
+/** A data flow source for `Filter.doFilter` method paramters. */
+private class DoFilterMethodSource extends DataFlow::Node {
+ DoFilterMethodSource() {
+ exists(Method m |
+ isDoFilterMethod(m) and
+ m.getAParameter().getAnAccess() = this.asExpr()
+ )
+ }
+}
+
+/** A data flow sink for `FilterChain.doFilter` method qualifying expression. */
+private class DoFilterMethodSink extends DataFlow::Node {
+ DoFilterMethodSink() {
+ exists(MethodAccess ma, Method m | ma.getMethod() = m |
+ m.hasName("doFilter") and
+ m.getDeclaringType*().hasQualifiedName("javax.servlet", "FilterChain") and
+ ma.getQualifier() = this.asExpr()
+ )
+ }
+}
+
+/** Taint-tracking configuration tracing flow from `doFilter` method paramter source to output
+ * `FilterChain.doFilter` method qualifying expression.
+ * */
+class DoFilterMethodConfig extends TaintTracking::Configuration {
+ DoFilterMethodConfig() { this = "DoFilterMethodConfig" }
+
+ override predicate isSource(DataFlow::Node source) { source instanceof DoFilterMethodSource }
+
+ override predicate isSink(DataFlow::Node sink) { sink instanceof DoFilterMethodSink }
+
+ override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
+ guard instanceof FilterVerifAuth
+ }
+}
+
+/** Implement class modeling verification for `Filter.doFilter`, return false if it fails. */
+boolean checks() {
+ exists(DataFlow::PathNode source, DataFlow::PathNode sink, DoFilterMethodConfig conf |
+ conf.hasFlowPath(source, sink) and
+ result = false
+ )
+}
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonpInjectionLib.qll b/java/ql/src/Security/CWE/CWE-352/JsonpInjectionLib.qll
index 4294a5e8c8f..3f730425823 100644
--- a/java/ql/src/Security/CWE/CWE-352/JsonpInjectionLib.qll
+++ b/java/ql/src/Security/CWE/CWE-352/JsonpInjectionLib.qll
@@ -5,6 +5,69 @@ import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.frameworks.spring.SpringController
+/** Holds if `m` is a method of some override of `HttpServlet.doGet`. */
+private predicate isGetServletMethod(Method m) {
+ isServletRequestMethod(m) and m.getName() = "doGet"
+}
+
+/** Holds if `m` is a method of some override of `HttpServlet.doGet`. */
+private predicate isGetSpringControllerMethod(Method m) {
+ exists(Annotation a |
+ a = m.getAnAnnotation() and
+ a.getType().hasQualifiedName("org.springframework.web.bind.annotation", "GetMapping")
+ )
+ or
+ exists(Annotation a |
+ a = m.getAnAnnotation() and
+ a.getType().hasQualifiedName("org.springframework.web.bind.annotation", "RequestMapping") and
+ a.getValue("method").toString().regexpMatch("RequestMethod.GET|\\{...\\}")
+ )
+}
+
+/** Method parameters use the annotation `@RequestParam` or the parameter type is `ServletRequest`, `String`, `Object` */
+predicate checkSpringMethodParameterType(Method m, int i) {
+ m.getParameter(i).getType() instanceof ServletRequest
+ or
+ exists(Parameter p |
+ p = m.getParameter(i) and
+ p.hasAnnotation() and
+ p.getAnAnnotation()
+ .getType()
+ .hasQualifiedName("org.springframework.web.bind.annotation", "RequestParam") and
+ p.getType().getName().regexpMatch("String|Object")
+ )
+ or
+ exists(Parameter p |
+ p = m.getParameter(i) and
+ not p.hasAnnotation() and
+ p.getType().getName().regexpMatch("String|Object")
+ )
+}
+
+/** A data flow source for get method request parameters. */
+abstract class GetHttpRequestSource extends DataFlow::Node { }
+
+/** A data flow source for servlet get method request parameters. */
+private class ServletGetHttpRequestSource extends GetHttpRequestSource {
+ ServletGetHttpRequestSource() {
+ exists(Method m |
+ isGetServletMethod(m) and
+ m.getParameter(0).getAnAccess() = this.asExpr()
+ )
+ }
+}
+
+/** A data flow source for spring controller get method request parameters. */
+private class SpringGetHttpRequestSource extends GetHttpRequestSource {
+ SpringGetHttpRequestSource() {
+ exists(SpringControllerMethod scm, int i |
+ isGetSpringControllerMethod(scm) and
+ checkSpringMethodParameterType(scm, i) and
+ scm.getParameter(i).getAnAccess() = this.asExpr()
+ )
+ }
+}
+
/** A data flow sink for unvalidated user input that is used to jsonp. */
abstract class JsonpInjectionSink extends DataFlow::Node { }
@@ -26,7 +89,7 @@ private class WriterPrintln extends JsonpInjectionSink {
private class SpringReturn extends JsonpInjectionSink {
SpringReturn() {
exists(ReturnStmt rs, Method m | m = rs.getEnclosingCallable() |
- m instanceof SpringRequestMappingMethod and
+ isGetSpringControllerMethod(m) and
rs.getResult() = this.asExpr()
)
}
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonpInjectionServlet.java b/java/ql/src/Security/CWE/CWE-352/JsonpInjectionServlet.java
new file mode 100644
index 00000000000..916cd9bf676
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-352/JsonpInjectionServlet.java
@@ -0,0 +1,60 @@
+import com.google.gson.Gson;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class JsonpInjectionServlet extends HttpServlet {
+
+ private static HashMap hashMap = new HashMap();
+
+ static {
+ hashMap.put("username","admin");
+ hashMap.put("password","123456");
+ }
+
+ private static final long serialVersionUID = 1L;
+
+ private String key = "test";
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ String jsonpCallback = req.getParameter("jsonpCallback");
+
+ PrintWriter pw = null;
+ Gson gson = new Gson();
+ String result = gson.toJson(hashMap);
+
+ String resultStr = null;
+ pw = resp.getWriter();
+ resultStr = jsonpCallback + "(" + result + ")";
+ pw.println(resultStr);
+ pw.flush();
+ }
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ String jsonpCallback = req.getParameter("jsonpCallback");
+ PrintWriter pw = null;
+ Gson gson = new Gson();
+ String result = gson.toJson(hashMap);
+
+ String resultStr = null;
+ pw = resp.getWriter();
+ resultStr = jsonpCallback + "(" + result + ")";
+ pw.println(resultStr);
+ pw.flush();
+ }
+
+ @Override
+ public void init(ServletConfig config) throws ServletException {
+ this.key = config.getInitParameter("key");
+ System.out.println("初始化" + this.key);
+ super.init(config);
+ }
+
+}
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonpInjectionServlet1.java b/java/ql/src/Security/CWE/CWE-352/JsonpInjectionServlet1.java
new file mode 100644
index 00000000000..14ef76275b1
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-352/JsonpInjectionServlet1.java
@@ -0,0 +1,64 @@
+import com.google.gson.Gson;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class JsonpInjectionServlet1 extends HttpServlet {
+
+ private static HashMap hashMap = new HashMap();
+
+ static {
+ hashMap.put("username","admin");
+ hashMap.put("password","123456");
+ }
+
+ private static final long serialVersionUID = 1L;
+
+ private String key = "test";
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ doPost(req, resp);
+ }
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ resp.setContentType("application/json");
+ String jsonpCallback = req.getParameter("jsonpCallback");
+ PrintWriter pw = null;
+ Gson gson = new Gson();
+ String jsonResult = gson.toJson(hashMap);
+
+ String referer = req.getHeader("Referer");
+
+ boolean result = verifReferer(referer);
+
+ // good
+ if (result){
+ String resultStr = null;
+ pw = resp.getWriter();
+ resultStr = jsonpCallback + "(" + jsonResult + ")";
+ pw.println(resultStr);
+ pw.flush();
+ }
+ }
+
+ public static boolean verifReferer(String referer){
+ if (!referer.startsWith("http://test.com/")){
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void init(ServletConfig config) throws ServletException {
+ this.key = config.getInitParameter("key");
+ System.out.println("初始化" + this.key);
+ super.init(config);
+ }
+
+}
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonpInjectionServlet2.java b/java/ql/src/Security/CWE/CWE-352/JsonpInjectionServlet2.java
new file mode 100644
index 00000000000..bbfbc2dc436
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-352/JsonpInjectionServlet2.java
@@ -0,0 +1,50 @@
+import com.google.gson.Gson;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class JsonpInjectionServlet2 extends HttpServlet {
+
+ private static HashMap hashMap = new HashMap();
+
+ static {
+ hashMap.put("username","admin");
+ hashMap.put("password","123456");
+ }
+
+ private static final long serialVersionUID = 1L;
+
+ private String key = "test";
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ doPost(req, resp);
+ }
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ resp.setContentType("application/json");
+ String jsonpCallback = req.getParameter("jsonpCallback");
+ PrintWriter pw = null;
+ Gson gson = new Gson();
+ String result = gson.toJson(hashMap);
+
+ String resultStr = null;
+ pw = resp.getWriter();
+ resultStr = jsonpCallback + "(" + result + ")";
+ pw.println(resultStr);
+ pw.flush();
+ }
+
+ @Override
+ public void init(ServletConfig config) throws ServletException {
+ this.key = config.getInitParameter("key");
+ System.out.println("初始化" + this.key);
+ super.init(config);
+ }
+
+}
diff --git a/java/ql/src/semmle/code/java/frameworks/Servlets.qll b/java/ql/src/semmle/code/java/frameworks/Servlets.qll
index 3fad8c4e18b..b2054dc30cb 100644
--- a/java/ql/src/semmle/code/java/frameworks/Servlets.qll
+++ b/java/ql/src/semmle/code/java/frameworks/Servlets.qll
@@ -337,3 +337,30 @@ predicate isRequestGetParamMethod(MethodAccess ma) {
ma.getMethod() instanceof ServletRequestGetParameterMapMethod or
ma.getMethod() instanceof HttpServletRequestGetQueryStringMethod
}
+
+
+/**
+ * A class that has `javax.servlet.Filter` as an ancestor.
+ */
+class FilterClass extends Class {
+ FilterClass() { getAnAncestor().hasQualifiedName("javax.servlet", "Filter") }
+}
+
+
+/**
+ * The interface `javax.servlet.FilterChain`
+ */
+class FilterChain extends RefType {
+ FilterChain() {
+ hasQualifiedName("javax.servlet", "FilterChain")
+ }
+}
+
+/** Holds if `m` is a request handler method (for example `doGet` or `doPost`). */
+predicate isDoFilterMethod(Method m) {
+ m.getDeclaringType() instanceof FilterClass and
+ m.getNumberOfParameters() = 3 and
+ m.getParameter(0).getType() instanceof ServletRequest and
+ m.getParameter(1).getType() instanceof ServletResponse and
+ m.getParameter(2).getType() instanceof FilterChain
+}
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.expected
index 019af8f5c05..7e3069cf1d9 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.expected
@@ -1,60 +1,60 @@
edges
-| JsonpInjection.java:28:32:28:68 | getParameter(...) : String | JsonpInjection.java:33:16:33:24 | resultStr |
-| JsonpInjection.java:32:21:32:54 | ... + ... : String | JsonpInjection.java:33:16:33:24 | resultStr |
-| JsonpInjection.java:40:32:40:68 | getParameter(...) : String | JsonpInjection.java:44:16:44:24 | resultStr |
-| JsonpInjection.java:42:21:42:80 | ... + ... : String | JsonpInjection.java:44:16:44:24 | resultStr |
-| JsonpInjection.java:51:32:51:68 | getParameter(...) : String | JsonpInjection.java:54:16:54:24 | resultStr |
-| JsonpInjection.java:53:21:53:55 | ... + ... : String | JsonpInjection.java:54:16:54:24 | resultStr |
-| JsonpInjection.java:61:32:61:68 | getParameter(...) : String | JsonpInjection.java:64:16:64:24 | resultStr |
-| JsonpInjection.java:63:21:63:54 | ... + ... : String | JsonpInjection.java:64:16:64:24 | resultStr |
-| JsonpInjection.java:72:32:72:68 | getParameter(...) : String | JsonpInjection.java:80:20:80:28 | resultStr |
+| JsonpInjection.java:29:32:29:38 | request : HttpServletRequest | JsonpInjection.java:34:16:34:24 | resultStr |
+| JsonpInjection.java:33:21:33:54 | ... + ... : String | JsonpInjection.java:34:16:34:24 | resultStr |
+| JsonpInjection.java:41:32:41:38 | request : HttpServletRequest | JsonpInjection.java:45:16:45:24 | resultStr |
+| JsonpInjection.java:43:21:43:80 | ... + ... : String | JsonpInjection.java:45:16:45:24 | resultStr |
+| JsonpInjection.java:52:32:52:38 | request : HttpServletRequest | JsonpInjection.java:55:16:55:24 | resultStr |
+| JsonpInjection.java:54:21:54:55 | ... + ... : String | JsonpInjection.java:55:16:55:24 | resultStr |
+| JsonpInjection.java:62:32:62:38 | request : HttpServletRequest | JsonpInjection.java:65:16:65:24 | resultStr |
+| JsonpInjection.java:64:21:64:54 | ... + ... : String | JsonpInjection.java:65:16:65:24 | resultStr |
+| JsonpInjection.java:72:32:72:38 | request : HttpServletRequest | JsonpInjection.java:80:20:80:28 | resultStr |
| JsonpInjection.java:79:21:79:54 | ... + ... : String | JsonpInjection.java:80:20:80:28 | resultStr |
-| JsonpInjection.java:88:32:88:68 | getParameter(...) : String | JsonpInjection.java:95:20:95:28 | resultStr |
-| JsonpInjection.java:94:21:94:54 | ... + ... : String | JsonpInjection.java:95:20:95:28 | resultStr |
-| JsonpInjection.java:102:32:102:68 | getParameter(...) : String | JsonpInjection.java:113:16:113:24 | resultStr |
-| JsonpInjection.java:128:25:128:59 | ... + ... : String | JsonpInjection.java:129:20:129:28 | resultStr |
-| JsonpInjection.java:147:25:147:59 | ... + ... : String | JsonpInjection.java:148:20:148:28 | resultStr |
+| JsonpInjection.java:87:32:87:38 | request : HttpServletRequest | JsonpInjection.java:94:20:94:28 | resultStr |
+| JsonpInjection.java:93:21:93:54 | ... + ... : String | JsonpInjection.java:94:20:94:28 | resultStr |
+| JsonpInjection.java:101:32:101:38 | request : HttpServletRequest | JsonpInjection.java:112:16:112:24 | resultStr |
+| JsonpInjection.java:127:25:127:59 | ... + ... : String | JsonpInjection.java:128:20:128:28 | resultStr |
+| JsonpInjection.java:148:25:148:59 | ... + ... : String | JsonpInjection.java:149:20:149:28 | resultStr |
nodes
-| JsonpInjection.java:28:32:28:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpInjection.java:32:21:32:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpInjection.java:33:16:33:24 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:33:16:33:24 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:40:32:40:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpInjection.java:42:21:42:80 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpInjection.java:44:16:44:24 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:44:16:44:24 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:51:32:51:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpInjection.java:53:21:53:55 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpInjection.java:54:16:54:24 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:54:16:54:24 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:61:32:61:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpInjection.java:63:21:63:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpInjection.java:64:16:64:24 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:64:16:64:24 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:72:32:72:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpInjection.java:29:32:29:38 | request : HttpServletRequest | semmle.label | request : HttpServletRequest |
+| JsonpInjection.java:33:21:33:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpInjection.java:34:16:34:24 | resultStr | semmle.label | resultStr |
+| JsonpInjection.java:34:16:34:24 | resultStr | semmle.label | resultStr |
+| JsonpInjection.java:41:32:41:38 | request : HttpServletRequest | semmle.label | request : HttpServletRequest |
+| JsonpInjection.java:43:21:43:80 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpInjection.java:45:16:45:24 | resultStr | semmle.label | resultStr |
+| JsonpInjection.java:45:16:45:24 | resultStr | semmle.label | resultStr |
+| JsonpInjection.java:52:32:52:38 | request : HttpServletRequest | semmle.label | request : HttpServletRequest |
+| JsonpInjection.java:54:21:54:55 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpInjection.java:55:16:55:24 | resultStr | semmle.label | resultStr |
+| JsonpInjection.java:55:16:55:24 | resultStr | semmle.label | resultStr |
+| JsonpInjection.java:62:32:62:38 | request : HttpServletRequest | semmle.label | request : HttpServletRequest |
+| JsonpInjection.java:64:21:64:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpInjection.java:65:16:65:24 | resultStr | semmle.label | resultStr |
+| JsonpInjection.java:65:16:65:24 | resultStr | semmle.label | resultStr |
+| JsonpInjection.java:72:32:72:38 | request : HttpServletRequest | semmle.label | request : HttpServletRequest |
| JsonpInjection.java:79:21:79:54 | ... + ... : String | semmle.label | ... + ... : String |
| JsonpInjection.java:80:20:80:28 | resultStr | semmle.label | resultStr |
| JsonpInjection.java:80:20:80:28 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:88:32:88:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpInjection.java:94:21:94:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpInjection.java:95:20:95:28 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:95:20:95:28 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:102:32:102:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpInjection.java:113:16:113:24 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:128:25:128:59 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpInjection.java:129:20:129:28 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:147:25:147:59 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpInjection.java:148:20:148:28 | resultStr | semmle.label | resultStr |
+| JsonpInjection.java:87:32:87:38 | request : HttpServletRequest | semmle.label | request : HttpServletRequest |
+| JsonpInjection.java:93:21:93:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpInjection.java:94:20:94:28 | resultStr | semmle.label | resultStr |
+| JsonpInjection.java:94:20:94:28 | resultStr | semmle.label | resultStr |
+| JsonpInjection.java:101:32:101:38 | request : HttpServletRequest | semmle.label | request : HttpServletRequest |
+| JsonpInjection.java:112:16:112:24 | resultStr | semmle.label | resultStr |
+| JsonpInjection.java:127:25:127:59 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpInjection.java:128:20:128:28 | resultStr | semmle.label | resultStr |
+| JsonpInjection.java:148:25:148:59 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpInjection.java:149:20:149:28 | resultStr | semmle.label | resultStr |
#select
-| JsonpInjection.java:33:16:33:24 | resultStr | JsonpInjection.java:28:32:28:68 | getParameter(...) : String | JsonpInjection.java:33:16:33:24 | resultStr | Json Hijacking query
-might include code from $@. | JsonpInjection.java:28:32:28:68 | getParameter(...) | this user input |
-| JsonpInjection.java:44:16:44:24 | resultStr | JsonpInjection.java:40:32:40:68 | getParameter(...) : String | JsonpInjection.java:44:16:44:24 | resultStr | Json Hijacking query
-might include code from $@. | JsonpInjection.java:40:32:40:68 | getParameter(...) | this user input |
-| JsonpInjection.java:54:16:54:24 | resultStr | JsonpInjection.java:51:32:51:68 | getParameter(...) : String | JsonpInjection.java:54:16:54:24 | resultStr | Json Hijacking query
-might include code from $@. | JsonpInjection.java:51:32:51:68 | getParameter(...) | this user input |
-| JsonpInjection.java:64:16:64:24 | resultStr | JsonpInjection.java:61:32:61:68 | getParameter(...) : String | JsonpInjection.java:64:16:64:24 | resultStr | Json Hijacking query
-might include code from $@. | JsonpInjection.java:61:32:61:68 | getParameter(...) | this user input |
-| JsonpInjection.java:80:20:80:28 | resultStr | JsonpInjection.java:72:32:72:68 | getParameter(...) : String | JsonpInjection.java:80:20:80:28 | resultStr | Json Hijacking query
-might include code from $@. | JsonpInjection.java:72:32:72:68 | getParameter(...) | this user input |
-| JsonpInjection.java:95:20:95:28 | resultStr | JsonpInjection.java:88:32:88:68 | getParameter(...) : String | JsonpInjection.java:95:20:95:28 | resultStr | Json Hijacking query
-might include code from $@. | JsonpInjection.java:88:32:88:68 | getParameter(...) | this user input |
\ No newline at end of file
+| JsonpInjection.java:34:16:34:24 | resultStr | JsonpInjection.java:29:32:29:38 | request : HttpServletRequest | JsonpInjection.java:34:16:34:24 |
+resultStr | Jsonp Injection query might include code from $@. | JsonpInjection.java:29:32:29:38 | request | this user input |
+| JsonpInjection.java:45:16:45:24 | resultStr | JsonpInjection.java:41:32:41:38 | request : HttpServletRequest | JsonpInjection.java:45:16:45:24 |
+resultStr | Jsonp Injection query might include code from $@. | JsonpInjection.java:41:32:41:38 | request | this user input |
+| JsonpInjection.java:55:16:55:24 | resultStr | JsonpInjection.java:52:32:52:38 | request : HttpServletRequest | JsonpInjection.java:55:16:55:24 |
+resultStr | Jsonp Injection query might include code from $@. | JsonpInjection.java:52:32:52:38 | request | this user input |
+| JsonpInjection.java:65:16:65:24 | resultStr | JsonpInjection.java:62:32:62:38 | request : HttpServletRequest | JsonpInjection.java:65:16:65:24 |
+resultStr | Jsonp Injection query might include code from $@. | JsonpInjection.java:62:32:62:38 | request | this user input |
+| JsonpInjection.java:80:20:80:28 | resultStr | JsonpInjection.java:72:32:72:38 | request : HttpServletRequest | JsonpInjection.java:80:20:80:28 |
+resultStr | Jsonp Injection query might include code from $@. | JsonpInjection.java:72:32:72:38 | request | this user input |
+| JsonpInjection.java:94:20:94:28 | resultStr | JsonpInjection.java:87:32:87:38 | request : HttpServletRequest | JsonpInjection.java:94:20:94:28 |
+resultStr | Jsonp Injection query might include code from $@. | JsonpInjection.java:87:32:87:38 | request | this user input |
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.java b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.java
index df3aa2c02fe..9f079513a8b 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.java
@@ -8,11 +8,12 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class JsonpInjection {
-
private static HashMap hashMap = new HashMap();
static {
@@ -21,7 +22,7 @@ public class JsonpInjection {
}
- @GetMapping(value = "jsonp1")
+ @GetMapping(value = "jsonp1", produces="text/javascript")
@ResponseBody
public String bad1(HttpServletRequest request) {
String resultStr = null;
@@ -68,7 +69,6 @@ public class JsonpInjection {
@ResponseBody
public void bad5(HttpServletRequest request,
HttpServletResponse response) throws Exception {
- response.setContentType("application/json");
String jsonpCallback = request.getParameter("jsonpCallback");
PrintWriter pw = null;
Gson gson = new Gson();
@@ -84,7 +84,6 @@ public class JsonpInjection {
@ResponseBody
public void bad6(HttpServletRequest request,
HttpServletResponse response) throws Exception {
- response.setContentType("application/json");
String jsonpCallback = request.getParameter("jsonpCallback");
PrintWriter pw = null;
ObjectMapper mapper = new ObjectMapper();
@@ -141,8 +140,10 @@ public class JsonpInjection {
String referer = request.getHeader("Referer");
boolean result = verifReferer(referer);
+
+ boolean test = result;
// good
- if (result){
+ if (test){
String jsonStr = getJsonStr(hashMap);
resultStr = jsonpCallback + "(" + jsonStr + ")";
return resultStr;
@@ -168,4 +169,4 @@ public class JsonpInjection {
}
return true;
}
-}
+}
\ No newline at end of file
diff --git a/java/ql/test/stubs/spring-core-5.3.2/org/springframework/core/annotation/AliasFor.class b/java/ql/test/stubs/spring-core-5.3.2/org/springframework/core/annotation/AliasFor.class
deleted file mode 100644
index 438065489278b92503abc2ad8d75716c5e56ac08..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 385
zcmb7=!Ab)$5QhJ0x31kDdrH&Kx0=)qG#3PM4!PcXZrOKO@(l3m};gAd?CiL-}x
zJqaEL=Fj~6^G&|KKRyB6W0qr@<21(^Vbrp1G~wdrcD3b}m1S3}bqdDS4}|lDb3So0
z-aYCKH#QMKxO{0`GCTd`S`$rab#IG=`O1e{#kVeF6L_cJeRx%s4_fgdPA#nAxb#7`
zj5*1|vPl9`tbG$Iy);(DbZ?q>Y=pc21QTZcMbG6{R|0?4KmBGoU|o}(H;@|2R}C^k
ghLPwaQNxHF$I?t>JeJBL3UL&FIx;a%x-6Xh0OC_*bpQYW
diff --git a/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/GetMapping.class b/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/GetMapping.class
deleted file mode 100644
index 5392ca0ebc1593d6bfa2f7d765ee4ca169fb260f..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 504
zcma)&y-ve06orr5G%4k$Ewlp@8)_FUu~3N#3Bgi?)JiO!oYW02i5(KVeK!UkfQLfd
z3?&dTFj%_xlg>TI=i~G39l#Za0geNl1Q;-QTBMR;Fd9$SVk3AWbj;^AS316C=-+5<
ztgy=HTe%W0u?%2nZA9WoH5`o>f62T|*k=Ym6S+tWhIV9h;Zj+SS#FjtD#y;;xIB_~
zDxp)|dubm;mXYs88HC|<=CoC*d{Tu96Imr8>11m1m={?Yb44Ckk!ycGS!yuAF9#FEVXJbh%nj0^%G
u-TFC+dFlH8Nm;4MC5#O62q7eGj&Kvy7#SEDn1GlW=t>44%>pEu7+3+XjWw+R
From 15a43ffe36e1a77eaf33ed17b3edbf6843dfc9df Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Sat, 27 Feb 2021 13:41:20 +0100
Subject: [PATCH 066/725] Simplified
returnsRemoteInvocationSerializingExporter()
---
.../SpringHttpInvokerUnsafeDeserialization.ql | 1 -
.../SpringHttpInvokerUnsafeDeserialization.expected | 3 ++-
.../SpringHttpInvokerUnsafeDeserialization.java | 12 ++++++++++--
.../rmi/RemoteInvocationSerializingExporter.java | 4 +---
4 files changed, 13 insertions(+), 7 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/SpringHttpInvokerUnsafeDeserialization.ql b/java/ql/src/experimental/Security/CWE/CWE-502/SpringHttpInvokerUnsafeDeserialization.ql
index 73c67582692..df01ae478cc 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-502/SpringHttpInvokerUnsafeDeserialization.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/SpringHttpInvokerUnsafeDeserialization.ql
@@ -30,7 +30,6 @@ private predicate isRemoteInvocationSerializingExporter(RefType type) {
* Holds if `method` returns an object that extends `RemoteInvocationSerializingExporter`.
*/
private predicate returnsRemoteInvocationSerializingExporter(Method method) {
- isRemoteInvocationSerializingExporter(method.getReturnType()) or
isRemoteInvocationSerializingExporter(method.getReturnType().(RefType).getASupertype*())
}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-502/SpringHttpInvokerUnsafeDeserialization.expected b/java/ql/test/experimental/query-tests/security/CWE-502/SpringHttpInvokerUnsafeDeserialization.expected
index 0ce0efca838..b76f3edc57e 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-502/SpringHttpInvokerUnsafeDeserialization.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-502/SpringHttpInvokerUnsafeDeserialization.expected
@@ -1 +1,2 @@
-| SpringHttpInvokerUnsafeDeserialization.java:9:32:9:37 | unsafe | Unasafe deserialization in a remote service exporter in 'unsafe' method |
+| SpringHttpInvokerUnsafeDeserialization.java:10:32:10:63 | unsafeHttpInvokerServiceExporter | Unasafe deserialization in a remote service exporter in 'unsafeHttpInvokerServiceExporter' method |
+| SpringHttpInvokerUnsafeDeserialization.java:18:41:18:88 | unsafeCustomeRemoteInvocationSerializingExporter | Unasafe deserialization in a remote service exporter in 'unsafeCustomeRemoteInvocationSerializingExporter' method |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-502/SpringHttpInvokerUnsafeDeserialization.java b/java/ql/test/experimental/query-tests/security/CWE-502/SpringHttpInvokerUnsafeDeserialization.java
index d678c8f0191..9d99aa1cbce 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-502/SpringHttpInvokerUnsafeDeserialization.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-502/SpringHttpInvokerUnsafeDeserialization.java
@@ -1,18 +1,24 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter;
+import org.springframework.remoting.rmi.RemoteInvocationSerializingExporter;
@Configuration
public class SpringHttpInvokerUnsafeDeserialization {
- @Bean(name = "/unsafe")
- HttpInvokerServiceExporter unsafe() {
+ @Bean(name = "/unsafeHttpInvokerServiceExporter")
+ HttpInvokerServiceExporter unsafeHttpInvokerServiceExporter() {
HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter();
exporter.setService(new AccountServiceImpl());
exporter.setServiceInterface(AccountService.class);
return exporter;
}
+ @Bean(name = "/unsafeCustomeRemoteInvocationSerializingExporter")
+ RemoteInvocationSerializingExporter unsafeCustomeRemoteInvocationSerializingExporter() {
+ return new CustomeRemoteInvocationSerializingExporter();
+ }
+
HttpInvokerServiceExporter notABean() {
HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter();
exporter.setService(new AccountServiceImpl());
@@ -21,6 +27,8 @@ public class SpringHttpInvokerUnsafeDeserialization {
}
}
+class CustomeRemoteInvocationSerializingExporter extends RemoteInvocationSerializingExporter {}
+
class NotAConfiguration {
@Bean(name = "/notAnEndpoint")
diff --git a/java/ql/test/stubs/springframework-5.2.3/org/springframework/remoting/rmi/RemoteInvocationSerializingExporter.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/remoting/rmi/RemoteInvocationSerializingExporter.java
index a6f6fec194f..5449b83ba86 100644
--- a/java/ql/test/stubs/springframework-5.2.3/org/springframework/remoting/rmi/RemoteInvocationSerializingExporter.java
+++ b/java/ql/test/stubs/springframework-5.2.3/org/springframework/remoting/rmi/RemoteInvocationSerializingExporter.java
@@ -1,5 +1,3 @@
package org.springframework.remoting.rmi;
-public abstract class RemoteInvocationSerializingExporter {
-
-}
+public abstract class RemoteInvocationSerializingExporter {}
From 95d1994196dc52ad516b7c6df06e673be8906d9b Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Mon, 1 Mar 2021 22:06:52 +0000
Subject: [PATCH 067/725] Query to check sensitive cookies without the HttpOnly
flag set
---
.../CWE-1004/SensitiveCookieNotHttpOnly.java | 44 +++
.../CWE-1004/SensitiveCookieNotHttpOnly.qhelp | 27 ++
.../CWE-1004/SensitiveCookieNotHttpOnly.ql | 194 ++++++++++
.../SensitiveCookieNotHttpOnly.expected | 13 +
.../CWE-1004/SensitiveCookieNotHttpOnly.java | 57 +++
.../CWE-1004/SensitiveCookieNotHttpOnly.qlref | 1 +
.../query-tests/security/CWE-1004/options | 1 +
.../javax/ws/rs/core/Cookie.java | 187 +++++++++
.../javax/ws/rs/core/NewCookie.java | 359 ++++++++++++++++++
.../javax/servlet/http/Cookie.java | 33 ++
.../servlet/http/HttpServletResponse.java | 6 +
11 files changed, 922 insertions(+)
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.java
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.qhelp
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-1004/SensitiveCookieNotHttpOnly.expected
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-1004/SensitiveCookieNotHttpOnly.java
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-1004/SensitiveCookieNotHttpOnly.qlref
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-1004/options
create mode 100644 java/ql/test/stubs/jsr311-api-1.1.1/javax/ws/rs/core/Cookie.java
create mode 100644 java/ql/test/stubs/jsr311-api-1.1.1/javax/ws/rs/core/NewCookie.java
diff --git a/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.java b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.java
new file mode 100644
index 00000000000..48d80707ff8
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.java
@@ -0,0 +1,44 @@
+class SensitiveCookieNotHttpOnly {
+ // GOOD - Create a sensitive cookie with the `HttpOnly` flag set.
+ public void addCookie(String jwt_token, HttpServletRequest request, HttpServletResponse response) {
+ Cookie jwtCookie =new Cookie("jwt_token", jwt_token);
+ jwtCookie.setPath("/");
+ jwtCookie.setMaxAge(3600*24*7);
+ jwtCookie.setHttpOnly(true);
+ response.addCookie(jwtCookie);
+ }
+
+ // BAD - Create a sensitive cookie without the `HttpOnly` flag set.
+ public void addCookie2(String jwt_token, String userId, HttpServletRequest request, HttpServletResponse response) {
+ Cookie jwtCookie =new Cookie("jwt_token", jwt_token);
+ jwtCookie.setPath("/");
+ jwtCookie.setMaxAge(3600*24*7);
+ response.addCookie(jwtCookie);
+ }
+
+ // GOOD - Set a sensitive cookie header with the `HttpOnly` flag set.
+ public void addCookie3(String authId, HttpServletRequest request, HttpServletResponse response) {
+ response.addHeader("Set-Cookie", "token=" +authId + ";HttpOnly;Secure");
+ }
+
+ // BAD - Set a sensitive cookie header without the `HttpOnly` flag set.
+ public void addCookie4(String authId, HttpServletRequest request, HttpServletResponse response) {
+ response.addHeader("Set-Cookie", "token=" +authId + ";Secure");
+ }
+
+ // GOOD - Set a sensitive cookie header using the class `javax.ws.rs.core.Cookie` with the `HttpOnly` flag set through string concatenation.
+ public void addCookie5(String accessKey, HttpServletRequest request, HttpServletResponse response) {
+ response.setHeader("Set-Cookie", new NewCookie("session-access-key", accessKey, "/", null, null, 0, true) + ";HttpOnly");
+ }
+
+ // BAD - Set a sensitive cookie header using the class `javax.ws.rs.core.Cookie` without the `HttpOnly` flag set.
+ public void addCookie6(String accessKey, HttpServletRequest request, HttpServletResponse response) {
+ response.setHeader("Set-Cookie", new NewCookie("session-access-key", accessKey, "/", null, null, 0, true).toString());
+ }
+
+ // GOOD - Set a sensitive cookie header using the class `javax.ws.rs.core.Cookie` with the `HttpOnly` flag set through the constructor.
+ public void addCookie7(String accessKey, HttpServletRequest request, HttpServletResponse response) {
+ NewCookie accessKeyCookie = new NewCookie("session-access-key", accessKey, "/", null, null, 0, true, true);
+ response.setHeader("Set-Cookie", accessKeyCookie.toString());
+ }
+}
diff --git a/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.qhelp b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.qhelp
new file mode 100644
index 00000000000..880ed767be9
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.qhelp
@@ -0,0 +1,27 @@
+
+
+
+
+ Cross-Site Scripting (XSS) is categorized as one of the OWASP Top 10 Security Vulnerabilities. The HttpOnly flag directs compatible browsers to prevent client-side script from accessing cookies. Including the HttpOnly flag in the Set-Cookie HTTP response header for a sensitive cookie helps mitigate the risk associated with XSS where an attacker's script code attempts to read the contents of a cookie and exfiltrate information obtained.
+
+
+
+ Use the HttpOnly flag when generating a cookie containing sensitive information to help mitigate the risk of client side script accessing the protected cookie.
+
+
+
+ The following example shows two ways of generating sensitive cookies. In the 'BAD' cases, the HttpOnly flag is not set. In the 'GOOD' cases, the HttpOnly flag is set.
+
+
+
+
+
+ PortSwigger:
+ Cookie without HttpOnly flag set
+
+
+ OWASP:
+ HttpOnly
+
+
+
diff --git a/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
new file mode 100644
index 00000000000..bf4e60134c9
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
@@ -0,0 +1,194 @@
+/**
+ * @name Sensitive cookies without the HttpOnly response header set
+ * @description Sensitive cookies without 'HttpOnly' leaves session cookies vulnerable to an XSS attack.
+ * @kind path-problem
+ * @id java/sensitive-cookie-not-httponly
+ * @tags security
+ * external/cwe/cwe-1004
+ */
+
+import java
+import semmle.code.java.frameworks.Servlets
+import semmle.code.java.dataflow.FlowSources
+import semmle.code.java.dataflow.TaintTracking
+import DataFlow::PathGraph
+
+/** Gets a regular expression for matching common names of sensitive cookies. */
+string getSensitiveCookieNameRegex() { result = "(?i).*(auth|session|token|key|credential).*" }
+
+/** Holds if a string is concatenated with the name of a sensitive cookie. */
+predicate isSensitiveCookieNameExpr(Expr expr) {
+ expr.(StringLiteral)
+ .getRepresentedString()
+ .toLowerCase()
+ .regexpMatch(getSensitiveCookieNameRegex()) or
+ isSensitiveCookieNameExpr(expr.(AddExpr).getAnOperand())
+}
+
+/** Holds if a string is concatenated with the `HttpOnly` flag. */
+predicate hasHttpOnlyExpr(Expr expr) {
+ expr.(StringLiteral).getRepresentedString().toLowerCase().matches("%httponly%") or
+ hasHttpOnlyExpr(expr.(AddExpr).getAnOperand())
+}
+
+/** The method call `Set-Cookie` of `addHeader` or `setHeader`. */
+class SetCookieMethodAccess extends MethodAccess {
+ SetCookieMethodAccess() {
+ (
+ this.getMethod() instanceof ResponseAddHeaderMethod or
+ this.getMethod() instanceof ResponseSetHeaderMethod
+ ) and
+ this.getArgument(0).(StringLiteral).getRepresentedString().toLowerCase() = "set-cookie"
+ }
+}
+
+/** Sensitive cookie name used in a `Cookie` constructor or a `Set-Cookie` call. */
+class SensitiveCookieNameExpr extends Expr {
+ SensitiveCookieNameExpr() {
+ isSensitiveCookieNameExpr(this) and
+ (
+ exists(
+ ClassInstanceExpr cie // new Cookie("jwt_token", token)
+ |
+ (
+ cie.getConstructor().getDeclaringType().hasQualifiedName("javax.servlet.http", "Cookie") or
+ cie.getConstructor()
+ .getDeclaringType()
+ .getASupertype*()
+ .hasQualifiedName("javax.ws.rs.core", "Cookie") or
+ cie.getConstructor()
+ .getDeclaringType()
+ .getASupertype*()
+ .hasQualifiedName("jakarta.ws.rs.core", "Cookie")
+ ) and
+ DataFlow::localExprFlow(this, cie.getArgument(0))
+ )
+ or
+ exists(
+ SetCookieMethodAccess ma // response.addHeader("Set-Cookie: token=" +authId + ";HttpOnly;Secure")
+ |
+ DataFlow::localExprFlow(this, ma.getArgument(1))
+ )
+ )
+ }
+}
+
+/** Sink of adding a cookie to the HTTP response. */
+class CookieResponseSink extends DataFlow::ExprNode {
+ CookieResponseSink() {
+ exists(MethodAccess ma |
+ (
+ ma.getMethod() instanceof ResponseAddCookieMethod or
+ ma instanceof SetCookieMethodAccess
+ ) and
+ ma.getAnArgument() = this.getExpr()
+ )
+ }
+}
+
+/** Holds if the `node` is a method call of `setHttpOnly(true)` on a cookie. */
+predicate setHttpOnlyMethodAccess(DataFlow::Node node) {
+ exists(
+ MethodAccess addCookie, Variable cookie, MethodAccess m // jwtCookie.setHttpOnly(true)
+ |
+ addCookie.getMethod() instanceof ResponseAddCookieMethod and
+ addCookie.getArgument(0) = cookie.getAnAccess() and
+ m.getMethod().getName() = "setHttpOnly" and
+ m.getArgument(0).(BooleanLiteral).getBooleanValue() = true and
+ m.getQualifier() = cookie.getAnAccess() and
+ node.asExpr() = cookie.getAnAccess()
+ )
+}
+
+/** Holds if the `node` is a method call of `Set-Cookie` header with the `HttpOnly` flag whose cookie name is sensitive. */
+predicate setHttpOnlyInSetCookie(DataFlow::Node node) {
+ exists(SetCookieMethodAccess sa |
+ hasHttpOnlyExpr(node.asExpr()) and
+ DataFlow::localExprFlow(node.asExpr(), sa.getArgument(1))
+ )
+}
+
+/** Holds if the `node` is an invocation of a JAX-RS `NewCookie` constructor that sets `HttpOnly` to true. */
+predicate setHttpOnlyInNewCookie(DataFlow::Node node) {
+ exists(ClassInstanceExpr cie |
+ cie.getConstructor().getDeclaringType().hasName("NewCookie") and
+ DataFlow::localExprFlow(node.asExpr(), cie.getArgument(0)) and
+ (
+ cie.getNumArgument() = 6 and cie.getArgument(5).(BooleanLiteral).getBooleanValue() = true // NewCookie(Cookie cookie, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly)
+ or
+ cie.getNumArgument() = 8 and
+ cie.getArgument(6).getType() instanceof BooleanType and
+ cie.getArgument(7).(BooleanLiteral).getBooleanValue() = true // NewCookie(String name, String value, String path, String domain, String comment, int maxAge, boolean secure, boolean httpOnly)
+ or
+ cie.getNumArgument() = 10 and cie.getArgument(9).(BooleanLiteral).getBooleanValue() = true // NewCookie(String name, String value, String path, String domain, int version, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly)
+ )
+ )
+}
+
+/**
+ * Holds if the node is a test method indicated by:
+ * a) in a test directory such as `src/test/java`
+ * b) in a test package whose name has the word `test`
+ * c) in a test class whose name has the word `test`
+ * d) in a test class implementing a test framework such as JUnit or TestNG
+ */
+predicate isTestMethod(DataFlow::Node node) {
+ exists(MethodAccess ma, Method m |
+ node.asExpr() = ma.getAnArgument() and
+ m = ma.getEnclosingCallable() and
+ (
+ m.getDeclaringType().getName().toLowerCase().matches("%test%") or // Simple check to exclude test classes to reduce FPs
+ m.getDeclaringType().getPackage().getName().toLowerCase().matches("%test%") or // Simple check to exclude classes in test packages to reduce FPs
+ exists(m.getLocation().getFile().getAbsolutePath().indexOf("/src/test/java")) or // Match test directory structure of build tools like maven
+ m instanceof TestMethod // Test method of a test case implementing a test framework such as JUnit or TestNG
+ )
+ )
+}
+
+/** A taint configuration tracking flow from a sensitive cookie without HttpOnly flag set to its HTTP response. */
+class MissingHttpOnlyConfiguration extends TaintTracking::Configuration {
+ MissingHttpOnlyConfiguration() { this = "MissingHttpOnlyConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) {
+ source.asExpr() instanceof SensitiveCookieNameExpr
+ }
+
+ override predicate isSink(DataFlow::Node sink) { sink instanceof CookieResponseSink }
+
+ override predicate isSanitizer(DataFlow::Node node) {
+ // cookie.setHttpOnly(true)
+ setHttpOnlyMethodAccess(node)
+ or
+ // response.addHeader("Set-Cookie: token=" +authId + ";HttpOnly;Secure")
+ setHttpOnlyInSetCookie(node)
+ or
+ // new NewCookie("session-access-key", accessKey, "/", null, null, 0, true, true)
+ setHttpOnlyInNewCookie(node)
+ or
+ // Test class or method
+ isTestMethod(node)
+ }
+
+ override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
+ exists(
+ ClassInstanceExpr cie // `NewCookie` constructor
+ |
+ cie.getAnArgument() = pred.asExpr() and
+ cie = succ.asExpr() and
+ cie.getConstructor().getDeclaringType().hasName("NewCookie")
+ )
+ or
+ exists(
+ MethodAccess ma // `toString` call on a cookie object
+ |
+ ma.getQualifier() = pred.asExpr() and
+ ma.getMethod().hasName("toString") and
+ ma = succ.asExpr()
+ )
+ }
+}
+
+from DataFlow::PathNode source, DataFlow::PathNode sink, MissingHttpOnlyConfiguration c
+where c.hasFlowPath(source, sink)
+select sink.getNode(), source, sink, "$@ doesn't have the HttpOnly flag set.", source.getNode(),
+ "This sensitive cookie"
diff --git a/java/ql/test/experimental/query-tests/security/CWE-1004/SensitiveCookieNotHttpOnly.expected b/java/ql/test/experimental/query-tests/security/CWE-1004/SensitiveCookieNotHttpOnly.expected
new file mode 100644
index 00000000000..84d6be3863a
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-1004/SensitiveCookieNotHttpOnly.expected
@@ -0,0 +1,13 @@
+edges
+| SensitiveCookieNotHttpOnly.java:22:38:22:48 | "jwt_token" : String | SensitiveCookieNotHttpOnly.java:28:28:28:36 | jwtCookie |
+| SensitiveCookieNotHttpOnly.java:49:56:49:75 | "session-access-key" : String | SensitiveCookieNotHttpOnly.java:49:42:49:124 | toString(...) |
+nodes
+| SensitiveCookieNotHttpOnly.java:22:38:22:48 | "jwt_token" : String | semmle.label | "jwt_token" : String |
+| SensitiveCookieNotHttpOnly.java:28:28:28:36 | jwtCookie | semmle.label | jwtCookie |
+| SensitiveCookieNotHttpOnly.java:39:42:39:69 | ... + ... | semmle.label | ... + ... |
+| SensitiveCookieNotHttpOnly.java:49:42:49:124 | toString(...) | semmle.label | toString(...) |
+| SensitiveCookieNotHttpOnly.java:49:56:49:75 | "session-access-key" : String | semmle.label | "session-access-key" : String |
+#select
+| SensitiveCookieNotHttpOnly.java:28:28:28:36 | jwtCookie | SensitiveCookieNotHttpOnly.java:22:38:22:48 | "jwt_token" : String | SensitiveCookieNotHttpOnly.java:28:28:28:36 | jwtCookie | $@ doesn't have the HttpOnly flag set. | SensitiveCookieNotHttpOnly.java:22:38:22:48 | "jwt_token" | This sensitive cookie |
+| SensitiveCookieNotHttpOnly.java:39:42:39:69 | ... + ... | SensitiveCookieNotHttpOnly.java:39:42:39:69 | ... + ... | SensitiveCookieNotHttpOnly.java:39:42:39:69 | ... + ... | $@ doesn't have the HttpOnly flag set. | SensitiveCookieNotHttpOnly.java:39:42:39:69 | ... + ... | This sensitive cookie |
+| SensitiveCookieNotHttpOnly.java:49:42:49:124 | toString(...) | SensitiveCookieNotHttpOnly.java:49:56:49:75 | "session-access-key" : String | SensitiveCookieNotHttpOnly.java:49:42:49:124 | toString(...) | $@ doesn't have the HttpOnly flag set. | SensitiveCookieNotHttpOnly.java:49:56:49:75 | "session-access-key" | This sensitive cookie |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-1004/SensitiveCookieNotHttpOnly.java b/java/ql/test/experimental/query-tests/security/CWE-1004/SensitiveCookieNotHttpOnly.java
new file mode 100644
index 00000000000..5e4f349f7c8
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-1004/SensitiveCookieNotHttpOnly.java
@@ -0,0 +1,57 @@
+import java.io.IOException;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.ServletException;
+
+import javax.ws.rs.core.NewCookie;
+
+class SensitiveCookieNotHttpOnly {
+ // GOOD - Tests adding a sensitive cookie with the `HttpOnly` flag set.
+ public void addCookie(String jwt_token, HttpServletRequest request, HttpServletResponse response) {
+ Cookie jwtCookie =new Cookie("jwt_token", jwt_token);
+ jwtCookie.setPath("/");
+ jwtCookie.setMaxAge(3600*24*7);
+ jwtCookie.setHttpOnly(true);
+ response.addCookie(jwtCookie);
+ }
+
+ // BAD - Tests adding a sensitive cookie without the `HttpOnly` flag set.
+ public void addCookie2(String jwt_token, String userId, HttpServletRequest request, HttpServletResponse response) {
+ Cookie jwtCookie =new Cookie("jwt_token", jwt_token);
+ Cookie userIdCookie =new Cookie("user_id", userId.toString());
+ jwtCookie.setPath("/");
+ userIdCookie.setPath("/");
+ jwtCookie.setMaxAge(3600*24*7);
+ userIdCookie.setMaxAge(3600*24*7);
+ response.addCookie(jwtCookie);
+ response.addCookie(userIdCookie);
+ }
+
+ // GOOD - Tests set a sensitive cookie header with the `HttpOnly` flag set.
+ public void addCookie3(String authId, HttpServletRequest request, HttpServletResponse response) {
+ response.addHeader("Set-Cookie", "token=" +authId + ";HttpOnly;Secure");
+ }
+
+ // BAD - Tests set a sensitive cookie header without the `HttpOnly` flag set.
+ public void addCookie4(String authId, HttpServletRequest request, HttpServletResponse response) {
+ response.addHeader("Set-Cookie", "token=" +authId + ";Secure");
+ }
+
+ // GOOD - Tests set a sensitive cookie header using the class `javax.ws.rs.core.Cookie` with the `HttpOnly` flag set through string concatenation.
+ public void addCookie5(String accessKey, HttpServletRequest request, HttpServletResponse response) {
+ response.setHeader("Set-Cookie", new NewCookie("session-access-key", accessKey, "/", null, null, 0, true) + ";HttpOnly");
+ }
+
+ // BAD - Tests set a sensitive cookie header using the class `javax.ws.rs.core.Cookie` without the `HttpOnly` flag set.
+ public void addCookie6(String accessKey, HttpServletRequest request, HttpServletResponse response) {
+ response.setHeader("Set-Cookie", new NewCookie("session-access-key", accessKey, "/", null, null, 0, true).toString());
+ }
+
+ // GOOD - Tests set a sensitive cookie header using the class `javax.ws.rs.core.Cookie` with the `HttpOnly` flag set through the constructor.
+ public void addCookie7(String accessKey, HttpServletRequest request, HttpServletResponse response) {
+ NewCookie accessKeyCookie = new NewCookie("session-access-key", accessKey, "/", null, null, 0, true, true);
+ response.setHeader("Set-Cookie", accessKeyCookie.toString());
+ }
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-1004/SensitiveCookieNotHttpOnly.qlref b/java/ql/test/experimental/query-tests/security/CWE-1004/SensitiveCookieNotHttpOnly.qlref
new file mode 100644
index 00000000000..cc2baaf6f7b
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-1004/SensitiveCookieNotHttpOnly.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
diff --git a/java/ql/test/experimental/query-tests/security/CWE-1004/options b/java/ql/test/experimental/query-tests/security/CWE-1004/options
new file mode 100644
index 00000000000..7f2b253fb20
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-1004/options
@@ -0,0 +1 @@
+// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/jsr311-api-1.1.1
\ No newline at end of file
diff --git a/java/ql/test/stubs/jsr311-api-1.1.1/javax/ws/rs/core/Cookie.java b/java/ql/test/stubs/jsr311-api-1.1.1/javax/ws/rs/core/Cookie.java
new file mode 100644
index 00000000000..4e4c7585c35
--- /dev/null
+++ b/java/ql/test/stubs/jsr311-api-1.1.1/javax/ws/rs/core/Cookie.java
@@ -0,0 +1,187 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2010-2015 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License"). You
+ * may not use this file except in compliance with the License. You can
+ * obtain a copy of the License at
+ * http://glassfish.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt. See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license." If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above. However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package javax.ws.rs.core;
+
+/**
+ * Represents the value of a HTTP cookie, transferred in a request.
+ * RFC 2109 specifies the legal characters for name,
+ * value, path and domain. The default version of 1 corresponds to RFC 2109.
+ *
+ * @author Paul Sandoz
+ * @author Marc Hadley
+ * @see IETF RFC 2109
+ * @since 1.0
+ */
+public class Cookie {
+ /**
+ * Cookies using the default version correspond to RFC 2109.
+ */
+ public static final int DEFAULT_VERSION = 1;
+
+ /**
+ * Create a new instance.
+ *
+ * @param name the name of the cookie.
+ * @param value the value of the cookie.
+ * @param path the URI path for which the cookie is valid.
+ * @param domain the host domain for which the cookie is valid.
+ * @param version the version of the specification to which the cookie complies.
+ * @throws IllegalArgumentException if name is {@code null}.
+ */
+ public Cookie(final String name, final String value, final String path, final String domain, final int version)
+ throws IllegalArgumentException {
+ }
+
+ /**
+ * Create a new instance.
+ *
+ * @param name the name of the cookie.
+ * @param value the value of the cookie.
+ * @param path the URI path for which the cookie is valid.
+ * @param domain the host domain for which the cookie is valid.
+ * @throws IllegalArgumentException if name is {@code null}.
+ */
+ public Cookie(final String name, final String value, final String path, final String domain)
+ throws IllegalArgumentException {
+ }
+
+ /**
+ * Create a new instance.
+ *
+ * @param name the name of the cookie.
+ * @param value the value of the cookie.
+ * @throws IllegalArgumentException if name is {@code null}.
+ */
+ public Cookie(final String name, final String value)
+ throws IllegalArgumentException {
+ }
+
+ /**
+ * Creates a new instance of {@code Cookie} by parsing the supplied string.
+ *
+ * @param value the cookie string.
+ * @return the newly created {@code Cookie}.
+ * @throws IllegalArgumentException if the supplied string cannot be parsed
+ * or is {@code null}.
+ */
+ public static Cookie valueOf(final String value) {
+ return null;
+ }
+
+ /**
+ * Get the name of the cookie.
+ *
+ * @return the cookie name.
+ */
+ public String getName() {
+ return null;
+ }
+
+ /**
+ * Get the value of the cookie.
+ *
+ * @return the cookie value.
+ */
+ public String getValue() {
+ return null;
+ }
+
+ /**
+ * Get the version of the cookie.
+ *
+ * @return the cookie version.
+ */
+ public int getVersion() {
+ return -1;
+ }
+
+ /**
+ * Get the domain of the cookie.
+ *
+ * @return the cookie domain.
+ */
+ public String getDomain() {
+ return null;
+ }
+
+ /**
+ * Get the path of the cookie.
+ *
+ * @return the cookie path.
+ */
+ public String getPath() {
+ return null;
+ }
+
+ /**
+ * Convert the cookie to a string suitable for use as the value of the
+ * corresponding HTTP header.
+ *
+ * @return a stringified cookie.
+ */
+ @Override
+ public String toString() {
+ return null;
+ }
+
+ /**
+ * Generate a hash code by hashing all of the cookies properties.
+ *
+ * @return the cookie hash code.
+ */
+ @Override
+ public int hashCode() {
+ return -1;
+ }
+
+ /**
+ * Compare for equality.
+ *
+ * @param obj the object to compare to.
+ * @return {@code true}, if the object is a {@code Cookie} with the same
+ * value for all properties, {@code false} otherwise.
+ */
+ @SuppressWarnings({"StringEquality", "RedundantIfStatement"})
+ @Override
+ public boolean equals(final Object obj) {
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/java/ql/test/stubs/jsr311-api-1.1.1/javax/ws/rs/core/NewCookie.java b/java/ql/test/stubs/jsr311-api-1.1.1/javax/ws/rs/core/NewCookie.java
new file mode 100644
index 00000000000..43a4899e0ca
--- /dev/null
+++ b/java/ql/test/stubs/jsr311-api-1.1.1/javax/ws/rs/core/NewCookie.java
@@ -0,0 +1,359 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2010-2015 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License"). You
+ * may not use this file except in compliance with the License. You can
+ * obtain a copy of the License at
+ * http://glassfish.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt. See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license." If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above. However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package javax.ws.rs.core;
+
+import java.util.Date;
+
+/**
+ * Used to create a new HTTP cookie, transferred in a response.
+ *
+ * @author Paul Sandoz
+ * @author Marc Hadley
+ * @see IETF RFC 2109
+ * @since 1.0
+ */
+public class NewCookie extends Cookie {
+
+ /**
+ * Specifies that the cookie expires with the current application/browser session.
+ */
+ public static final int DEFAULT_MAX_AGE = -1;
+
+ private final String comment;
+ private final int maxAge;
+ private final Date expiry;
+ private final boolean secure;
+ private final boolean httpOnly;
+
+ /**
+ * Create a new instance.
+ *
+ * @param name the name of the cookie.
+ * @param value the value of the cookie.
+ * @throws IllegalArgumentException if name is {@code null}.
+ */
+ public NewCookie(String name, String value) {
+ this(name, value, null, null, DEFAULT_VERSION, null, DEFAULT_MAX_AGE, null, false, false);
+ }
+
+ /**
+ * Create a new instance.
+ *
+ * @param name the name of the cookie.
+ * @param value the value of the cookie.
+ * @param path the URI path for which the cookie is valid.
+ * @param domain the host domain for which the cookie is valid.
+ * @param comment the comment.
+ * @param maxAge the maximum age of the cookie in seconds.
+ * @param secure specifies whether the cookie will only be sent over a secure connection.
+ * @throws IllegalArgumentException if name is {@code null}.
+ */
+ public NewCookie(String name,
+ String value,
+ String path,
+ String domain,
+ String comment,
+ int maxAge,
+ boolean secure) {
+ this(name, value, path, domain, DEFAULT_VERSION, comment, maxAge, null, secure, false);
+ }
+
+ /**
+ * Create a new instance.
+ *
+ * @param name the name of the cookie.
+ * @param value the value of the cookie.
+ * @param path the URI path for which the cookie is valid.
+ * @param domain the host domain for which the cookie is valid.
+ * @param comment the comment.
+ * @param maxAge the maximum age of the cookie in seconds.
+ * @param secure specifies whether the cookie will only be sent over a secure connection.
+ * @param httpOnly if {@code true} make the cookie HTTP only, i.e. only visible as part of an HTTP request.
+ * @throws IllegalArgumentException if name is {@code null}.
+ * @since 2.0
+ */
+ public NewCookie(String name,
+ String value,
+ String path,
+ String domain,
+ String comment,
+ int maxAge,
+ boolean secure,
+ boolean httpOnly) {
+ this(name, value, path, domain, DEFAULT_VERSION, comment, maxAge, null, secure, httpOnly);
+ }
+
+ /**
+ * Create a new instance.
+ *
+ * @param name the name of the cookie
+ * @param value the value of the cookie
+ * @param path the URI path for which the cookie is valid
+ * @param domain the host domain for which the cookie is valid
+ * @param version the version of the specification to which the cookie complies
+ * @param comment the comment
+ * @param maxAge the maximum age of the cookie in seconds
+ * @param secure specifies whether the cookie will only be sent over a secure connection
+ * @throws IllegalArgumentException if name is {@code null}.
+ */
+ public NewCookie(String name,
+ String value,
+ String path,
+ String domain,
+ int version,
+ String comment,
+ int maxAge,
+ boolean secure) {
+ this(name, value, path, domain, version, comment, maxAge, null, secure, false);
+ }
+
+ /**
+ * Create a new instance.
+ *
+ * @param name the name of the cookie
+ * @param value the value of the cookie
+ * @param path the URI path for which the cookie is valid
+ * @param domain the host domain for which the cookie is valid
+ * @param version the version of the specification to which the cookie complies
+ * @param comment the comment
+ * @param maxAge the maximum age of the cookie in seconds
+ * @param expiry the cookie expiry date.
+ * @param secure specifies whether the cookie will only be sent over a secure connection
+ * @param httpOnly if {@code true} make the cookie HTTP only, i.e. only visible as part of an HTTP request.
+ * @throws IllegalArgumentException if name is {@code null}.
+ * @since 2.0
+ */
+ public NewCookie(String name,
+ String value,
+ String path,
+ String domain,
+ int version,
+ String comment,
+ int maxAge,
+ Date expiry,
+ boolean secure,
+ boolean httpOnly) {
+ super(name, value, path, domain, version);
+ this.comment = comment;
+ this.maxAge = maxAge;
+ this.expiry = expiry;
+ this.secure = secure;
+ this.httpOnly = httpOnly;
+ }
+
+ /**
+ * Create a new instance copying the information in the supplied cookie.
+ *
+ * @param cookie the cookie to clone.
+ * @throws IllegalArgumentException if cookie is {@code null}.
+ */
+ public NewCookie(Cookie cookie) {
+ this(cookie, null, DEFAULT_MAX_AGE, null, false, false);
+ }
+
+ /**
+ * Create a new instance supplementing the information in the supplied cookie.
+ *
+ * @param cookie the cookie to clone.
+ * @param comment the comment.
+ * @param maxAge the maximum age of the cookie in seconds.
+ * @param secure specifies whether the cookie will only be sent over a secure connection.
+ * @throws IllegalArgumentException if cookie is {@code null}.
+ */
+ public NewCookie(Cookie cookie, String comment, int maxAge, boolean secure) {
+ this(cookie, comment, maxAge, null, secure, false);
+ }
+
+ /**
+ * Create a new instance supplementing the information in the supplied cookie.
+ *
+ * @param cookie the cookie to clone.
+ * @param comment the comment.
+ * @param maxAge the maximum age of the cookie in seconds.
+ * @param expiry the cookie expiry date.
+ * @param secure specifies whether the cookie will only be sent over a secure connection.
+ * @param httpOnly if {@code true} make the cookie HTTP only, i.e. only visible as part of an HTTP request.
+ * @throws IllegalArgumentException if cookie is {@code null}.
+ * @since 2.0
+ */
+ public NewCookie(Cookie cookie, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly) {
+ super(cookie == null ? null : cookie.getName(),
+ cookie == null ? null : cookie.getValue(),
+ cookie == null ? null : cookie.getPath(),
+ cookie == null ? null : cookie.getDomain(),
+ cookie == null ? Cookie.DEFAULT_VERSION : cookie.getVersion());
+ this.comment = comment;
+ this.maxAge = maxAge;
+ this.expiry = expiry;
+ this.secure = secure;
+ this.httpOnly = httpOnly;
+ }
+
+ /**
+ * Creates a new instance of NewCookie by parsing the supplied string.
+ *
+ * @param value the cookie string.
+ * @return the newly created {@code NewCookie}.
+ * @throws IllegalArgumentException if the supplied string cannot be parsed
+ * or is {@code null}.
+ */
+ public static NewCookie valueOf(String value) {
+ return null;
+ }
+
+ /**
+ * Get the comment associated with the cookie.
+ *
+ * @return the comment or null if none set
+ */
+ public String getComment() {
+ return null;
+ }
+
+ /**
+ * Get the maximum age of the the cookie in seconds. Cookies older than
+ * the maximum age are discarded. A cookie can be unset by sending a new
+ * cookie with maximum age of 0 since it will overwrite any existing cookie
+ * and then be immediately discarded. The default value of {@code -1} indicates
+ * that the cookie will be discarded at the end of the browser/application session.
+ *
+ * Note that it is recommended to use {@code Max-Age} to control cookie
+ * expiration, however some browsers do not understand {@code Max-Age}, in which case
+ * setting {@link #getExpiry()} Expires} parameter may be necessary.
+ *
+ *
+ * @return the maximum age in seconds.
+ * @see #getExpiry()
+ */
+ public int getMaxAge() {
+ return -1;
+ }
+
+ /**
+ * Get the cookie expiry date. Cookies whose expiry date has passed are discarded.
+ * A cookie can be unset by setting a new cookie with an expiry date in the past,
+ * typically the lowest possible date that can be set.
+ *
+ * Note that it is recommended to use {@link #getMaxAge() Max-Age} to control cookie
+ * expiration, however some browsers do not understand {@code Max-Age}, in which case
+ * setting {@code Expires} parameter may be necessary.
+ *
+ *
+ * @return cookie expiry date or {@code null} if no expiry date was set.
+ * @see #getMaxAge()
+ * @since 2.0
+ */
+ public Date getExpiry() {
+ return null;
+ }
+
+ /**
+ * Whether the cookie will only be sent over a secure connection. Defaults
+ * to {@code false}.
+ *
+ * @return {@code true} if the cookie will only be sent over a secure connection,
+ * {@code false} otherwise.
+ */
+ public boolean isSecure() {
+ return false;
+ }
+
+ /**
+ * Returns {@code true} if this cookie contains the {@code HttpOnly} attribute.
+ * This means that the cookie should not be accessible to scripting engines,
+ * like javascript.
+ *
+ * @return {@code true} if this cookie should be considered http only, {@code false}
+ * otherwise.
+ * @since 2.0
+ */
+ public boolean isHttpOnly() {
+ return false;
+ }
+
+ /**
+ * Obtain a new instance of a {@link Cookie} with the same name, value, path,
+ * domain and version as this {@code NewCookie}. This method can be used to
+ * obtain an object that can be compared for equality with another {@code Cookie};
+ * since a {@code Cookie} will never compare equal to a {@code NewCookie}.
+ *
+ * @return a {@link Cookie}
+ */
+ public Cookie toCookie() {
+ return null;
+ }
+
+ /**
+ * Convert the cookie to a string suitable for use as the value of the
+ * corresponding HTTP header.
+ *
+ * @return a stringified cookie.
+ */
+ @Override
+ public String toString() {
+ return null;
+ }
+
+ /**
+ * Generate a hash code by hashing all of the properties.
+ *
+ * @return the hash code.
+ */
+ @Override
+ public int hashCode() {
+ return -1;
+ }
+
+ /**
+ * Compare for equality. Use {@link #toCookie()} to compare a
+ * {@code NewCookie} to a {@code Cookie} considering only the common
+ * properties.
+ *
+ * @param obj the object to compare to
+ * @return true if the object is a {@code NewCookie} with the same value for
+ * all properties, false otherwise.
+ */
+ @SuppressWarnings({"StringEquality", "RedundantIfStatement"})
+ @Override
+ public boolean equals(Object obj) {
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/java/ql/test/stubs/servlet-api-2.4/javax/servlet/http/Cookie.java b/java/ql/test/stubs/servlet-api-2.4/javax/servlet/http/Cookie.java
index d8348e13e2e..a93fec853e0 100644
--- a/java/ql/test/stubs/servlet-api-2.4/javax/servlet/http/Cookie.java
+++ b/java/ql/test/stubs/servlet-api-2.4/javax/servlet/http/Cookie.java
@@ -32,6 +32,7 @@ public class Cookie implements Cloneable {
private String path; // ;Path=VALUE ... URLs that see the cookie
private boolean secure; // ;Secure ... e.g. use SSL
private int version = 0; // ;Version=1 ... means RFC 2109++ style
+ private boolean isHttpOnly = false;
public Cookie(String name, String value) {
this.name = name;
@@ -81,4 +82,36 @@ public class Cookie implements Cloneable {
}
public void setVersion(int v) {
}
+
+ /**
+ * Marks or unmarks this Cookie as HttpOnly.
+ *
+ * If isHttpOnly is set to true, this cookie is
+ * marked as HttpOnly, by adding the HttpOnly attribute
+ * to it.
+ *
+ *
HttpOnly cookies are not supposed to be exposed to
+ * client-side scripting code, and may therefore help mitigate certain
+ * kinds of cross-site scripting attacks.
+ *
+ * @param isHttpOnly true if this cookie is to be marked as
+ * HttpOnly, false otherwise
+ *
+ * @since Servlet 3.0
+ */
+ public void setHttpOnly(boolean isHttpOnly) {
+ this.isHttpOnly = isHttpOnly;
+ }
+
+ /**
+ * Checks whether this Cookie has been marked as HttpOnly.
+ *
+ * @return true if this Cookie has been marked as HttpOnly,
+ * false otherwise
+ *
+ * @since Servlet 3.0
+ */
+ public boolean isHttpOnly() {
+ return isHttpOnly;
+ }
}
diff --git a/java/ql/test/stubs/servlet-api-2.4/javax/servlet/http/HttpServletResponse.java b/java/ql/test/stubs/servlet-api-2.4/javax/servlet/http/HttpServletResponse.java
index 2971e023390..162ac0db3cc 100644
--- a/java/ql/test/stubs/servlet-api-2.4/javax/servlet/http/HttpServletResponse.java
+++ b/java/ql/test/stubs/servlet-api-2.4/javax/servlet/http/HttpServletResponse.java
@@ -24,6 +24,7 @@
package javax.servlet.http;
import java.io.IOException;
+import java.util.Collection;
import javax.servlet.ServletResponse;
public interface HttpServletResponse extends ServletResponse {
@@ -44,6 +45,11 @@ public interface HttpServletResponse extends ServletResponse {
public void addIntHeader(String name, int value);
public void setStatus(int sc);
public void setStatus(int sc, String sm);
+ public int getStatus();
+ public String getHeader(String name);
+ public Collection getHeaders(String name);
+ public Collection getHeaderNames();
+
public static final int SC_CONTINUE = 100;
public static final int SC_SWITCHING_PROTOCOLS = 101;
From ffc6af73b70b9bf8aa49c17938629f90e4ddb89d Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Tue, 2 Mar 2021 11:00:43 +0100
Subject: [PATCH 068/725] C++: Accept test changes.
---
.../dataflow/taint-tests/standalone_iterators.cpp | 4 ++--
.../library-tests/dataflow/taint-tests/string.cpp | 4 ++--
.../dataflow/taint-tests/stringstream.cpp | 4 ++--
.../library-tests/dataflow/taint-tests/taint.cpp | 6 +++---
.../dataflow/taint-tests/taint.expected | 13 -------------
5 files changed, 9 insertions(+), 22 deletions(-)
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/standalone_iterators.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/standalone_iterators.cpp
index 362784eeb1a..376582381ac 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/standalone_iterators.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/standalone_iterators.cpp
@@ -39,13 +39,13 @@ public:
void test_typedefs(int_iterator_by_typedefs source1) {
sink(*source1); // $ ast,ir
sink(*(source1++)); // $ ast,ir
- sink(*(++source1)); // $ ast MISSING: ir
+ sink(*(++source1)); // $ ast,ir
}
void test_trait(int_iterator_by_trait source1) {
sink(*source1); // $ ast,ir
sink(*(source1++)); // $ ast,ir
- sink(*(++source1)); // $ ast MISSING: ir
+ sink(*(++source1)); // $ ast,ir
}
void test_non_iterator(non_iterator source1) {
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp
index 9169113c1d9..43b6e9bc53b 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp
@@ -396,9 +396,9 @@ void test_string_iterators() {
sink(*(i2+1)); // $ ast,ir
sink(*(i2-1)); // $ ast,ir
i3 = i2;
- sink(*(++i3)); // $ ast MISSING: ir
+ sink(*(++i3)); // $ ast,ir
i4 = i2;
- sink(*(--i4)); // $ ast MISSING: ir
+ sink(*(--i4)); // $ ast,ir
i5 = i2;
i5++;
sink(*i5); // $ ast,ir
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp
index 7b7712f0c01..7a6c5e51820 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp
@@ -229,7 +229,7 @@ void test_getline()
sink(ss2.getline(b7, 1000).getline(b8, 1000)); // $ ast,ir
sink(b7); // $ ast,ir
- sink(b8); // $ ast MISSING: ir
+ sink(b8); // $ ast,ir
sink(getline(ss1, s1));
sink(getline(ss2, s2)); // $ ast,ir
@@ -261,7 +261,7 @@ void test_chaining()
sink(ss1.get(b1, 100).unget().get(b2, 100)); // $ ast,ir
sink(b1); // $ ast,ir
- sink(b2); // $ ast MISSING: ir
+ sink(b2); // $ ast,ir
sink(ss2.write("abc", 3).flush().write(source(), 3).flush().write("xyz", 3)); // $ ast MISSING: ir
sink(ss2); // $ ast MISSING: ir
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp
index 6db77f37ac1..baf26ee9af4 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp
@@ -192,7 +192,7 @@ void *memcpy(void *dest, void *src, int len);
void test_memcpy(int *source) {
int x;
memcpy(&x, source, sizeof(int));
- sink(x); // $ ast=192:23 MISSING: ir SPURIOUS: ast=193:6
+ sink(x); // $ ast=192:23 ir SPURIOUS: ast=193:6
}
// --- std::swap ---
@@ -518,7 +518,7 @@ void *mempcpy(void *dest, const void *src, size_t n);
void test_mempcpy(int *source) {
int x;
mempcpy(&x, source, sizeof(int));
- sink(x); // $ ast=518:24 MISSING: ir SPURIOUS: ast=519:6
+ sink(x); // $ ast=518:24 ir SPURIOUS: ast=519:6
}
// --- memccpy ---
@@ -528,7 +528,7 @@ void *memccpy(void *dest, const void *src, int c, size_t n);
void test_memccpy(int *source) {
int dest[16];
memccpy(dest, source, 42, sizeof(dest));
- sink(dest); // $ ast=528:24 MISSING: ir SPURIOUS: ast=529:6
+ sink(dest); // $ ast=528:24 ir SPURIOUS: ast=529:6
}
// --- strcat and related functions ---
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
index e42e84d80cd..e69de29bb2d 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
@@ -1,13 +0,0 @@
-| standalone_iterators.cpp:42:10:42:10 | call to operator* | Fixed missing result:ir= |
-| standalone_iterators.cpp:48:10:48:10 | call to operator* | Fixed missing result:ir= |
-| string.cpp:399:8:399:8 | call to operator* | Fixed missing result:ir= |
-| string.cpp:399:8:399:15 | (reference dereference) | Fixed missing result:ir= |
-| string.cpp:401:8:401:8 | call to operator* | Fixed missing result:ir= |
-| string.cpp:401:8:401:15 | (reference dereference) | Fixed missing result:ir= |
-| stringstream.cpp:232:7:232:8 | Argument 0 indirection | Fixed missing result:ir= |
-| stringstream.cpp:232:7:232:8 | call to basic_string | Fixed missing result:ir= |
-| stringstream.cpp:264:7:264:8 | Argument 0 indirection | Fixed missing result:ir= |
-| stringstream.cpp:264:7:264:8 | call to basic_string | Fixed missing result:ir= |
-| taint.cpp:195:7:195:7 | x | Fixed missing result:ir= |
-| taint.cpp:521:7:521:7 | x | Fixed missing result:ir= |
-| taint.cpp:531:7:531:10 | Argument 0 indirection | Fixed missing result:ir= |
From 9f02c144a89cfc7f976a3b1b2b4a7d5705eb35ea Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Tue, 2 Mar 2021 11:09:19 +0100
Subject: [PATCH 069/725] C++: Remove files that were incorrectly added when
resolving merge conflicts.
---
.../DefaultTaintTracking/tainted.expected | 246 --------
.../dataflow/DefaultTaintTracking/tainted.ql | 9 -
.../DefaultTaintTracking/test_diff.expected | 133 ----
.../DefaultTaintTracking/test_diff.ql | 28 -
.../dataflow/taint-tests/test_diff.expected | 269 --------
.../dataflow/taint-tests/test_ir.expected | 580 ------------------
6 files changed, 1265 deletions(-)
delete mode 100644 cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.expected
delete mode 100644 cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.ql
delete mode 100644 cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.expected
delete mode 100644 cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.ql
delete mode 100644 cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
delete mode 100644 cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected
diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.expected b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.expected
deleted file mode 100644
index 9f8ed0d28d3..00000000000
--- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.expected
+++ /dev/null
@@ -1,246 +0,0 @@
-| defaulttainttracking.cpp:16:16:16:21 | call to getenv | defaulttainttracking.cpp:16:16:16:21 | call to getenv |
-| defaulttainttracking.cpp:16:16:16:21 | call to getenv | defaulttainttracking.cpp:16:16:16:28 | (const char *)... |
-| defaulttainttracking.cpp:17:15:17:20 | call to getenv | defaulttainttracking.cpp:17:15:17:20 | call to getenv |
-| defaulttainttracking.cpp:17:15:17:20 | call to getenv | defaulttainttracking.cpp:17:15:17:27 | (const char *)... |
-| defaulttainttracking.cpp:18:27:18:32 | call to getenv | defaulttainttracking.cpp:18:27:18:32 | call to getenv |
-| defaulttainttracking.cpp:18:27:18:32 | call to getenv | defaulttainttracking.cpp:18:27:18:39 | (const char *)... |
-| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:22:20:22:25 | call to getenv |
-| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:22:20:22:32 | (const char *)... |
-| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:24:8:24:10 | buf |
-| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:38:25:38:30 | call to getenv |
-| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:38:25:38:37 | (void *)... |
-| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:26:39:34 | call to inet_addr |
-| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:50:39:61 | & ... |
-| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:40:10:40:10 | a |
-| defaulttainttracking.cpp:64:10:64:15 | call to getenv | defaulttainttracking.cpp:58:14:58:14 | p |
-| defaulttainttracking.cpp:64:10:64:15 | call to getenv | defaulttainttracking.cpp:64:10:64:15 | call to getenv |
-| defaulttainttracking.cpp:64:10:64:15 | call to getenv | defaulttainttracking.cpp:64:10:64:22 | (const char *)... |
-| defaulttainttracking.cpp:66:17:66:22 | call to getenv | defaulttainttracking.cpp:58:14:58:14 | p |
-| defaulttainttracking.cpp:66:17:66:22 | call to getenv | defaulttainttracking.cpp:66:17:66:22 | call to getenv |
-| defaulttainttracking.cpp:66:17:66:22 | call to getenv | defaulttainttracking.cpp:66:17:66:29 | (const char *)... |
-| defaulttainttracking.cpp:67:28:67:33 | call to getenv | defaulttainttracking.cpp:58:14:58:14 | p |
-| defaulttainttracking.cpp:67:28:67:33 | call to getenv | defaulttainttracking.cpp:67:28:67:33 | call to getenv |
-| defaulttainttracking.cpp:67:28:67:33 | call to getenv | defaulttainttracking.cpp:67:28:67:40 | (const char *)... |
-| defaulttainttracking.cpp:68:29:68:34 | call to getenv | defaulttainttracking.cpp:58:14:58:14 | p |
-| defaulttainttracking.cpp:68:29:68:34 | call to getenv | defaulttainttracking.cpp:68:29:68:34 | call to getenv |
-| defaulttainttracking.cpp:68:29:68:34 | call to getenv | defaulttainttracking.cpp:68:29:68:41 | (const char *)... |
-| defaulttainttracking.cpp:69:33:69:38 | call to getenv | defaulttainttracking.cpp:58:14:58:14 | p |
-| defaulttainttracking.cpp:69:33:69:38 | call to getenv | defaulttainttracking.cpp:69:33:69:38 | call to getenv |
-| defaulttainttracking.cpp:69:33:69:38 | call to getenv | defaulttainttracking.cpp:69:33:69:45 | (const char *)... |
-| defaulttainttracking.cpp:72:11:72:16 | call to getenv | defaulttainttracking.cpp:72:11:72:16 | call to getenv |
-| defaulttainttracking.cpp:72:11:72:16 | call to getenv | defaulttainttracking.cpp:72:11:72:23 | (const char *)... |
-| defaulttainttracking.cpp:74:18:74:23 | call to getenv | defaulttainttracking.cpp:74:18:74:23 | call to getenv |
-| defaulttainttracking.cpp:74:18:74:23 | call to getenv | defaulttainttracking.cpp:74:18:74:30 | (const char *)... |
-| defaulttainttracking.cpp:75:29:75:34 | call to getenv | defaulttainttracking.cpp:75:29:75:34 | call to getenv |
-| defaulttainttracking.cpp:75:29:75:34 | call to getenv | defaulttainttracking.cpp:75:29:75:41 | (const char *)... |
-| defaulttainttracking.cpp:76:30:76:35 | call to getenv | defaulttainttracking.cpp:76:30:76:35 | call to getenv |
-| defaulttainttracking.cpp:76:30:76:35 | call to getenv | defaulttainttracking.cpp:76:30:76:42 | (const char *)... |
-| defaulttainttracking.cpp:77:34:77:39 | call to getenv | defaulttainttracking.cpp:77:34:77:39 | call to getenv |
-| defaulttainttracking.cpp:77:34:77:39 | call to getenv | defaulttainttracking.cpp:77:34:77:46 | (const char *)... |
-| defaulttainttracking.cpp:79:30:79:35 | call to getenv | defaulttainttracking.cpp:58:14:58:14 | p |
-| defaulttainttracking.cpp:79:30:79:35 | call to getenv | defaulttainttracking.cpp:79:30:79:35 | call to getenv |
-| defaulttainttracking.cpp:79:30:79:35 | call to getenv | defaulttainttracking.cpp:79:30:79:42 | (const char *)... |
-| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:16 | call to move |
-| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:32 | (const char *)... |
-| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:32 | (reference dereference) |
-| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:18:88:23 | call to getenv |
-| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:18:88:30 | (reference to) |
-| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:18:88:30 | temporary object |
-| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:92:12:92:14 | arg |
-| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:97:27:97:32 | call to getenv |
-| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:98:10:98:11 | (const char *)... |
-| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:98:10:98:11 | p2 |
-| defaulttainttracking.cpp:110:17:110:22 | call to getenv | defaulttainttracking.cpp:110:17:110:22 | call to getenv |
-| defaulttainttracking.cpp:110:17:110:22 | call to getenv | defaulttainttracking.cpp:110:17:110:32 | (int)... |
-| defaulttainttracking.cpp:110:17:110:22 | call to getenv | defaulttainttracking.cpp:110:17:110:32 | access to array |
-| defaulttainttracking.cpp:110:17:110:22 | call to getenv | defaulttainttracking.cpp:111:12:111:18 | tainted |
-| defaulttainttracking.cpp:133:9:133:14 | call to getenv | defaulttainttracking.cpp:126:16:126:16 | x |
-| defaulttainttracking.cpp:133:9:133:14 | call to getenv | defaulttainttracking.cpp:133:9:133:14 | call to getenv |
-| defaulttainttracking.cpp:133:9:133:14 | call to getenv | defaulttainttracking.cpp:133:9:133:24 | (int)... |
-| defaulttainttracking.cpp:133:9:133:14 | call to getenv | defaulttainttracking.cpp:133:9:133:24 | access to array |
-| defaulttainttracking.cpp:133:9:133:14 | call to getenv | defaulttainttracking.cpp:134:10:134:10 | x |
-| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:140:11:140:16 | call to getenv |
-| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:140:11:140:26 | (int)... |
-| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:140:11:140:26 | access to array |
-| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:166:10:166:10 | x |
-| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:157:9:157:14 | call to getenv |
-| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:157:9:157:24 | (int)... |
-| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:157:9:157:24 | access to array |
-| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:159:10:159:10 | x |
-| defaulttainttracking.cpp:170:11:170:16 | call to getenv | defaulttainttracking.cpp:170:11:170:16 | call to getenv |
-| defaulttainttracking.cpp:170:11:170:16 | call to getenv | defaulttainttracking.cpp:170:11:170:26 | (int)... |
-| defaulttainttracking.cpp:170:11:170:16 | call to getenv | defaulttainttracking.cpp:170:11:170:26 | access to array |
-| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:181:11:181:16 | call to getenv |
-| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:181:11:181:26 | (int)... |
-| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:181:11:181:26 | access to array |
-| defaulttainttracking.cpp:195:11:195:16 | call to getenv | defaulttainttracking.cpp:195:11:195:16 | call to getenv |
-| defaulttainttracking.cpp:195:11:195:16 | call to getenv | defaulttainttracking.cpp:195:11:195:26 | (int)... |
-| defaulttainttracking.cpp:195:11:195:16 | call to getenv | defaulttainttracking.cpp:195:11:195:26 | access to array |
-| defaulttainttracking.cpp:201:13:201:18 | call to getenv | defaulttainttracking.cpp:201:13:201:18 | call to getenv |
-| defaulttainttracking.cpp:201:13:201:18 | call to getenv | defaulttainttracking.cpp:201:13:201:28 | (int)... |
-| defaulttainttracking.cpp:201:13:201:18 | call to getenv | defaulttainttracking.cpp:201:13:201:28 | access to array |
-| defaulttainttracking.cpp:208:27:208:32 | call to getenv | defaulttainttracking.cpp:208:27:208:32 | call to getenv |
-| defaulttainttracking.cpp:208:27:208:32 | call to getenv | defaulttainttracking.cpp:208:27:208:42 | (int)... |
-| defaulttainttracking.cpp:208:27:208:32 | call to getenv | defaulttainttracking.cpp:208:27:208:42 | access to array |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:218:12:218:17 | call to getenv |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:224:17:224:17 | (const void *)... |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:224:17:224:17 | s |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:228:7:228:12 | buffer |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:229:7:229:10 | ptr1 |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:232:7:232:10 | ptr3 |
-| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:240:12:240:17 | call to getenv |
-| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:246:16:246:16 | (const void *)... |
-| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:246:16:246:16 | s |
-| dispatch.cpp:28:29:28:34 | call to getenv | dispatch.cpp:28:24:28:27 | call to atoi |
-| dispatch.cpp:28:29:28:34 | call to getenv | dispatch.cpp:28:29:28:34 | call to getenv |
-| dispatch.cpp:28:29:28:34 | call to getenv | dispatch.cpp:28:29:28:45 | (const char *)... |
-| dispatch.cpp:29:32:29:37 | call to getenv | dispatch.cpp:29:27:29:30 | call to atoi |
-| dispatch.cpp:29:32:29:37 | call to getenv | dispatch.cpp:29:32:29:37 | call to getenv |
-| dispatch.cpp:29:32:29:37 | call to getenv | dispatch.cpp:29:32:29:48 | (const char *)... |
-| dispatch.cpp:31:28:31:33 | call to getenv | dispatch.cpp:8:8:8:16 | sinkParam |
-| dispatch.cpp:31:28:31:33 | call to getenv | dispatch.cpp:31:23:31:26 | call to atoi |
-| dispatch.cpp:31:28:31:33 | call to getenv | dispatch.cpp:31:28:31:33 | call to getenv |
-| dispatch.cpp:31:28:31:33 | call to getenv | dispatch.cpp:31:28:31:44 | (const char *)... |
-| dispatch.cpp:32:31:32:36 | call to getenv | dispatch.cpp:8:8:8:16 | sinkParam |
-| dispatch.cpp:32:31:32:36 | call to getenv | dispatch.cpp:32:26:32:29 | call to atoi |
-| dispatch.cpp:32:31:32:36 | call to getenv | dispatch.cpp:32:31:32:36 | call to getenv |
-| dispatch.cpp:32:31:32:36 | call to getenv | dispatch.cpp:32:31:32:47 | (const char *)... |
-| dispatch.cpp:34:22:34:27 | call to getenv | dispatch.cpp:8:8:8:16 | sinkParam |
-| dispatch.cpp:34:22:34:27 | call to getenv | dispatch.cpp:34:17:34:20 | call to atoi |
-| dispatch.cpp:34:22:34:27 | call to getenv | dispatch.cpp:34:22:34:27 | call to getenv |
-| dispatch.cpp:34:22:34:27 | call to getenv | dispatch.cpp:34:22:34:38 | (const char *)... |
-| globals.cpp:5:20:5:25 | call to getenv | globals.cpp:5:20:5:25 | call to getenv |
-| globals.cpp:5:20:5:25 | call to getenv | globals.cpp:6:10:6:14 | (const char *)... |
-| globals.cpp:5:20:5:25 | call to getenv | globals.cpp:6:10:6:14 | local |
-| globals.cpp:13:15:13:20 | call to getenv | globals.cpp:13:15:13:20 | call to getenv |
-| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:23:15:23:20 | call to getenv |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:62:25:62:30 | call to getenv |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:68:12:68:17 | call to source |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:70:16:70:21 | call to source |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:70:16:70:23 | (const char *)... |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:70:16:70:24 | call to basic_string |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:72:7:72:7 | (const char *)... |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:72:7:72:7 | a |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:74:7:74:7 | c |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:76:7:76:7 | c |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:76:9:76:13 | call to c_str |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:21 | call to source |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:23 | (const char *)... |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:24 | call to basic_string |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:6:85:6 | call to operator<< |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:6:85:17 | (reference dereference) |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:9:85:14 | call to source |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:9:85:16 | (const char *)... |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:15:86:15 | call to operator<< |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:15:86:26 | (reference dereference) |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:18:86:23 | call to source |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:18:86:25 | (const char *)... |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:6:87:6 | call to operator<< |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:6:87:19 | (reference dereference) |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:6:87:19 | (reference to) |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:9:87:14 | call to source |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:9:87:16 | (const char *)... |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:18:87:18 | call to operator<< |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:18:87:26 | (reference dereference) |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:88:9:88:9 | t |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:91:7:91:9 | ss2 |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:93:7:93:9 | ss4 |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:9 | ss2 |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:15 | (const string)... |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:15 | (reference to) |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:15 | temporary object |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:11:96:13 | call to str |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:9 | ss4 |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:15 | (const string)... |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:15 | (reference to) |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:15 | temporary object |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:11:98:13 | call to str |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:118:10:118:15 | call to source |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:125:16:125:28 | call to basic_string |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:125:17:125:26 | call to user_input |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:125:17:125:28 | (const char *)... |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:126:7:126:11 | path1 |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:126:13:126:17 | call to c_str |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:19 | call to user_input |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:21 | (const char *)... |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:21 | call to basic_string |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:21 | temporary object |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:130:7:130:11 | path2 |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:130:13:130:17 | call to c_str |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:132:15:132:24 | call to user_input |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:132:15:132:26 | (const char *)... |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:132:15:132:27 | call to basic_string |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:133:7:133:11 | path3 |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:133:13:133:17 | call to c_str |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:138:19:138:24 | call to source |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:138:19:138:26 | (const char *)... |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:141:17:141:18 | cs |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:141:17:141:19 | call to basic_string |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:143:7:143:8 | cs |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:144:7:144:8 | ss |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:149:19:149:24 | call to source |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:149:19:149:26 | (const char *)... |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:152:17:152:18 | cs |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:152:17:152:19 | call to basic_string |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:155:7:155:8 | ss |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:155:10:155:14 | call to c_str |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:157:7:157:8 | cs |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:158:7:158:8 | ss |
-| test_diff.cpp:92:10:92:13 | argv | test_diff.cpp:92:10:92:13 | argv |
-| test_diff.cpp:92:10:92:13 | argv | test_diff.cpp:92:10:92:16 | (const char *)... |
-| test_diff.cpp:92:10:92:13 | argv | test_diff.cpp:92:10:92:16 | access to array |
-| test_diff.cpp:94:32:94:35 | argv | test_diff.cpp:94:10:94:36 | reinterpret_cast... |
-| test_diff.cpp:94:32:94:35 | argv | test_diff.cpp:94:32:94:35 | argv |
-| test_diff.cpp:96:26:96:29 | argv | test_diff.cpp:17:10:17:10 | a |
-| test_diff.cpp:96:26:96:29 | argv | test_diff.cpp:96:26:96:29 | argv |
-| test_diff.cpp:96:26:96:29 | argv | test_diff.cpp:96:26:96:32 | (const char *)... |
-| test_diff.cpp:96:26:96:29 | argv | test_diff.cpp:96:26:96:32 | access to array |
-| test_diff.cpp:98:18:98:21 | argv | test_diff.cpp:17:10:17:10 | a |
-| test_diff.cpp:98:18:98:21 | argv | test_diff.cpp:98:17:98:21 | & ... |
-| test_diff.cpp:98:18:98:21 | argv | test_diff.cpp:98:18:98:21 | argv |
-| test_diff.cpp:98:18:98:21 | argv | test_diff.cpp:100:10:100:14 | (const char *)... |
-| test_diff.cpp:98:18:98:21 | argv | test_diff.cpp:100:10:100:14 | * ... |
-| test_diff.cpp:98:18:98:21 | argv | test_diff.cpp:100:11:100:11 | p |
-| test_diff.cpp:98:18:98:21 | argv | test_diff.cpp:100:11:100:14 | access to array |
-| test_diff.cpp:98:18:98:21 | argv | test_diff.cpp:102:26:102:30 | (const char *)... |
-| test_diff.cpp:98:18:98:21 | argv | test_diff.cpp:102:26:102:30 | * ... |
-| test_diff.cpp:98:18:98:21 | argv | test_diff.cpp:102:27:102:27 | p |
-| test_diff.cpp:98:18:98:21 | argv | test_diff.cpp:102:27:102:30 | access to array |
-| test_diff.cpp:104:12:104:15 | argv | test_diff.cpp:104:10:104:20 | (const char *)... |
-| test_diff.cpp:104:12:104:15 | argv | test_diff.cpp:104:10:104:20 | * ... |
-| test_diff.cpp:104:12:104:15 | argv | test_diff.cpp:104:11:104:20 | (...) |
-| test_diff.cpp:104:12:104:15 | argv | test_diff.cpp:104:12:104:15 | argv |
-| test_diff.cpp:104:12:104:15 | argv | test_diff.cpp:104:12:104:19 | ... + ... |
-| test_diff.cpp:108:10:108:13 | argv | test_diff.cpp:30:14:30:14 | p |
-| test_diff.cpp:108:10:108:13 | argv | test_diff.cpp:108:10:108:13 | argv |
-| test_diff.cpp:108:10:108:13 | argv | test_diff.cpp:108:10:108:16 | (const char *)... |
-| test_diff.cpp:108:10:108:13 | argv | test_diff.cpp:108:10:108:16 | access to array |
-| test_diff.cpp:111:10:111:13 | argv | test_diff.cpp:111:10:111:13 | argv |
-| test_diff.cpp:111:10:111:13 | argv | test_diff.cpp:111:10:111:16 | (const char *)... |
-| test_diff.cpp:111:10:111:13 | argv | test_diff.cpp:111:10:111:16 | access to array |
-| test_diff.cpp:115:11:115:14 | argv | test_diff.cpp:42:14:42:14 | p |
-| test_diff.cpp:115:11:115:14 | argv | test_diff.cpp:53:37:53:37 | p |
-| test_diff.cpp:115:11:115:14 | argv | test_diff.cpp:115:11:115:14 | argv |
-| test_diff.cpp:115:11:115:14 | argv | test_diff.cpp:115:11:115:17 | (const char *)... |
-| test_diff.cpp:115:11:115:14 | argv | test_diff.cpp:115:11:115:17 | access to array |
-| test_diff.cpp:118:26:118:29 | argv | test_diff.cpp:61:34:61:34 | p |
-| test_diff.cpp:118:26:118:29 | argv | test_diff.cpp:118:26:118:29 | argv |
-| test_diff.cpp:118:26:118:29 | argv | test_diff.cpp:118:26:118:32 | (const char *)... |
-| test_diff.cpp:118:26:118:29 | argv | test_diff.cpp:118:26:118:32 | access to array |
-| test_diff.cpp:121:23:121:26 | argv | test_diff.cpp:61:34:61:34 | p |
-| test_diff.cpp:121:23:121:26 | argv | test_diff.cpp:68:14:68:14 | p |
-| test_diff.cpp:121:23:121:26 | argv | test_diff.cpp:121:23:121:26 | argv |
-| test_diff.cpp:121:23:121:26 | argv | test_diff.cpp:121:23:121:29 | (const char *)... |
-| test_diff.cpp:121:23:121:26 | argv | test_diff.cpp:121:23:121:29 | access to array |
-| test_diff.cpp:124:19:124:22 | argv | test_diff.cpp:82:14:82:14 | p |
-| test_diff.cpp:124:19:124:22 | argv | test_diff.cpp:124:19:124:22 | argv |
-| test_diff.cpp:124:19:124:22 | argv | test_diff.cpp:124:19:124:25 | (const char *)... |
-| test_diff.cpp:124:19:124:22 | argv | test_diff.cpp:124:19:124:25 | access to array |
-| test_diff.cpp:126:43:126:46 | argv | test_diff.cpp:82:14:82:14 | p |
-| test_diff.cpp:126:43:126:46 | argv | test_diff.cpp:126:43:126:46 | argv |
-| test_diff.cpp:126:43:126:46 | argv | test_diff.cpp:126:43:126:49 | (const char *)... |
-| test_diff.cpp:126:43:126:46 | argv | test_diff.cpp:126:43:126:49 | access to array |
-| test_diff.cpp:128:44:128:47 | argv | test_diff.cpp:82:14:82:14 | p |
-| test_diff.cpp:128:44:128:47 | argv | test_diff.cpp:128:44:128:47 | argv |
-| test_diff.cpp:128:44:128:47 | argv | test_diff.cpp:128:44:128:50 | (const char *)... |
-| test_diff.cpp:128:44:128:47 | argv | test_diff.cpp:128:44:128:50 | access to array |
diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.ql b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.ql
deleted file mode 100644
index 240418f9bf4..00000000000
--- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.ql
+++ /dev/null
@@ -1,9 +0,0 @@
-import semmle.code.cpp.ir.dataflow.DefaultTaintTracking
-
-class SourceConfiguration extends TaintedWithPath::TaintTrackingConfiguration {
- override predicate isSink(Element e) { any() }
-}
-
-from Expr source, Element tainted
-where TaintedWithPath::taintedWithPath(source, tainted, _, _)
-select source, tainted
diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.expected b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.expected
deleted file mode 100644
index ffc640c15f3..00000000000
--- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.expected
+++ /dev/null
@@ -1,133 +0,0 @@
-| defaulttainttracking.cpp:17:15:17:20 | call to getenv | defaulttainttracking.cpp:17:8:17:13 | call to strdup | AST only |
-| defaulttainttracking.cpp:17:15:17:20 | call to getenv | defaulttainttracking.cpp:17:8:17:28 | (const char *)... | AST only |
-| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:21:8:21:10 | buf | AST only |
-| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:22:8:22:13 | call to strcat | AST only |
-| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:22:8:22:33 | (const char *)... | AST only |
-| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:22:15:22:17 | buf | AST only |
-| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:38:11:38:21 | env_pointer | AST only |
-| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:22:39:22 | a | AST only |
-| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:36:39:61 | (const char *)... | AST only |
-| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:51:39:61 | env_pointer | AST only |
-| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:16 | call to move | IR only |
-| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:32 | (const char *)... | IR only |
-| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:32 | (reference dereference) | IR only |
-| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:18:88:30 | (reference to) | IR only |
-| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:18:88:30 | temporary object | IR only |
-| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:92:5:92:8 | * ... | AST only |
-| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:92:6:92:8 | ret | AST only |
-| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:98:10:98:11 | (const char *)... | IR only |
-| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:98:10:98:11 | p2 | IR only |
-| defaulttainttracking.cpp:110:17:110:22 | call to getenv | defaulttainttracking.cpp:110:7:110:13 | tainted | AST only |
-| defaulttainttracking.cpp:110:17:110:22 | call to getenv | defaulttainttracking.cpp:111:8:111:8 | y | AST only |
-| defaulttainttracking.cpp:133:9:133:14 | call to getenv | defaulttainttracking.cpp:126:16:126:16 | x | IR only |
-| defaulttainttracking.cpp:133:9:133:14 | call to getenv | defaulttainttracking.cpp:133:5:133:5 | x | AST only |
-| defaulttainttracking.cpp:133:9:133:14 | call to getenv | defaulttainttracking.cpp:134:10:134:10 | x | IR only |
-| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:140:7:140:7 | x | AST only |
-| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:166:10:166:10 | x | IR only |
-| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:157:5:157:5 | x | AST only |
-| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:159:10:159:10 | x | IR only |
-| defaulttainttracking.cpp:170:11:170:16 | call to getenv | defaulttainttracking.cpp:170:7:170:7 | x | AST only |
-| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:181:7:181:7 | x | AST only |
-| defaulttainttracking.cpp:195:11:195:16 | call to getenv | defaulttainttracking.cpp:195:7:195:7 | x | AST only |
-| defaulttainttracking.cpp:201:13:201:18 | call to getenv | defaulttainttracking.cpp:201:9:201:9 | x | AST only |
-| defaulttainttracking.cpp:208:27:208:32 | call to getenv | defaulttainttracking.cpp:208:23:208:23 | x | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:217:7:217:12 | buffer | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:218:8:218:8 | s | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:219:8:219:11 | ptr1 | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:219:16:219:19 | ptr2 | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:220:8:220:11 | ptr3 | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:220:16:220:19 | ptr4 | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:222:2:222:5 | ptr1 | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:222:9:222:14 | buffer | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:223:2:223:5 | ptr2 | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:223:9:223:13 | & ... | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:223:10:223:13 | ptr1 | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:224:2:224:7 | call to memcpy | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:224:9:224:14 | buffer | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:225:2:225:5 | ptr3 | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:225:9:225:14 | buffer | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:226:2:226:5 | ptr4 | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:226:9:226:13 | & ... | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:226:10:226:13 | ptr3 | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:229:7:229:10 | (const char *)... | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:230:7:230:10 | ptr2 | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:231:7:231:11 | (const char *)... | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:231:7:231:11 | * ... | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:231:8:231:11 | ptr2 | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:232:7:232:10 | (const char *)... | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:233:7:233:10 | ptr4 | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:234:7:234:11 | (const char *)... | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:234:7:234:11 | * ... | AST only |
-| defaulttainttracking.cpp:218:12:218:17 | call to getenv | defaulttainttracking.cpp:234:8:234:11 | ptr4 | AST only |
-| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:240:8:240:8 | s | AST only |
-| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:241:8:241:11 | ptr1 | AST only |
-| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:241:16:241:19 | ptr2 | AST only |
-| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:245:2:245:5 | ptr2 | AST only |
-| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:245:9:245:13 | & ... | AST only |
-| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:245:10:245:13 | ptr1 | AST only |
-| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:246:2:246:7 | call to memcpy | AST only |
-| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:246:9:246:13 | * ... | AST only |
-| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:246:10:246:13 | ptr2 | AST only |
-| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:251:7:251:10 | (const char *)... | AST only |
-| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:251:7:251:10 | ptr1 | AST only |
-| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:252:7:252:10 | ptr2 | AST only |
-| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:253:7:253:11 | (const char *)... | AST only |
-| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:253:7:253:11 | * ... | AST only |
-| defaulttainttracking.cpp:240:12:240:17 | call to getenv | defaulttainttracking.cpp:253:8:253:11 | ptr2 | AST only |
-| globals.cpp:5:20:5:25 | call to getenv | globals.cpp:5:12:5:16 | local | AST only |
-| globals.cpp:13:15:13:20 | call to getenv | globals.cpp:9:8:9:14 | global1 | AST only |
-| globals.cpp:13:15:13:20 | call to getenv | globals.cpp:13:5:13:11 | global1 | AST only |
-| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:16:15:16:21 | global2 | AST only |
-| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:23:5:23:11 | global2 | AST only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:62:7:62:12 | source | AST only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:68:8:68:8 | a | AST only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:70:16:70:24 | call to basic_string | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:74:7:74:7 | c | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:76:7:76:7 | c | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:76:9:76:13 | call to c_str | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:24 | call to basic_string | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:6:85:6 | call to operator<< | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:6:85:17 | (reference dereference) | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:15:86:15 | call to operator<< | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:15:86:26 | (reference dereference) | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:6:87:6 | call to operator<< | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:6:87:19 | (reference dereference) | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:6:87:19 | (reference to) | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:9:87:16 | (const char *)... | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:18:87:18 | call to operator<< | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:18:87:26 | (reference dereference) | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:88:9:88:9 | t | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:91:7:91:9 | ss2 | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:93:7:93:9 | ss4 | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:9 | ss2 | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:15 | (const string)... | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:15 | (reference to) | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:15 | temporary object | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:11:96:13 | call to str | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:9 | ss4 | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:15 | (const string)... | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:15 | (reference to) | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:15 | temporary object | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:11:98:13 | call to str | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:117:7:117:16 | user_input | AST only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:125:16:125:28 | call to basic_string | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:126:7:126:11 | path1 | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:126:13:126:17 | call to c_str | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:21 | call to basic_string | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:21 | temporary object | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:130:7:130:11 | path2 | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:130:13:130:17 | call to c_str | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:132:15:132:27 | call to basic_string | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:133:7:133:11 | path3 | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:133:13:133:17 | call to c_str | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:138:14:138:15 | cs | AST only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:141:17:141:19 | call to basic_string | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:144:7:144:8 | ss | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:149:14:149:15 | cs | AST only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:152:17:152:19 | call to basic_string | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:155:7:155:8 | ss | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:155:10:155:14 | call to c_str | IR only |
-| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:158:7:158:8 | ss | IR only |
-| test_diff.cpp:98:18:98:21 | argv | test_diff.cpp:98:13:98:13 | p | AST only |
-| test_diff.cpp:104:12:104:15 | argv | test_diff.cpp:104:11:104:20 | (...) | IR only |
-| test_diff.cpp:111:10:111:13 | argv | test_diff.cpp:30:14:30:14 | p | AST only |
diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.ql b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.ql
deleted file mode 100644
index 40bed5680c0..00000000000
--- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.ql
+++ /dev/null
@@ -1,28 +0,0 @@
-import cpp
-import semmle.code.cpp.security.Security
-import semmle.code.cpp.security.TaintTrackingImpl as ASTTaintTracking
-import semmle.code.cpp.ir.dataflow.DefaultTaintTracking as IRDefaultTaintTracking
-
-class SourceConfiguration extends IRDefaultTaintTracking::TaintedWithPath::TaintTrackingConfiguration {
- override predicate isSink(Element e) { any() }
-}
-
-predicate astFlow(Expr source, Element sink) { ASTTaintTracking::tainted(source, sink) }
-
-predicate irFlow(Expr source, Element sink) {
- IRDefaultTaintTracking::TaintedWithPath::taintedWithPath(source, sink, _, _)
-}
-
-from Expr source, Element sink, string note
-where
- not sink instanceof Parameter and
- (
- astFlow(source, sink) and
- not irFlow(source, sink) and
- note = "AST only"
- or
- irFlow(source, sink) and
- not astFlow(source, sink) and
- note = "IR only"
- )
-select source, sink, note
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
deleted file mode 100644
index 88f79183d2a..00000000000
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
+++ /dev/null
@@ -1,269 +0,0 @@
-| arrayassignment.cpp:16:7:16:7 | arrayassignment.cpp:14:9:14:14 | IR only |
-| arrayassignment.cpp:18:7:18:11 | arrayassignment.cpp:14:9:14:14 | IR only |
-| arrayassignment.cpp:19:7:19:9 | arrayassignment.cpp:14:9:14:14 | IR only |
-| arrayassignment.cpp:31:7:31:7 | arrayassignment.cpp:29:8:29:13 | IR only |
-| arrayassignment.cpp:32:7:32:10 | arrayassignment.cpp:29:8:29:13 | IR only |
-| arrayassignment.cpp:34:7:34:10 | arrayassignment.cpp:29:8:29:13 | IR only |
-| arrayassignment.cpp:56:7:56:8 | arrayassignment.cpp:54:9:54:14 | IR only |
-| arrayassignment.cpp:57:10:57:12 | arrayassignment.cpp:54:9:54:14 | AST only |
-| arrayassignment.cpp:57:10:57:15 | arrayassignment.cpp:54:9:54:14 | IR only |
-| arrayassignment.cpp:66:7:66:8 | arrayassignment.cpp:64:13:64:18 | IR only |
-| arrayassignment.cpp:67:10:67:12 | arrayassignment.cpp:64:13:64:18 | AST only |
-| arrayassignment.cpp:67:10:67:15 | arrayassignment.cpp:64:13:64:18 | IR only |
-| arrayassignment.cpp:136:7:136:13 | arrayassignment.cpp:134:9:134:14 | IR only |
-| arrayassignment.cpp:141:7:141:13 | arrayassignment.cpp:139:10:139:15 | IR only |
-| arrayassignment.cpp:146:7:146:13 | arrayassignment.cpp:144:12:144:17 | IR only |
-| copyableclass.cpp:67:11:67:11 | copyableclass.cpp:67:13:67:18 | AST only |
-| copyableclass.cpp:67:11:67:21 | copyableclass.cpp:67:13:67:18 | IR only |
-| copyableclass_declonly.cpp:67:11:67:11 | copyableclass_declonly.cpp:67:13:67:18 | AST only |
-| map.cpp:49:9:49:13 | map.cpp:48:37:48:42 | IR only |
-| map.cpp:54:9:54:13 | map.cpp:48:37:48:42 | IR only |
-| map.cpp:60:9:60:13 | map.cpp:48:37:48:42 | IR only |
-| map.cpp:70:9:70:13 | map.cpp:65:37:65:42 | IR only |
-| map.cpp:71:9:71:14 | map.cpp:65:37:65:42 | IR only |
-| map.cpp:73:9:73:13 | map.cpp:65:37:65:42 | IR only |
-| map.cpp:76:9:76:13 | map.cpp:66:37:66:42 | IR only |
-| map.cpp:79:9:79:13 | map.cpp:66:37:66:42 | IR only |
-| map.cpp:80:9:80:14 | map.cpp:66:37:66:42 | IR only |
-| map.cpp:90:34:90:38 | map.cpp:90:24:90:29 | IR only |
-| map.cpp:108:7:108:54 | map.cpp:108:39:108:44 | IR only |
-| map.cpp:111:7:111:48 | map.cpp:111:34:111:39 | IR only |
-| map.cpp:155:12:155:16 | map.cpp:108:39:108:44 | IR only |
-| map.cpp:156:12:156:17 | map.cpp:108:39:108:44 | IR only |
-| map.cpp:161:12:161:16 | map.cpp:108:39:108:44 | IR only |
-| map.cpp:162:12:162:17 | map.cpp:108:39:108:44 | IR only |
-| map.cpp:172:10:172:10 | map.cpp:168:20:168:25 | AST only |
-| map.cpp:174:10:174:10 | map.cpp:170:23:170:28 | AST only |
-| map.cpp:184:7:184:31 | map.cpp:108:39:108:44 | IR only |
-| map.cpp:185:7:185:32 | map.cpp:108:39:108:44 | IR only |
-| map.cpp:187:7:187:32 | map.cpp:108:39:108:44 | IR only |
-| map.cpp:235:7:235:40 | map.cpp:235:26:235:31 | IR only |
-| map.cpp:246:7:246:44 | map.cpp:246:30:246:35 | IR only |
-| map.cpp:260:7:260:54 | map.cpp:260:39:260:44 | IR only |
-| map.cpp:263:7:263:48 | map.cpp:263:34:263:39 | IR only |
-| map.cpp:307:12:307:16 | map.cpp:260:39:260:44 | IR only |
-| map.cpp:308:12:308:17 | map.cpp:260:39:260:44 | IR only |
-| map.cpp:313:12:313:16 | map.cpp:260:39:260:44 | IR only |
-| map.cpp:314:12:314:17 | map.cpp:260:39:260:44 | IR only |
-| map.cpp:324:10:324:10 | map.cpp:320:20:320:25 | AST only |
-| map.cpp:326:10:326:10 | map.cpp:322:23:322:28 | AST only |
-| map.cpp:334:7:334:31 | map.cpp:260:39:260:44 | IR only |
-| map.cpp:335:7:335:32 | map.cpp:260:39:260:44 | IR only |
-| map.cpp:336:7:336:32 | map.cpp:260:39:260:44 | IR only |
-| map.cpp:384:7:384:40 | map.cpp:384:26:384:31 | IR only |
-| map.cpp:396:7:396:44 | map.cpp:396:30:396:35 | IR only |
-| map.cpp:397:40:397:45 | map.cpp:396:30:396:35 | IR only |
-| map.cpp:397:40:397:45 | map.cpp:397:30:397:35 | IR only |
-| map.cpp:418:7:418:16 | map.cpp:416:30:416:35 | AST only |
-| map.cpp:421:7:421:16 | map.cpp:419:33:419:38 | AST only |
-| map.cpp:431:7:431:67 | map.cpp:431:52:431:57 | IR only |
-| movableclass.cpp:65:11:65:11 | movableclass.cpp:65:13:65:18 | AST only |
-| movableclass.cpp:65:11:65:21 | movableclass.cpp:65:13:65:18 | IR only |
-| set.cpp:20:7:20:31 | set.cpp:20:17:20:22 | IR only |
-| set.cpp:61:8:61:11 | set.cpp:20:17:20:22 | IR only |
-| set.cpp:71:7:71:32 | set.cpp:67:13:67:18 | IR only |
-| set.cpp:72:7:72:33 | set.cpp:67:13:67:18 | IR only |
-| set.cpp:120:7:120:33 | set.cpp:120:19:120:24 | IR only |
-| set.cpp:134:7:134:31 | set.cpp:134:17:134:22 | IR only |
-| set.cpp:175:8:175:11 | set.cpp:134:17:134:22 | IR only |
-| set.cpp:183:7:183:32 | set.cpp:181:13:181:18 | IR only |
-| set.cpp:184:7:184:33 | set.cpp:181:13:181:18 | IR only |
-| set.cpp:232:7:232:33 | set.cpp:232:19:232:24 | IR only |
-| smart_pointer.cpp:12:10:12:10 | smart_pointer.cpp:11:52:11:57 | AST only |
-| smart_pointer.cpp:24:10:24:10 | smart_pointer.cpp:23:52:23:57 | AST only |
-| standalone_iterators.cpp:41:10:41:10 | standalone_iterators.cpp:39:45:39:51 | AST only |
-| standalone_iterators.cpp:42:10:42:10 | standalone_iterators.cpp:39:45:39:51 | AST only |
-| standalone_iterators.cpp:47:10:47:10 | standalone_iterators.cpp:45:39:45:45 | AST only |
-| standalone_iterators.cpp:48:10:48:10 | standalone_iterators.cpp:45:39:45:45 | AST only |
-| standalone_iterators.cpp:87:10:87:11 | standalone_iterators.cpp:86:13:86:18 | AST only |
-| string.cpp:33:9:33:13 | string.cpp:27:16:27:21 | AST only |
-| string.cpp:39:13:39:17 | string.cpp:14:10:14:15 | AST only |
-| string.cpp:43:13:43:17 | string.cpp:14:10:14:15 | AST only |
-| string.cpp:46:13:46:17 | string.cpp:14:10:14:15 | AST only |
-| string.cpp:70:7:70:8 | string.cpp:62:19:62:24 | AST only |
-| string.cpp:126:8:126:11 | string.cpp:120:16:120:21 | IR only |
-| string.cpp:145:8:145:14 | string.cpp:142:18:142:23 | IR only |
-| string.cpp:146:8:146:14 | string.cpp:142:18:142:23 | IR only |
-| string.cpp:147:8:147:14 | string.cpp:142:18:142:23 | IR only |
-| string.cpp:150:8:150:20 | string.cpp:150:13:150:18 | IR only |
-| string.cpp:162:11:162:11 | string.cpp:155:18:155:23 | AST only |
-| string.cpp:166:11:166:11 | string.cpp:166:14:166:19 | AST only |
-| string.cpp:167:11:167:11 | string.cpp:166:14:166:19 | AST only |
-| string.cpp:199:10:199:15 | string.cpp:191:17:191:22 | AST only |
-| string.cpp:202:10:202:15 | string.cpp:192:11:192:25 | AST only |
-| string.cpp:220:10:220:15 | string.cpp:211:17:211:22 | AST only |
-| string.cpp:224:10:224:15 | string.cpp:211:17:211:22 | AST only |
-| string.cpp:228:10:228:15 | string.cpp:212:11:212:25 | AST only |
-| string.cpp:243:10:243:16 | string.cpp:234:17:234:22 | AST only |
-| string.cpp:247:10:247:16 | string.cpp:234:17:234:22 | AST only |
-| string.cpp:251:10:251:16 | string.cpp:235:11:235:25 | AST only |
-| string.cpp:312:9:312:12 | string.cpp:309:16:309:21 | AST only |
-| string.cpp:323:7:323:29 | string.cpp:320:16:320:21 | IR only |
-| string.cpp:340:7:340:7 | string.cpp:336:9:336:23 | AST only |
-| string.cpp:341:7:341:7 | string.cpp:337:12:337:26 | AST only |
-| string.cpp:342:7:342:7 | string.cpp:336:9:336:23 | AST only |
-| string.cpp:350:7:350:9 | string.cpp:349:18:349:32 | AST only |
-| string.cpp:351:11:351:14 | string.cpp:349:18:349:32 | AST only |
-| string.cpp:363:11:363:16 | string.cpp:358:18:358:23 | AST only |
-| string.cpp:382:8:382:14 | string.cpp:374:18:374:23 | IR only |
-| string.cpp:383:13:383:15 | string.cpp:374:18:374:23 | IR only |
-| string.cpp:396:8:396:15 | string.cpp:389:18:389:23 | IR only |
-| string.cpp:397:8:397:15 | string.cpp:389:18:389:23 | IR only |
-| string.cpp:399:8:399:8 | string.cpp:389:18:389:23 | AST only |
-| string.cpp:401:8:401:8 | string.cpp:389:18:389:23 | AST only |
-| string.cpp:404:8:404:11 | string.cpp:389:18:389:23 | IR only |
-| string.cpp:407:8:407:11 | string.cpp:389:18:389:23 | IR only |
-| string.cpp:409:8:409:8 | string.cpp:389:18:389:23 | AST only |
-| string.cpp:411:8:411:8 | string.cpp:389:18:389:23 | AST only |
-| string.cpp:415:8:415:11 | string.cpp:389:18:389:23 | IR only |
-| string.cpp:418:8:418:8 | string.cpp:389:18:389:23 | AST only |
-| string.cpp:421:8:421:8 | string.cpp:389:18:389:23 | AST only |
-| string.cpp:436:10:436:15 | string.cpp:431:14:431:19 | AST only |
-| string.cpp:449:10:449:15 | string.cpp:449:32:449:46 | AST only |
-| string.cpp:462:10:462:15 | string.cpp:457:18:457:23 | AST only |
-| string.cpp:465:11:465:16 | string.cpp:457:18:457:23 | AST only |
-| string.cpp:478:10:478:15 | string.cpp:473:18:473:23 | AST only |
-| string.cpp:481:11:481:16 | string.cpp:473:18:473:23 | AST only |
-| string.cpp:494:10:494:15 | string.cpp:489:18:489:23 | AST only |
-| string.cpp:522:9:522:13 | string.cpp:521:14:521:28 | AST only |
-| string.cpp:523:9:523:12 | string.cpp:521:14:521:28 | AST only |
-| string.cpp:536:11:536:11 | string.cpp:536:20:536:25 | AST only |
-| string.cpp:537:21:537:21 | string.cpp:537:24:537:29 | AST only |
-| string.cpp:538:25:538:25 | string.cpp:538:15:538:20 | AST only |
-| string.cpp:541:8:541:8 | string.cpp:536:20:536:25 | AST only |
-| string.cpp:543:8:543:8 | string.cpp:537:24:537:29 | AST only |
-| string.cpp:556:11:556:16 | string.cpp:556:27:556:32 | AST only |
-| string.cpp:557:24:557:29 | string.cpp:557:31:557:36 | AST only |
-| string.cpp:561:8:561:8 | string.cpp:556:27:556:32 | AST only |
-| string.cpp:563:8:563:8 | string.cpp:557:31:557:36 | AST only |
-| stringstream.cpp:32:11:32:22 | stringstream.cpp:32:14:32:19 | IR only |
-| stringstream.cpp:33:20:33:31 | stringstream.cpp:33:23:33:28 | IR only |
-| stringstream.cpp:34:23:34:31 | stringstream.cpp:34:14:34:19 | IR only |
-| stringstream.cpp:35:11:35:11 | stringstream.cpp:29:16:29:21 | AST only |
-| stringstream.cpp:39:7:39:9 | stringstream.cpp:33:23:33:28 | AST only |
-| stringstream.cpp:41:7:41:9 | stringstream.cpp:29:16:29:21 | AST only |
-| stringstream.cpp:43:7:43:15 | stringstream.cpp:32:14:32:19 | IR only |
-| stringstream.cpp:44:11:44:13 | stringstream.cpp:33:23:33:28 | AST only |
-| stringstream.cpp:45:7:45:15 | stringstream.cpp:34:14:34:19 | IR only |
-| stringstream.cpp:46:11:46:13 | stringstream.cpp:29:16:29:21 | AST only |
-| stringstream.cpp:56:11:56:13 | stringstream.cpp:56:15:56:29 | AST only |
-| stringstream.cpp:57:44:57:46 | stringstream.cpp:57:25:57:39 | AST only |
-| stringstream.cpp:60:7:60:10 | stringstream.cpp:57:25:57:39 | AST only |
-| stringstream.cpp:63:12:63:16 | stringstream.cpp:63:18:63:23 | AST only |
-| stringstream.cpp:64:54:64:58 | stringstream.cpp:64:36:64:41 | AST only |
-| stringstream.cpp:67:7:67:10 | stringstream.cpp:64:36:64:41 | AST only |
-| stringstream.cpp:76:11:76:11 | stringstream.cpp:70:32:70:37 | AST only |
-| stringstream.cpp:78:11:78:11 | stringstream.cpp:70:32:70:37 | AST only |
-| stringstream.cpp:83:7:83:15 | stringstream.cpp:70:32:70:37 | IR only |
-| stringstream.cpp:100:11:100:11 | stringstream.cpp:100:31:100:36 | AST only |
-| stringstream.cpp:143:11:143:22 | stringstream.cpp:143:14:143:19 | IR only |
-| stringstream.cpp:146:11:146:11 | stringstream.cpp:143:14:143:19 | AST only |
-| stringstream.cpp:147:17:147:17 | stringstream.cpp:143:14:143:19 | AST only |
-| stringstream.cpp:151:7:151:8 | stringstream.cpp:143:14:143:19 | AST only |
-| stringstream.cpp:154:11:154:11 | stringstream.cpp:143:14:143:19 | AST only |
-| stringstream.cpp:155:17:155:17 | stringstream.cpp:143:14:143:19 | AST only |
-| stringstream.cpp:159:7:159:8 | stringstream.cpp:143:14:143:19 | AST only |
-| stringstream.cpp:162:11:162:14 | stringstream.cpp:143:14:143:19 | AST only |
-| stringstream.cpp:166:11:166:13 | stringstream.cpp:143:14:143:19 | AST only |
-| stringstream.cpp:179:11:179:13 | stringstream.cpp:143:14:143:19 | AST only |
-| stringstream.cpp:196:10:196:16 | stringstream.cpp:196:18:196:32 | AST only |
-| stringstream.cpp:215:11:215:17 | stringstream.cpp:203:24:203:29 | AST only |
-| stringstream.cpp:216:11:216:17 | stringstream.cpp:203:24:203:29 | AST only |
-| stringstream.cpp:223:11:223:17 | stringstream.cpp:203:24:203:29 | AST only |
-| stringstream.cpp:224:11:224:17 | stringstream.cpp:203:24:203:29 | AST only |
-| stringstream.cpp:230:29:230:35 | stringstream.cpp:203:24:203:29 | AST only |
-| stringstream.cpp:232:7:232:8 | stringstream.cpp:203:24:203:29 | AST only |
-| stringstream.cpp:235:7:235:13 | stringstream.cpp:203:24:203:29 | AST only |
-| stringstream.cpp:236:7:236:13 | stringstream.cpp:203:24:203:29 | AST only |
-| stringstream.cpp:243:7:243:13 | stringstream.cpp:203:24:203:29 | AST only |
-| stringstream.cpp:244:7:244:13 | stringstream.cpp:203:24:203:29 | AST only |
-| stringstream.cpp:250:7:250:13 | stringstream.cpp:203:24:203:29 | AST only |
-| stringstream.cpp:252:7:252:8 | stringstream.cpp:203:24:203:29 | AST only |
-| stringstream.cpp:262:32:262:34 | stringstream.cpp:257:24:257:29 | AST only |
-| stringstream.cpp:264:7:264:8 | stringstream.cpp:257:24:257:29 | AST only |
-| stringstream.cpp:266:62:266:66 | stringstream.cpp:266:41:266:46 | AST only |
-| stringstream.cpp:267:7:267:9 | stringstream.cpp:266:41:266:46 | AST only |
-| swap1.cpp:78:12:78:16 | swap1.cpp:69:23:69:23 | AST only |
-| swap1.cpp:88:13:88:17 | swap1.cpp:81:27:81:28 | AST only |
-| swap1.cpp:102:12:102:16 | swap1.cpp:93:23:93:23 | AST only |
-| swap1.cpp:115:18:115:22 | swap1.cpp:108:23:108:31 | AST only |
-| swap1.cpp:129:12:129:16 | swap1.cpp:120:23:120:23 | AST only |
-| swap1.cpp:144:12:144:16 | swap1.cpp:135:23:135:23 | AST only |
-| swap2.cpp:78:12:78:16 | swap2.cpp:69:23:69:23 | AST only |
-| swap2.cpp:87:13:87:17 | swap2.cpp:82:16:82:21 | IR only |
-| swap2.cpp:88:13:88:17 | swap2.cpp:81:27:81:28 | AST only |
-| swap2.cpp:102:12:102:16 | swap2.cpp:93:23:93:23 | AST only |
-| swap2.cpp:115:18:115:22 | swap2.cpp:108:23:108:31 | AST only |
-| swap2.cpp:129:12:129:16 | swap2.cpp:120:23:120:23 | AST only |
-| swap2.cpp:144:12:144:16 | swap2.cpp:135:23:135:23 | AST only |
-| taint.cpp:41:7:41:13 | taint.cpp:35:12:35:17 | AST only |
-| taint.cpp:42:7:42:13 | taint.cpp:35:12:35:17 | AST only |
-| taint.cpp:43:7:43:13 | taint.cpp:37:22:37:27 | AST only |
-| taint.cpp:137:7:137:9 | taint.cpp:120:11:120:16 | AST only |
-| taint.cpp:195:7:195:7 | taint.cpp:193:6:193:6 | AST only |
-| taint.cpp:236:3:236:6 | taint.cpp:223:10:223:15 | AST only |
-| taint.cpp:372:7:372:7 | taint.cpp:365:24:365:29 | AST only |
-| taint.cpp:374:7:374:7 | taint.cpp:365:24:365:29 | AST only |
-| taint.cpp:391:7:391:7 | taint.cpp:385:27:385:32 | AST only |
-| taint.cpp:429:7:429:7 | taint.cpp:428:13:428:18 | IR only |
-| taint.cpp:431:9:431:17 | taint.cpp:428:13:428:18 | IR only |
-| taint.cpp:447:9:447:17 | taint.cpp:445:14:445:28 | AST only |
-| vector.cpp:24:8:24:11 | vector.cpp:16:43:16:49 | IR only |
-| vector.cpp:52:7:52:8 | vector.cpp:51:10:51:15 | AST only |
-| vector.cpp:53:9:53:9 | vector.cpp:51:10:51:15 | AST only |
-| vector.cpp:54:9:54:9 | vector.cpp:51:10:51:15 | AST only |
-| vector.cpp:55:9:55:9 | vector.cpp:51:10:51:15 | AST only |
-| vector.cpp:58:7:58:8 | vector.cpp:51:10:51:15 | AST only |
-| vector.cpp:59:9:59:9 | vector.cpp:51:10:51:15 | AST only |
-| vector.cpp:60:9:60:9 | vector.cpp:51:10:51:15 | AST only |
-| vector.cpp:61:9:61:9 | vector.cpp:51:10:51:15 | AST only |
-| vector.cpp:64:7:64:8 | vector.cpp:63:10:63:15 | AST only |
-| vector.cpp:65:9:65:9 | vector.cpp:63:10:63:15 | AST only |
-| vector.cpp:66:9:66:9 | vector.cpp:63:10:63:15 | AST only |
-| vector.cpp:67:9:67:9 | vector.cpp:63:10:63:15 | AST only |
-| vector.cpp:71:10:71:14 | vector.cpp:69:15:69:20 | AST only |
-| vector.cpp:72:10:72:13 | vector.cpp:69:15:69:20 | AST only |
-| vector.cpp:75:7:75:8 | vector.cpp:74:17:74:22 | AST only |
-| vector.cpp:76:7:76:18 | vector.cpp:74:17:74:22 | AST only |
-| vector.cpp:84:10:84:14 | vector.cpp:81:17:81:22 | AST only |
-| vector.cpp:85:10:85:13 | vector.cpp:81:17:81:22 | AST only |
-| vector.cpp:97:7:97:8 | vector.cpp:96:13:96:18 | AST only |
-| vector.cpp:98:10:98:11 | vector.cpp:96:13:96:18 | AST only |
-| vector.cpp:99:10:99:11 | vector.cpp:96:13:96:18 | AST only |
-| vector.cpp:100:10:100:11 | vector.cpp:96:13:96:18 | AST only |
-| vector.cpp:171:13:171:13 | vector.cpp:170:14:170:19 | AST only |
-| vector.cpp:180:13:180:13 | vector.cpp:179:14:179:19 | AST only |
-| vector.cpp:201:13:201:13 | vector.cpp:200:14:200:19 | AST only |
-| vector.cpp:286:10:286:13 | vector.cpp:284:15:284:20 | AST only |
-| vector.cpp:287:7:287:18 | vector.cpp:284:15:284:20 | AST only |
-| vector.cpp:290:7:290:8 | vector.cpp:289:17:289:30 | AST only |
-| vector.cpp:291:10:291:13 | vector.cpp:289:17:289:30 | AST only |
-| vector.cpp:292:7:292:18 | vector.cpp:289:17:289:30 | AST only |
-| vector.cpp:308:9:308:14 | vector.cpp:303:14:303:19 | AST only |
-| vector.cpp:311:9:311:14 | vector.cpp:303:14:303:19 | AST only |
-| vector.cpp:342:7:342:8 | vector.cpp:341:8:341:13 | AST only |
-| vector.cpp:347:7:347:8 | vector.cpp:345:9:345:14 | AST only |
-| vector.cpp:357:7:357:8 | vector.cpp:330:10:330:15 | AST only |
-| vector.cpp:361:7:361:8 | vector.cpp:360:8:360:13 | AST only |
-| vector.cpp:363:7:363:8 | vector.cpp:360:8:360:13 | AST only |
-| vector.cpp:367:7:367:8 | vector.cpp:366:8:366:13 | AST only |
-| vector.cpp:369:7:369:8 | vector.cpp:366:8:366:13 | AST only |
-| vector.cpp:374:8:374:9 | vector.cpp:373:9:373:14 | AST only |
-| vector.cpp:379:7:379:8 | vector.cpp:373:9:373:14 | AST only |
-| vector.cpp:383:7:383:8 | vector.cpp:382:8:382:13 | AST only |
-| vector.cpp:385:7:385:8 | vector.cpp:382:8:382:13 | AST only |
-| vector.cpp:392:7:392:8 | vector.cpp:330:10:330:15 | AST only |
-| vector.cpp:392:7:392:8 | vector.cpp:389:8:389:13 | AST only |
-| vector.cpp:400:7:400:9 | vector.cpp:399:38:399:43 | AST only |
-| vector.cpp:405:7:405:9 | vector.cpp:404:9:404:14 | AST only |
-| vector.cpp:409:7:409:9 | vector.cpp:408:11:408:16 | AST only |
-| vector.cpp:414:7:414:9 | vector.cpp:413:11:413:16 | AST only |
-| vector.cpp:422:8:422:10 | vector.cpp:417:33:417:45 | AST only |
-| vector.cpp:429:8:429:10 | vector.cpp:417:33:417:45 | AST only |
-| vector.cpp:436:8:436:10 | vector.cpp:435:11:435:16 | AST only |
-| vector.cpp:443:8:443:10 | vector.cpp:417:33:417:45 | AST only |
-| vector.cpp:450:8:450:10 | vector.cpp:449:11:449:16 | AST only |
-| vector.cpp:473:8:473:8 | vector.cpp:468:11:468:16 | AST only |
-| vector.cpp:486:8:486:9 | vector.cpp:478:21:478:37 | AST only |
-| vector.cpp:494:7:494:8 | vector.cpp:493:18:493:23 | AST only |
-| vector.cpp:497:7:497:8 | vector.cpp:496:25:496:30 | AST only |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected
deleted file mode 100644
index e84ce0d4c2e..00000000000
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected
+++ /dev/null
@@ -1,580 +0,0 @@
-| arrayassignment.cpp:16:7:16:7 | x | arrayassignment.cpp:14:9:14:14 | call to source |
-| arrayassignment.cpp:17:7:17:10 | * ... | arrayassignment.cpp:14:9:14:14 | call to source |
-| arrayassignment.cpp:18:7:18:11 | * ... | arrayassignment.cpp:14:9:14:14 | call to source |
-| arrayassignment.cpp:19:7:19:9 | (reference dereference) | arrayassignment.cpp:14:9:14:14 | call to source |
-| arrayassignment.cpp:31:7:31:7 | x | arrayassignment.cpp:29:8:29:13 | call to source |
-| arrayassignment.cpp:32:7:32:10 | * ... | arrayassignment.cpp:29:8:29:13 | call to source |
-| arrayassignment.cpp:33:7:33:9 | (reference dereference) | arrayassignment.cpp:29:8:29:13 | call to source |
-| arrayassignment.cpp:34:7:34:10 | (reference dereference) | arrayassignment.cpp:29:8:29:13 | call to source |
-| arrayassignment.cpp:56:7:56:8 | mi | arrayassignment.cpp:54:9:54:14 | call to source |
-| arrayassignment.cpp:57:10:57:15 | (reference dereference) | arrayassignment.cpp:54:9:54:14 | call to source |
-| arrayassignment.cpp:66:7:66:8 | mi | arrayassignment.cpp:64:13:64:18 | call to source |
-| arrayassignment.cpp:67:10:67:15 | (reference dereference) | arrayassignment.cpp:64:13:64:18 | call to source |
-| arrayassignment.cpp:101:7:101:18 | access to array | arrayassignment.cpp:99:17:99:22 | call to source |
-| arrayassignment.cpp:135:7:135:10 | (reference dereference) | arrayassignment.cpp:134:9:134:14 | call to source |
-| arrayassignment.cpp:136:7:136:13 | access to array | arrayassignment.cpp:134:9:134:14 | call to source |
-| arrayassignment.cpp:140:7:140:11 | * ... | arrayassignment.cpp:139:10:139:15 | call to source |
-| arrayassignment.cpp:141:7:141:13 | access to array | arrayassignment.cpp:139:10:139:15 | call to source |
-| arrayassignment.cpp:145:7:145:13 | access to array | arrayassignment.cpp:144:12:144:17 | call to source |
-| arrayassignment.cpp:146:7:146:13 | access to array | arrayassignment.cpp:144:12:144:17 | call to source |
-| copyableclass.cpp:40:8:40:9 | s1 | copyableclass.cpp:34:22:34:27 | call to source |
-| copyableclass.cpp:41:8:41:9 | s2 | copyableclass.cpp:35:24:35:29 | call to source |
-| copyableclass.cpp:42:8:42:9 | s3 | copyableclass.cpp:34:22:34:27 | call to source |
-| copyableclass.cpp:43:8:43:9 | s4 | copyableclass.cpp:38:8:38:13 | call to source |
-| copyableclass.cpp:65:8:65:9 | s1 | copyableclass.cpp:60:40:60:45 | call to source |
-| copyableclass.cpp:66:8:66:9 | s2 | copyableclass.cpp:63:24:63:29 | call to source |
-| copyableclass.cpp:67:11:67:21 | (reference dereference) | copyableclass.cpp:67:13:67:18 | call to source |
-| copyableclass_declonly.cpp:40:8:40:9 | s1 | copyableclass_declonly.cpp:34:30:34:35 | call to source |
-| copyableclass_declonly.cpp:41:8:41:9 | s2 | copyableclass_declonly.cpp:35:32:35:37 | call to source |
-| copyableclass_declonly.cpp:42:8:42:9 | s3 | copyableclass_declonly.cpp:34:30:34:35 | call to source |
-| copyableclass_declonly.cpp:43:8:43:9 | s4 | copyableclass_declonly.cpp:38:8:38:13 | call to source |
-| copyableclass_declonly.cpp:65:8:65:9 | s1 | copyableclass_declonly.cpp:60:56:60:61 | call to source |
-| copyableclass_declonly.cpp:66:8:66:9 | s2 | copyableclass_declonly.cpp:63:32:63:37 | call to source |
-| format.cpp:57:8:57:13 | Argument 0 indirection | format.cpp:56:36:56:49 | call to source |
-| format.cpp:62:8:62:13 | Argument 0 indirection | format.cpp:61:30:61:43 | call to source |
-| format.cpp:67:8:67:13 | Argument 0 indirection | format.cpp:66:52:66:65 | call to source |
-| format.cpp:72:8:72:13 | Argument 0 indirection | format.cpp:71:42:71:55 | call to source |
-| format.cpp:83:8:83:13 | Argument 0 indirection | format.cpp:82:36:82:41 | call to source |
-| format.cpp:88:8:88:13 | Argument 0 indirection | format.cpp:87:38:87:43 | call to source |
-| format.cpp:94:8:94:13 | Argument 0 indirection | format.cpp:93:36:93:49 | call to source |
-| format.cpp:100:8:100:13 | Argument 0 indirection | format.cpp:99:30:99:43 | call to source |
-| format.cpp:105:8:105:13 | Argument 0 indirection | format.cpp:104:31:104:45 | call to source |
-| format.cpp:110:8:110:14 | Argument 0 indirection | format.cpp:109:38:109:52 | call to source |
-| format.cpp:115:8:115:13 | Argument 0 indirection | format.cpp:114:37:114:50 | call to source |
-| format.cpp:157:7:157:22 | access to array | format.cpp:147:12:147:25 | call to source |
-| format.cpp:158:7:158:27 | ... + ... | format.cpp:148:16:148:30 | call to source |
-| map.cpp:29:9:29:13 | first | map.cpp:28:12:28:17 | call to source |
-| map.cpp:35:9:35:14 | second | map.cpp:33:13:33:18 | call to source |
-| map.cpp:44:9:44:13 | first | map.cpp:43:30:43:35 | call to source |
-| map.cpp:49:9:49:13 | first | map.cpp:48:37:48:42 | call to source |
-| map.cpp:50:9:50:14 | second | map.cpp:48:37:48:42 | call to source |
-| map.cpp:51:7:51:7 | f | map.cpp:48:37:48:42 | call to source |
-| map.cpp:54:9:54:13 | first | map.cpp:48:37:48:42 | call to source |
-| map.cpp:55:9:55:14 | second | map.cpp:48:37:48:42 | call to source |
-| map.cpp:56:7:56:7 | g | map.cpp:48:37:48:42 | call to source |
-| map.cpp:60:9:60:13 | first | map.cpp:48:37:48:42 | call to source |
-| map.cpp:61:9:61:14 | second | map.cpp:48:37:48:42 | call to source |
-| map.cpp:62:7:62:7 | h | map.cpp:48:37:48:42 | call to source |
-| map.cpp:70:9:70:13 | first | map.cpp:65:37:65:42 | call to source |
-| map.cpp:71:9:71:14 | second | map.cpp:65:37:65:42 | call to source |
-| map.cpp:72:7:72:7 | i | map.cpp:65:37:65:42 | call to source |
-| map.cpp:73:9:73:13 | first | map.cpp:65:37:65:42 | call to source |
-| map.cpp:74:9:74:14 | second | map.cpp:65:37:65:42 | call to source |
-| map.cpp:75:7:75:7 | j | map.cpp:65:37:65:42 | call to source |
-| map.cpp:76:9:76:13 | first | map.cpp:66:37:66:42 | call to source |
-| map.cpp:77:9:77:14 | second | map.cpp:66:37:66:42 | call to source |
-| map.cpp:78:7:78:7 | k | map.cpp:66:37:66:42 | call to source |
-| map.cpp:79:9:79:13 | first | map.cpp:66:37:66:42 | call to source |
-| map.cpp:80:9:80:14 | second | map.cpp:66:37:66:42 | call to source |
-| map.cpp:81:7:81:7 | l | map.cpp:66:37:66:42 | call to source |
-| map.cpp:87:34:87:38 | first | map.cpp:87:17:87:22 | call to source |
-| map.cpp:89:7:89:32 | call to pair | map.cpp:89:24:89:29 | call to source |
-| map.cpp:90:34:90:38 | first | map.cpp:90:24:90:29 | call to source |
-| map.cpp:91:34:91:39 | second | map.cpp:91:24:91:29 | call to source |
-| map.cpp:108:7:108:54 | call to iterator | map.cpp:108:39:108:44 | call to source |
-| map.cpp:110:10:110:15 | call to insert | map.cpp:110:62:110:67 | call to source |
-| map.cpp:111:7:111:48 | call to iterator | map.cpp:111:34:111:39 | call to source |
-| map.cpp:112:10:112:25 | call to insert_or_assign | map.cpp:112:46:112:51 | call to source |
-| map.cpp:114:7:114:8 | call to map | map.cpp:108:39:108:44 | call to source |
-| map.cpp:116:7:116:8 | call to map | map.cpp:110:62:110:67 | call to source |
-| map.cpp:117:7:117:8 | call to map | map.cpp:111:34:111:39 | call to source |
-| map.cpp:118:7:118:8 | call to map | map.cpp:112:46:112:51 | call to source |
-| map.cpp:120:10:120:13 | call to find | map.cpp:108:39:108:44 | call to source |
-| map.cpp:122:10:122:13 | call to find | map.cpp:110:62:110:67 | call to source |
-| map.cpp:123:10:123:13 | call to find | map.cpp:111:34:111:39 | call to source |
-| map.cpp:124:10:124:13 | call to find | map.cpp:112:46:112:51 | call to source |
-| map.cpp:126:10:126:13 | call to find | map.cpp:108:39:108:44 | call to source |
-| map.cpp:128:10:128:13 | call to find | map.cpp:110:62:110:67 | call to source |
-| map.cpp:129:10:129:13 | call to find | map.cpp:111:34:111:39 | call to source |
-| map.cpp:130:10:130:13 | call to find | map.cpp:112:46:112:51 | call to source |
-| map.cpp:137:7:137:8 | call to map | map.cpp:108:39:108:44 | call to source |
-| map.cpp:138:7:138:8 | call to map | map.cpp:108:39:108:44 | call to source |
-| map.cpp:139:7:139:8 | call to map | map.cpp:108:39:108:44 | call to source |
-| map.cpp:140:10:140:13 | call to find | map.cpp:108:39:108:44 | call to source |
-| map.cpp:141:10:141:13 | call to find | map.cpp:108:39:108:44 | call to source |
-| map.cpp:142:10:142:13 | call to find | map.cpp:108:39:108:44 | call to source |
-| map.cpp:154:8:154:10 | call to pair | map.cpp:108:39:108:44 | call to source |
-| map.cpp:155:12:155:16 | first | map.cpp:108:39:108:44 | call to source |
-| map.cpp:156:12:156:17 | second | map.cpp:108:39:108:44 | call to source |
-| map.cpp:161:12:161:16 | first | map.cpp:108:39:108:44 | call to source |
-| map.cpp:162:12:162:17 | second | map.cpp:108:39:108:44 | call to source |
-| map.cpp:168:7:168:27 | ... = ... | map.cpp:168:20:168:25 | call to source |
-| map.cpp:170:7:170:30 | ... = ... | map.cpp:170:23:170:28 | call to source |
-| map.cpp:182:10:182:20 | call to lower_bound | map.cpp:108:39:108:44 | call to source |
-| map.cpp:183:10:183:20 | call to upper_bound | map.cpp:108:39:108:44 | call to source |
-| map.cpp:184:7:184:31 | call to iterator | map.cpp:108:39:108:44 | call to source |
-| map.cpp:185:7:185:32 | call to iterator | map.cpp:108:39:108:44 | call to source |
-| map.cpp:186:10:186:20 | call to upper_bound | map.cpp:108:39:108:44 | call to source |
-| map.cpp:187:7:187:32 | call to iterator | map.cpp:108:39:108:44 | call to source |
-| map.cpp:193:7:193:9 | call to map | map.cpp:191:49:191:54 | call to source |
-| map.cpp:196:7:196:9 | call to map | map.cpp:192:49:192:54 | call to source |
-| map.cpp:199:7:199:9 | call to map | map.cpp:191:49:191:54 | call to source |
-| map.cpp:200:7:200:9 | call to map | map.cpp:191:49:191:54 | call to source |
-| map.cpp:201:7:201:9 | call to map | map.cpp:192:49:192:54 | call to source |
-| map.cpp:202:7:202:9 | call to map | map.cpp:192:49:192:54 | call to source |
-| map.cpp:210:7:210:9 | call to map | map.cpp:206:49:206:54 | call to source |
-| map.cpp:213:7:213:9 | call to map | map.cpp:209:49:209:54 | call to source |
-| map.cpp:216:7:216:9 | call to map | map.cpp:206:49:206:54 | call to source |
-| map.cpp:218:7:218:9 | call to map | map.cpp:209:49:209:54 | call to source |
-| map.cpp:219:7:219:9 | call to map | map.cpp:209:49:209:54 | call to source |
-| map.cpp:225:7:225:9 | call to map | map.cpp:223:49:223:54 | call to source |
-| map.cpp:225:7:225:9 | call to map | map.cpp:224:49:224:54 | call to source |
-| map.cpp:226:11:226:15 | call to erase | map.cpp:223:49:223:54 | call to source |
-| map.cpp:226:11:226:15 | call to erase | map.cpp:224:49:224:54 | call to source |
-| map.cpp:227:7:227:9 | call to map | map.cpp:223:49:223:54 | call to source |
-| map.cpp:227:7:227:9 | call to map | map.cpp:224:49:224:54 | call to source |
-| map.cpp:229:7:229:9 | call to map | map.cpp:223:49:223:54 | call to source |
-| map.cpp:229:7:229:9 | call to map | map.cpp:224:49:224:54 | call to source |
-| map.cpp:235:7:235:40 | call to iterator | map.cpp:235:26:235:31 | call to source |
-| map.cpp:236:7:236:9 | call to map | map.cpp:235:26:235:31 | call to source |
-| map.cpp:239:11:239:22 | call to emplace_hint | map.cpp:239:44:239:49 | call to source |
-| map.cpp:240:7:240:9 | call to map | map.cpp:239:44:239:49 | call to source |
-| map.cpp:246:7:246:44 | call to iterator | map.cpp:246:30:246:35 | call to source |
-| map.cpp:247:7:247:9 | call to map | map.cpp:246:30:246:35 | call to source |
-| map.cpp:250:11:250:21 | call to try_emplace | map.cpp:250:43:250:48 | call to source |
-| map.cpp:251:7:251:9 | call to map | map.cpp:250:43:250:48 | call to source |
-| map.cpp:260:7:260:54 | call to iterator | map.cpp:260:39:260:44 | call to source |
-| map.cpp:262:10:262:15 | call to insert | map.cpp:262:62:262:67 | call to source |
-| map.cpp:263:7:263:48 | call to iterator | map.cpp:263:34:263:39 | call to source |
-| map.cpp:264:10:264:25 | call to insert_or_assign | map.cpp:264:46:264:51 | call to source |
-| map.cpp:266:7:266:8 | call to unordered_map | map.cpp:260:39:260:44 | call to source |
-| map.cpp:268:7:268:8 | call to unordered_map | map.cpp:262:62:262:67 | call to source |
-| map.cpp:269:7:269:8 | call to unordered_map | map.cpp:263:34:263:39 | call to source |
-| map.cpp:270:7:270:8 | call to unordered_map | map.cpp:264:46:264:51 | call to source |
-| map.cpp:272:10:272:13 | call to find | map.cpp:260:39:260:44 | call to source |
-| map.cpp:274:10:274:13 | call to find | map.cpp:262:62:262:67 | call to source |
-| map.cpp:275:10:275:13 | call to find | map.cpp:263:34:263:39 | call to source |
-| map.cpp:276:10:276:13 | call to find | map.cpp:264:46:264:51 | call to source |
-| map.cpp:278:10:278:13 | call to find | map.cpp:260:39:260:44 | call to source |
-| map.cpp:280:10:280:13 | call to find | map.cpp:262:62:262:67 | call to source |
-| map.cpp:281:10:281:13 | call to find | map.cpp:263:34:263:39 | call to source |
-| map.cpp:282:10:282:13 | call to find | map.cpp:264:46:264:51 | call to source |
-| map.cpp:289:7:289:8 | call to unordered_map | map.cpp:260:39:260:44 | call to source |
-| map.cpp:290:7:290:8 | call to unordered_map | map.cpp:260:39:260:44 | call to source |
-| map.cpp:291:7:291:8 | call to unordered_map | map.cpp:260:39:260:44 | call to source |
-| map.cpp:292:10:292:13 | call to find | map.cpp:260:39:260:44 | call to source |
-| map.cpp:293:10:293:13 | call to find | map.cpp:260:39:260:44 | call to source |
-| map.cpp:294:10:294:13 | call to find | map.cpp:260:39:260:44 | call to source |
-| map.cpp:306:8:306:10 | call to pair | map.cpp:260:39:260:44 | call to source |
-| map.cpp:307:12:307:16 | first | map.cpp:260:39:260:44 | call to source |
-| map.cpp:308:12:308:17 | second | map.cpp:260:39:260:44 | call to source |
-| map.cpp:313:12:313:16 | first | map.cpp:260:39:260:44 | call to source |
-| map.cpp:314:12:314:17 | second | map.cpp:260:39:260:44 | call to source |
-| map.cpp:320:7:320:27 | ... = ... | map.cpp:320:20:320:25 | call to source |
-| map.cpp:322:7:322:30 | ... = ... | map.cpp:322:23:322:28 | call to source |
-| map.cpp:334:7:334:31 | call to iterator | map.cpp:260:39:260:44 | call to source |
-| map.cpp:335:7:335:32 | call to iterator | map.cpp:260:39:260:44 | call to source |
-| map.cpp:336:7:336:32 | call to iterator | map.cpp:260:39:260:44 | call to source |
-| map.cpp:342:7:342:9 | call to unordered_map | map.cpp:340:49:340:54 | call to source |
-| map.cpp:345:7:345:9 | call to unordered_map | map.cpp:341:49:341:54 | call to source |
-| map.cpp:348:7:348:9 | call to unordered_map | map.cpp:340:49:340:54 | call to source |
-| map.cpp:349:7:349:9 | call to unordered_map | map.cpp:340:49:340:54 | call to source |
-| map.cpp:350:7:350:9 | call to unordered_map | map.cpp:341:49:341:54 | call to source |
-| map.cpp:351:7:351:9 | call to unordered_map | map.cpp:341:49:341:54 | call to source |
-| map.cpp:359:7:359:9 | call to unordered_map | map.cpp:355:49:355:54 | call to source |
-| map.cpp:362:7:362:9 | call to unordered_map | map.cpp:358:49:358:54 | call to source |
-| map.cpp:365:7:365:9 | call to unordered_map | map.cpp:355:49:355:54 | call to source |
-| map.cpp:367:7:367:9 | call to unordered_map | map.cpp:358:49:358:54 | call to source |
-| map.cpp:368:7:368:9 | call to unordered_map | map.cpp:358:49:358:54 | call to source |
-| map.cpp:374:7:374:9 | call to unordered_map | map.cpp:372:49:372:54 | call to source |
-| map.cpp:374:7:374:9 | call to unordered_map | map.cpp:373:49:373:54 | call to source |
-| map.cpp:375:11:375:15 | call to erase | map.cpp:372:49:372:54 | call to source |
-| map.cpp:375:11:375:15 | call to erase | map.cpp:373:49:373:54 | call to source |
-| map.cpp:376:7:376:9 | call to unordered_map | map.cpp:372:49:372:54 | call to source |
-| map.cpp:376:7:376:9 | call to unordered_map | map.cpp:373:49:373:54 | call to source |
-| map.cpp:378:7:378:9 | call to unordered_map | map.cpp:372:49:372:54 | call to source |
-| map.cpp:378:7:378:9 | call to unordered_map | map.cpp:373:49:373:54 | call to source |
-| map.cpp:384:7:384:40 | call to iterator | map.cpp:384:26:384:31 | call to source |
-| map.cpp:385:7:385:9 | call to unordered_map | map.cpp:384:26:384:31 | call to source |
-| map.cpp:388:11:388:22 | call to emplace_hint | map.cpp:388:44:388:49 | call to source |
-| map.cpp:389:7:389:9 | call to unordered_map | map.cpp:388:44:388:49 | call to source |
-| map.cpp:396:7:396:44 | call to iterator | map.cpp:396:30:396:35 | call to source |
-| map.cpp:397:40:397:45 | second | map.cpp:396:30:396:35 | call to source |
-| map.cpp:397:40:397:45 | second | map.cpp:397:30:397:35 | call to source |
-| map.cpp:398:7:398:9 | call to unordered_map | map.cpp:396:30:396:35 | call to source |
-| map.cpp:398:7:398:9 | call to unordered_map | map.cpp:397:30:397:35 | call to source |
-| map.cpp:401:11:401:21 | call to try_emplace | map.cpp:401:43:401:48 | call to source |
-| map.cpp:402:7:402:9 | call to unordered_map | map.cpp:401:43:401:48 | call to source |
-| map.cpp:416:7:416:41 | call to pair | map.cpp:416:30:416:35 | call to source |
-| map.cpp:417:7:417:9 | call to unordered_map | map.cpp:416:30:416:35 | call to source |
-| map.cpp:419:7:419:41 | call to pair | map.cpp:419:33:419:38 | call to source |
-| map.cpp:420:7:420:9 | call to unordered_map | map.cpp:419:33:419:38 | call to source |
-| map.cpp:431:7:431:67 | call to iterator | map.cpp:431:52:431:57 | call to source |
-| map.cpp:432:7:432:9 | call to unordered_map | map.cpp:431:52:431:57 | call to source |
-| map.cpp:433:11:433:22 | call to emplace_hint | map.cpp:431:52:431:57 | call to source |
-| movableclass.cpp:44:8:44:9 | s1 | movableclass.cpp:39:21:39:26 | call to source |
-| movableclass.cpp:45:8:45:9 | s2 | movableclass.cpp:40:23:40:28 | call to source |
-| movableclass.cpp:46:8:46:9 | s3 | movableclass.cpp:42:8:42:13 | call to source |
-| movableclass.cpp:54:8:54:9 | s1 | movableclass.cpp:50:38:50:43 | call to source |
-| movableclass.cpp:55:8:55:9 | s2 | movableclass.cpp:52:23:52:28 | call to source |
-| movableclass.cpp:64:8:64:9 | s2 | movableclass.cpp:23:55:23:60 | call to source |
-| movableclass.cpp:65:11:65:21 | (reference dereference) | movableclass.cpp:65:13:65:18 | call to source |
-| set.cpp:20:7:20:31 | call to iterator | set.cpp:20:17:20:22 | call to source |
-| set.cpp:22:10:22:15 | call to insert | set.cpp:22:29:22:34 | call to source |
-| set.cpp:26:7:26:8 | call to set | set.cpp:20:17:20:22 | call to source |
-| set.cpp:28:7:28:8 | call to set | set.cpp:22:29:22:34 | call to source |
-| set.cpp:30:7:30:8 | call to set | set.cpp:20:17:20:22 | call to source |
-| set.cpp:32:10:32:13 | call to find | set.cpp:20:17:20:22 | call to source |
-| set.cpp:34:10:34:13 | call to find | set.cpp:22:29:22:34 | call to source |
-| set.cpp:36:10:36:13 | call to find | set.cpp:20:17:20:22 | call to source |
-| set.cpp:44:7:44:8 | call to set | set.cpp:20:17:20:22 | call to source |
-| set.cpp:45:7:45:8 | call to set | set.cpp:20:17:20:22 | call to source |
-| set.cpp:46:7:46:8 | call to set | set.cpp:20:17:20:22 | call to source |
-| set.cpp:47:7:47:9 | call to set | set.cpp:20:17:20:22 | call to source |
-| set.cpp:48:10:48:13 | call to find | set.cpp:20:17:20:22 | call to source |
-| set.cpp:49:10:49:13 | call to find | set.cpp:20:17:20:22 | call to source |
-| set.cpp:50:10:50:13 | call to find | set.cpp:20:17:20:22 | call to source |
-| set.cpp:51:11:51:14 | call to find | set.cpp:20:17:20:22 | call to source |
-| set.cpp:61:8:61:8 | call to operator* | set.cpp:20:17:20:22 | call to source |
-| set.cpp:61:8:61:11 | (reference dereference) | set.cpp:20:17:20:22 | call to source |
-| set.cpp:69:11:69:21 | call to lower_bound | set.cpp:67:13:67:18 | call to source |
-| set.cpp:70:11:70:21 | call to upper_bound | set.cpp:67:13:67:18 | call to source |
-| set.cpp:71:7:71:32 | call to iterator | set.cpp:67:13:67:18 | call to source |
-| set.cpp:72:7:72:33 | call to iterator | set.cpp:67:13:67:18 | call to source |
-| set.cpp:78:7:78:9 | call to set | set.cpp:76:13:76:18 | call to source |
-| set.cpp:81:7:81:9 | call to set | set.cpp:77:13:77:18 | call to source |
-| set.cpp:84:7:84:9 | call to set | set.cpp:76:13:76:18 | call to source |
-| set.cpp:85:7:85:9 | call to set | set.cpp:76:13:76:18 | call to source |
-| set.cpp:86:7:86:9 | call to set | set.cpp:77:13:77:18 | call to source |
-| set.cpp:87:7:87:9 | call to set | set.cpp:77:13:77:18 | call to source |
-| set.cpp:95:7:95:9 | call to set | set.cpp:91:13:91:18 | call to source |
-| set.cpp:98:7:98:9 | call to set | set.cpp:94:13:94:18 | call to source |
-| set.cpp:101:7:101:9 | call to set | set.cpp:91:13:91:18 | call to source |
-| set.cpp:103:7:103:9 | call to set | set.cpp:94:13:94:18 | call to source |
-| set.cpp:104:7:104:9 | call to set | set.cpp:94:13:94:18 | call to source |
-| set.cpp:110:7:110:9 | call to set | set.cpp:108:13:108:18 | call to source |
-| set.cpp:110:7:110:9 | call to set | set.cpp:109:13:109:18 | call to source |
-| set.cpp:111:11:111:15 | call to erase | set.cpp:108:13:108:18 | call to source |
-| set.cpp:111:11:111:15 | call to erase | set.cpp:109:13:109:18 | call to source |
-| set.cpp:112:7:112:9 | call to set | set.cpp:108:13:108:18 | call to source |
-| set.cpp:112:7:112:9 | call to set | set.cpp:109:13:109:18 | call to source |
-| set.cpp:114:7:114:9 | call to set | set.cpp:108:13:108:18 | call to source |
-| set.cpp:114:7:114:9 | call to set | set.cpp:109:13:109:18 | call to source |
-| set.cpp:120:7:120:33 | call to iterator | set.cpp:120:19:120:24 | call to source |
-| set.cpp:121:7:121:9 | call to set | set.cpp:120:19:120:24 | call to source |
-| set.cpp:124:11:124:22 | call to emplace_hint | set.cpp:124:37:124:42 | call to source |
-| set.cpp:125:7:125:9 | call to set | set.cpp:124:37:124:42 | call to source |
-| set.cpp:134:7:134:31 | call to iterator | set.cpp:134:17:134:22 | call to source |
-| set.cpp:136:10:136:15 | call to insert | set.cpp:136:29:136:34 | call to source |
-| set.cpp:140:7:140:8 | call to unordered_set | set.cpp:134:17:134:22 | call to source |
-| set.cpp:142:7:142:8 | call to unordered_set | set.cpp:136:29:136:34 | call to source |
-| set.cpp:144:7:144:8 | call to unordered_set | set.cpp:134:17:134:22 | call to source |
-| set.cpp:146:10:146:13 | call to find | set.cpp:134:17:134:22 | call to source |
-| set.cpp:148:10:148:13 | call to find | set.cpp:136:29:136:34 | call to source |
-| set.cpp:150:10:150:13 | call to find | set.cpp:134:17:134:22 | call to source |
-| set.cpp:158:7:158:8 | call to unordered_set | set.cpp:134:17:134:22 | call to source |
-| set.cpp:159:7:159:8 | call to unordered_set | set.cpp:134:17:134:22 | call to source |
-| set.cpp:160:7:160:8 | call to unordered_set | set.cpp:134:17:134:22 | call to source |
-| set.cpp:161:7:161:9 | call to unordered_set | set.cpp:134:17:134:22 | call to source |
-| set.cpp:162:10:162:13 | call to find | set.cpp:134:17:134:22 | call to source |
-| set.cpp:163:10:163:13 | call to find | set.cpp:134:17:134:22 | call to source |
-| set.cpp:164:10:164:13 | call to find | set.cpp:134:17:134:22 | call to source |
-| set.cpp:165:11:165:14 | call to find | set.cpp:134:17:134:22 | call to source |
-| set.cpp:175:8:175:8 | call to operator* | set.cpp:134:17:134:22 | call to source |
-| set.cpp:175:8:175:11 | (reference dereference) | set.cpp:134:17:134:22 | call to source |
-| set.cpp:183:7:183:32 | call to iterator | set.cpp:181:13:181:18 | call to source |
-| set.cpp:184:7:184:33 | call to iterator | set.cpp:181:13:181:18 | call to source |
-| set.cpp:190:7:190:9 | call to unordered_set | set.cpp:188:13:188:18 | call to source |
-| set.cpp:193:7:193:9 | call to unordered_set | set.cpp:189:13:189:18 | call to source |
-| set.cpp:196:7:196:9 | call to unordered_set | set.cpp:188:13:188:18 | call to source |
-| set.cpp:197:7:197:9 | call to unordered_set | set.cpp:188:13:188:18 | call to source |
-| set.cpp:198:7:198:9 | call to unordered_set | set.cpp:189:13:189:18 | call to source |
-| set.cpp:199:7:199:9 | call to unordered_set | set.cpp:189:13:189:18 | call to source |
-| set.cpp:207:7:207:9 | call to unordered_set | set.cpp:203:13:203:18 | call to source |
-| set.cpp:210:7:210:9 | call to unordered_set | set.cpp:206:13:206:18 | call to source |
-| set.cpp:213:7:213:9 | call to unordered_set | set.cpp:203:13:203:18 | call to source |
-| set.cpp:215:7:215:9 | call to unordered_set | set.cpp:206:13:206:18 | call to source |
-| set.cpp:216:7:216:9 | call to unordered_set | set.cpp:206:13:206:18 | call to source |
-| set.cpp:222:7:222:9 | call to unordered_set | set.cpp:220:13:220:18 | call to source |
-| set.cpp:222:7:222:9 | call to unordered_set | set.cpp:221:13:221:18 | call to source |
-| set.cpp:223:11:223:15 | call to erase | set.cpp:220:13:220:18 | call to source |
-| set.cpp:223:11:223:15 | call to erase | set.cpp:221:13:221:18 | call to source |
-| set.cpp:224:7:224:9 | call to unordered_set | set.cpp:220:13:220:18 | call to source |
-| set.cpp:224:7:224:9 | call to unordered_set | set.cpp:221:13:221:18 | call to source |
-| set.cpp:226:7:226:9 | call to unordered_set | set.cpp:220:13:220:18 | call to source |
-| set.cpp:226:7:226:9 | call to unordered_set | set.cpp:221:13:221:18 | call to source |
-| set.cpp:232:7:232:33 | call to iterator | set.cpp:232:19:232:24 | call to source |
-| set.cpp:233:7:233:9 | call to unordered_set | set.cpp:232:19:232:24 | call to source |
-| set.cpp:236:11:236:22 | call to emplace_hint | set.cpp:236:37:236:42 | call to source |
-| set.cpp:237:7:237:9 | call to unordered_set | set.cpp:236:37:236:42 | call to source |
-| smart_pointer.cpp:13:10:13:10 | Argument 0 indirection | smart_pointer.cpp:11:52:11:57 | call to source |
-| smart_pointer.cpp:25:10:25:10 | Argument 0 indirection | smart_pointer.cpp:23:52:23:57 | call to source |
-| smart_pointer.cpp:52:12:52:14 | call to get | smart_pointer.cpp:51:52:51:57 | call to source |
-| smart_pointer.cpp:57:12:57:14 | call to get | smart_pointer.cpp:56:52:56:57 | call to source |
-| standalone_iterators.cpp:40:10:40:10 | call to operator* | standalone_iterators.cpp:39:45:39:51 | source1 |
-| standalone_iterators.cpp:46:10:46:10 | call to operator* | standalone_iterators.cpp:45:39:45:45 | source1 |
-| string.cpp:29:7:29:7 | a | string.cpp:25:12:25:17 | call to source |
-| string.cpp:31:7:31:7 | Argument 0 indirection | string.cpp:27:16:27:21 | call to source |
-| string.cpp:56:7:56:8 | cs | string.cpp:51:19:51:24 | call to source |
-| string.cpp:57:7:57:8 | Argument 0 indirection | string.cpp:51:19:51:24 | call to source |
-| string.cpp:71:7:71:8 | Argument 0 indirection | string.cpp:62:19:62:24 | call to source |
-| string.cpp:93:8:93:9 | Argument 0 indirection | string.cpp:88:18:88:23 | call to source |
-| string.cpp:94:8:94:9 | Argument 0 indirection | string.cpp:89:20:89:25 | call to source |
-| string.cpp:95:8:95:9 | Argument 0 indirection | string.cpp:91:8:91:13 | call to source |
-| string.cpp:114:8:114:9 | Argument 0 indirection | string.cpp:110:32:110:37 | call to source |
-| string.cpp:115:8:115:9 | Argument 0 indirection | string.cpp:112:20:112:25 | call to source |
-| string.cpp:122:8:122:8 | c | string.cpp:120:16:120:21 | call to source |
-| string.cpp:126:8:126:8 | call to operator* | string.cpp:120:16:120:21 | call to source |
-| string.cpp:126:8:126:11 | (reference dereference) | string.cpp:120:16:120:21 | call to source |
-| string.cpp:130:8:130:8 | (reference dereference) | string.cpp:120:16:120:21 | call to source |
-| string.cpp:130:8:130:8 | c | string.cpp:120:16:120:21 | call to source |
-| string.cpp:135:8:135:8 | (reference dereference) | string.cpp:133:28:133:33 | call to source |
-| string.cpp:135:8:135:8 | c | string.cpp:133:28:133:33 | call to source |
-| string.cpp:145:8:145:14 | Argument 0 indirection | string.cpp:142:18:142:23 | call to source |
-| string.cpp:145:11:145:11 | call to operator+ | string.cpp:142:18:142:23 | call to source |
-| string.cpp:146:8:146:14 | Argument 0 indirection | string.cpp:142:18:142:23 | call to source |
-| string.cpp:146:11:146:11 | call to operator+ | string.cpp:142:18:142:23 | call to source |
-| string.cpp:147:8:147:14 | Argument 0 indirection | string.cpp:142:18:142:23 | call to source |
-| string.cpp:147:11:147:11 | call to operator+ | string.cpp:142:18:142:23 | call to source |
-| string.cpp:150:8:150:20 | Argument 0 indirection | string.cpp:150:13:150:18 | call to source |
-| string.cpp:150:11:150:11 | call to operator+ | string.cpp:150:13:150:18 | call to source |
-| string.cpp:159:8:159:9 | Argument 0 indirection | string.cpp:155:18:155:23 | call to source |
-| string.cpp:163:8:163:9 | Argument 0 indirection | string.cpp:155:18:155:23 | call to source |
-| string.cpp:168:8:168:9 | Argument 0 indirection | string.cpp:166:14:166:19 | call to source |
-| string.cpp:172:8:172:9 | Argument 0 indirection | string.cpp:155:18:155:23 | call to source |
-| string.cpp:177:8:177:9 | Argument 0 indirection | string.cpp:175:13:175:18 | call to source |
-| string.cpp:185:8:185:10 | Argument 0 indirection | string.cpp:182:12:182:26 | call to source |
-| string.cpp:200:7:200:8 | Argument 0 indirection | string.cpp:191:17:191:22 | call to source |
-| string.cpp:203:7:203:8 | Argument 0 indirection | string.cpp:192:11:192:25 | call to source |
-| string.cpp:206:7:206:8 | Argument 0 indirection | string.cpp:194:17:194:22 | call to source |
-| string.cpp:221:7:221:8 | Argument 0 indirection | string.cpp:211:17:211:22 | call to source |
-| string.cpp:225:7:225:8 | Argument 0 indirection | string.cpp:211:17:211:22 | call to source |
-| string.cpp:229:7:229:8 | Argument 0 indirection | string.cpp:212:11:212:25 | call to source |
-| string.cpp:244:7:244:8 | Argument 0 indirection | string.cpp:234:17:234:22 | call to source |
-| string.cpp:248:7:248:8 | Argument 0 indirection | string.cpp:234:17:234:22 | call to source |
-| string.cpp:252:7:252:8 | Argument 0 indirection | string.cpp:235:11:235:25 | call to source |
-| string.cpp:265:7:265:8 | Argument 0 indirection | string.cpp:259:17:259:22 | call to source |
-| string.cpp:275:7:275:8 | Argument 0 indirection | string.cpp:270:17:270:22 | call to source |
-| string.cpp:277:7:277:8 | Argument 0 indirection | string.cpp:272:17:272:22 | call to source |
-| string.cpp:282:7:282:8 | Argument 0 indirection | string.cpp:270:17:270:22 | call to source |
-| string.cpp:283:7:283:8 | Argument 0 indirection | string.cpp:270:17:270:22 | call to source |
-| string.cpp:284:7:284:8 | Argument 0 indirection | string.cpp:272:17:272:22 | call to source |
-| string.cpp:285:7:285:8 | Argument 0 indirection | string.cpp:272:17:272:22 | call to source |
-| string.cpp:293:7:293:8 | Argument 0 indirection | string.cpp:289:17:289:22 | call to source |
-| string.cpp:294:7:294:8 | Argument 0 indirection | string.cpp:290:17:290:22 | call to source |
-| string.cpp:295:7:295:8 | Argument 0 indirection | string.cpp:291:17:291:22 | call to source |
-| string.cpp:301:7:301:8 | Argument 0 indirection | string.cpp:289:17:289:22 | call to source |
-| string.cpp:303:7:303:8 | Argument 0 indirection | string.cpp:291:17:291:22 | call to source |
-| string.cpp:323:7:323:29 | Argument 0 indirection | string.cpp:320:16:320:21 | call to source |
-| string.cpp:323:9:323:14 | call to substr | string.cpp:320:16:320:21 | call to source |
-| string.cpp:364:8:364:9 | Argument 0 indirection | string.cpp:358:18:358:23 | call to source |
-| string.cpp:382:8:382:8 | call to operator* | string.cpp:374:18:374:23 | call to source |
-| string.cpp:382:8:382:14 | (reference dereference) | string.cpp:374:18:374:23 | call to source |
-| string.cpp:383:13:383:13 | call to operator[] | string.cpp:374:18:374:23 | call to source |
-| string.cpp:383:13:383:15 | (reference dereference) | string.cpp:374:18:374:23 | call to source |
-| string.cpp:396:8:396:8 | call to operator* | string.cpp:389:18:389:23 | call to source |
-| string.cpp:396:8:396:15 | (reference dereference) | string.cpp:389:18:389:23 | call to source |
-| string.cpp:397:8:397:8 | call to operator* | string.cpp:389:18:389:23 | call to source |
-| string.cpp:397:8:397:15 | (reference dereference) | string.cpp:389:18:389:23 | call to source |
-| string.cpp:404:8:404:8 | call to operator* | string.cpp:389:18:389:23 | call to source |
-| string.cpp:404:8:404:11 | (reference dereference) | string.cpp:389:18:389:23 | call to source |
-| string.cpp:407:8:407:8 | call to operator* | string.cpp:389:18:389:23 | call to source |
-| string.cpp:407:8:407:11 | (reference dereference) | string.cpp:389:18:389:23 | call to source |
-| string.cpp:415:8:415:8 | call to operator* | string.cpp:389:18:389:23 | call to source |
-| string.cpp:415:8:415:11 | (reference dereference) | string.cpp:389:18:389:23 | call to source |
-| string.cpp:419:8:419:10 | call to iterator | string.cpp:389:18:389:23 | call to source |
-| string.cpp:422:8:422:10 | call to iterator | string.cpp:389:18:389:23 | call to source |
-| string.cpp:437:7:437:8 | Argument 0 indirection | string.cpp:431:14:431:19 | call to source |
-| string.cpp:450:8:450:8 | Argument 0 indirection | string.cpp:449:32:449:46 | call to source |
-| string.cpp:463:8:463:8 | Argument 0 indirection | string.cpp:457:18:457:23 | call to source |
-| string.cpp:466:8:466:9 | Argument 0 indirection | string.cpp:457:18:457:23 | call to source |
-| string.cpp:479:8:479:8 | Argument 0 indirection | string.cpp:473:18:473:23 | call to source |
-| string.cpp:482:8:482:9 | Argument 0 indirection | string.cpp:473:18:473:23 | call to source |
-| string.cpp:495:8:495:8 | Argument 0 indirection | string.cpp:489:18:489:23 | call to source |
-| string.cpp:498:8:498:9 | Argument 0 indirection | string.cpp:489:18:489:23 | call to source |
-| string.cpp:511:7:511:8 | Argument 0 indirection | string.cpp:504:14:504:19 | call to source |
-| string.cpp:513:7:513:8 | Argument 0 indirection | string.cpp:504:14:504:19 | call to source |
-| string.cpp:542:8:542:8 | Argument 0 indirection | string.cpp:536:20:536:25 | call to source |
-| string.cpp:544:8:544:8 | Argument 0 indirection | string.cpp:538:15:538:20 | call to source |
-| string.cpp:562:8:562:8 | Argument 0 indirection | string.cpp:556:27:556:32 | call to source |
-| string.cpp:564:8:564:8 | Argument 0 indirection | string.cpp:558:18:558:23 | call to source |
-| stringstream.cpp:32:11:32:11 | call to operator<< | stringstream.cpp:32:14:32:19 | call to source |
-| stringstream.cpp:32:11:32:22 | (reference dereference) | stringstream.cpp:32:14:32:19 | call to source |
-| stringstream.cpp:33:20:33:20 | call to operator<< | stringstream.cpp:33:23:33:28 | call to source |
-| stringstream.cpp:33:20:33:31 | (reference dereference) | stringstream.cpp:33:23:33:28 | call to source |
-| stringstream.cpp:34:23:34:23 | call to operator<< | stringstream.cpp:34:14:34:19 | call to source |
-| stringstream.cpp:34:23:34:31 | (reference dereference) | stringstream.cpp:34:14:34:19 | call to source |
-| stringstream.cpp:38:7:38:9 | Argument 0 indirection | stringstream.cpp:32:14:32:19 | call to source |
-| stringstream.cpp:40:7:40:9 | Argument 0 indirection | stringstream.cpp:34:14:34:19 | call to source |
-| stringstream.cpp:43:7:43:15 | Argument 0 indirection | stringstream.cpp:32:14:32:19 | call to source |
-| stringstream.cpp:43:11:43:13 | call to str | stringstream.cpp:32:14:32:19 | call to source |
-| stringstream.cpp:45:7:45:15 | Argument 0 indirection | stringstream.cpp:34:14:34:19 | call to source |
-| stringstream.cpp:45:11:45:13 | call to str | stringstream.cpp:34:14:34:19 | call to source |
-| stringstream.cpp:52:7:52:9 | Argument 0 indirection | stringstream.cpp:49:10:49:15 | call to source |
-| stringstream.cpp:53:7:53:9 | Argument 0 indirection | stringstream.cpp:50:10:50:15 | call to source |
-| stringstream.cpp:59:7:59:9 | Argument 0 indirection | stringstream.cpp:56:15:56:29 | call to source |
-| stringstream.cpp:66:7:66:10 | Argument 0 indirection | stringstream.cpp:63:18:63:23 | call to source |
-| stringstream.cpp:81:7:81:9 | Argument 0 indirection | stringstream.cpp:70:32:70:37 | source |
-| stringstream.cpp:83:7:83:15 | Argument 0 indirection | stringstream.cpp:70:32:70:37 | source |
-| stringstream.cpp:83:11:83:13 | call to str | stringstream.cpp:70:32:70:37 | source |
-| stringstream.cpp:85:7:85:8 | v2 | stringstream.cpp:70:32:70:37 | source |
-| stringstream.cpp:103:7:103:9 | Argument 0 indirection | stringstream.cpp:91:19:91:24 | call to source |
-| stringstream.cpp:105:7:105:9 | Argument 0 indirection | stringstream.cpp:95:44:95:49 | call to source |
-| stringstream.cpp:107:7:107:9 | Argument 0 indirection | stringstream.cpp:100:31:100:36 | call to source |
-| stringstream.cpp:120:7:120:9 | Argument 0 indirection | stringstream.cpp:113:24:113:29 | call to source |
-| stringstream.cpp:121:7:121:9 | Argument 0 indirection | stringstream.cpp:113:24:113:29 | call to source |
-| stringstream.cpp:122:7:122:9 | Argument 0 indirection | stringstream.cpp:115:24:115:29 | call to source |
-| stringstream.cpp:123:7:123:9 | Argument 0 indirection | stringstream.cpp:115:24:115:29 | call to source |
-| stringstream.cpp:143:11:143:11 | call to operator<< | stringstream.cpp:143:14:143:19 | call to source |
-| stringstream.cpp:143:11:143:22 | (reference dereference) | stringstream.cpp:143:14:143:19 | call to source |
-| stringstream.cpp:149:7:149:8 | Argument 0 indirection | stringstream.cpp:143:14:143:19 | call to source |
-| stringstream.cpp:150:7:150:8 | Argument 0 indirection | stringstream.cpp:143:14:143:19 | call to source |
-| stringstream.cpp:157:7:157:8 | Argument 0 indirection | stringstream.cpp:143:14:143:19 | call to source |
-| stringstream.cpp:157:7:157:8 | call to basic_string | stringstream.cpp:143:14:143:19 | call to source |
-| stringstream.cpp:158:7:158:8 | Argument 0 indirection | stringstream.cpp:143:14:143:19 | call to source |
-| stringstream.cpp:158:7:158:8 | call to basic_string | stringstream.cpp:143:14:143:19 | call to source |
-| stringstream.cpp:168:7:168:8 | Argument 0 indirection | stringstream.cpp:143:14:143:19 | call to source |
-| stringstream.cpp:168:7:168:8 | call to basic_string | stringstream.cpp:143:14:143:19 | call to source |
-| stringstream.cpp:170:7:170:8 | Argument 0 indirection | stringstream.cpp:143:14:143:19 | call to source |
-| stringstream.cpp:170:7:170:8 | call to basic_string | stringstream.cpp:143:14:143:19 | call to source |
-| stringstream.cpp:172:7:172:9 | Argument 0 indirection | stringstream.cpp:143:14:143:19 | call to source |
-| stringstream.cpp:172:7:172:9 | call to basic_string | stringstream.cpp:143:14:143:19 | call to source |
-| stringstream.cpp:175:7:175:20 | ... = ... | stringstream.cpp:143:14:143:19 | call to source |
-| stringstream.cpp:177:7:177:21 | ... = ... | stringstream.cpp:143:14:143:19 | call to source |
-| stringstream.cpp:181:7:181:8 | c2 | stringstream.cpp:143:14:143:19 | call to source |
-| stringstream.cpp:183:7:183:8 | c4 | stringstream.cpp:143:14:143:19 | call to source |
-| stringstream.cpp:185:7:185:8 | c6 | stringstream.cpp:143:14:143:19 | call to source |
-| stringstream.cpp:197:10:197:12 | call to get | stringstream.cpp:196:18:196:32 | call to source |
-| stringstream.cpp:219:7:219:8 | Argument 0 indirection | stringstream.cpp:203:24:203:29 | call to source |
-| stringstream.cpp:219:7:219:8 | call to basic_string | stringstream.cpp:203:24:203:29 | call to source |
-| stringstream.cpp:220:7:220:8 | Argument 0 indirection | stringstream.cpp:203:24:203:29 | call to source |
-| stringstream.cpp:220:7:220:8 | call to basic_string | stringstream.cpp:203:24:203:29 | call to source |
-| stringstream.cpp:227:7:227:8 | Argument 0 indirection | stringstream.cpp:203:24:203:29 | call to source |
-| stringstream.cpp:227:7:227:8 | call to basic_string | stringstream.cpp:203:24:203:29 | call to source |
-| stringstream.cpp:228:7:228:8 | Argument 0 indirection | stringstream.cpp:203:24:203:29 | call to source |
-| stringstream.cpp:228:7:228:8 | call to basic_string | stringstream.cpp:203:24:203:29 | call to source |
-| stringstream.cpp:231:7:231:8 | Argument 0 indirection | stringstream.cpp:203:24:203:29 | call to source |
-| stringstream.cpp:231:7:231:8 | call to basic_string | stringstream.cpp:203:24:203:29 | call to source |
-| stringstream.cpp:239:7:239:8 | Argument 0 indirection | stringstream.cpp:203:24:203:29 | call to source |
-| stringstream.cpp:240:7:240:8 | Argument 0 indirection | stringstream.cpp:203:24:203:29 | call to source |
-| stringstream.cpp:247:7:247:8 | Argument 0 indirection | stringstream.cpp:203:24:203:29 | call to source |
-| stringstream.cpp:248:7:248:8 | Argument 0 indirection | stringstream.cpp:203:24:203:29 | call to source |
-| stringstream.cpp:251:7:251:8 | Argument 0 indirection | stringstream.cpp:203:24:203:29 | call to source |
-| stringstream.cpp:263:7:263:8 | Argument 0 indirection | stringstream.cpp:257:24:257:29 | call to source |
-| stringstream.cpp:263:7:263:8 | call to basic_string | stringstream.cpp:257:24:257:29 | call to source |
-| structlikeclass.cpp:35:8:35:9 | s1 | structlikeclass.cpp:29:22:29:27 | call to source |
-| structlikeclass.cpp:36:8:36:9 | s2 | structlikeclass.cpp:30:24:30:29 | call to source |
-| structlikeclass.cpp:37:8:37:9 | s3 | structlikeclass.cpp:29:22:29:27 | call to source |
-| structlikeclass.cpp:38:8:38:9 | s4 | structlikeclass.cpp:33:8:33:13 | call to source |
-| structlikeclass.cpp:60:8:60:9 | s1 | structlikeclass.cpp:55:40:55:45 | call to source |
-| structlikeclass.cpp:61:8:61:9 | s2 | structlikeclass.cpp:58:24:58:29 | call to source |
-| structlikeclass.cpp:62:8:62:20 | ... = ... | structlikeclass.cpp:62:13:62:18 | call to source |
-| swap1.cpp:73:12:73:16 | data1 | swap1.cpp:71:15:71:20 | call to source |
-| swap1.cpp:78:12:78:16 | data1 | swap1.cpp:71:15:71:20 | call to source |
-| swap1.cpp:79:12:79:16 | data1 | swap1.cpp:71:15:71:20 | call to source |
-| swap1.cpp:83:13:83:17 | data1 | swap1.cpp:82:16:82:21 | call to source |
-| swap1.cpp:87:13:87:17 | data1 | swap1.cpp:82:16:82:21 | call to source |
-| swap1.cpp:88:13:88:17 | data1 | swap1.cpp:82:16:82:21 | call to source |
-| swap1.cpp:97:12:97:16 | data1 | swap1.cpp:95:15:95:20 | call to source |
-| swap1.cpp:102:12:102:16 | data1 | swap1.cpp:95:15:95:20 | call to source |
-| swap1.cpp:103:12:103:16 | data1 | swap1.cpp:95:15:95:20 | call to source |
-| swap1.cpp:111:20:111:24 | data1 | swap1.cpp:109:23:109:28 | call to source |
-| swap1.cpp:115:18:115:22 | data1 | swap1.cpp:109:23:109:28 | call to source |
-| swap1.cpp:124:12:124:16 | data1 | swap1.cpp:122:15:122:20 | call to source |
-| swap1.cpp:129:12:129:16 | data1 | swap1.cpp:122:15:122:20 | call to source |
-| swap1.cpp:130:12:130:16 | data1 | swap1.cpp:122:15:122:20 | call to source |
-| swap1.cpp:139:12:139:16 | data1 | swap1.cpp:137:15:137:20 | call to source |
-| swap1.cpp:144:12:144:16 | data1 | swap1.cpp:137:15:137:20 | call to source |
-| swap1.cpp:145:12:145:16 | data1 | swap1.cpp:137:15:137:20 | call to source |
-| swap2.cpp:73:12:73:16 | data1 | swap2.cpp:71:15:71:20 | call to source |
-| swap2.cpp:78:12:78:16 | data1 | swap2.cpp:71:15:71:20 | call to source |
-| swap2.cpp:79:12:79:16 | data1 | swap2.cpp:71:15:71:20 | call to source |
-| swap2.cpp:83:13:83:17 | data1 | swap2.cpp:82:16:82:21 | call to source |
-| swap2.cpp:87:13:87:17 | data1 | swap2.cpp:82:16:82:21 | call to source |
-| swap2.cpp:88:13:88:17 | data1 | swap2.cpp:82:16:82:21 | call to source |
-| swap2.cpp:97:12:97:16 | data1 | swap2.cpp:95:15:95:20 | call to source |
-| swap2.cpp:102:12:102:16 | data1 | swap2.cpp:95:15:95:20 | call to source |
-| swap2.cpp:103:12:103:16 | data1 | swap2.cpp:95:15:95:20 | call to source |
-| swap2.cpp:111:20:111:24 | data1 | swap2.cpp:109:23:109:28 | call to source |
-| swap2.cpp:115:18:115:22 | data1 | swap2.cpp:109:23:109:28 | call to source |
-| swap2.cpp:124:12:124:16 | data1 | swap2.cpp:122:15:122:20 | call to source |
-| swap2.cpp:129:12:129:16 | data1 | swap2.cpp:122:15:122:20 | call to source |
-| swap2.cpp:130:12:130:16 | data1 | swap2.cpp:122:15:122:20 | call to source |
-| swap2.cpp:139:12:139:16 | data1 | swap2.cpp:137:15:137:20 | call to source |
-| swap2.cpp:144:12:144:16 | data1 | swap2.cpp:137:15:137:20 | call to source |
-| swap2.cpp:145:12:145:16 | data1 | swap2.cpp:137:15:137:20 | call to source |
-| taint.cpp:8:8:8:13 | clean1 | taint.cpp:4:27:4:33 | source1 |
-| taint.cpp:16:8:16:14 | source1 | taint.cpp:12:22:12:27 | call to source |
-| taint.cpp:17:8:17:16 | ++ ... | taint.cpp:12:22:12:27 | call to source |
-| taint.cpp:89:11:89:11 | b | taint.cpp:71:22:71:27 | call to source |
-| taint.cpp:90:11:90:11 | c | taint.cpp:72:7:72:12 | call to source |
-| taint.cpp:91:11:91:11 | d | taint.cpp:77:7:77:12 | call to source |
-| taint.cpp:93:11:93:11 | b | taint.cpp:71:22:71:27 | call to source |
-| taint.cpp:94:11:94:11 | c | taint.cpp:72:7:72:12 | call to source |
-| taint.cpp:109:7:109:13 | access to array | taint.cpp:105:12:105:17 | call to source |
-| taint.cpp:110:7:110:13 | access to array | taint.cpp:105:12:105:17 | call to source |
-| taint.cpp:111:7:111:13 | access to array | taint.cpp:106:12:106:17 | call to source |
-| taint.cpp:112:7:112:13 | access to array | taint.cpp:106:12:106:17 | call to source |
-| taint.cpp:129:7:129:9 | * ... | taint.cpp:120:11:120:16 | call to source |
-| taint.cpp:130:7:130:9 | * ... | taint.cpp:127:8:127:13 | call to source |
-| taint.cpp:134:7:134:9 | * ... | taint.cpp:120:11:120:16 | call to source |
-| taint.cpp:151:7:151:12 | call to select | taint.cpp:151:20:151:25 | call to source |
-| taint.cpp:167:8:167:13 | call to source | taint.cpp:167:8:167:13 | call to source |
-| taint.cpp:168:8:168:14 | tainted | taint.cpp:164:19:164:24 | call to source |
-| taint.cpp:173:8:173:13 | Argument 0 indirection | taint.cpp:164:19:164:24 | call to source |
-| taint.cpp:181:8:181:9 | * ... | taint.cpp:185:11:185:16 | call to source |
-| taint.cpp:195:7:195:7 | x | taint.cpp:192:23:192:28 | source |
-| taint.cpp:210:7:210:7 | x | taint.cpp:207:6:207:11 | call to source |
-| taint.cpp:215:7:215:7 | x | taint.cpp:207:6:207:11 | call to source |
-| taint.cpp:216:7:216:7 | y | taint.cpp:207:6:207:11 | call to source |
-| taint.cpp:229:3:229:6 | t | taint.cpp:223:10:223:15 | call to source |
-| taint.cpp:233:8:233:8 | call to operator() | taint.cpp:223:10:223:15 | call to source |
-| taint.cpp:244:3:244:6 | t | taint.cpp:223:10:223:15 | call to source |
-| taint.cpp:250:8:250:8 | a | taint.cpp:223:10:223:15 | call to source |
-| taint.cpp:256:8:256:8 | (reference dereference) | taint.cpp:223:10:223:15 | call to source |
-| taint.cpp:261:7:261:7 | w | taint.cpp:258:7:258:12 | call to source |
-| taint.cpp:280:7:280:7 | t | taint.cpp:275:6:275:11 | call to source |
-| taint.cpp:289:7:289:7 | t | taint.cpp:275:6:275:11 | call to source |
-| taint.cpp:290:7:290:7 | x | taint.cpp:275:6:275:11 | call to source |
-| taint.cpp:291:7:291:7 | y | taint.cpp:275:6:275:11 | call to source |
-| taint.cpp:337:7:337:7 | t | taint.cpp:330:6:330:11 | call to source |
-| taint.cpp:350:7:350:7 | t | taint.cpp:330:6:330:11 | call to source |
-| taint.cpp:351:7:351:7 | a | taint.cpp:330:6:330:11 | call to source |
-| taint.cpp:352:7:352:7 | b | taint.cpp:330:6:330:11 | call to source |
-| taint.cpp:353:7:353:7 | c | taint.cpp:330:6:330:11 | call to source |
-| taint.cpp:354:7:354:7 | d | taint.cpp:330:6:330:11 | call to source |
-| taint.cpp:382:7:382:7 | a | taint.cpp:377:23:377:28 | source |
-| taint.cpp:429:7:429:7 | b | taint.cpp:428:13:428:18 | call to source |
-| taint.cpp:430:9:430:14 | member | taint.cpp:428:13:428:18 | call to source |
-| taint.cpp:465:7:465:7 | x | taint.cpp:462:6:462:11 | call to source |
-| taint.cpp:470:7:470:7 | x | taint.cpp:462:6:462:11 | call to source |
-| taint.cpp:471:7:471:7 | y | taint.cpp:462:6:462:11 | call to source |
-| taint.cpp:485:7:485:10 | line | taint.cpp:480:26:480:32 | source1 |
-| vector.cpp:20:8:20:8 | x | vector.cpp:16:43:16:49 | source1 |
-| vector.cpp:24:8:24:8 | call to operator* | vector.cpp:16:43:16:49 | source1 |
-| vector.cpp:24:8:24:11 | (reference dereference) | vector.cpp:16:43:16:49 | source1 |
-| vector.cpp:28:8:28:8 | (reference dereference) | vector.cpp:16:43:16:49 | source1 |
-| vector.cpp:28:8:28:8 | x | vector.cpp:16:43:16:49 | source1 |
-| vector.cpp:33:8:33:8 | (reference dereference) | vector.cpp:16:43:16:49 | source1 |
-| vector.cpp:33:8:33:8 | x | vector.cpp:16:43:16:49 | source1 |
-| vector.cpp:70:7:70:8 | Argument 0 indirection | vector.cpp:69:15:69:20 | call to source |
-| vector.cpp:83:7:83:8 | Argument 0 indirection | vector.cpp:81:17:81:22 | call to source |
-| vector.cpp:109:7:109:8 | Argument 0 indirection | vector.cpp:106:15:106:20 | call to source |
-| vector.cpp:112:7:112:8 | Argument 0 indirection | vector.cpp:107:15:107:20 | call to source |
-| vector.cpp:117:7:117:8 | Argument 0 indirection | vector.cpp:106:15:106:20 | call to source |
-| vector.cpp:118:7:118:8 | Argument 0 indirection | vector.cpp:106:15:106:20 | call to source |
-| vector.cpp:119:7:119:8 | Argument 0 indirection | vector.cpp:107:15:107:20 | call to source |
-| vector.cpp:120:7:120:8 | Argument 0 indirection | vector.cpp:107:15:107:20 | call to source |
-| vector.cpp:130:7:130:8 | Argument 0 indirection | vector.cpp:126:15:126:20 | call to source |
-| vector.cpp:131:7:131:8 | Argument 0 indirection | vector.cpp:127:15:127:20 | call to source |
-| vector.cpp:132:7:132:8 | Argument 0 indirection | vector.cpp:128:15:128:20 | call to source |
-| vector.cpp:139:7:139:8 | Argument 0 indirection | vector.cpp:126:15:126:20 | call to source |
-| vector.cpp:140:7:140:8 | Argument 0 indirection | vector.cpp:127:15:127:20 | call to source |
-| vector.cpp:141:7:141:8 | Argument 0 indirection | vector.cpp:128:15:128:20 | call to source |
-| vector.cpp:162:8:162:15 | access to array | vector.cpp:161:14:161:19 | call to source |
-| vector.cpp:242:7:242:8 | Argument 0 indirection | vector.cpp:238:17:238:30 | call to source |
-| vector.cpp:243:7:243:8 | Argument 0 indirection | vector.cpp:239:15:239:20 | call to source |
-| vector.cpp:258:8:258:9 | Argument 0 indirection | vector.cpp:239:15:239:20 | call to source |
-| vector.cpp:259:8:259:9 | Argument 0 indirection | vector.cpp:239:15:239:20 | call to source |
-| vector.cpp:260:8:260:9 | Argument 0 indirection | vector.cpp:239:15:239:20 | call to source |
-| vector.cpp:261:8:261:9 | Argument 0 indirection | vector.cpp:239:15:239:20 | call to source |
-| vector.cpp:273:8:273:9 | Argument 0 indirection | vector.cpp:269:18:269:31 | call to source |
-| vector.cpp:274:8:274:9 | Argument 0 indirection | vector.cpp:270:18:270:35 | call to source |
-| vector.cpp:275:8:275:9 | Argument 0 indirection | vector.cpp:271:18:271:34 | call to source |
-| vector.cpp:285:7:285:8 | Argument 0 indirection | vector.cpp:284:15:284:20 | call to source |
-| vector.cpp:309:7:309:7 | Argument 0 indirection | vector.cpp:303:14:303:19 | call to source |
-| vector.cpp:312:7:312:7 | Argument 0 indirection | vector.cpp:303:14:303:19 | call to source |
-| vector.cpp:324:7:324:8 | Argument 0 indirection | vector.cpp:318:15:318:20 | call to source |
-| vector.cpp:326:7:326:8 | Argument 0 indirection | vector.cpp:318:15:318:20 | call to source |
-| vector.cpp:482:8:482:10 | Argument 0 indirection | vector.cpp:478:21:478:37 | call to source |
-| vector.cpp:485:8:485:10 | Argument 0 indirection | vector.cpp:478:21:478:37 | call to source |
From 6ba35f4aac18f6debb29fff0355d03f32ea67fc1 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Tue, 2 Mar 2021 11:25:22 +0100
Subject: [PATCH 070/725] C++: Fix function renaming and accept test change.
---
.../annotate_sinks_only/defaulttainttracking.cpp | 4 ++--
.../DefaultTaintTracking/annotate_sinks_only/tainted.expected | 1 -
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp
index c676b590da2..47d95297e6f 100644
--- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp
+++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp
@@ -9,7 +9,7 @@
-int test() {
+int main() {
@@ -19,7 +19,7 @@ int test() {
char untainted_buf[100] = "";
char buf[100] = "VAR = ";
- sink(strcat(buf, getenv("VAR"))); // $ ast,ir
+ sink(strcat(buf, getenv("VAR"))); // $ ast MISSING: ir
sink(buf); // $ ast,ir
sink(untainted_buf); // the two buffers would be conflated if we added flow through all partial chi inputs
diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/tainted.expected b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/tainted.expected
index f1db2289108..e69de29bb2d 100644
--- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/tainted.expected
+++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/tainted.expected
@@ -1 +0,0 @@
-| defaulttainttracking.cpp:22:37:22:47 | // $ ast,ir | Missing result:ir= |
From 23d31090715ada19830c2a34cc081bb79991aba9 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Tue, 2 Mar 2021 13:40:39 +0100
Subject: [PATCH 071/725] C++: Use taintedWithPath in more tests. This is the
predicate that's currently hooked up to the new IR taint tracking library.
---
.../annotate_path_to_sink/tainted.ql | 9 ++++++++-
.../annotate_sinks_only/defaulttainttracking.cpp | 12 ++++++------
.../annotate_sinks_only/stl.cpp | 16 ++++++++--------
.../annotate_sinks_only/tainted.ql | 11 ++++++++---
4 files changed, 30 insertions(+), 18 deletions(-)
diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_path_to_sink/tainted.ql b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_path_to_sink/tainted.ql
index 89a7622c49a..27f45cf64ef 100644
--- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_path_to_sink/tainted.ql
+++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_path_to_sink/tainted.ql
@@ -6,6 +6,7 @@
import cpp
import semmle.code.cpp.security.TaintTrackingImpl as ASTTaintTracking
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking as IRDefaultTaintTracking
+import IRDefaultTaintTracking::TaintedWithPath as TaintedWithPath
import TestUtilities.InlineExpectationsTest
predicate isSink(Element sink) {
@@ -17,7 +18,13 @@ predicate isSink(Element sink) {
predicate astTaint(Expr source, Element sink) { ASTTaintTracking::tainted(source, sink) }
-predicate irTaint(Expr source, Element sink) { IRDefaultTaintTracking::tainted(source, sink) }
+class SourceConfiguration extends TaintedWithPath::TaintTrackingConfiguration {
+ override predicate isSink(Element e) { any() }
+}
+
+predicate irTaint(Expr source, Element sink) {
+ TaintedWithPath::taintedWithPath(source, sink, _, _)
+}
class IRDefaultTaintTrackingTest extends InlineExpectationsTest {
IRDefaultTaintTrackingTest() { this = "IRDefaultTaintTrackingTest" }
diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp
index 47d95297e6f..6895ef4d6bb 100644
--- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp
+++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp
@@ -13,8 +13,8 @@ int main() {
- sink(_strdup(getenv("VAR"))); // $ ir MISSING: ast
- sink(strdup(getenv("VAR"))); // $ ast,ir
+ sink(_strdup(getenv("VAR"))); // $ MISSING: ast,ir
+ sink(strdup(getenv("VAR"))); // $ ast MISSING: ir
sink(unmodeled_function(getenv("VAR"))); // clean by assumption
char untainted_buf[100] = "";
@@ -250,12 +250,12 @@ void sink(iovec);
int test_readv_and_writev(iovec* iovs) {
readv(0, iovs, 16);
sink(iovs); // $ast,ir
- sink(iovs[0]); // $ast MISSING: ir
- sink(*iovs); // $ast MISSING: ir
+ sink(iovs[0]); // $ast,ir
+ sink(*iovs); // $ast,ir
char* p = (char*)iovs[1].iov_base;
- sink(p); // $ MISSING: ast,ir
- sink(*p); // $ MISSING: ast,ir
+ sink(p); // $ ir MISSING: ast
+ sink(*p); // $ ir MISSING: ast
writev(0, iovs, 16); // $ remote
}
diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/stl.cpp b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/stl.cpp
index e2f5d3b16bd..0264f29f4af 100644
--- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/stl.cpp
+++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/stl.cpp
@@ -73,7 +73,7 @@ void test_string()
sink(b); // clean
sink(c); // $ ir MISSING: ast
sink(b.c_str()); // clean
- sink(c.c_str()); // $ MISSING: ast,ir
+ sink(c.c_str()); // $ ir MISSING: ast
}
void test_stringstream()
@@ -91,11 +91,11 @@ void test_stringstream()
sink(ss2); // $ ir MISSING: ast
sink(ss3); // $ MISSING: ast,ir
sink(ss4); // $ ir MISSING: ast
- sink(ss5); // $ ir MISSING: ast
+ sink(ss5); // $ MISSING: ast,ir
sink(ss1.str());
- sink(ss2.str()); // $ MISSING: ast,ir
+ sink(ss2.str()); // $ ir MISSING: ast
sink(ss3.str()); // $ MISSING: ast,ir
- sink(ss4.str()); // $ MISSING: ast,ir
+ sink(ss4.str()); // $ ir MISSING: ast
sink(ss5.str()); // $ MISSING: ast,ir
}
@@ -123,14 +123,14 @@ void sink(const char *filename, const char *mode);
void test_strings2()
{
string path1 = user_input();
- sink(path1.c_str(), "r"); // $ MISSING: ast,ir
+ sink(path1.c_str(), "r"); // $ ir MISSING: ast
string path2;
path2 = user_input();
- sink(path2.c_str(), "r"); // $ MISSING: ast,ir
+ sink(path2.c_str(), "r"); // $ ir MISSING: ast
string path3(user_input());
- sink(path3.c_str(), "r"); // $ MISSING: ast,ir
+ sink(path3.c_str(), "r"); // $ ir MISSING: ast
}
void test_string3()
@@ -154,6 +154,6 @@ void test_string4()
// convert back std::string -> char *
cs = ss.c_str();
- sink(cs); // $ ast MISSING: ir
+ sink(cs); // $ ast,ir
sink(ss); // $ ir MISSING: ast
}
diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/tainted.ql b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/tainted.ql
index d6ddf93bf49..5784906862b 100644
--- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/tainted.ql
+++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/tainted.ql
@@ -7,9 +7,10 @@
import cpp
import semmle.code.cpp.security.TaintTrackingImpl as ASTTaintTracking
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking as IRDefaultTaintTracking
+import IRDefaultTaintTracking::TaintedWithPath as TaintedWithPath
import TestUtilities.InlineExpectationsTest
-predicate isSink(Element sink) {
+predicate argToSinkCall(Element sink) {
exists(FunctionCall call |
call.getTarget().getName() = "sink" and
sink = call.getAnArgument()
@@ -17,11 +18,15 @@ predicate isSink(Element sink) {
}
predicate astTaint(Expr source, Element sink) {
- ASTTaintTracking::tainted(source, sink) and isSink(sink)
+ ASTTaintTracking::tainted(source, sink) and argToSinkCall(sink)
+}
+
+class SourceConfiguration extends TaintedWithPath::TaintTrackingConfiguration {
+ override predicate isSink(Element e) { argToSinkCall(e) }
}
predicate irTaint(Expr source, Element sink) {
- IRDefaultTaintTracking::tainted(source, sink) and isSink(sink)
+ TaintedWithPath::taintedWithPath(source, sink, _, _)
}
class IRDefaultTaintTrackingTest extends InlineExpectationsTest {
From eb4f1e1ba03cdbda41df975295dbf8e5e4dcd0f7 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Tue, 2 Mar 2021 15:45:40 +0100
Subject: [PATCH 072/725] C++: Restore some of the lost test results by doing
operand -> instruction taint steps in IR TaintTracking.
---
.../cpp/ir/dataflow/DefaultTaintTracking.qll | 2 +-
.../cpp/ir/dataflow/internal/ModelUtil.qll | 38 +++-------
.../dataflow/internal/TaintTrackingUtil.qll | 76 ++++++++++++-------
.../defaulttainttracking.cpp | 4 +-
.../dataflow/taint-tests/taint.cpp | 6 +-
5 files changed, 62 insertions(+), 64 deletions(-)
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
index 428ec5a13f0..7a4581cf31f 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
@@ -613,7 +613,7 @@ module TaintedWithPath {
// Step to return value of a modeled function when an input taints the
// dereference of the return value
exists(CallInstruction call, Function func, FunctionInput modelIn, FunctionOutput modelOut |
- n1 = callInput(call, modelIn) and
+ n1.asOperand() = callInput(call, modelIn) and
(
func.(TaintFunction).hasTaintFlow(modelIn, modelOut)
or
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll
index 93d74519ca5..2f4037d4ec8 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll
@@ -9,30 +9,18 @@ private import semmle.code.cpp.ir.dataflow.DataFlow
/**
* Gets the instruction that goes into `input` for `call`.
*/
-DataFlow::Node callInput(CallInstruction call, FunctionInput input) {
- // A positional argument
+Operand callInput(CallInstruction call, FunctionInput input) {
+ // An argument or qualifier
exists(int index |
- result.asInstruction() = call.getPositionalArgument(index) and
- input.isParameter(index)
+ result = call.getArgumentOperand(index) and
+ input.isParameterOrQualifierAddress(index)
)
or
- // A value pointed to by a positional argument
+ // A value pointed to by an argument or qualifier
exists(ReadSideEffectInstruction read |
- result.asOperand() = read.getSideEffectOperand() and
+ result = read.getSideEffectOperand() and
read.getPrimaryInstruction() = call and
- input.isParameterDeref(read.getIndex())
- )
- or
- // The qualifier pointer
- result.asInstruction() = call.getThisArgument() and
- input.isQualifierAddress()
- or
- // The qualifier object
- exists(ReadSideEffectInstruction read |
- result.asOperand() = read.getSideEffectOperand() and
- read.getPrimaryInstruction() = call and
- read.getIndex() = -1 and
- input.isQualifierObject()
+ input.isParameterDerefOrQualifierObject(read.getIndex())
)
}
@@ -44,19 +32,11 @@ Instruction callOutput(CallInstruction call, FunctionOutput output) {
result = call and
output.isReturnValue()
or
- // The side effect of a call on the value pointed to by a positional argument
+ // The side effect of a call on the value pointed to by an argument or qualifier
exists(WriteSideEffectInstruction effect |
result = effect and
effect.getPrimaryInstruction() = call and
- output.isParameterDeref(effect.getIndex())
- )
- or
- // The side effect of a call on the qualifier object
- exists(WriteSideEffectInstruction effect |
- result = effect and
- effect.getPrimaryInstruction() = call and
- effect.getIndex() = -1 and
- output.isQualifierObject()
+ output.isParameterDerefOrQualifierObject(effect.getIndex())
)
// TODO: return value dereference
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll
index 0ea24dc7862..c5c168efb0d 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll
@@ -21,53 +21,69 @@ predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
*/
cached
predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
- localInstructionTaintStep(nodeFrom.asInstruction(), nodeTo.asInstruction())
+ operandToInstructionTaintStep(nodeFrom.asOperand(), nodeTo.asInstruction())
or
- modeledTaintStep(nodeFrom, nodeTo)
+ instructionToOperandTaintStep(nodeFrom.asInstruction(), nodeTo.asOperand())
+}
+
+private predicate instructionToOperandTaintStep(Instruction fromInstr, Operand toOperand) {
+ // Propagate flow from the definition of an operand to the operand, even when the overlap is inexact.
+ // We only do this in certain cases:
+ // 1. The instruction's result must not be conflated, and
+ // 2. The instruction's result type is one the types where we expect element-to-object flow. Currently
+ // this is array types and union types. This matches the other two cases of element-to-object flow in
+ // `DefaultTaintTracking`.
+ toOperand.getAnyDef() = fromInstr and
+ not fromInstr.isResultConflated() and
+ (
+ fromInstr.getResultType() instanceof ArrayType or
+ fromInstr.getResultType() instanceof Union
+ )
+ or
+ exists(ReadSideEffectInstruction readInstr |
+ fromInstr = readInstr.getArgumentDef() and
+ toOperand = readInstr.getSideEffectOperand()
+ )
+ or
+ toOperand.(LoadOperand).getAnyDef() = fromInstr
}
/**
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
* (intra-procedural) step.
*/
-private predicate localInstructionTaintStep(Instruction nodeFrom, Instruction nodeTo) {
+private predicate operandToInstructionTaintStep(Operand opFrom, Instruction instrTo) {
// Taint can flow through expressions that alter the value but preserve
// more than one bit of it _or_ expressions that follow data through
// pointer indirections.
- nodeTo.getAnOperand().getAnyDef() = nodeFrom and
+ instrTo.getAnOperand() = opFrom and
(
- nodeTo instanceof ArithmeticInstruction
+ instrTo instanceof ArithmeticInstruction
or
- nodeTo instanceof BitwiseInstruction
+ instrTo instanceof BitwiseInstruction
or
- nodeTo instanceof PointerArithmeticInstruction
+ instrTo instanceof PointerArithmeticInstruction
or
- nodeTo instanceof FieldAddressInstruction
+ instrTo instanceof FieldAddressInstruction
or
// The `CopyInstruction` case is also present in non-taint data flow, but
// that uses `getDef` rather than `getAnyDef`. For taint, we want flow
// from a definition of `myStruct` to a `myStruct.myField` expression.
- nodeTo instanceof CopyInstruction
+ instrTo instanceof CopyInstruction
)
or
- nodeTo.(LoadInstruction).getSourceAddress() = nodeFrom
- or
- // Flow through partial reads of arrays and unions
- nodeTo.(LoadInstruction).getSourceValueOperand().getAnyDef() = nodeFrom and
- not nodeFrom.isResultConflated() and
- (
- nodeFrom.getResultType() instanceof ArrayType or
- nodeFrom.getResultType() instanceof Union
- )
+ instrTo.(LoadInstruction).getSourceAddressOperand() = opFrom
or
// Flow from an element to an array or union that contains it.
- nodeTo.(ChiInstruction).getPartial() = nodeFrom and
- not nodeTo.isResultConflated() and
- exists(Type t | nodeTo.getResultLanguageType().hasType(t, false) |
+ instrTo.(ChiInstruction).getPartialOperand() = opFrom and
+ not instrTo.isResultConflated() and
+ exists(Type t | instrTo.getResultLanguageType().hasType(t, false) |
t instanceof Union
or
t instanceof ArrayType
)
+ or
+ modeledTaintStep(opFrom, instrTo)
}
/**
@@ -110,17 +126,19 @@ predicate defaultTaintSanitizer(DataFlow::Node node) { none() }
* Holds if taint can flow from `instrIn` to `instrOut` through a call to a
* modeled function.
*/
-predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut) {
+predicate modeledTaintStep(Operand nodeIn, Instruction nodeOut) {
exists(CallInstruction call, TaintFunction func, FunctionInput modelIn, FunctionOutput modelOut |
(
nodeIn = callInput(call, modelIn)
or
exists(int n |
- modelIn.isParameterDeref(n) and
- nodeIn = callInput(call, any(InParameter inParam | inParam.getIndex() = n))
+ modelIn.isParameterDerefOrQualifierObject(n) and
+ if n = -1
+ then nodeIn = callInput(call, any(InQualifierObject inQualifier))
+ else nodeIn = callInput(call, any(InParameter inParam | inParam.getIndex() = n))
)
) and
- nodeOut.asInstruction() = callOutput(call, modelOut) and
+ nodeOut = callOutput(call, modelOut) and
call.getStaticCallTarget() = func and
func.hasTaintFlow(modelIn, modelOut)
)
@@ -135,7 +153,7 @@ predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut) {
int indexMid, InParameter modelMidIn, OutReturnValue modelOut
|
nodeIn = callInput(call, modelIn) and
- nodeOut.asInstruction() = callOutput(call, modelOut) and
+ nodeOut = callOutput(call, modelOut) and
call.getStaticCallTarget() = func and
func.(TaintFunction).hasTaintFlow(modelIn, modelMidOut) and
func.(DataFlowFunction).hasDataFlow(modelMidIn, modelOut) and
@@ -149,8 +167,8 @@ predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut) {
CallInstruction call, ReadSideEffectInstruction read, Function func, FunctionInput modelIn,
FunctionOutput modelOut
|
- read.getSideEffectOperand() = callInput(call, modelIn).asOperand() and
- read.getArgumentDef() = nodeIn.asInstruction() and
+ read.getSideEffectOperand() = callInput(call, modelIn) and
+ read.getArgumentDef() = nodeIn.getDef() and
not read.getSideEffect().isResultModeled() and
call.getStaticCallTarget() = func and
(
@@ -158,6 +176,6 @@ predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut) {
or
func.(TaintFunction).hasTaintFlow(modelIn, modelOut)
) and
- nodeOut.asInstruction() = callOutput(call, modelOut)
+ nodeOut = callOutput(call, modelOut)
)
}
diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp
index 6895ef4d6bb..d64f1966b49 100644
--- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp
+++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp
@@ -13,8 +13,8 @@ int main() {
- sink(_strdup(getenv("VAR"))); // $ MISSING: ast,ir
- sink(strdup(getenv("VAR"))); // $ ast MISSING: ir
+ sink(_strdup(getenv("VAR"))); // $ ir MISSING: ast
+ sink(strdup(getenv("VAR"))); // $ ast,ir
sink(unmodeled_function(getenv("VAR"))); // clean by assumption
char untainted_buf[100] = "";
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp
index baf26ee9af4..7797d905f29 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp
@@ -369,9 +369,9 @@ void test_strdup(char *source)
a = strdup(source);
b = strdup("hello, world");
c = strndup(source, 100);
- sink(a); // $ ast MISSING: ir
+ sink(a); // $ ast,ir
sink(b);
- sink(c); // $ ast MISSING: ir
+ sink(c); // $ ast,ir
}
void test_strndup(int source)
@@ -388,7 +388,7 @@ void test_wcsdup(wchar_t *source)
a = wcsdup(source);
b = wcsdup(L"hello, world");
- sink(a); // $ ast MISSING: ir
+ sink(a); // $ ast,ir
sink(b);
}
From 617ba65ef5994e285a26bc4ac0a799d37b3fdb60 Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Tue, 2 Mar 2021 21:36:14 +0100
Subject: [PATCH 073/725] Improved docs for
SpringHttpInvokerUnsafeDeserialization.ql
---
.../CWE-502/SpringHttpInvokerUnsafeDeserialization.qhelp | 8 ++++----
.../CWE/CWE-502/SpringHttpInvokerUnsafeDeserialization.ql | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/SpringHttpInvokerUnsafeDeserialization.qhelp b/java/ql/src/experimental/Security/CWE/CWE-502/SpringHttpInvokerUnsafeDeserialization.qhelp
index 49237a8500e..ffb8dddae56 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-502/SpringHttpInvokerUnsafeDeserialization.qhelp
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/SpringHttpInvokerUnsafeDeserialization.qhelp
@@ -3,14 +3,14 @@
-Spring Framework provides an abstract base class RemoteInvocationSerializingExporter
+The Spring Framework provides an abstract base class RemoteInvocationSerializingExporter
for defining remote service exporters.
A Spring exporter, which is based on this class, deserializes incoming data using ObjectInputStream.
Deserializing untrusted data is easily exploitable and in many cases allows an attacker
to execute arbitrary code.
-Spring Framework also provides two classes that extend RemoteInvocationSerializingExporter:
+The Spring Framework also provides two classes that extend RemoteInvocationSerializingExporter:
HttpInvokerServiceExporter
@@ -24,7 +24,7 @@ using unsafe ObjectInputStream. If a remote attacker can reach such
it results in remote code execution in the worst case.
-CVE-2016-1000027 has been assigned to this issue in Spring Framework. There is no fix for that.
+CVE-2016-1000027 has been assigned to this issue in the Spring Framework. It is regarded as a design limitation, and can be mitigated but not fixed outright.
@@ -36,7 +36,7 @@ Instead, use other message formats for API endpoints (for example, JSON),
but make sure that the underlying deserialization mechanism is properly configured
so that deserialization attacks are not possible. If the vulnerable exporters can not be replaced,
consider using global deserialization filters introduced by JEP 290.
-In general, avoid deserialization of untrusted data.
+In general, avoid using Java's built-in deserialization methods on untrusted data.
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/SpringHttpInvokerUnsafeDeserialization.ql b/java/ql/src/experimental/Security/CWE/CWE-502/SpringHttpInvokerUnsafeDeserialization.ql
index df01ae478cc..54e8272575b 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-502/SpringHttpInvokerUnsafeDeserialization.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/SpringHttpInvokerUnsafeDeserialization.ql
@@ -52,4 +52,4 @@ private predicate createsRemoteInvocationSerializingExporterBean(Method method)
from Method method
where createsRemoteInvocationSerializingExporterBean(method)
select method,
- "Unasafe deserialization in a remote service exporter in '" + method.getName() + "' method"
+ "Unsafe deserialization in a remote service exporter in '" + method.getName() + "' method"
From b366ffa69e98e21ac8998bb08a8815fc4cae6b4f Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Wed, 3 Mar 2021 13:38:18 +0000
Subject: [PATCH 074/725] Revamp source of the query
---
.../CWE-1004/SensitiveCookieNotHttpOnly.qhelp | 2 +-
.../CWE-1004/SensitiveCookieNotHttpOnly.ql | 116 +++++++++---------
.../SensitiveCookieNotHttpOnly.expected | 12 +-
.../javax/ws/rs/core/Cookie.java | 59 +++------
.../javax/ws/rs/core/NewCookie.java | 36 ------
5 files changed, 82 insertions(+), 143 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.qhelp b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.qhelp
index 880ed767be9..ee3e8a4181a 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.qhelp
+++ b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.qhelp
@@ -2,7 +2,7 @@
- Cross-Site Scripting (XSS) is categorized as one of the OWASP Top 10 Security Vulnerabilities. The HttpOnly flag directs compatible browsers to prevent client-side script from accessing cookies. Including the HttpOnly flag in the Set-Cookie HTTP response header for a sensitive cookie helps mitigate the risk associated with XSS where an attacker's script code attempts to read the contents of a cookie and exfiltrate information obtained.
+ Cross-Site Scripting (XSS) is categorized as one of the OWASP Top 10 Security Vulnerabilities. The HttpOnly flag directs compatible browsers to prevent client-side script from accessing cookies. Including the HttpOnly flag in the Set-Cookie HTTP response header for a sensitive cookie helps mitigate the risk associated with XSS where an attacker's script code attempts to read the contents of a cookie and exfiltrate information obtained.
diff --git a/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
index bf4e60134c9..842e8b7eabb 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
@@ -9,7 +9,6 @@
import java
import semmle.code.java.frameworks.Servlets
-import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking
import DataFlow::PathGraph
@@ -18,8 +17,8 @@ string getSensitiveCookieNameRegex() { result = "(?i).*(auth|session|token|key|c
/** Holds if a string is concatenated with the name of a sensitive cookie. */
predicate isSensitiveCookieNameExpr(Expr expr) {
- expr.(StringLiteral)
- .getRepresentedString()
+ expr.(CompileTimeConstantExpr)
+ .getStringValue()
.toLowerCase()
.regexpMatch(getSensitiveCookieNameRegex()) or
isSensitiveCookieNameExpr(expr.(AddExpr).getAnOperand())
@@ -27,7 +26,7 @@ predicate isSensitiveCookieNameExpr(Expr expr) {
/** Holds if a string is concatenated with the `HttpOnly` flag. */
predicate hasHttpOnlyExpr(Expr expr) {
- expr.(StringLiteral).getRepresentedString().toLowerCase().matches("%httponly%") or
+ expr.(CompileTimeConstantExpr).getStringValue().toLowerCase().matches("%httponly%") or
hasHttpOnlyExpr(expr.(AddExpr).getAnOperand())
}
@@ -38,37 +37,34 @@ class SetCookieMethodAccess extends MethodAccess {
this.getMethod() instanceof ResponseAddHeaderMethod or
this.getMethod() instanceof ResponseSetHeaderMethod
) and
- this.getArgument(0).(StringLiteral).getRepresentedString().toLowerCase() = "set-cookie"
+ this.getArgument(0).(CompileTimeConstantExpr).getStringValue().toLowerCase() = "set-cookie"
}
}
/** Sensitive cookie name used in a `Cookie` constructor or a `Set-Cookie` call. */
class SensitiveCookieNameExpr extends Expr {
SensitiveCookieNameExpr() {
- isSensitiveCookieNameExpr(this) and
- (
- exists(
- ClassInstanceExpr cie // new Cookie("jwt_token", token)
- |
- (
- cie.getConstructor().getDeclaringType().hasQualifiedName("javax.servlet.http", "Cookie") or
- cie.getConstructor()
- .getDeclaringType()
- .getASupertype*()
- .hasQualifiedName("javax.ws.rs.core", "Cookie") or
- cie.getConstructor()
- .getDeclaringType()
- .getASupertype*()
- .hasQualifiedName("jakarta.ws.rs.core", "Cookie")
- ) and
- DataFlow::localExprFlow(this, cie.getArgument(0))
- )
- or
- exists(
- SetCookieMethodAccess ma // response.addHeader("Set-Cookie: token=" +authId + ";HttpOnly;Secure")
- |
- DataFlow::localExprFlow(this, ma.getArgument(1))
- )
+ exists(
+ ClassInstanceExpr cie, Expr e // new Cookie("jwt_token", token)
+ |
+ (
+ cie.getConstructor().getDeclaringType().hasQualifiedName("javax.servlet.http", "Cookie") or
+ cie.getConstructor()
+ .getDeclaringType()
+ .getASupertype*()
+ .hasQualifiedName(["javax.ws.rs.core", "jakarta.ws.rs.core"], "Cookie")
+ ) and
+ this = cie and
+ isSensitiveCookieNameExpr(e) and
+ DataFlow::localExprFlow(e, cie.getArgument(0))
+ )
+ or
+ exists(
+ SetCookieMethodAccess ma, Expr e // response.addHeader("Set-Cookie: token=" +authId + ";HttpOnly;Secure")
+ |
+ this = ma.getArgument(1) and
+ isSensitiveCookieNameExpr(e) and
+ DataFlow::localExprFlow(e, ma.getArgument(1))
)
}
}
@@ -78,15 +74,20 @@ class CookieResponseSink extends DataFlow::ExprNode {
CookieResponseSink() {
exists(MethodAccess ma |
(
- ma.getMethod() instanceof ResponseAddCookieMethod or
- ma instanceof SetCookieMethodAccess
- ) and
- ma.getAnArgument() = this.getExpr()
+ ma.getMethod() instanceof ResponseAddCookieMethod and
+ this.getExpr() = ma.getArgument(0)
+ or
+ ma instanceof SetCookieMethodAccess and
+ this.getExpr() = ma.getArgument(1)
+ )
)
}
}
-/** Holds if the `node` is a method call of `setHttpOnly(true)` on a cookie. */
+/**
+ * Holds if `node` is an access to a variable which has `setHttpOnly(true)` called on it and is also
+ * the first argument to a call to the method `addCookie` of `javax.servlet.http.HttpServletResponse`.
+ */
predicate setHttpOnlyMethodAccess(DataFlow::Node node) {
exists(
MethodAccess addCookie, Variable cookie, MethodAccess m // jwtCookie.setHttpOnly(true)
@@ -100,7 +101,10 @@ predicate setHttpOnlyMethodAccess(DataFlow::Node node) {
)
}
-/** Holds if the `node` is a method call of `Set-Cookie` header with the `HttpOnly` flag whose cookie name is sensitive. */
+/**
+ * Holds if `node` is a string that contains `httponly` and which flows to the second argument
+ * of a method to set a cookie.
+ */
predicate setHttpOnlyInSetCookie(DataFlow::Node node) {
exists(SetCookieMethodAccess sa |
hasHttpOnlyExpr(node.asExpr()) and
@@ -108,20 +112,17 @@ predicate setHttpOnlyInSetCookie(DataFlow::Node node) {
)
}
-/** Holds if the `node` is an invocation of a JAX-RS `NewCookie` constructor that sets `HttpOnly` to true. */
-predicate setHttpOnlyInNewCookie(DataFlow::Node node) {
- exists(ClassInstanceExpr cie |
- cie.getConstructor().getDeclaringType().hasName("NewCookie") and
- DataFlow::localExprFlow(node.asExpr(), cie.getArgument(0)) and
- (
- cie.getNumArgument() = 6 and cie.getArgument(5).(BooleanLiteral).getBooleanValue() = true // NewCookie(Cookie cookie, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly)
- or
- cie.getNumArgument() = 8 and
- cie.getArgument(6).getType() instanceof BooleanType and
- cie.getArgument(7).(BooleanLiteral).getBooleanValue() = true // NewCookie(String name, String value, String path, String domain, String comment, int maxAge, boolean secure, boolean httpOnly)
- or
- cie.getNumArgument() = 10 and cie.getArgument(9).(BooleanLiteral).getBooleanValue() = true // NewCookie(String name, String value, String path, String domain, int version, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly)
- )
+/** Holds if `cie` is an invocation of a JAX-RS `NewCookie` constructor that sets `HttpOnly` to true. */
+predicate setHttpOnlyInNewCookie(ClassInstanceExpr cie) {
+ cie.getConstructedType().hasQualifiedName(["javax.ws.rs.core", "jakarta.ws.rs.core"], "NewCookie") and
+ (
+ cie.getNumArgument() = 6 and cie.getArgument(5).(BooleanLiteral).getBooleanValue() = true // NewCookie(Cookie cookie, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly)
+ or
+ cie.getNumArgument() = 8 and
+ cie.getArgument(6).getType() instanceof BooleanType and
+ cie.getArgument(7).(BooleanLiteral).getBooleanValue() = true // NewCookie(String name, String value, String path, String domain, String comment, int maxAge, boolean secure, boolean httpOnly)
+ or
+ cie.getNumArgument() = 10 and cie.getArgument(9).(BooleanLiteral).getBooleanValue() = true // NewCookie(String name, String value, String path, String domain, int version, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly)
)
}
@@ -145,7 +146,10 @@ predicate isTestMethod(DataFlow::Node node) {
)
}
-/** A taint configuration tracking flow from a sensitive cookie without HttpOnly flag set to its HTTP response. */
+/**
+ * A taint configuration tracking flow from a sensitive cookie without the `HttpOnly` flag
+ * set to its HTTP response.
+ */
class MissingHttpOnlyConfiguration extends TaintTracking::Configuration {
MissingHttpOnlyConfiguration() { this = "MissingHttpOnlyConfiguration" }
@@ -159,25 +163,17 @@ class MissingHttpOnlyConfiguration extends TaintTracking::Configuration {
// cookie.setHttpOnly(true)
setHttpOnlyMethodAccess(node)
or
- // response.addHeader("Set-Cookie: token=" +authId + ";HttpOnly;Secure")
+ // response.addHeader("Set-Cookie", "token=" +authId + ";HttpOnly;Secure")
setHttpOnlyInSetCookie(node)
or
// new NewCookie("session-access-key", accessKey, "/", null, null, 0, true, true)
- setHttpOnlyInNewCookie(node)
+ setHttpOnlyInNewCookie(node.asExpr())
or
// Test class or method
isTestMethod(node)
}
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
- exists(
- ClassInstanceExpr cie // `NewCookie` constructor
- |
- cie.getAnArgument() = pred.asExpr() and
- cie = succ.asExpr() and
- cie.getConstructor().getDeclaringType().hasName("NewCookie")
- )
- or
exists(
MethodAccess ma // `toString` call on a cookie object
|
diff --git a/java/ql/test/experimental/query-tests/security/CWE-1004/SensitiveCookieNotHttpOnly.expected b/java/ql/test/experimental/query-tests/security/CWE-1004/SensitiveCookieNotHttpOnly.expected
index 84d6be3863a..e2d05a9b24d 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-1004/SensitiveCookieNotHttpOnly.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-1004/SensitiveCookieNotHttpOnly.expected
@@ -1,13 +1,13 @@
edges
-| SensitiveCookieNotHttpOnly.java:22:38:22:48 | "jwt_token" : String | SensitiveCookieNotHttpOnly.java:28:28:28:36 | jwtCookie |
-| SensitiveCookieNotHttpOnly.java:49:56:49:75 | "session-access-key" : String | SensitiveCookieNotHttpOnly.java:49:42:49:124 | toString(...) |
+| SensitiveCookieNotHttpOnly.java:22:27:22:60 | new Cookie(...) : Cookie | SensitiveCookieNotHttpOnly.java:28:28:28:36 | jwtCookie |
+| SensitiveCookieNotHttpOnly.java:49:42:49:113 | new NewCookie(...) : NewCookie | SensitiveCookieNotHttpOnly.java:49:42:49:124 | toString(...) |
nodes
-| SensitiveCookieNotHttpOnly.java:22:38:22:48 | "jwt_token" : String | semmle.label | "jwt_token" : String |
+| SensitiveCookieNotHttpOnly.java:22:27:22:60 | new Cookie(...) : Cookie | semmle.label | new Cookie(...) : Cookie |
| SensitiveCookieNotHttpOnly.java:28:28:28:36 | jwtCookie | semmle.label | jwtCookie |
| SensitiveCookieNotHttpOnly.java:39:42:39:69 | ... + ... | semmle.label | ... + ... |
+| SensitiveCookieNotHttpOnly.java:49:42:49:113 | new NewCookie(...) : NewCookie | semmle.label | new NewCookie(...) : NewCookie |
| SensitiveCookieNotHttpOnly.java:49:42:49:124 | toString(...) | semmle.label | toString(...) |
-| SensitiveCookieNotHttpOnly.java:49:56:49:75 | "session-access-key" : String | semmle.label | "session-access-key" : String |
#select
-| SensitiveCookieNotHttpOnly.java:28:28:28:36 | jwtCookie | SensitiveCookieNotHttpOnly.java:22:38:22:48 | "jwt_token" : String | SensitiveCookieNotHttpOnly.java:28:28:28:36 | jwtCookie | $@ doesn't have the HttpOnly flag set. | SensitiveCookieNotHttpOnly.java:22:38:22:48 | "jwt_token" | This sensitive cookie |
+| SensitiveCookieNotHttpOnly.java:28:28:28:36 | jwtCookie | SensitiveCookieNotHttpOnly.java:22:27:22:60 | new Cookie(...) : Cookie | SensitiveCookieNotHttpOnly.java:28:28:28:36 | jwtCookie | $@ doesn't have the HttpOnly flag set. | SensitiveCookieNotHttpOnly.java:22:27:22:60 | new Cookie(...) | This sensitive cookie |
| SensitiveCookieNotHttpOnly.java:39:42:39:69 | ... + ... | SensitiveCookieNotHttpOnly.java:39:42:39:69 | ... + ... | SensitiveCookieNotHttpOnly.java:39:42:39:69 | ... + ... | $@ doesn't have the HttpOnly flag set. | SensitiveCookieNotHttpOnly.java:39:42:39:69 | ... + ... | This sensitive cookie |
-| SensitiveCookieNotHttpOnly.java:49:42:49:124 | toString(...) | SensitiveCookieNotHttpOnly.java:49:56:49:75 | "session-access-key" : String | SensitiveCookieNotHttpOnly.java:49:42:49:124 | toString(...) | $@ doesn't have the HttpOnly flag set. | SensitiveCookieNotHttpOnly.java:49:56:49:75 | "session-access-key" | This sensitive cookie |
+| SensitiveCookieNotHttpOnly.java:49:42:49:124 | toString(...) | SensitiveCookieNotHttpOnly.java:49:42:49:113 | new NewCookie(...) : NewCookie | SensitiveCookieNotHttpOnly.java:49:42:49:124 | toString(...) | $@ doesn't have the HttpOnly flag set. | SensitiveCookieNotHttpOnly.java:49:42:49:113 | new NewCookie(...) | This sensitive cookie |
diff --git a/java/ql/test/stubs/jsr311-api-1.1.1/javax/ws/rs/core/Cookie.java b/java/ql/test/stubs/jsr311-api-1.1.1/javax/ws/rs/core/Cookie.java
index 4e4c7585c35..f810600a766 100644
--- a/java/ql/test/stubs/jsr311-api-1.1.1/javax/ws/rs/core/Cookie.java
+++ b/java/ql/test/stubs/jsr311-api-1.1.1/javax/ws/rs/core/Cookie.java
@@ -51,10 +51,16 @@ package javax.ws.rs.core;
* @since 1.0
*/
public class Cookie {
+
/**
* Cookies using the default version correspond to RFC 2109.
*/
public static final int DEFAULT_VERSION = 1;
+ private final String name;
+ private final String value;
+ private final int version;
+ private final String path;
+ private final String domain;
/**
* Create a new instance.
@@ -68,6 +74,11 @@ public class Cookie {
*/
public Cookie(final String name, final String value, final String path, final String domain, final int version)
throws IllegalArgumentException {
+ this.name = name;
+ this.value = value;
+ this.version = version;
+ this.domain = domain;
+ this.path = path;
}
/**
@@ -81,6 +92,7 @@ public class Cookie {
*/
public Cookie(final String name, final String value, final String path, final String domain)
throws IllegalArgumentException {
+ this(name, value, path, domain, DEFAULT_VERSION);
}
/**
@@ -92,6 +104,7 @@ public class Cookie {
*/
public Cookie(final String name, final String value)
throws IllegalArgumentException {
+ this(name, value, null, null);
}
/**
@@ -112,7 +125,7 @@ public class Cookie {
* @return the cookie name.
*/
public String getName() {
- return null;
+ return name;
}
/**
@@ -121,7 +134,7 @@ public class Cookie {
* @return the cookie value.
*/
public String getValue() {
- return null;
+ return value;
}
/**
@@ -130,7 +143,7 @@ public class Cookie {
* @return the cookie version.
*/
public int getVersion() {
- return -1;
+ return version;
}
/**
@@ -139,7 +152,7 @@ public class Cookie {
* @return the cookie domain.
*/
public String getDomain() {
- return null;
+ return domain;
}
/**
@@ -148,40 +161,6 @@ public class Cookie {
* @return the cookie path.
*/
public String getPath() {
- return null;
+ return path;
}
-
- /**
- * Convert the cookie to a string suitable for use as the value of the
- * corresponding HTTP header.
- *
- * @return a stringified cookie.
- */
- @Override
- public String toString() {
- return null;
- }
-
- /**
- * Generate a hash code by hashing all of the cookies properties.
- *
- * @return the cookie hash code.
- */
- @Override
- public int hashCode() {
- return -1;
- }
-
- /**
- * Compare for equality.
- *
- * @param obj the object to compare to.
- * @return {@code true}, if the object is a {@code Cookie} with the same
- * value for all properties, {@code false} otherwise.
- */
- @SuppressWarnings({"StringEquality", "RedundantIfStatement"})
- @Override
- public boolean equals(final Object obj) {
- return true;
- }
-}
\ No newline at end of file
+}
diff --git a/java/ql/test/stubs/jsr311-api-1.1.1/javax/ws/rs/core/NewCookie.java b/java/ql/test/stubs/jsr311-api-1.1.1/javax/ws/rs/core/NewCookie.java
index 43a4899e0ca..7f2e3ec0535 100644
--- a/java/ql/test/stubs/jsr311-api-1.1.1/javax/ws/rs/core/NewCookie.java
+++ b/java/ql/test/stubs/jsr311-api-1.1.1/javax/ws/rs/core/NewCookie.java
@@ -320,40 +320,4 @@ public class NewCookie extends Cookie {
public Cookie toCookie() {
return null;
}
-
- /**
- * Convert the cookie to a string suitable for use as the value of the
- * corresponding HTTP header.
- *
- * @return a stringified cookie.
- */
- @Override
- public String toString() {
- return null;
- }
-
- /**
- * Generate a hash code by hashing all of the properties.
- *
- * @return the hash code.
- */
- @Override
- public int hashCode() {
- return -1;
- }
-
- /**
- * Compare for equality. Use {@link #toCookie()} to compare a
- * {@code NewCookie} to a {@code Cookie} considering only the common
- * properties.
- *
- * @param obj the object to compare to
- * @return true if the object is a {@code NewCookie} with the same value for
- * all properties, false otherwise.
- */
- @SuppressWarnings({"StringEquality", "RedundantIfStatement"})
- @Override
- public boolean equals(Object obj) {
- return true;
- }
}
\ No newline at end of file
From 1b1c3f953b9ebb828e161e4b36d67075a7bca510 Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Wed, 3 Mar 2021 13:54:26 +0000
Subject: [PATCH 075/725] Remove localflow from the source
---
.../CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
index 842e8b7eabb..258b2545b27 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
@@ -45,7 +45,7 @@ class SetCookieMethodAccess extends MethodAccess {
class SensitiveCookieNameExpr extends Expr {
SensitiveCookieNameExpr() {
exists(
- ClassInstanceExpr cie, Expr e // new Cookie("jwt_token", token)
+ ClassInstanceExpr cie // new Cookie("jwt_token", token)
|
(
cie.getConstructor().getDeclaringType().hasQualifiedName("javax.servlet.http", "Cookie") or
@@ -55,16 +55,14 @@ class SensitiveCookieNameExpr extends Expr {
.hasQualifiedName(["javax.ws.rs.core", "jakarta.ws.rs.core"], "Cookie")
) and
this = cie and
- isSensitiveCookieNameExpr(e) and
- DataFlow::localExprFlow(e, cie.getArgument(0))
+ isSensitiveCookieNameExpr(cie.getArgument(0))
)
or
exists(
- SetCookieMethodAccess ma, Expr e // response.addHeader("Set-Cookie: token=" +authId + ";HttpOnly;Secure")
+ SetCookieMethodAccess ma // response.addHeader("Set-Cookie: token=" +authId + ";HttpOnly;Secure")
|
this = ma.getArgument(1) and
- isSensitiveCookieNameExpr(e) and
- DataFlow::localExprFlow(e, ma.getArgument(1))
+ isSensitiveCookieNameExpr(this)
)
}
}
From 502cf38fccef4c0b2a1120f2fe175571ec1b9cb5 Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Wed, 3 Mar 2021 14:07:43 +0000
Subject: [PATCH 076/725] Use concise API
---
.../Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
index 258b2545b27..d9104cbad97 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
@@ -48,9 +48,8 @@ class SensitiveCookieNameExpr extends Expr {
ClassInstanceExpr cie // new Cookie("jwt_token", token)
|
(
- cie.getConstructor().getDeclaringType().hasQualifiedName("javax.servlet.http", "Cookie") or
- cie.getConstructor()
- .getDeclaringType()
+ cie.getConstructedType().hasQualifiedName("javax.servlet.http", "Cookie") or
+ cie.getConstructedType()
.getASupertype*()
.hasQualifiedName(["javax.ws.rs.core", "jakarta.ws.rs.core"], "Cookie")
) and
From 1784c202a7ad4b06932ee33c8e0609aed5181ffc Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Wed, 3 Mar 2021 17:03:37 +0000
Subject: [PATCH 077/725] Clean up the query
---
.../Security/CWE/CWE-759/HashWithoutSalt.ql | 40 +++++++------------
.../security/CWE-759/HashWithoutSalt.java | 27 +++++--------
2 files changed, 26 insertions(+), 41 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
index e1ac21e9200..7ea8c7598d2 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
@@ -17,6 +17,7 @@ class MessageDigest extends RefType {
MessageDigest() { this.hasQualifiedName("java.security", "MessageDigest") }
}
+/** The method call `MessageDigest.getInstance(...)` */
class MDConstructor extends StaticMethodAccess {
MDConstructor() {
exists(Method m | m = this.getMethod() |
@@ -57,26 +58,22 @@ class MDHashMethodAccess extends MethodAccess {
string getPasswordRegex() { result = "(?i).*pass(wd|word|code|phrase).*" }
/** Finds variables that hold password information judging by their names. */
-class PasswordVarExpr extends Expr {
+class PasswordVarExpr extends VarAccess {
PasswordVarExpr() {
- this.(VarAccess).getVariable().getName().toLowerCase().regexpMatch(getPasswordRegex()) and
- not this.(VarAccess).getVariable().getName().toLowerCase().matches("%hash%") // Exclude variable names such as `passwordHash` since their values were already hashed
+ exists(string name | name = this.getVariable().getName().toLowerCase() |
+ name.regexpMatch(getPasswordRegex()) and not name.matches("%hash%") // Exclude variable names such as `passwordHash` since their values were already hashed
+ )
}
}
/** Holds if `Expr` e is a direct or indirect operand of `ae`. */
-predicate hasAddExpr(AddExpr ae, Expr e) { ae.getAnOperand+() = e }
+predicate hasAddExprAncestor(AddExpr ae, Expr e) { ae.getAnOperand+() = e }
-/** Holds if `MethodAccess` ma has a flow to another `MDHashMethodAccess` call. */
-predicate hasAnotherHashCall(MethodAccess ma) {
- exists(MDHashMethodAccess ma2, DataFlow::Node node1, DataFlow::Node node2 |
+/** Holds if `MDHashMethodAccess ma` is a second `MDHashMethodAccess` call by the same object. */
+predicate hasAnotherHashCall(MDHashMethodAccess ma) {
+ exists(MDHashMethodAccess ma2 |
ma2 != ma and
- node1.asExpr() = ma.getAChildExpr() and
- node2.asExpr() = ma2.getAChildExpr() and
- (
- TaintTracking2::localTaint(node1, node2) or
- TaintTracking2::localTaint(node2, node1)
- )
+ ma2.getQualifier() = ma.getQualifier().(VarAccess).getVariable().getAnAccess()
)
}
@@ -84,19 +81,12 @@ predicate hasAnotherHashCall(MethodAccess ma) {
predicate isSingleHashMethodCall(MDHashMethodAccess ma) { not hasAnotherHashCall(ma) }
/** Holds if `MethodAccess` ma is invoked by `MethodAccess` ma2 either directly or indirectly. */
-predicate hasParentCall(MethodAccess ma2, MethodAccess ma) {
- ma.getCaller() = ma2.getMethod()
- or
- exists(MethodAccess ma3 |
- ma.getCaller() = ma3.getMethod() and
- hasParentCall(ma2, ma3)
- )
-}
+predicate hasParentCall(MethodAccess ma2, MethodAccess ma) { ma.getCaller() = ma2.getMethod() }
/** Holds if `MethodAccess` is a single hashing call that is not invoked by a wrapper method. */
predicate isSink(MethodAccess ma) {
isSingleHashMethodCall(ma) and
- not exists(MethodAccess ma2 | hasParentCall(ma2, ma)) // Not invoked by a wrapper method which could invoke MDHashMethod in another call stack to reduce FPs
+ not hasParentCall(_, ma) // Not invoked by a wrapper method which could invoke MDHashMethod in another call stack. This reduces FPs.
}
/** Sink of hashing calls. */
@@ -131,9 +121,9 @@ class HashWithoutSaltConfiguration extends TaintTracking::Configuration {
ma.getArgument(0) = node.asExpr()
) // System.arraycopy(password.getBytes(), ...)
or
- exists(AddExpr e | hasAddExpr(e, node.asExpr())) // password+salt
+ exists(AddExpr e | hasAddExprAncestor(e, node.asExpr())) // password+salt
or
- exists(ConditionalExpr ce | ce = node.asExpr()) // useSalt?password+":"+salt:password
+ exists(ConditionalExpr ce | ce.getAChildExpr() = node.asExpr()) // useSalt?password+":"+salt:password
or
exists(MethodAccess ma |
ma.getMethod().getDeclaringType().hasQualifiedName("java.lang", "StringBuilder") and
@@ -143,7 +133,7 @@ class HashWithoutSaltConfiguration extends TaintTracking::Configuration {
or
exists(MethodAccess ma |
ma.getQualifier().(VarAccess).getVariable().getType() instanceof Interface and
- ma.getAnArgument() = node.asExpr() // Method access of interface type variables requires runtime determination
+ ma.getAnArgument() = node.asExpr() // Method access of interface type variables requires runtime determination thus not handled
)
}
}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java
index ef2fef8dd9c..026ccfa70be 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java
@@ -54,12 +54,16 @@ public class HashWithoutSalt {
// GOOD - Hash with a given salt stored somewhere else.
public String getSHA256Hash(String password, String salt) throws NoSuchAlgorithmException {
- return hash(password+":"+salt);
+ MessageDigest alg = MessageDigest.getInstance("SHA-256");
+ String payload = password+":"+salt;
+ return Base64.getEncoder().encodeToString(alg.digest(payload.getBytes(java.nio.charset.StandardCharsets.UTF_8)));
}
// GOOD - Hash with a given salt stored somewhere else.
public String getSHA256Hash2(String password, String salt, boolean useSalt) throws NoSuchAlgorithmException {
- return hash(useSalt?password+":"+salt:password);
+ MessageDigest alg = MessageDigest.getInstance("SHA-256");
+ String payload = useSalt?password+":"+salt:password;
+ return Base64.getEncoder().encodeToString(alg.digest(payload.getBytes(java.nio.charset.StandardCharsets.UTF_8)));
}
// GOOD - Hash with a salt for a variable named passwordHash, whose value is a hash used as an input for a hashing function.
@@ -73,10 +77,6 @@ public class HashWithoutSalt {
sha256.update(foo, start, len);
}
- public void update2(SHA256 sha256, byte[] foo, int start, int len) throws NoSuchAlgorithmException {
- sha256.update(foo, start, len);
- }
-
// BAD - Invoking a wrapper implementation without a salt is not detected.
public String getSHA256Hash4(String password) throws NoSuchAlgorithmException, ClassNotFoundException, IllegalAccessException, InstantiationException {
SHA256 sha256 = new SHA256();
@@ -98,15 +98,15 @@ public class HashWithoutSalt {
}
// BAD - Invoking a wrapper implementation without a salt is not detected.
- public String getSHA256Hash6(String password) throws NoSuchAlgorithmException {
- SHA256 sha256 = new SHA256();
+ public String getSHA512Hash6(String password) throws NoSuchAlgorithmException {
+ SHA512 sha512 = new SHA512();
byte[] passBytes = password.getBytes();
- sha256.update(passBytes, 0, passBytes.length);
- return Base64.getEncoder().encodeToString(sha256.digest());
+ sha512.update(passBytes, 0, passBytes.length);
+ return Base64.getEncoder().encodeToString(sha512.digest());
}
// BAD - Invoke a wrapper implementation with a salt, which is not detected with an interface type variable.
- public String getSHA256Hash7(byte[] passphrase) throws NoSuchAlgorithmException, ClassNotFoundException, IllegalAccessException, InstantiationException {
+ public String getSHA512Hash7(byte[] passphrase) throws NoSuchAlgorithmException, ClassNotFoundException, IllegalAccessException, InstantiationException {
Class c = Class.forName("SHA512");
HASH sha512 = (HASH) (c.newInstance());
byte[] tmp = new byte[4];
@@ -120,11 +120,6 @@ public class HashWithoutSalt {
return Base64.getEncoder().encodeToString(key);
}
- private String hash(String payload) throws NoSuchAlgorithmException {
- MessageDigest alg = MessageDigest.getInstance("SHA-256");
- return Base64.getEncoder().encodeToString(alg.digest(payload.getBytes(java.nio.charset.StandardCharsets.UTF_8)));
- }
-
public static byte[] getSalt() throws NoSuchAlgorithmException {
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
byte[] salt = new byte[16];
From c5577cb09a073c2fb4f6feeca887c437d5fbe2fb Mon Sep 17 00:00:00 2001
From: haby0
Date: Thu, 4 Mar 2021 19:54:49 +0800
Subject: [PATCH 078/725] Fix the problem
---
.../Security/CWE/CWE-352/JsonpInjection.ql | 68 --------
.../CWE/CWE-352/JsonpInjectionFilterLib.qll | 77 ---------
.../CWE/CWE-352/JsonpInjectionLib.qll | 155 ------------------
.../Security/CWE/CWE-352/JsonStringLib.qll | 0
.../CWE/CWE-352/JsonpController.java} | 48 +-----
.../Security/CWE/CWE-352/JsonpInjection.java | 0
.../Security/CWE/CWE-352/JsonpInjection.qhelp | 2 +-
.../Security/CWE/CWE-352/JsonpInjection.ql | 64 ++++++++
.../CWE/CWE-352/JsonpInjectionLib.qll | 130 +++++++++++++++
.../CWE/CWE-352/JsonpInjectionServlet1.java | 0
.../CWE/CWE-352/JsonpInjectionServlet2.java | 0
.../Security/CWE/CWE-352/RefererFilter.java | 43 +++++
.../semmle/code/java/frameworks/Servlets.qll | 12 +-
.../security/CWE-352/JsonpController.java | 128 +++++++++++++++
.../security/CWE-352/JsonpInjection.expected | 60 -------
.../CWE-352/JsonpInjectionServlet1.java | 64 ++++++++
.../CWE-352/JsonpInjectionServlet2.java} | 16 +-
.../CWE-352/JsonpInjection_1.expected | 60 +++++++
.../CWE-352/JsonpInjection_2.expected | 78 +++++++++
.../CWE-352/JsonpInjection_3.expected | 66 ++++++++
.../query-tests/security/CWE-352/Readme | 3 +
.../security/CWE-352/RefererFilter.java | 43 +++++
.../gson-2.8.6/com/google/gson/Gson.java | 7 +
.../org/springframework/util/StringUtils.java | 8 +
.../javax/servlet/Filter.java | 13 ++
.../javax/servlet/FilterChain.java | 8 +
.../javax/servlet/FilterConfig.java | 3 +
.../javax/servlet/ServletException.java | 8 +
.../javax/servlet/ServletRequest.java | 87 ++++++++++
.../javax/servlet/ServletResponse.java | 39 +++++
.../servlet/http/HttpServletRequest.java | 116 +++++++++++++
.../servlet/http/HttpServletResponse.java | 106 ++++++++++++
32 files changed, 1084 insertions(+), 428 deletions(-)
delete mode 100644 java/ql/src/Security/CWE/CWE-352/JsonpInjection.ql
delete mode 100644 java/ql/src/Security/CWE/CWE-352/JsonpInjectionFilterLib.qll
delete mode 100644 java/ql/src/Security/CWE/CWE-352/JsonpInjectionLib.qll
rename java/ql/src/{ => experimental}/Security/CWE/CWE-352/JsonStringLib.qll (100%)
rename java/ql/{test/experimental/query-tests/security/CWE-352/JsonpInjection.java => src/experimental/Security/CWE/CWE-352/JsonpController.java} (72%)
rename java/ql/src/{ => experimental}/Security/CWE/CWE-352/JsonpInjection.java (100%)
rename java/ql/src/{ => experimental}/Security/CWE/CWE-352/JsonpInjection.qhelp (96%)
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.ql
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionLib.qll
rename java/ql/src/{ => experimental}/Security/CWE/CWE-352/JsonpInjectionServlet1.java (100%)
rename java/ql/src/{ => experimental}/Security/CWE/CWE-352/JsonpInjectionServlet2.java (100%)
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-352/RefererFilter.java
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonpController.java
delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.expected
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionServlet1.java
rename java/ql/{src/Security/CWE/CWE-352/JsonpInjectionServlet.java => test/experimental/query-tests/security/CWE-352/JsonpInjectionServlet2.java} (75%)
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_1.expected
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_2.expected
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_3.expected
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/Readme
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/RefererFilter.java
create mode 100644 java/ql/test/stubs/gson-2.8.6/com/google/gson/Gson.java
create mode 100644 java/ql/test/stubs/spring-core-5.3.2/org/springframework/util/StringUtils.java
create mode 100644 java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/Filter.java
create mode 100644 java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/FilterChain.java
create mode 100644 java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/FilterConfig.java
create mode 100644 java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/ServletException.java
create mode 100644 java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/ServletRequest.java
create mode 100644 java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/ServletResponse.java
create mode 100644 java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/http/HttpServletRequest.java
create mode 100644 java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/http/HttpServletResponse.java
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonpInjection.ql b/java/ql/src/Security/CWE/CWE-352/JsonpInjection.ql
deleted file mode 100644
index 53ee6182511..00000000000
--- a/java/ql/src/Security/CWE/CWE-352/JsonpInjection.ql
+++ /dev/null
@@ -1,68 +0,0 @@
-/**
- * @name JSONP Injection
- * @description User-controlled callback function names that are not verified are vulnerable
- * to jsonp injection attacks.
- * @kind path-problem
- * @problem.severity error
- * @precision high
- * @id java/JSONP-Injection
- * @tags security
- * external/cwe/cwe-352
- */
-
-import java
-import JsonpInjectionLib
-import JsonpInjectionFilterLib
-import semmle.code.java.dataflow.FlowSources
-import semmle.code.java.deadcode.WebEntryPoints
-import DataFlow::PathGraph
-
-
-/** If there is a method to verify `token`, `auth`, `referer`, and `origin`, it will not pass. */
-class ServletVerifAuth extends DataFlow::BarrierGuard {
- ServletVerifAuth() {
- exists(MethodAccess ma, Node prod, Node succ |
- ma.getMethod().getName().regexpMatch("(?i).*(token|auth|referer|origin).*") and
- prod instanceof RemoteFlowSource and
- succ.asExpr() = ma.getAnArgument() and
- ma.getMethod().getAParameter().getName().regexpMatch("(?i).*(token|auth|referer|origin).*") and
- localFlowStep*(prod, succ) and
- this = ma
- )
- }
-
- override predicate checks(Expr e, boolean branch) {
- exists(Node node |
- node instanceof JsonpInjectionSink and
- e = node.asExpr() and
- branch = true
- )
- }
-}
-
-/** Taint-tracking configuration tracing flow from get method request sources to output jsonp data. */
-class JsonpInjectionConfig extends TaintTracking::Configuration {
- JsonpInjectionConfig() { this = "JsonpInjectionConfig" }
-
- override predicate isSource(DataFlow::Node source) { source instanceof GetHttpRequestSource }
-
- override predicate isSink(DataFlow::Node sink) { sink instanceof JsonpInjectionSink }
-
- override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
- guard instanceof ServletVerifAuth
- }
-
- override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
- exists(MethodAccess ma |
- isRequestGetParamMethod(ma) and pred.asExpr() = ma.getQualifier() and succ.asExpr() = ma
- )
- }
-}
-
-from DataFlow::PathNode source, DataFlow::PathNode sink, JsonpInjectionConfig conf
-where
- not checks() = false and
- conf.hasFlowPath(source, sink) and
- exists(JsonpInjectionFlowConfig jhfc | jhfc.hasFlowTo(sink.getNode()))
-select sink.getNode(), source, sink, "Jsonp Injection query might include code from $@.",
- source.getNode(), "this user input"
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonpInjectionFilterLib.qll b/java/ql/src/Security/CWE/CWE-352/JsonpInjectionFilterLib.qll
deleted file mode 100644
index b349bed2641..00000000000
--- a/java/ql/src/Security/CWE/CWE-352/JsonpInjectionFilterLib.qll
+++ /dev/null
@@ -1,77 +0,0 @@
-/**
- * @name JSONP Injection
- * @description User-controlled callback function names that are not verified are vulnerable
- * to json hijacking attacks.
- * @kind path-problem
- */
-
-import java
-import DataFlow
-import semmle.code.java.dataflow.FlowSources
-import semmle.code.java.dataflow.TaintTracking2
-import DataFlow::PathGraph
-
-class FilterVerifAuth extends DataFlow::BarrierGuard {
- FilterVerifAuth() {
- exists(MethodAccess ma, Node prod, Node succ |
- ma.getMethod().getName().regexpMatch("(?i).*(token|auth|referer|origin).*") and
- prod instanceof RemoteFlowSource and
- succ.asExpr() = ma.getAnArgument() and
- ma.getMethod().getAParameter().getName().regexpMatch("(?i).*(token|auth|referer|origin).*") and
- localFlowStep*(prod, succ) and
- this = ma
- )
- }
-
- override predicate checks(Expr e, boolean branch) {
- exists(Node node |
- node instanceof DoFilterMethodSink and
- e = node.asExpr() and
- branch = true
- )
- }
-}
-
-/** A data flow source for `Filter.doFilter` method paramters. */
-private class DoFilterMethodSource extends DataFlow::Node {
- DoFilterMethodSource() {
- exists(Method m |
- isDoFilterMethod(m) and
- m.getAParameter().getAnAccess() = this.asExpr()
- )
- }
-}
-
-/** A data flow sink for `FilterChain.doFilter` method qualifying expression. */
-private class DoFilterMethodSink extends DataFlow::Node {
- DoFilterMethodSink() {
- exists(MethodAccess ma, Method m | ma.getMethod() = m |
- m.hasName("doFilter") and
- m.getDeclaringType*().hasQualifiedName("javax.servlet", "FilterChain") and
- ma.getQualifier() = this.asExpr()
- )
- }
-}
-
-/** Taint-tracking configuration tracing flow from `doFilter` method paramter source to output
- * `FilterChain.doFilter` method qualifying expression.
- * */
-class DoFilterMethodConfig extends TaintTracking::Configuration {
- DoFilterMethodConfig() { this = "DoFilterMethodConfig" }
-
- override predicate isSource(DataFlow::Node source) { source instanceof DoFilterMethodSource }
-
- override predicate isSink(DataFlow::Node sink) { sink instanceof DoFilterMethodSink }
-
- override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
- guard instanceof FilterVerifAuth
- }
-}
-
-/** Implement class modeling verification for `Filter.doFilter`, return false if it fails. */
-boolean checks() {
- exists(DataFlow::PathNode source, DataFlow::PathNode sink, DoFilterMethodConfig conf |
- conf.hasFlowPath(source, sink) and
- result = false
- )
-}
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonpInjectionLib.qll b/java/ql/src/Security/CWE/CWE-352/JsonpInjectionLib.qll
deleted file mode 100644
index 3f730425823..00000000000
--- a/java/ql/src/Security/CWE/CWE-352/JsonpInjectionLib.qll
+++ /dev/null
@@ -1,155 +0,0 @@
-import java
-import DataFlow
-import JsonStringLib
-import semmle.code.java.dataflow.DataFlow
-import semmle.code.java.dataflow.FlowSources
-import semmle.code.java.frameworks.spring.SpringController
-
-/** Holds if `m` is a method of some override of `HttpServlet.doGet`. */
-private predicate isGetServletMethod(Method m) {
- isServletRequestMethod(m) and m.getName() = "doGet"
-}
-
-/** Holds if `m` is a method of some override of `HttpServlet.doGet`. */
-private predicate isGetSpringControllerMethod(Method m) {
- exists(Annotation a |
- a = m.getAnAnnotation() and
- a.getType().hasQualifiedName("org.springframework.web.bind.annotation", "GetMapping")
- )
- or
- exists(Annotation a |
- a = m.getAnAnnotation() and
- a.getType().hasQualifiedName("org.springframework.web.bind.annotation", "RequestMapping") and
- a.getValue("method").toString().regexpMatch("RequestMethod.GET|\\{...\\}")
- )
-}
-
-/** Method parameters use the annotation `@RequestParam` or the parameter type is `ServletRequest`, `String`, `Object` */
-predicate checkSpringMethodParameterType(Method m, int i) {
- m.getParameter(i).getType() instanceof ServletRequest
- or
- exists(Parameter p |
- p = m.getParameter(i) and
- p.hasAnnotation() and
- p.getAnAnnotation()
- .getType()
- .hasQualifiedName("org.springframework.web.bind.annotation", "RequestParam") and
- p.getType().getName().regexpMatch("String|Object")
- )
- or
- exists(Parameter p |
- p = m.getParameter(i) and
- not p.hasAnnotation() and
- p.getType().getName().regexpMatch("String|Object")
- )
-}
-
-/** A data flow source for get method request parameters. */
-abstract class GetHttpRequestSource extends DataFlow::Node { }
-
-/** A data flow source for servlet get method request parameters. */
-private class ServletGetHttpRequestSource extends GetHttpRequestSource {
- ServletGetHttpRequestSource() {
- exists(Method m |
- isGetServletMethod(m) and
- m.getParameter(0).getAnAccess() = this.asExpr()
- )
- }
-}
-
-/** A data flow source for spring controller get method request parameters. */
-private class SpringGetHttpRequestSource extends GetHttpRequestSource {
- SpringGetHttpRequestSource() {
- exists(SpringControllerMethod scm, int i |
- isGetSpringControllerMethod(scm) and
- checkSpringMethodParameterType(scm, i) and
- scm.getParameter(i).getAnAccess() = this.asExpr()
- )
- }
-}
-
-/** A data flow sink for unvalidated user input that is used to jsonp. */
-abstract class JsonpInjectionSink extends DataFlow::Node { }
-
-/** Use ```print```, ```println```, ```write``` to output result. */
-private class WriterPrintln extends JsonpInjectionSink {
- WriterPrintln() {
- exists(MethodAccess ma |
- ma.getMethod().getName().regexpMatch("print|println|write") and
- ma.getMethod()
- .getDeclaringType()
- .getASourceSupertype*()
- .hasQualifiedName("java.io", "PrintWriter") and
- ma.getArgument(0) = this.asExpr()
- )
- }
-}
-
-/** Spring Request Method return result. */
-private class SpringReturn extends JsonpInjectionSink {
- SpringReturn() {
- exists(ReturnStmt rs, Method m | m = rs.getEnclosingCallable() |
- isGetSpringControllerMethod(m) and
- rs.getResult() = this.asExpr()
- )
- }
-}
-
-/** A concatenate expression using `(` and `)` or `);`. */
-class JsonpInjectionExpr extends AddExpr {
- JsonpInjectionExpr() {
- getRightOperand().toString().regexpMatch("\"\\)\"|\"\\);\"") and
- getLeftOperand()
- .(AddExpr)
- .getLeftOperand()
- .(AddExpr)
- .getRightOperand()
- .toString()
- .regexpMatch("\"\\(\"")
- }
-
- /** Get the jsonp function name of this expression */
- Expr getFunctionName() {
- result = getLeftOperand().(AddExpr).getLeftOperand().(AddExpr).getLeftOperand()
- }
-
- /** Get the json data of this expression */
- Expr getJsonExpr() { result = getLeftOperand().(AddExpr).getRightOperand() }
-}
-
-/** A data flow configuration tracing flow from remote sources to jsonp function name. */
-class RemoteFlowConfig extends DataFlow2::Configuration {
- RemoteFlowConfig() { this = "RemoteFlowConfig" }
-
- override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
-
- override predicate isSink(DataFlow::Node sink) {
- exists(JsonpInjectionExpr jhe | jhe.getFunctionName() = sink.asExpr())
- }
-}
-
-/** A data flow configuration tracing flow from json data to splicing jsonp data. */
-class JsonDataFlowConfig extends DataFlow2::Configuration {
- JsonDataFlowConfig() { this = "JsonDataFlowConfig" }
-
- override predicate isSource(DataFlow::Node src) { src instanceof JsonpStringSource }
-
- override predicate isSink(DataFlow::Node sink) {
- exists(JsonpInjectionExpr jhe | jhe.getJsonExpr() = sink.asExpr())
- }
-}
-
-/** Taint-tracking configuration tracing flow from user-controllable function name jsonp data to output jsonp data. */
-class JsonpInjectionFlowConfig extends DataFlow::Configuration {
- JsonpInjectionFlowConfig() { this = "JsonpInjectionFlowConfig" }
-
- override predicate isSource(DataFlow::Node src) {
- exists(JsonpInjectionExpr jhe, JsonDataFlowConfig jdfc, RemoteFlowConfig rfc |
- jhe = src.asExpr() and
- jdfc.hasFlowTo(DataFlow::exprNode(jhe.getJsonExpr())) and
- rfc.hasFlowTo(DataFlow::exprNode(jhe.getFunctionName()))
- )
- }
-
- override predicate isSink(DataFlow::Node sink) { sink instanceof JsonpInjectionSink }
-}
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonStringLib.qll b/java/ql/src/experimental/Security/CWE/CWE-352/JsonStringLib.qll
similarity index 100%
rename from java/ql/src/Security/CWE/CWE-352/JsonStringLib.qll
rename to java/ql/src/experimental/Security/CWE/CWE-352/JsonStringLib.qll
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.java b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpController.java
similarity index 72%
rename from java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.java
rename to java/ql/src/experimental/Security/CWE/CWE-352/JsonpController.java
index 9f079513a8b..84a172a7aeb 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.java
+++ b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpController.java
@@ -3,17 +3,14 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import java.io.PrintWriter;
import java.util.HashMap;
-import java.util.Random;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
-public class JsonpInjection {
+public class JsonpController {
private static HashMap hashMap = new HashMap();
static {
@@ -96,54 +93,13 @@ public class JsonpInjection {
@GetMapping(value = "jsonp7")
@ResponseBody
- public String good(HttpServletRequest request) {
- String resultStr = null;
- String jsonpCallback = request.getParameter("jsonpCallback");
-
- String val = "";
- Random random = new Random();
- for (int i = 0; i < 10; i++) {
- val += String.valueOf(random.nextInt(10));
- }
- // good
- jsonpCallback = jsonpCallback + "_" + val;
- String jsonStr = getJsonStr(hashMap);
- resultStr = jsonpCallback + "(" + jsonStr + ")";
- return resultStr;
- }
-
- @GetMapping(value = "jsonp8")
- @ResponseBody
public String good1(HttpServletRequest request) {
String resultStr = null;
- String jsonpCallback = request.getParameter("jsonpCallback");
String token = request.getParameter("token");
- // good
if (verifToken(token)){
- System.out.println(token);
- String jsonStr = getJsonStr(hashMap);
- resultStr = jsonpCallback + "(" + jsonStr + ")";
- return resultStr;
- }
-
- return "error";
- }
-
- @GetMapping(value = "jsonp9")
- @ResponseBody
- public String good2(HttpServletRequest request) {
- String resultStr = null;
- String jsonpCallback = request.getParameter("jsonpCallback");
-
- String referer = request.getHeader("Referer");
-
- boolean result = verifReferer(referer);
-
- boolean test = result;
- // good
- if (test){
+ String jsonpCallback = request.getParameter("jsonpCallback");
String jsonStr = getJsonStr(hashMap);
resultStr = jsonpCallback + "(" + jsonStr + ")";
return resultStr;
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonpInjection.java b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.java
similarity index 100%
rename from java/ql/src/Security/CWE/CWE-352/JsonpInjection.java
rename to java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.java
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonpInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.qhelp
similarity index 96%
rename from java/ql/src/Security/CWE/CWE-352/JsonpInjection.qhelp
rename to java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.qhelp
index b063b409d3a..bb5d628ac0b 100644
--- a/java/ql/src/Security/CWE/CWE-352/JsonpInjection.qhelp
+++ b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.qhelp
@@ -16,7 +16,7 @@ there is a problem of sensitive information leakage.
The following example shows the case of no verification processing and verification processing for the external input function name.
-
+
diff --git a/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.ql
new file mode 100644
index 00000000000..f3ae25daa03
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.ql
@@ -0,0 +1,64 @@
+/**
+ * @name JSONP Injection
+ * @description User-controlled callback function names that are not verified are vulnerable
+ * to jsonp injection attacks.
+ * @kind path-problem
+ * @problem.severity error
+ * @precision high
+ * @id java/JSONP-Injection
+ * @tags security
+ * external/cwe/cwe-352
+ */
+
+import java
+import JsonpInjectionLib
+import semmle.code.java.dataflow.FlowSources
+import semmle.code.java.deadcode.WebEntryPoints
+import semmle.code.java.security.XSS
+import DataFlow::PathGraph
+
+/** Determine whether there is a verification method for the remote streaming source data flow path method. */
+predicate existsFilterVerificationMethod() {
+ exists(MethodAccess ma,Node existsNode, Method m|
+ ma.getMethod() instanceof VerificationMethodClass and
+ existsNode.asExpr() = ma and
+ m = getAnMethod(existsNode.getEnclosingCallable()) and
+ isDoFilterMethod(m)
+ )
+}
+
+/** Determine whether there is a verification method for the remote streaming source data flow path method. */
+predicate existsServletVerificationMethod(Node checkNode) {
+ exists(MethodAccess ma,Node existsNode|
+ ma.getMethod() instanceof VerificationMethodClass and
+ existsNode.asExpr() = ma and
+ getAnMethod(existsNode.getEnclosingCallable()) = getAnMethod(checkNode.getEnclosingCallable())
+ )
+}
+
+/** Taint-tracking configuration tracing flow from get method request sources to output jsonp data. */
+class RequestResponseFlowConfig extends TaintTracking::Configuration {
+ RequestResponseFlowConfig() { this = "RequestResponseFlowConfig" }
+
+ override predicate isSource(DataFlow::Node source) {
+ source instanceof RemoteFlowSource and
+ getAnMethod(source.getEnclosingCallable()) instanceof RequestGetMethod
+ }
+
+ override predicate isSink(DataFlow::Node sink) { sink instanceof XssSink }
+
+ override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
+ exists(MethodAccess ma |
+ isRequestGetParamMethod(ma) and pred.asExpr() = ma.getQualifier() and succ.asExpr() = ma
+ )
+ }
+}
+
+from DataFlow::PathNode source, DataFlow::PathNode sink, RequestResponseFlowConfig conf
+where
+ not existsServletVerificationMethod(source.getNode()) and
+ not existsFilterVerificationMethod() and
+ conf.hasFlowPath(source, sink) and
+ exists(JsonpInjectionFlowConfig jhfc | jhfc.hasFlowTo(sink.getNode()))
+select sink.getNode(), source, sink, "Jsonp Injection query might include code from $@.",
+ source.getNode(), "this user input"
\ No newline at end of file
diff --git a/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionLib.qll b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionLib.qll
new file mode 100644
index 00000000000..b8964524a9f
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionLib.qll
@@ -0,0 +1,130 @@
+import java
+import DataFlow
+import JsonStringLib
+import semmle.code.java.security.XSS
+import semmle.code.java.dataflow.DataFlow
+import semmle.code.java.dataflow.FlowSources
+import semmle.code.java.frameworks.spring.SpringController
+
+/** Taint-tracking configuration tracing flow from user-controllable function name jsonp data to output jsonp data. */
+class VerificationMethodFlowConfig extends TaintTracking::Configuration {
+ VerificationMethodFlowConfig() { this = "VerificationMethodFlowConfig" }
+
+ override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(MethodAccess ma, BarrierGuard bg |
+ ma.getMethod().getAParameter().getName().regexpMatch("(?i).*(token|auth|referer|origin).*") and
+ bg = ma and
+ sink.asExpr() = ma.getAnArgument()
+ )
+ }
+}
+
+/** The parameter name of the method is `token`, `auth`, `referer`, `origin`. */
+class VerificationMethodClass extends Method {
+ VerificationMethodClass() {
+ exists(MethodAccess ma, BarrierGuard bg, VerificationMethodFlowConfig vmfc, Node node |
+ this = ma.getMethod() and
+ this.getAParameter().getName().regexpMatch("(?i).*(token|auth|referer|origin).*") and
+ bg = ma and
+ node.asExpr() = ma.getAnArgument() and
+ vmfc.hasFlowTo(node)
+ )
+ }
+}
+
+/** Get Callable by recursive method. */
+Callable getAnMethod(Callable call) {
+ result = call
+ or
+ result = getAnMethod(call.getAReference().getEnclosingCallable())
+}
+
+abstract class RequestGetMethod extends Method { }
+
+/** Holds if `m` is a method of some override of `HttpServlet.doGet`. */
+private class ServletGetMethod extends RequestGetMethod {
+ ServletGetMethod() {
+ exists(Method m |
+ m = this and
+ isServletRequestMethod(m) and
+ m.getName() = "doGet"
+ )
+ }
+}
+
+/** Holds if `m` is a method of some override of `HttpServlet.doGet`. */
+private class SpringControllerGetMethod extends RequestGetMethod {
+ SpringControllerGetMethod() {
+ exists(Annotation a |
+ a = this.getAnAnnotation() and
+ a.getType().hasQualifiedName("org.springframework.web.bind.annotation", "GetMapping")
+ )
+ or
+ exists(Annotation a |
+ a = this.getAnAnnotation() and
+ a.getType().hasQualifiedName("org.springframework.web.bind.annotation", "RequestMapping") and
+ a.getValue("method").toString().regexpMatch("RequestMethod.GET|\\{...\\}")
+ )
+ }
+}
+
+/** A concatenate expression using `(` and `)` or `);`. */
+class JsonpInjectionExpr extends AddExpr {
+ JsonpInjectionExpr() {
+ getRightOperand().toString().regexpMatch("\"\\)\"|\"\\);\"") and
+ getLeftOperand()
+ .(AddExpr)
+ .getLeftOperand()
+ .(AddExpr)
+ .getRightOperand()
+ .toString()
+ .regexpMatch("\"\\(\"")
+ }
+
+ /** Get the jsonp function name of this expression */
+ Expr getFunctionName() {
+ result = getLeftOperand().(AddExpr).getLeftOperand().(AddExpr).getLeftOperand()
+ }
+
+ /** Get the json data of this expression */
+ Expr getJsonExpr() { result = getLeftOperand().(AddExpr).getRightOperand() }
+}
+
+/** A data flow configuration tracing flow from remote sources to jsonp function name. */
+class RemoteFlowConfig extends DataFlow2::Configuration {
+ RemoteFlowConfig() { this = "RemoteFlowConfig" }
+
+ override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(JsonpInjectionExpr jhe | jhe.getFunctionName() = sink.asExpr())
+ }
+}
+
+/** A data flow configuration tracing flow from json data to splicing jsonp data. */
+class JsonDataFlowConfig extends DataFlow2::Configuration {
+ JsonDataFlowConfig() { this = "JsonDataFlowConfig" }
+
+ override predicate isSource(DataFlow::Node src) { src instanceof JsonpStringSource }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(JsonpInjectionExpr jhe | jhe.getJsonExpr() = sink.asExpr())
+ }
+}
+
+/** Taint-tracking configuration tracing flow from user-controllable function name jsonp data to output jsonp data. */
+class JsonpInjectionFlowConfig extends TaintTracking::Configuration {
+ JsonpInjectionFlowConfig() { this = "JsonpInjectionFlowConfig" }
+
+ override predicate isSource(DataFlow::Node src) {
+ exists(JsonpInjectionExpr jhe, JsonDataFlowConfig jdfc, RemoteFlowConfig rfc |
+ jhe = src.asExpr() and
+ jdfc.hasFlowTo(DataFlow::exprNode(jhe.getJsonExpr())) and
+ rfc.hasFlowTo(DataFlow::exprNode(jhe.getFunctionName()))
+ )
+ }
+
+ override predicate isSink(DataFlow::Node sink) { sink instanceof XssSink }
+}
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonpInjectionServlet1.java b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionServlet1.java
similarity index 100%
rename from java/ql/src/Security/CWE/CWE-352/JsonpInjectionServlet1.java
rename to java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionServlet1.java
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonpInjectionServlet2.java b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionServlet2.java
similarity index 100%
rename from java/ql/src/Security/CWE/CWE-352/JsonpInjectionServlet2.java
rename to java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionServlet2.java
diff --git a/java/ql/src/experimental/Security/CWE/CWE-352/RefererFilter.java b/java/ql/src/experimental/Security/CWE/CWE-352/RefererFilter.java
new file mode 100644
index 00000000000..97444932ae1
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-352/RefererFilter.java
@@ -0,0 +1,43 @@
+import java.io.IOException;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.util.StringUtils;
+
+public class RefererFilter implements Filter {
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+ HttpServletRequest request = (HttpServletRequest) servletRequest;
+ HttpServletResponse response = (HttpServletResponse) servletResponse;
+ String refefer = request.getHeader("Referer");
+ boolean result = verifReferer(refefer);
+ if (result){
+ filterChain.doFilter(servletRequest, servletResponse);
+ }
+ response.sendError(444, "Referer xxx.");
+ }
+
+ @Override
+ public void destroy() {
+ }
+
+ public static boolean verifReferer(String referer){
+ if (StringUtils.isEmpty(referer)){
+ return false;
+ }
+ if (referer.startsWith("http://www.baidu.com/")){
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/java/ql/src/semmle/code/java/frameworks/Servlets.qll b/java/ql/src/semmle/code/java/frameworks/Servlets.qll
index b2054dc30cb..5cccf62122f 100644
--- a/java/ql/src/semmle/code/java/frameworks/Servlets.qll
+++ b/java/ql/src/semmle/code/java/frameworks/Servlets.qll
@@ -338,7 +338,6 @@ predicate isRequestGetParamMethod(MethodAccess ma) {
ma.getMethod() instanceof HttpServletRequestGetQueryStringMethod
}
-
/**
* A class that has `javax.servlet.Filter` as an ancestor.
*/
@@ -346,21 +345,18 @@ class FilterClass extends Class {
FilterClass() { getAnAncestor().hasQualifiedName("javax.servlet", "Filter") }
}
-
/**
* The interface `javax.servlet.FilterChain`
*/
-class FilterChain extends RefType {
- FilterChain() {
- hasQualifiedName("javax.servlet", "FilterChain")
- }
+class FilterChain extends Interface {
+ FilterChain() { hasQualifiedName("javax.servlet", "FilterChain") }
}
-/** Holds if `m` is a request handler method (for example `doGet` or `doPost`). */
+/** Holds if `m` is a filter handler method (for example `doFilter`). */
predicate isDoFilterMethod(Method m) {
m.getDeclaringType() instanceof FilterClass and
m.getNumberOfParameters() = 3 and
m.getParameter(0).getType() instanceof ServletRequest and
m.getParameter(1).getType() instanceof ServletResponse and
m.getParameter(2).getType() instanceof FilterChain
-}
\ No newline at end of file
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpController.java b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpController.java
new file mode 100644
index 00000000000..cf860c75640
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpController.java
@@ -0,0 +1,128 @@
+import com.alibaba.fastjson.JSONObject;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.gson.Gson;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+@Controller
+public class JsonpController {
+ private static HashMap hashMap = new HashMap();
+
+ static {
+ hashMap.put("username","admin");
+ hashMap.put("password","123456");
+ }
+
+
+ @GetMapping(value = "jsonp1", produces="text/javascript")
+ @ResponseBody
+ public String bad1(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+
+ Gson gson = new Gson();
+ String result = gson.toJson(hashMap);
+ resultStr = jsonpCallback + "(" + result + ")";
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp2")
+ @ResponseBody
+ public String bad2(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+
+ resultStr = jsonpCallback + "(" + JSONObject.toJSONString(hashMap) + ")";
+
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp3")
+ @ResponseBody
+ public String bad3(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String jsonStr = getJsonStr(hashMap);
+ resultStr = jsonpCallback + "(" + jsonStr + ")";
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp4")
+ @ResponseBody
+ public String bad4(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String restr = JSONObject.toJSONString(hashMap);
+ resultStr = jsonpCallback + "(" + restr + ");";
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp5")
+ @ResponseBody
+ public void bad5(HttpServletRequest request,
+ HttpServletResponse response) throws Exception {
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ PrintWriter pw = null;
+ Gson gson = new Gson();
+ String result = gson.toJson(hashMap);
+
+ String resultStr = null;
+ pw = response.getWriter();
+ resultStr = jsonpCallback + "(" + result + ")";
+ pw.println(resultStr);
+ }
+
+ @GetMapping(value = "jsonp6")
+ @ResponseBody
+ public void bad6(HttpServletRequest request,
+ HttpServletResponse response) throws Exception {
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ PrintWriter pw = null;
+ ObjectMapper mapper = new ObjectMapper();
+ String result = mapper.writeValueAsString(hashMap);
+ String resultStr = null;
+ pw = response.getWriter();
+ resultStr = jsonpCallback + "(" + result + ")";
+ pw.println(resultStr);
+ }
+
+ @GetMapping(value = "jsonp7")
+ @ResponseBody
+ public String good1(HttpServletRequest request) {
+ String resultStr = null;
+
+ String token = request.getParameter("token");
+
+ if (verifToken(token)){
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String jsonStr = getJsonStr(hashMap);
+ resultStr = jsonpCallback + "(" + jsonStr + ")";
+ return resultStr;
+ }
+
+ return "error";
+ }
+
+ public static String getJsonStr(Object result) {
+ return JSONObject.toJSONString(result);
+ }
+
+ public static boolean verifToken(String token){
+ if (token != "xxxx"){
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean verifReferer(String referer){
+ if (!referer.startsWith("http://test.com/")){
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.expected
deleted file mode 100644
index 7e3069cf1d9..00000000000
--- a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.expected
+++ /dev/null
@@ -1,60 +0,0 @@
-edges
-| JsonpInjection.java:29:32:29:38 | request : HttpServletRequest | JsonpInjection.java:34:16:34:24 | resultStr |
-| JsonpInjection.java:33:21:33:54 | ... + ... : String | JsonpInjection.java:34:16:34:24 | resultStr |
-| JsonpInjection.java:41:32:41:38 | request : HttpServletRequest | JsonpInjection.java:45:16:45:24 | resultStr |
-| JsonpInjection.java:43:21:43:80 | ... + ... : String | JsonpInjection.java:45:16:45:24 | resultStr |
-| JsonpInjection.java:52:32:52:38 | request : HttpServletRequest | JsonpInjection.java:55:16:55:24 | resultStr |
-| JsonpInjection.java:54:21:54:55 | ... + ... : String | JsonpInjection.java:55:16:55:24 | resultStr |
-| JsonpInjection.java:62:32:62:38 | request : HttpServletRequest | JsonpInjection.java:65:16:65:24 | resultStr |
-| JsonpInjection.java:64:21:64:54 | ... + ... : String | JsonpInjection.java:65:16:65:24 | resultStr |
-| JsonpInjection.java:72:32:72:38 | request : HttpServletRequest | JsonpInjection.java:80:20:80:28 | resultStr |
-| JsonpInjection.java:79:21:79:54 | ... + ... : String | JsonpInjection.java:80:20:80:28 | resultStr |
-| JsonpInjection.java:87:32:87:38 | request : HttpServletRequest | JsonpInjection.java:94:20:94:28 | resultStr |
-| JsonpInjection.java:93:21:93:54 | ... + ... : String | JsonpInjection.java:94:20:94:28 | resultStr |
-| JsonpInjection.java:101:32:101:38 | request : HttpServletRequest | JsonpInjection.java:112:16:112:24 | resultStr |
-| JsonpInjection.java:127:25:127:59 | ... + ... : String | JsonpInjection.java:128:20:128:28 | resultStr |
-| JsonpInjection.java:148:25:148:59 | ... + ... : String | JsonpInjection.java:149:20:149:28 | resultStr |
-nodes
-| JsonpInjection.java:29:32:29:38 | request : HttpServletRequest | semmle.label | request : HttpServletRequest |
-| JsonpInjection.java:33:21:33:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpInjection.java:34:16:34:24 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:34:16:34:24 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:41:32:41:38 | request : HttpServletRequest | semmle.label | request : HttpServletRequest |
-| JsonpInjection.java:43:21:43:80 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpInjection.java:45:16:45:24 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:45:16:45:24 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:52:32:52:38 | request : HttpServletRequest | semmle.label | request : HttpServletRequest |
-| JsonpInjection.java:54:21:54:55 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpInjection.java:55:16:55:24 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:55:16:55:24 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:62:32:62:38 | request : HttpServletRequest | semmle.label | request : HttpServletRequest |
-| JsonpInjection.java:64:21:64:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpInjection.java:65:16:65:24 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:65:16:65:24 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:72:32:72:38 | request : HttpServletRequest | semmle.label | request : HttpServletRequest |
-| JsonpInjection.java:79:21:79:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpInjection.java:80:20:80:28 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:80:20:80:28 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:87:32:87:38 | request : HttpServletRequest | semmle.label | request : HttpServletRequest |
-| JsonpInjection.java:93:21:93:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpInjection.java:94:20:94:28 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:94:20:94:28 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:101:32:101:38 | request : HttpServletRequest | semmle.label | request : HttpServletRequest |
-| JsonpInjection.java:112:16:112:24 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:127:25:127:59 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpInjection.java:128:20:128:28 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:148:25:148:59 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpInjection.java:149:20:149:28 | resultStr | semmle.label | resultStr |
-#select
-| JsonpInjection.java:34:16:34:24 | resultStr | JsonpInjection.java:29:32:29:38 | request : HttpServletRequest | JsonpInjection.java:34:16:34:24 |
-resultStr | Jsonp Injection query might include code from $@. | JsonpInjection.java:29:32:29:38 | request | this user input |
-| JsonpInjection.java:45:16:45:24 | resultStr | JsonpInjection.java:41:32:41:38 | request : HttpServletRequest | JsonpInjection.java:45:16:45:24 |
-resultStr | Jsonp Injection query might include code from $@. | JsonpInjection.java:41:32:41:38 | request | this user input |
-| JsonpInjection.java:55:16:55:24 | resultStr | JsonpInjection.java:52:32:52:38 | request : HttpServletRequest | JsonpInjection.java:55:16:55:24 |
-resultStr | Jsonp Injection query might include code from $@. | JsonpInjection.java:52:32:52:38 | request | this user input |
-| JsonpInjection.java:65:16:65:24 | resultStr | JsonpInjection.java:62:32:62:38 | request : HttpServletRequest | JsonpInjection.java:65:16:65:24 |
-resultStr | Jsonp Injection query might include code from $@. | JsonpInjection.java:62:32:62:38 | request | this user input |
-| JsonpInjection.java:80:20:80:28 | resultStr | JsonpInjection.java:72:32:72:38 | request : HttpServletRequest | JsonpInjection.java:80:20:80:28 |
-resultStr | Jsonp Injection query might include code from $@. | JsonpInjection.java:72:32:72:38 | request | this user input |
-| JsonpInjection.java:94:20:94:28 | resultStr | JsonpInjection.java:87:32:87:38 | request : HttpServletRequest | JsonpInjection.java:94:20:94:28 |
-resultStr | Jsonp Injection query might include code from $@. | JsonpInjection.java:87:32:87:38 | request | this user input |
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionServlet1.java b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionServlet1.java
new file mode 100644
index 00000000000..14ef76275b1
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionServlet1.java
@@ -0,0 +1,64 @@
+import com.google.gson.Gson;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class JsonpInjectionServlet1 extends HttpServlet {
+
+ private static HashMap hashMap = new HashMap();
+
+ static {
+ hashMap.put("username","admin");
+ hashMap.put("password","123456");
+ }
+
+ private static final long serialVersionUID = 1L;
+
+ private String key = "test";
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ doPost(req, resp);
+ }
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ resp.setContentType("application/json");
+ String jsonpCallback = req.getParameter("jsonpCallback");
+ PrintWriter pw = null;
+ Gson gson = new Gson();
+ String jsonResult = gson.toJson(hashMap);
+
+ String referer = req.getHeader("Referer");
+
+ boolean result = verifReferer(referer);
+
+ // good
+ if (result){
+ String resultStr = null;
+ pw = resp.getWriter();
+ resultStr = jsonpCallback + "(" + jsonResult + ")";
+ pw.println(resultStr);
+ pw.flush();
+ }
+ }
+
+ public static boolean verifReferer(String referer){
+ if (!referer.startsWith("http://test.com/")){
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void init(ServletConfig config) throws ServletException {
+ this.key = config.getInitParameter("key");
+ System.out.println("初始化" + this.key);
+ super.init(config);
+ }
+
+}
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonpInjectionServlet.java b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionServlet2.java
similarity index 75%
rename from java/ql/src/Security/CWE/CWE-352/JsonpInjectionServlet.java
rename to java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionServlet2.java
index 916cd9bf676..bbfbc2dc436 100644
--- a/java/ql/src/Security/CWE/CWE-352/JsonpInjectionServlet.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionServlet2.java
@@ -4,12 +4,11 @@ import java.io.PrintWriter;
import java.util.HashMap;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
-import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-public class JsonpInjectionServlet extends HttpServlet {
+public class JsonpInjectionServlet2 extends HttpServlet {
private static HashMap hashMap = new HashMap();
@@ -23,21 +22,12 @@ public class JsonpInjectionServlet extends HttpServlet {
private String key = "test";
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- String jsonpCallback = req.getParameter("jsonpCallback");
-
- PrintWriter pw = null;
- Gson gson = new Gson();
- String result = gson.toJson(hashMap);
-
- String resultStr = null;
- pw = resp.getWriter();
- resultStr = jsonpCallback + "(" + result + ")";
- pw.println(resultStr);
- pw.flush();
+ doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ resp.setContentType("application/json");
String jsonpCallback = req.getParameter("jsonpCallback");
PrintWriter pw = null;
Gson gson = new Gson();
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_1.expected b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_1.expected
new file mode 100644
index 00000000000..a89d03b67a7
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_1.expected
@@ -0,0 +1,60 @@
+edges
+| JsonpController.java:26:32:26:68 | getParameter(...) : String | JsonpController.java:31:16:31:24 | resultStr |
+| JsonpController.java:30:21:30:54 | ... + ... : String | JsonpController.java:31:16:31:24 | resultStr |
+| JsonpController.java:38:32:38:68 | getParameter(...) : String | JsonpController.java:42:16:42:24 | resultStr |
+| JsonpController.java:40:21:40:80 | ... + ... : String | JsonpController.java:42:16:42:24 | resultStr |
+| JsonpController.java:49:32:49:68 | getParameter(...) : String | JsonpController.java:52:16:52:24 | resultStr |
+| JsonpController.java:51:21:51:55 | ... + ... : String | JsonpController.java:52:16:52:24 | resultStr |
+| JsonpController.java:59:32:59:68 | getParameter(...) : String | JsonpController.java:62:16:62:24 | resultStr |
+| JsonpController.java:61:21:61:54 | ... + ... : String | JsonpController.java:62:16:62:24 | resultStr |
+| JsonpController.java:69:32:69:68 | getParameter(...) : String | JsonpController.java:77:20:77:28 | resultStr |
+| JsonpController.java:76:21:76:54 | ... + ... : String | JsonpController.java:77:20:77:28 | resultStr |
+| JsonpController.java:84:32:84:68 | getParameter(...) : String | JsonpController.java:91:20:91:28 | resultStr |
+| JsonpController.java:90:21:90:54 | ... + ... : String | JsonpController.java:91:20:91:28 | resultStr |
+| JsonpController.java:99:24:99:52 | getParameter(...) : String | JsonpController.java:101:24:101:28 | token |
+| JsonpController.java:102:36:102:72 | getParameter(...) : String | JsonpController.java:105:20:105:28 | resultStr |
+| JsonpController.java:104:25:104:59 | ... + ... : String | JsonpController.java:105:20:105:28 | resultStr |
+nodes
+| JsonpController.java:26:32:26:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:30:21:30:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:31:16:31:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:31:16:31:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:38:32:38:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:40:21:40:80 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:42:16:42:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:42:16:42:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:49:32:49:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:51:21:51:55 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:52:16:52:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:52:16:52:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:59:32:59:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:61:21:61:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:62:16:62:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:62:16:62:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:69:32:69:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:76:21:76:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:77:20:77:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:77:20:77:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:84:32:84:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:90:21:90:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:91:20:91:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:91:20:91:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:99:24:99:52 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:101:24:101:28 | token | semmle.label | token |
+| JsonpController.java:102:36:102:72 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:104:25:104:59 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:105:20:105:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:105:20:105:28 | resultStr | semmle.label | resultStr |
+#select
+| JsonpController.java:31:16:31:24 | resultStr | JsonpController.java:26:32:26:68 | getParameter(...) : String | JsonpController.java:31:16:31:24 |
+ resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:26:32:26:68 | getParameter(...) | this user input |
+| JsonpController.java:42:16:42:24 | resultStr | JsonpController.java:38:32:38:68 | getParameter(...) : String | JsonpController.java:42:16:42:24 |
+ resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:38:32:38:68 | getParameter(...) | this user input |
+| JsonpController.java:52:16:52:24 | resultStr | JsonpController.java:49:32:49:68 | getParameter(...) : String | JsonpController.java:52:16:52:24 |
+ resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:49:32:49:68 | getParameter(...) | this user input |
+| JsonpController.java:62:16:62:24 | resultStr | JsonpController.java:59:32:59:68 | getParameter(...) : String | JsonpController.java:62:16:62:24 |
+ resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:59:32:59:68 | getParameter(...) | this user input |
+| JsonpController.java:77:20:77:28 | resultStr | JsonpController.java:69:32:69:68 | getParameter(...) : String | JsonpController.java:77:20:77:28 |
+ resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:69:32:69:68 | getParameter(...) | this user input |
+| JsonpController.java:91:20:91:28 | resultStr | JsonpController.java:84:32:84:68 | getParameter(...) : String | JsonpController.java:91:20:91:28 |
+ resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:84:32:84:68 | getParameter(...) | this user input |
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_2.expected b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_2.expected
new file mode 100644
index 00000000000..4b12308a212
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_2.expected
@@ -0,0 +1,78 @@
+edges
+| JsonpController.java:26:32:26:68 | getParameter(...) : String | JsonpController.java:31:16:31:24 | resultStr |
+| JsonpController.java:30:21:30:54 | ... + ... : String | JsonpController.java:31:16:31:24 | resultStr |
+| JsonpController.java:38:32:38:68 | getParameter(...) : String | JsonpController.java:42:16:42:24 | resultStr |
+| JsonpController.java:40:21:40:80 | ... + ... : String | JsonpController.java:42:16:42:24 | resultStr |
+| JsonpController.java:49:32:49:68 | getParameter(...) : String | JsonpController.java:52:16:52:24 | resultStr |
+| JsonpController.java:51:21:51:55 | ... + ... : String | JsonpController.java:52:16:52:24 | resultStr |
+| JsonpController.java:59:32:59:68 | getParameter(...) : String | JsonpController.java:62:16:62:24 | resultStr |
+| JsonpController.java:61:21:61:54 | ... + ... : String | JsonpController.java:62:16:62:24 | resultStr |
+| JsonpController.java:69:32:69:68 | getParameter(...) : String | JsonpController.java:77:20:77:28 | resultStr |
+| JsonpController.java:76:21:76:54 | ... + ... : String | JsonpController.java:77:20:77:28 | resultStr |
+| JsonpController.java:84:32:84:68 | getParameter(...) : String | JsonpController.java:91:20:91:28 | resultStr |
+| JsonpController.java:90:21:90:54 | ... + ... : String | JsonpController.java:91:20:91:28 | resultStr |
+| JsonpController.java:99:24:99:52 | getParameter(...) : String | JsonpController.java:101:24:101:28 | token |
+| JsonpController.java:102:36:102:72 | getParameter(...) : String | JsonpController.java:105:20:105:28 | resultStr |
+| JsonpController.java:104:25:104:59 | ... + ... : String | JsonpController.java:105:20:105:28 | resultStr |
+| JsonpInjectionServlet1.java:31:32:31:64 | getParameter(...) : String | JsonpInjectionServlet1.java:45:24:45:32 | resultStr |
+| JsonpInjectionServlet1.java:36:26:36:49 | getHeader(...) : String | JsonpInjectionServlet1.java:38:39:38:45 | referer |
+| JsonpInjectionServlet1.java:44:25:44:62 | ... + ... : String | JsonpInjectionServlet1.java:45:24:45:32 | resultStr |
+| JsonpInjectionServlet2.java:31:32:31:64 | getParameter(...) : String | JsonpInjectionServlet2.java:39:20:39:28 | resultStr |
+| JsonpInjectionServlet2.java:38:21:38:54 | ... + ... : String | JsonpInjectionServlet2.java:39:20:39:28 | resultStr |
+nodes
+| JsonpController.java:26:32:26:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:30:21:30:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:31:16:31:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:31:16:31:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:38:32:38:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:40:21:40:80 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:42:16:42:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:42:16:42:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:49:32:49:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:51:21:51:55 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:52:16:52:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:52:16:52:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:59:32:59:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:61:21:61:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:62:16:62:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:62:16:62:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:69:32:69:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:76:21:76:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:77:20:77:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:77:20:77:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:84:32:84:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:90:21:90:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:91:20:91:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:91:20:91:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:99:24:99:52 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:101:24:101:28 | token | semmle.label | token |
+| JsonpController.java:102:36:102:72 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:104:25:104:59 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:105:20:105:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:105:20:105:28 | resultStr | semmle.label | resultStr |
+| JsonpInjectionServlet1.java:31:32:31:64 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpInjectionServlet1.java:36:26:36:49 | getHeader(...) : String | semmle.label | getHeader(...) : String |
+| JsonpInjectionServlet1.java:38:39:38:45 | referer | semmle.label | referer |
+| JsonpInjectionServlet1.java:44:25:44:62 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpInjectionServlet1.java:45:24:45:32 | resultStr | semmle.label | resultStr |
+| JsonpInjectionServlet1.java:45:24:45:32 | resultStr | semmle.label | resultStr |
+| JsonpInjectionServlet2.java:31:32:31:64 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpInjectionServlet2.java:38:21:38:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpInjectionServlet2.java:39:20:39:28 | resultStr | semmle.label | resultStr |
+| JsonpInjectionServlet2.java:39:20:39:28 | resultStr | semmle.label | resultStr |
+#select
+| JsonpController.java:31:16:31:24 | resultStr | JsonpController.java:26:32:26:68 | getParameter(...) : String | JsonpController.java:31:16:31:24 |
+ resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:26:32:26:68 | getParameter(...) | this user input |
+| JsonpController.java:42:16:42:24 | resultStr | JsonpController.java:38:32:38:68 | getParameter(...) : String | JsonpController.java:42:16:42:24 |
+ resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:38:32:38:68 | getParameter(...) | this user input |
+| JsonpController.java:52:16:52:24 | resultStr | JsonpController.java:49:32:49:68 | getParameter(...) : String | JsonpController.java:52:16:52:24 |
+ resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:49:32:49:68 | getParameter(...) | this user input |
+| JsonpController.java:62:16:62:24 | resultStr | JsonpController.java:59:32:59:68 | getParameter(...) : String | JsonpController.java:62:16:62:24 |
+ resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:59:32:59:68 | getParameter(...) | this user input |
+| JsonpController.java:77:20:77:28 | resultStr | JsonpController.java:69:32:69:68 | getParameter(...) : String | JsonpController.java:77:20:77:28 |
+ resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:69:32:69:68 | getParameter(...) | this user input |
+| JsonpController.java:91:20:91:28 | resultStr | JsonpController.java:84:32:84:68 | getParameter(...) : String | JsonpController.java:91:20:91:28 |
+ resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:84:32:84:68 | getParameter(...) | this user input |
+| JsonpInjectionServlet2.java:39:20:39:28 | resultStr | JsonpInjectionServlet2.java:31:32:31:64 | getParameter(...) : String | JsonpInjectionServle
+t2.java:39:20:39:28 | resultStr | Jsonp Injection query might include code from $@. | JsonpInjectionServlet2.java:31:32:31:64 | getParameter(...) |
+ this user input |
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_3.expected b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_3.expected
new file mode 100644
index 00000000000..8e33ca6984c
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_3.expected
@@ -0,0 +1,66 @@
+edges
+| JsonpController.java:26:32:26:68 | getParameter(...) : String | JsonpController.java:31:16:31:24 | resultStr |
+| JsonpController.java:30:21:30:54 | ... + ... : String | JsonpController.java:31:16:31:24 | resultStr |
+| JsonpController.java:38:32:38:68 | getParameter(...) : String | JsonpController.java:42:16:42:24 | resultStr |
+| JsonpController.java:40:21:40:80 | ... + ... : String | JsonpController.java:42:16:42:24 | resultStr |
+| JsonpController.java:49:32:49:68 | getParameter(...) : String | JsonpController.java:52:16:52:24 | resultStr |
+| JsonpController.java:51:21:51:55 | ... + ... : String | JsonpController.java:52:16:52:24 | resultStr |
+| JsonpController.java:59:32:59:68 | getParameter(...) : String | JsonpController.java:62:16:62:24 | resultStr |
+| JsonpController.java:61:21:61:54 | ... + ... : String | JsonpController.java:62:16:62:24 | resultStr |
+| JsonpController.java:69:32:69:68 | getParameter(...) : String | JsonpController.java:77:20:77:28 | resultStr |
+| JsonpController.java:76:21:76:54 | ... + ... : String | JsonpController.java:77:20:77:28 | resultStr |
+| JsonpController.java:84:32:84:68 | getParameter(...) : String | JsonpController.java:91:20:91:28 | resultStr |
+| JsonpController.java:90:21:90:54 | ... + ... : String | JsonpController.java:91:20:91:28 | resultStr |
+| JsonpController.java:99:24:99:52 | getParameter(...) : String | JsonpController.java:101:24:101:28 | token |
+| JsonpController.java:102:36:102:72 | getParameter(...) : String | JsonpController.java:105:20:105:28 | resultStr |
+| JsonpController.java:104:25:104:59 | ... + ... : String | JsonpController.java:105:20:105:28 | resultStr |
+| JsonpInjectionServlet1.java:31:32:31:64 | getParameter(...) : String | JsonpInjectionServlet1.java:45:24:45:32 | resultStr |
+| JsonpInjectionServlet1.java:36:26:36:49 | getHeader(...) : String | JsonpInjectionServlet1.java:38:39:38:45 | referer |
+| JsonpInjectionServlet1.java:44:25:44:62 | ... + ... : String | JsonpInjectionServlet1.java:45:24:45:32 | resultStr |
+| JsonpInjectionServlet2.java:31:32:31:64 | getParameter(...) : String | JsonpInjectionServlet2.java:39:20:39:28 | resultStr |
+| JsonpInjectionServlet2.java:38:21:38:54 | ... + ... : String | JsonpInjectionServlet2.java:39:20:39:28 | resultStr |
+| RefererFilter.java:22:26:22:53 | getHeader(...) : String | RefererFilter.java:23:39:23:45 | refefer |
+nodes
+| JsonpController.java:26:32:26:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:30:21:30:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:31:16:31:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:31:16:31:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:38:32:38:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:40:21:40:80 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:42:16:42:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:42:16:42:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:49:32:49:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:51:21:51:55 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:52:16:52:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:52:16:52:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:59:32:59:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:61:21:61:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:62:16:62:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:62:16:62:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:69:32:69:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:76:21:76:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:77:20:77:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:77:20:77:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:84:32:84:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:90:21:90:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:91:20:91:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:91:20:91:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:99:24:99:52 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:101:24:101:28 | token | semmle.label | token |
+| JsonpController.java:102:36:102:72 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:104:25:104:59 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:105:20:105:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:105:20:105:28 | resultStr | semmle.label | resultStr |
+| JsonpInjectionServlet1.java:31:32:31:64 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpInjectionServlet1.java:36:26:36:49 | getHeader(...) : String | semmle.label | getHeader(...) : String |
+| JsonpInjectionServlet1.java:38:39:38:45 | referer | semmle.label | referer |
+| JsonpInjectionServlet1.java:44:25:44:62 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpInjectionServlet1.java:45:24:45:32 | resultStr | semmle.label | resultStr |
+| JsonpInjectionServlet1.java:45:24:45:32 | resultStr | semmle.label | resultStr |
+| JsonpInjectionServlet2.java:31:32:31:64 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpInjectionServlet2.java:38:21:38:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpInjectionServlet2.java:39:20:39:28 | resultStr | semmle.label | resultStr |
+| JsonpInjectionServlet2.java:39:20:39:28 | resultStr | semmle.label | resultStr |
+| RefererFilter.java:22:26:22:53 | getHeader(...) : String | semmle.label | getHeader(...) : String |
+| RefererFilter.java:23:39:23:45 | refefer | semmle.label | refefer |
+#select
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/Readme b/java/ql/test/experimental/query-tests/security/CWE-352/Readme
new file mode 100644
index 00000000000..15715d6187c
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/Readme
@@ -0,0 +1,3 @@
+1. The JsonpInjection_1.expected result is obtained through the test of `JsonpController.java`.
+2. The JsonpInjection_2.expected result is obtained through the test of `JsonpController.java`, `JsonpInjectionServlet1.java`, `JsonpInjectionServlet2.java`.
+3. The JsonpInjection_3.expected result is obtained through the test of `JsonpController.java`, `JsonpInjectionServlet1.java`, `JsonpInjectionServlet2.java`, `RefererFilter.java`.
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/RefererFilter.java b/java/ql/test/experimental/query-tests/security/CWE-352/RefererFilter.java
new file mode 100644
index 00000000000..97444932ae1
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/RefererFilter.java
@@ -0,0 +1,43 @@
+import java.io.IOException;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.util.StringUtils;
+
+public class RefererFilter implements Filter {
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+ HttpServletRequest request = (HttpServletRequest) servletRequest;
+ HttpServletResponse response = (HttpServletResponse) servletResponse;
+ String refefer = request.getHeader("Referer");
+ boolean result = verifReferer(refefer);
+ if (result){
+ filterChain.doFilter(servletRequest, servletResponse);
+ }
+ response.sendError(444, "Referer xxx.");
+ }
+
+ @Override
+ public void destroy() {
+ }
+
+ public static boolean verifReferer(String referer){
+ if (StringUtils.isEmpty(referer)){
+ return false;
+ }
+ if (referer.startsWith("http://www.baidu.com/")){
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/Gson.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/Gson.java
new file mode 100644
index 00000000000..bbe53dc2a5f
--- /dev/null
+++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/Gson.java
@@ -0,0 +1,7 @@
+package com.google.gson;
+
+public final class Gson {
+ public String toJson(Object src) {
+ return null;
+ }
+}
diff --git a/java/ql/test/stubs/spring-core-5.3.2/org/springframework/util/StringUtils.java b/java/ql/test/stubs/spring-core-5.3.2/org/springframework/util/StringUtils.java
new file mode 100644
index 00000000000..6ee07f84593
--- /dev/null
+++ b/java/ql/test/stubs/spring-core-5.3.2/org/springframework/util/StringUtils.java
@@ -0,0 +1,8 @@
+package org.springframework.util;
+
+public abstract class StringUtils {
+
+ public static boolean isEmpty(Object str) {
+ return str == null || "".equals(str);
+ }
+}
\ No newline at end of file
diff --git a/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/Filter.java b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/Filter.java
new file mode 100644
index 00000000000..5833e3c909d
--- /dev/null
+++ b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/Filter.java
@@ -0,0 +1,13 @@
+package javax.servlet;
+
+import java.io.IOException;
+
+public interface Filter {
+ default void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
+
+ default void destroy() {
+ }
+}
diff --git a/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/FilterChain.java b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/FilterChain.java
new file mode 100644
index 00000000000..6a1dfc588b6
--- /dev/null
+++ b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/FilterChain.java
@@ -0,0 +1,8 @@
+package javax.servlet;
+
+import java.io.IOException;
+
+public interface FilterChain {
+ void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
+}
+
diff --git a/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/FilterConfig.java b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/FilterConfig.java
new file mode 100644
index 00000000000..66c13eb54f0
--- /dev/null
+++ b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/FilterConfig.java
@@ -0,0 +1,3 @@
+package javax.servlet;
+
+public interface FilterConfig {}
diff --git a/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/ServletException.java b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/ServletException.java
new file mode 100644
index 00000000000..ce5f7c4465a
--- /dev/null
+++ b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/ServletException.java
@@ -0,0 +1,8 @@
+package javax.servlet;
+
+public class ServletException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public ServletException() {
+ }
+}
diff --git a/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/ServletRequest.java b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/ServletRequest.java
new file mode 100644
index 00000000000..4ee0026d066
--- /dev/null
+++ b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/ServletRequest.java
@@ -0,0 +1,87 @@
+package javax.servlet;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.Map;
+
+public interface ServletRequest {
+ Object getAttribute(String var1);
+
+ Enumeration getAttributeNames();
+
+ String getCharacterEncoding();
+
+ void setCharacterEncoding(String var1) throws UnsupportedEncodingException;
+
+ int getContentLength();
+
+ long getContentLengthLong();
+
+ String getContentType();
+
+ ServletInputStream getInputStream() throws IOException;
+
+ String getParameter(String var1);
+
+ Enumeration getParameterNames();
+
+ String[] getParameterValues(String var1);
+
+ Map getParameterMap();
+
+ String getProtocol();
+
+ String getScheme();
+
+ String getServerName();
+
+ int getServerPort();
+
+ BufferedReader getReader() throws IOException;
+
+ String getRemoteAddr();
+
+ String getRemoteHost();
+
+ void setAttribute(String var1, Object var2);
+
+ void removeAttribute(String var1);
+
+ Locale getLocale();
+
+ Enumeration getLocales();
+
+ boolean isSecure();
+
+ RequestDispatcher getRequestDispatcher(String var1);
+
+ /** @deprecated */
+ @Deprecated
+ String getRealPath(String var1);
+
+ int getRemotePort();
+
+ String getLocalName();
+
+ String getLocalAddr();
+
+ int getLocalPort();
+
+ ServletContext getServletContext();
+
+ AsyncContext startAsync() throws IllegalStateException;
+
+ AsyncContext startAsync(ServletRequest var1, ServletResponse var2) throws IllegalStateException;
+
+ boolean isAsyncStarted();
+
+ boolean isAsyncSupported();
+
+ AsyncContext getAsyncContext();
+
+ DispatcherType getDispatcherType();
+}
+
diff --git a/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/ServletResponse.java b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/ServletResponse.java
new file mode 100644
index 00000000000..0aa6121e686
--- /dev/null
+++ b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/ServletResponse.java
@@ -0,0 +1,39 @@
+package javax.servlet;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Locale;
+
+public interface ServletResponse {
+ String getCharacterEncoding();
+
+ String getContentType();
+
+ ServletOutputStream getOutputStream() throws IOException;
+
+ PrintWriter getWriter() throws IOException;
+
+ void setCharacterEncoding(String var1);
+
+ void setContentLength(int var1);
+
+ void setContentLengthLong(long var1);
+
+ void setContentType(String var1);
+
+ void setBufferSize(int var1);
+
+ int getBufferSize();
+
+ void flushBuffer() throws IOException;
+
+ void resetBuffer();
+
+ boolean isCommitted();
+
+ void reset();
+
+ void setLocale(Locale var1);
+
+ Locale getLocale();
+}
diff --git a/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/http/HttpServletRequest.java b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/http/HttpServletRequest.java
new file mode 100644
index 00000000000..02d53a96a33
--- /dev/null
+++ b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/http/HttpServletRequest.java
@@ -0,0 +1,116 @@
+package javax.servlet.http;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Map;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+
+public interface HttpServletRequest extends ServletRequest {
+ String BASIC_AUTH = "BASIC";
+ String FORM_AUTH = "FORM";
+ String CLIENT_CERT_AUTH = "CLIENT_CERT";
+ String DIGEST_AUTH = "DIGEST";
+
+ String getAuthType();
+
+ Cookie[] getCookies();
+
+ long getDateHeader(String var1);
+
+ String getHeader(String var1);
+
+ Enumeration getHeaders(String var1);
+
+ Enumeration getHeaderNames();
+
+ int getIntHeader(String var1);
+
+ default HttpServletMapping getHttpServletMapping() {
+ return new HttpServletMapping() {
+ public String getMatchValue() {
+ return "";
+ }
+
+ public String getPattern() {
+ return "";
+ }
+
+ public String getServletName() {
+ return "";
+ }
+
+ public MappingMatch getMappingMatch() {
+ return null;
+ }
+ };
+ }
+
+ String getMethod();
+
+ String getPathInfo();
+
+ String getPathTranslated();
+
+ default PushBuilder newPushBuilder() {
+ return null;
+ }
+
+ String getContextPath();
+
+ String getQueryString();
+
+ String getRemoteUser();
+
+ boolean isUserInRole(String var1);
+
+ Principal getUserPrincipal();
+
+ String getRequestedSessionId();
+
+ String getRequestURI();
+
+ StringBuffer getRequestURL();
+
+ String getServletPath();
+
+ HttpSession getSession(boolean var1);
+
+ HttpSession getSession();
+
+ String changeSessionId();
+
+ boolean isRequestedSessionIdValid();
+
+ boolean isRequestedSessionIdFromCookie();
+
+ boolean isRequestedSessionIdFromURL();
+
+ /** @deprecated */
+ @Deprecated
+ boolean isRequestedSessionIdFromUrl();
+
+ boolean authenticate(HttpServletResponse var1) throws IOException, ServletException;
+
+ void login(String var1, String var2) throws ServletException;
+
+ void logout() throws ServletException;
+
+ Collection getParts() throws IOException, ServletException;
+
+ Part getPart(String var1) throws IOException, ServletException;
+
+ T upgrade(Class var1) throws IOException, ServletException;
+
+ default Map getTrailerFields() {
+ return Collections.emptyMap();
+ }
+
+ default boolean isTrailerFieldsReady() {
+ return false;
+ }
+}
+
diff --git a/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/http/HttpServletResponse.java b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/http/HttpServletResponse.java
new file mode 100644
index 00000000000..0a2c6af0913
--- /dev/null
+++ b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/http/HttpServletResponse.java
@@ -0,0 +1,106 @@
+package javax.servlet.http;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Map;
+import java.util.function.Supplier;
+import javax.servlet.ServletResponse;
+
+public interface HttpServletResponse extends ServletResponse {
+ int SC_CONTINUE = 100;
+ int SC_SWITCHING_PROTOCOLS = 101;
+ int SC_OK = 200;
+ int SC_CREATED = 201;
+ int SC_ACCEPTED = 202;
+ int SC_NON_AUTHORITATIVE_INFORMATION = 203;
+ int SC_NO_CONTENT = 204;
+ int SC_RESET_CONTENT = 205;
+ int SC_PARTIAL_CONTENT = 206;
+ int SC_MULTIPLE_CHOICES = 300;
+ int SC_MOVED_PERMANENTLY = 301;
+ int SC_MOVED_TEMPORARILY = 302;
+ int SC_FOUND = 302;
+ int SC_SEE_OTHER = 303;
+ int SC_NOT_MODIFIED = 304;
+ int SC_USE_PROXY = 305;
+ int SC_TEMPORARY_REDIRECT = 307;
+ int SC_BAD_REQUEST = 400;
+ int SC_UNAUTHORIZED = 401;
+ int SC_PAYMENT_REQUIRED = 402;
+ int SC_FORBIDDEN = 403;
+ int SC_NOT_FOUND = 404;
+ int SC_METHOD_NOT_ALLOWED = 405;
+ int SC_NOT_ACCEPTABLE = 406;
+ int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
+ int SC_REQUEST_TIMEOUT = 408;
+ int SC_CONFLICT = 409;
+ int SC_GONE = 410;
+ int SC_LENGTH_REQUIRED = 411;
+ int SC_PRECONDITION_FAILED = 412;
+ int SC_REQUEST_ENTITY_TOO_LARGE = 413;
+ int SC_REQUEST_URI_TOO_LONG = 414;
+ int SC_UNSUPPORTED_MEDIA_TYPE = 415;
+ int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
+ int SC_EXPECTATION_FAILED = 417;
+ int SC_INTERNAL_SERVER_ERROR = 500;
+ int SC_NOT_IMPLEMENTED = 501;
+ int SC_BAD_GATEWAY = 502;
+ int SC_SERVICE_UNAVAILABLE = 503;
+ int SC_GATEWAY_TIMEOUT = 504;
+ int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
+
+ void addCookie(Cookie var1);
+
+ boolean containsHeader(String var1);
+
+ String encodeURL(String var1);
+
+ String encodeRedirectURL(String var1);
+
+ /** @deprecated */
+ @Deprecated
+ String encodeUrl(String var1);
+
+ /** @deprecated */
+ @Deprecated
+ String encodeRedirectUrl(String var1);
+
+ void sendError(int var1, String var2) throws IOException;
+
+ void sendError(int var1) throws IOException;
+
+ void sendRedirect(String var1) throws IOException;
+
+ void setDateHeader(String var1, long var2);
+
+ void addDateHeader(String var1, long var2);
+
+ void setHeader(String var1, String var2);
+
+ void addHeader(String var1, String var2);
+
+ void setIntHeader(String var1, int var2);
+
+ void addIntHeader(String var1, int var2);
+
+ void setStatus(int var1);
+
+ /** @deprecated */
+ @Deprecated
+ void setStatus(int var1, String var2);
+
+ int getStatus();
+
+ String getHeader(String var1);
+
+ Collection getHeaders(String var1);
+
+ Collection getHeaderNames();
+
+ default void setTrailerFields(Supplier
-The following example defines a vulnerable HTTP endpoint:
+The following example shows how a vulnerable HTTP endpoint can be defined
+using HttpInvokerServiceExporter and Spring annotations:
+
+The next examples shows how the same vulnerable endpoint can be defined in a Spring XML config:
+
+
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/SpringExporterUnsafeDeserialization.xml b/java/ql/src/experimental/Security/CWE/CWE-502/SpringExporterUnsafeDeserialization.xml
new file mode 100644
index 00000000000..bf058cfffc1
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/SpringExporterUnsafeDeserialization.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
From 891b975899bafae5e8777c2f0b9de4c299c7835b Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Sat, 6 Mar 2021 22:07:43 +0100
Subject: [PATCH 091/725] Use correct file names in
SpringExporterUnsafeDeserialization.qhelp
---
.../CWE/CWE-502/SpringExporterUnsafeDeserialization.qhelp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/SpringExporterUnsafeDeserialization.qhelp b/java/ql/src/experimental/Security/CWE/CWE-502/SpringExporterUnsafeDeserialization.qhelp
index da681b7125c..04ddac33aaf 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-502/SpringExporterUnsafeDeserialization.qhelp
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/SpringExporterUnsafeDeserialization.qhelp
@@ -45,11 +45,11 @@ consider using global deserialization filters introduced in JEP 290.
The following example shows how a vulnerable HTTP endpoint can be defined
using HttpInvokerServiceExporter and Spring annotations:
-
+
The next examples shows how the same vulnerable endpoint can be defined in a Spring XML config:
-
+
From 0ef3eee4edc48adb290ad6f5eb73b4682aa6f33c Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Sat, 6 Mar 2021 22:41:54 +0000
Subject: [PATCH 092/725] Revamp the source and the sink of the query
---
.../Security/CWE/CWE-759/HashWithoutSalt.ql | 101 +++++++++++++-----
.../security/CWE-759/HashWithoutSalt.expected | 16 ++-
.../security/CWE-759/HashWithoutSalt.java | 54 ++++++----
3 files changed, 121 insertions(+), 50 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
index 7ea8c7598d2..343a073a27b 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql
@@ -9,9 +9,26 @@
import java
import semmle.code.java.dataflow.TaintTracking
-import semmle.code.java.dataflow.TaintTracking2
import DataFlow::PathGraph
+/**
+ * Gets a regular expression for matching common names of variables
+ * that indicate the value being held is a password.
+ */
+string getPasswordRegex() { result = "(?i).*pass(wd|word|code|phrase).*" }
+
+/** Finds variables that hold password information judging by their names. */
+class PasswordVarExpr extends VarAccess {
+ PasswordVarExpr() {
+ exists(string name | name = this.getVariable().getName().toLowerCase() |
+ name.regexpMatch(getPasswordRegex()) and not name.matches("%hash%") // Exclude variable names such as `passwordHash` since their values were already hashed
+ )
+ }
+}
+
+/** Holds if `Expr` e is a direct or indirect operand of `ae`. */
+predicate hasAddExprAncestor(AddExpr ae, Expr e) { ae.getAnOperand+() = e }
+
/** The Java class `java.security.MessageDigest`. */
class MessageDigest extends RefType {
MessageDigest() { this.hasQualifiedName("java.security", "MessageDigest") }
@@ -54,41 +71,66 @@ class MDHashMethodAccess extends MethodAccess {
}
}
-/** Gets a regular expression for matching common names of variables that indicate the value being held is a password. */
-string getPasswordRegex() { result = "(?i).*pass(wd|word|code|phrase).*" }
-
-/** Finds variables that hold password information judging by their names. */
-class PasswordVarExpr extends VarAccess {
- PasswordVarExpr() {
- exists(string name | name = this.getVariable().getName().toLowerCase() |
- name.regexpMatch(getPasswordRegex()) and not name.matches("%hash%") // Exclude variable names such as `passwordHash` since their values were already hashed
- )
- }
+/**
+ * Holds if `MethodAccess` ma is a method access of `MDHashMethodAccess` or
+ * invokes a method access of `MDHashMethodAccess` directly or indirectly.
+ */
+predicate isHashAccess(MethodAccess ma) {
+ ma instanceof MDHashMethodAccess
+ or
+ exists(MethodAccess mca |
+ ma.getMethod().calls(mca.getMethod()) and
+ isHashAccess(mca) and
+ DataFlow::localExprFlow(ma.getMethod().getAParameter().getAnAccess(), mca.getAnArgument())
+ )
}
-/** Holds if `Expr` e is a direct or indirect operand of `ae`. */
-predicate hasAddExprAncestor(AddExpr ae, Expr e) { ae.getAnOperand+() = e }
-
-/** Holds if `MDHashMethodAccess ma` is a second `MDHashMethodAccess` call by the same object. */
-predicate hasAnotherHashCall(MDHashMethodAccess ma) {
- exists(MDHashMethodAccess ma2 |
+/**
+ * Holds if there is a second method access that satisfies `isHashAccess` whose qualifier or argument
+ * is the same as the method call `ma` that satisfies `isHashAccess`.
+ */
+predicate hasAnotherHashCall(MethodAccess ma) {
+ isHashAccess(ma) and
+ exists(MethodAccess ma2, VarAccess va |
ma2 != ma and
- ma2.getQualifier() = ma.getQualifier().(VarAccess).getVariable().getAnAccess()
+ isHashAccess(ma2) and
+ not va.getVariable().getType() instanceof PrimitiveType and
+ (
+ ma.getQualifier() = va and
+ ma2.getQualifier() = va.getVariable().getAnAccess()
+ or
+ ma.getQualifier() = va and
+ ma2.getAnArgument() = va.getVariable().getAnAccess()
+ or
+ ma.getAnArgument() = va and
+ ma2.getQualifier() = va.getVariable().getAnAccess()
+ or
+ ma.getAnArgument() = va and
+ ma2.getAnArgument() = va.getVariable().getAnAccess()
+ )
+ )
+}
+
+/**
+ * Holds if `MethodAccess` ma is part of a call graph that satisfies `isHashAccess`
+ * but is not at the top of the call hierarchy.
+ */
+predicate hasHashAncestor(MethodAccess ma) {
+ exists(MethodAccess mpa |
+ mpa.getMethod().calls(ma.getMethod()) and
+ isHashAccess(mpa) and
+ DataFlow::localExprFlow(mpa.getMethod().getAParameter().getAnAccess(), ma.getAnArgument())
)
}
/** Holds if `MethodAccess` ma is a hashing call without a sibling node making another hashing call. */
-predicate isSingleHashMethodCall(MDHashMethodAccess ma) { not hasAnotherHashCall(ma) }
-
-/** Holds if `MethodAccess` ma is invoked by `MethodAccess` ma2 either directly or indirectly. */
-predicate hasParentCall(MethodAccess ma2, MethodAccess ma) { ma.getCaller() = ma2.getMethod() }
-
-/** Holds if `MethodAccess` is a single hashing call that is not invoked by a wrapper method. */
-predicate isSink(MethodAccess ma) {
- isSingleHashMethodCall(ma) and
- not hasParentCall(_, ma) // Not invoked by a wrapper method which could invoke MDHashMethod in another call stack. This reduces FPs.
+predicate isSingleHashMethodCall(MethodAccess ma) {
+ isHashAccess(ma) and not hasAnotherHashCall(ma)
}
+/** Holds if `MethodAccess` ma is a single hashing call that is not invoked by a wrapper method. */
+predicate isSink(MethodAccess ma) { isSingleHashMethodCall(ma) and not hasHashAncestor(ma) }
+
/** Sink of hashing calls. */
class HashWithoutSaltSink extends DataFlow::ExprNode {
HashWithoutSaltSink() {
@@ -99,7 +141,10 @@ class HashWithoutSaltSink extends DataFlow::ExprNode {
}
}
-/** Taint configuration tracking flow from an expression whose name suggests it holds password data to a method call that generates a hash without a salt. */
+/**
+ * Taint configuration tracking flow from an expression whose name suggests it holds password data
+ * to a method call that generates a hash without a salt.
+ */
class HashWithoutSaltConfiguration extends TaintTracking::Configuration {
HashWithoutSaltConfiguration() { this = "HashWithoutSaltConfiguration" }
diff --git a/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.expected b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.expected
index fa36297a8a1..b7a5ea8ee4a 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.expected
@@ -1,11 +1,19 @@
edges
| HashWithoutSalt.java:10:36:10:43 | password : String | HashWithoutSalt.java:10:36:10:54 | getBytes(...) |
-| HashWithoutSalt.java:17:13:17:20 | password : String | HashWithoutSalt.java:17:13:17:31 | getBytes(...) |
+| HashWithoutSalt.java:25:13:25:20 | password : String | HashWithoutSalt.java:25:13:25:31 | getBytes(...) |
+| HashWithoutSalt.java:93:22:93:29 | password : String | HashWithoutSalt.java:94:17:94:25 | passBytes |
+| HashWithoutSalt.java:111:22:111:29 | password : String | HashWithoutSalt.java:112:18:112:26 | passBytes |
nodes
| HashWithoutSalt.java:10:36:10:43 | password : String | semmle.label | password : String |
| HashWithoutSalt.java:10:36:10:54 | getBytes(...) | semmle.label | getBytes(...) |
-| HashWithoutSalt.java:17:13:17:20 | password : String | semmle.label | password : String |
-| HashWithoutSalt.java:17:13:17:31 | getBytes(...) | semmle.label | getBytes(...) |
+| HashWithoutSalt.java:25:13:25:20 | password : String | semmle.label | password : String |
+| HashWithoutSalt.java:25:13:25:31 | getBytes(...) | semmle.label | getBytes(...) |
+| HashWithoutSalt.java:93:22:93:29 | password : String | semmle.label | password : String |
+| HashWithoutSalt.java:94:17:94:25 | passBytes | semmle.label | passBytes |
+| HashWithoutSalt.java:111:22:111:29 | password : String | semmle.label | password : String |
+| HashWithoutSalt.java:112:18:112:26 | passBytes | semmle.label | passBytes |
#select
| HashWithoutSalt.java:10:36:10:54 | getBytes(...) | HashWithoutSalt.java:10:36:10:43 | password : String | HashWithoutSalt.java:10:36:10:54 | getBytes(...) | $@ is hashed without a salt. | HashWithoutSalt.java:10:36:10:43 | password : String | The password |
-| HashWithoutSalt.java:17:13:17:31 | getBytes(...) | HashWithoutSalt.java:17:13:17:20 | password : String | HashWithoutSalt.java:17:13:17:31 | getBytes(...) | $@ is hashed without a salt. | HashWithoutSalt.java:17:13:17:20 | password : String | The password |
+| HashWithoutSalt.java:25:13:25:31 | getBytes(...) | HashWithoutSalt.java:25:13:25:20 | password : String | HashWithoutSalt.java:25:13:25:31 | getBytes(...) | $@ is hashed without a salt. | HashWithoutSalt.java:25:13:25:20 | password : String | The password |
+| HashWithoutSalt.java:94:17:94:25 | passBytes | HashWithoutSalt.java:93:22:93:29 | password : String | HashWithoutSalt.java:94:17:94:25 | passBytes | $@ is hashed without a salt. | HashWithoutSalt.java:93:22:93:29 | password : String | The password |
+| HashWithoutSalt.java:112:18:112:26 | passBytes | HashWithoutSalt.java:111:22:111:29 | password : String | HashWithoutSalt.java:112:18:112:26 | passBytes | $@ is hashed without a salt. | HashWithoutSalt.java:111:22:111:29 | password : String | The password |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java
index 026ccfa70be..48911486db1 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java
@@ -11,14 +11,6 @@ public class HashWithoutSalt {
return Base64.getEncoder().encodeToString(messageDigest);
}
- // BAD - Hash without a salt.
- public String getSHA256Hash2(String password) throws NoSuchAlgorithmException {
- MessageDigest md = MessageDigest.getInstance("SHA-256");
- md.update(password.getBytes());
- byte[] messageDigest = md.digest();
- return Base64.getEncoder().encodeToString(messageDigest);
- }
-
// GOOD - Hash with a salt.
public String getSHA256Hash(String password, byte[] salt) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
@@ -27,6 +19,14 @@ public class HashWithoutSalt {
return Base64.getEncoder().encodeToString(messageDigest);
}
+ // BAD - Hash without a salt.
+ public String getSHA256Hash2(String password) throws NoSuchAlgorithmException {
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ md.update(password.getBytes());
+ byte[] messageDigest = md.digest();
+ return Base64.getEncoder().encodeToString(messageDigest);
+ }
+
// GOOD - Hash with a salt.
public String getSHA256Hash2(String password, byte[] salt) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
@@ -77,8 +77,8 @@ public class HashWithoutSalt {
sha256.update(foo, start, len);
}
- // BAD - Invoking a wrapper implementation without a salt is not detected.
- public String getSHA256Hash4(String password) throws NoSuchAlgorithmException, ClassNotFoundException, IllegalAccessException, InstantiationException {
+ // GOOD - Invoking a wrapper implementation through qualifier with a salt.
+ public String getWrapperSHA256Hash(String password) throws NoSuchAlgorithmException, ClassNotFoundException, IllegalAccessException, InstantiationException {
SHA256 sha256 = new SHA256();
byte[] salt = getSalt();
byte[] passBytes = password.getBytes();
@@ -87,8 +87,16 @@ public class HashWithoutSalt {
return Base64.getEncoder().encodeToString(sha256.digest());
}
- // BAD - Invoking a wrapper implementation without a salt is not detected.
- public String getSHA256Hash5(String password) throws NoSuchAlgorithmException {
+ // BAD - Invoking a wrapper implementation through qualifier without a salt.
+ public String getWrapperSHA256Hash2(String password) throws NoSuchAlgorithmException, ClassNotFoundException, IllegalAccessException, InstantiationException {
+ SHA256 sha256 = new SHA256();
+ byte[] passBytes = password.getBytes();
+ sha256.update(passBytes, 0, passBytes.length);
+ return Base64.getEncoder().encodeToString(sha256.digest());
+ }
+
+ // GOOD - Invoking a wrapper implementation through qualifier and argument with a salt.
+ public String getWrapperSHA256Hash3(String password) throws NoSuchAlgorithmException {
SHA256 sha256 = new SHA256();
byte[] salt = getSalt();
byte[] passBytes = password.getBytes();
@@ -97,16 +105,26 @@ public class HashWithoutSalt {
return Base64.getEncoder().encodeToString(sha256.digest());
}
- // BAD - Invoking a wrapper implementation without a salt is not detected.
- public String getSHA512Hash6(String password) throws NoSuchAlgorithmException {
- SHA512 sha512 = new SHA512();
+ // BAD - Invoking a wrapper implementation through argument without a salt.
+ public String getWrapperSHA256Hash4(String password) throws NoSuchAlgorithmException {
+ SHA256 sha256 = new SHA256();
byte[] passBytes = password.getBytes();
- sha512.update(passBytes, 0, passBytes.length);
- return Base64.getEncoder().encodeToString(sha512.digest());
+ update(sha256, passBytes, 0, passBytes.length);
+ return Base64.getEncoder().encodeToString(sha256.digest());
+ }
+
+ // GOOD - Invoking a wrapper implementation through argument with a salt.
+ public String getWrapperSHA256Hash5(String password) throws NoSuchAlgorithmException {
+ SHA256 sha256 = new SHA256();
+ byte[] salt = getSalt();
+ byte[] passBytes = password.getBytes();
+ update(sha256, passBytes, 0, passBytes.length);
+ update(sha256, salt, 0, salt.length);
+ return Base64.getEncoder().encodeToString(sha256.digest());
}
// BAD - Invoke a wrapper implementation with a salt, which is not detected with an interface type variable.
- public String getSHA512Hash7(byte[] passphrase) throws NoSuchAlgorithmException, ClassNotFoundException, IllegalAccessException, InstantiationException {
+ public String getSHA512Hash8(byte[] passphrase) throws NoSuchAlgorithmException, ClassNotFoundException, IllegalAccessException, InstantiationException {
Class c = Class.forName("SHA512");
HASH sha512 = (HASH) (c.newInstance());
byte[] tmp = new byte[4];
From bb53780ba9aa9278deb35f1f6c23d1a719546e9b Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Mon, 8 Mar 2021 09:42:47 +0100
Subject: [PATCH 093/725] C++: Add flow through unary instructions and
pointer/indirection conflation for parameters. These rules are copy/pasted
from DefaultTaintTracking. The conflation rules will hopefully be removed as
part of #5089.
---
.../dataflow/internal/TaintTrackingUtil.qll | 39 ++++++++++++++++++-
1 file changed, 37 insertions(+), 2 deletions(-)
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll
index 1f7808c5b5b..ab1177daea9 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll
@@ -64,14 +64,26 @@ private predicate operandToInstructionTaintStep(Operand opFrom, Instruction inst
or
instrTo instanceof PointerArithmeticInstruction
or
- instrTo.(FieldAddressInstruction).getField().getDeclaringType() instanceof Union
- or
// The `CopyInstruction` case is also present in non-taint data flow, but
// that uses `getDef` rather than `getAnyDef`. For taint, we want flow
// from a definition of `myStruct` to a `myStruct.myField` expression.
instrTo instanceof CopyInstruction
)
or
+ // Unary instructions tend to preserve enough information in practice that we
+ // want taint to flow through.
+ // The exception is `FieldAddressInstruction`. Together with the rules below for
+ // `LoadInstruction`s and `ChiInstruction`s, flow through `FieldAddressInstruction`
+ // could cause flow into one field to come out an unrelated field.
+ // This would happen across function boundaries, where the IR would not be able to
+ // match loads to stores.
+ instrTo.(UnaryInstruction).getUnaryOperand() = opFrom and
+ (
+ not instrTo instanceof FieldAddressInstruction
+ or
+ instrTo.(FieldAddressInstruction).getField().getDeclaringType() instanceof Union
+ )
+ or
instrTo.(LoadInstruction).getSourceAddressOperand() = opFrom
or
// Flow from an element to an array or union that contains it.
@@ -83,6 +95,29 @@ private predicate operandToInstructionTaintStep(Operand opFrom, Instruction inst
t instanceof ArrayType
)
or
+ // Until we have flow through indirections across calls, we'll take flow out
+ // of the indirection and into the argument.
+ // When we get proper flow through indirections across calls, this code can be
+ // moved to `adjusedSink` or possibly into the `DataFlow::ExprNode` class.
+ exists(ReadSideEffectInstruction read |
+ read.getSideEffectOperand() = opFrom and
+ read.getArgumentDef() = instrTo
+ )
+ or
+ // Until we have from through indirections across calls, we'll take flow out
+ // of the parameter and into its indirection.
+ // `InitializeIndirectionInstruction` only has a single operand: the address of the
+ // value whose indirection we are initializing. When initializing an indirection of a parameter `p`,
+ // the IR looks like this:
+ // ```
+ // m1 = InitializeParameter[p] : &r1
+ // r2 = Load[p] : r2, m1
+ // m3 = InitializeIndirection[p] : &r2
+ // ```
+ // So by having flow from `r2` to `m3` we're enabling flow from `m1` to `m3`. This relies on the
+ // `LoadOperand`'s overlap being exact.
+ instrTo.(InitializeIndirectionInstruction).getAnOperand() = opFrom
+ or
modeledTaintStep(opFrom, instrTo)
}
From a78f2115f22cf0becb7214bc4fed48c75bbfa617 Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Tue, 9 Mar 2021 00:02:57 +0300
Subject: [PATCH 094/725] Split SpringExporterUnsafeDeserialization.ql
---
...eSpringExporterInConfigurationClass.qhelp} | 4 -
...safeSpringExporterInConfigurationClass.ql} | 37 ++-------
...safeSpringExporterInXMLConfiguration.qhelp | 81 +++++++++++++++++++
.../UnsafeSpringExporterInXMLConfiguration.ql | 20 +++++
.../CWE/CWE-502/UnsafeSpringExporterLib.qll | 9 +++
...ringExporterUnsafeDeserialization.expected | 4 -
.../SpringExporterUnsafeDeserialization.qlref | 1 -
...pringExporterInConfigurationClass.expected | 2 +
...feSpringExporterInConfigurationClass.qlref | 1 +
...eSpringExporterInXMLConfiguration.expected | 2 +
...safeSpringExporterInXMLConfiguration.qlref | 1 +
11 files changed, 122 insertions(+), 40 deletions(-)
rename java/ql/src/experimental/Security/CWE/CWE-502/{SpringExporterUnsafeDeserialization.qhelp => UnsafeSpringExporterInConfigurationClass.qhelp} (94%)
rename java/ql/src/experimental/Security/CWE/CWE-502/{SpringExporterUnsafeDeserialization.ql => UnsafeSpringExporterInConfigurationClass.ql} (52%)
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInXMLConfiguration.qhelp
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInXMLConfiguration.ql
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterLib.qll
delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-502/SpringExporterUnsafeDeserialization.expected
delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-502/SpringExporterUnsafeDeserialization.qlref
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-502/UnsafeSpringExporterInConfigurationClass.expected
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-502/UnsafeSpringExporterInConfigurationClass.qlref
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-502/UnsafeSpringExporterInXMLConfiguration.expected
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-502/UnsafeSpringExporterInXMLConfiguration.qlref
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/SpringExporterUnsafeDeserialization.qhelp b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInConfigurationClass.qhelp
similarity index 94%
rename from java/ql/src/experimental/Security/CWE/CWE-502/SpringExporterUnsafeDeserialization.qhelp
rename to java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInConfigurationClass.qhelp
index 04ddac33aaf..87a57b0a43c 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-502/SpringExporterUnsafeDeserialization.qhelp
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInConfigurationClass.qhelp
@@ -46,10 +46,6 @@ The following example shows how a vulnerable HTTP endpoint can be defined
using HttpInvokerServiceExporter and Spring annotations:
-
-The next examples shows how the same vulnerable endpoint can be defined in a Spring XML config:
-
-
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/SpringExporterUnsafeDeserialization.ql b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInConfigurationClass.ql
similarity index 52%
rename from java/ql/src/experimental/Security/CWE/CWE-502/SpringExporterUnsafeDeserialization.ql
rename to java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInConfigurationClass.ql
index 417f1c8790b..34605d16fbd 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-502/SpringExporterUnsafeDeserialization.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInConfigurationClass.ql
@@ -6,28 +6,13 @@
* @kind problem
* @problem.severity error
* @precision high
- * @id java/spring-exporter-unsafe-deserialization
+ * @id java/unsafe-deserialization-spring-exporter-in-configuration-class
* @tags security
* external/cwe/cwe-502
*/
import java
-import semmle.code.java.frameworks.spring.SpringBean
-
-/**
- * Holds if `type` is `RemoteInvocationSerializingExporter`.
- */
-private predicate isRemoteInvocationSerializingExporter(RefType type) {
- type.getASupertype*()
- .hasQualifiedName("org.springframework.remoting.rmi", "RemoteInvocationSerializingExporter")
-}
-
-/**
- * Holds if `method` belongs to a Spring configuration.
- */
-private predicate isInConfiguration(Method method) {
- method.getDeclaringType().hasAnnotation("org.springframework.context.annotation", "Configuration")
-}
+import UnsafeSpringExporterLib
/**
* A method that initializes a unsafe bean based on `RemoteInvocationSerializingExporter`.
@@ -36,8 +21,8 @@ private class UnsafeBeanInitMethod extends Method {
string identifier;
UnsafeBeanInitMethod() {
- isInConfiguration(this) and
isRemoteInvocationSerializingExporter(this.getReturnType()) and
+ this.getDeclaringType().hasAnnotation("org.springframework.context.annotation", "Configuration") and
exists(Annotation a |
a.getType().hasQualifiedName("org.springframework.context.annotation", "Bean")
|
@@ -51,16 +36,6 @@ private class UnsafeBeanInitMethod extends Method {
string getBeanIdentifier() { result = identifier }
}
-from File file, string identifier
-where
- exists(UnsafeBeanInitMethod method |
- file = method.getFile() and
- identifier = method.getBeanIdentifier()
- )
- or
- exists(SpringBean bean |
- isRemoteInvocationSerializingExporter(bean.getClass()) and
- file = bean.getFile() and
- identifier = bean.getBeanIdentifier()
- )
-select file, "Unsafe deserialization in Spring exporter bean '" + identifier + "'"
+from UnsafeBeanInitMethod method
+select method,
+ "Unsafe deserialization in a Spring exporter bean '" + method.getBeanIdentifier() + "'"
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInXMLConfiguration.qhelp b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInXMLConfiguration.qhelp
new file mode 100644
index 00000000000..dcefdfb97b0
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInXMLConfiguration.qhelp
@@ -0,0 +1,81 @@
+
+
+
+
+
+The Spring Framework provides an abstract base class RemoteInvocationSerializingExporter
+for creating remote service exporters.
+A Spring exporter, which is based on this class, deserializes incoming data using ObjectInputStream.
+Deserializing untrusted data is easily exploitable and in many cases allows an attacker
+to execute arbitrary code.
+
+
+The Spring Framework also provides two classes that extend RemoteInvocationSerializingExporter:
+
+HttpInvokerServiceExporter
+
+
+SimpleHttpInvokerServiceExporter
+
+
+
+These classes export specified beans as HTTP endpoints that deserialize data from an HTTP request
+using unsafe ObjectInputStream. If a remote attacker can reach such endpoints,
+it results in remote code execution in the worst case.
+
+
+CVE-2016-1000027 has been assigned to this issue in the Spring Framework.
+It is regarded as a design limitation, and can be mitigated but not fixed outright.
+
+
+
+
+
+Avoid using HttpInvokerServiceExporter, SimpleHttpInvokerServiceExporter
+and any other exporter that is based on RemoteInvocationSerializingExporter.
+Instead, use other message formats for API endpoints (for example, JSON),
+but make sure that the underlying deserialization mechanism is properly configured
+so that deserialization attacks are not possible. If the vulnerable exporters can not be replaced,
+consider using global deserialization filters introduced in JEP 290.
+
+
+
+
+
+The following examples shows how a vulnerable HTTP endpoint can be defined in a Spring XML config:
+
+
+
+
+
+
+OWASP:
+Deserialization of untrusted data.
+
+
+Spring Framework API documentation:
+RemoteInvocationSerializingExporter class
+
+
+Spring Framework API documentation:
+HttpInvokerServiceExporter class
+
+
+National Vulnerability Database:
+CVE-2016-1000027
+
+
+Tenable Research Advisory:
+[R2] Pivotal Spring Framework HttpInvokerServiceExporter readRemoteInvocation Method Untrusted Java Deserialization
+
+
+Spring Framework bug tracker:
+Sonatype vulnerability CVE-2016-1000027 in Spring-web project
+
+
+OpenJDK:
+JEP 290: Filter Incoming Serialization Data
+
+
+
+
\ No newline at end of file
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInXMLConfiguration.ql b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInXMLConfiguration.ql
new file mode 100644
index 00000000000..d7606587df3
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInXMLConfiguration.ql
@@ -0,0 +1,20 @@
+/**
+ * @name Unsafe deserialization with Spring's remote service exporters.
+ * @description A Spring bean, which is based on RemoteInvocationSerializingExporter,
+ * initializes an endpoint that uses ObjectInputStream to deserialize
+ * incoming data. In the worst case, that may lead to remote code execution.
+ * @kind problem
+ * @problem.severity error
+ * @precision high
+ * @id java/unsafe-deserialization-spring-exporter-in-xml-configuration
+ * @tags security
+ * external/cwe/cwe-502
+ */
+
+import java
+import semmle.code.java.frameworks.spring.SpringBean
+import UnsafeSpringExporterLib
+
+from SpringBean bean
+where isRemoteInvocationSerializingExporter(bean.getClass())
+select bean, "Unsafe deserialization in a Spring exporter bean '" + bean.getBeanIdentifier() + "'"
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterLib.qll b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterLib.qll
new file mode 100644
index 00000000000..f49ee44304f
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterLib.qll
@@ -0,0 +1,9 @@
+import java
+
+/**
+ * Holds if `type` is `RemoteInvocationSerializingExporter`.
+ */
+predicate isRemoteInvocationSerializingExporter(RefType type) {
+ type.getASupertype*()
+ .hasQualifiedName("org.springframework.remoting.rmi", "RemoteInvocationSerializingExporter")
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-502/SpringExporterUnsafeDeserialization.expected b/java/ql/test/experimental/query-tests/security/CWE-502/SpringExporterUnsafeDeserialization.expected
deleted file mode 100644
index 1b5c1d2e67d..00000000000
--- a/java/ql/test/experimental/query-tests/security/CWE-502/SpringExporterUnsafeDeserialization.expected
+++ /dev/null
@@ -1,4 +0,0 @@
-| SpringExporterUnsafeDeserialization.java:0:0:0:0 | SpringExporterUnsafeDeserialization | Unsafe deserialization in Spring exporter bean '/unsafeCustomeRemoteInvocationSerializingExporter' |
-| SpringExporterUnsafeDeserialization.java:0:0:0:0 | SpringExporterUnsafeDeserialization | Unsafe deserialization in Spring exporter bean '/unsafeHttpInvokerServiceExporter' |
-| beans.xml:0:0:0:0 | beans.xml | Unsafe deserialization in Spring exporter bean '/unsafeBooking' |
-| beans.xml:0:0:0:0 | beans.xml | Unsafe deserialization in Spring exporter bean 'org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter' |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-502/SpringExporterUnsafeDeserialization.qlref b/java/ql/test/experimental/query-tests/security/CWE-502/SpringExporterUnsafeDeserialization.qlref
deleted file mode 100644
index 7be52b73cba..00000000000
--- a/java/ql/test/experimental/query-tests/security/CWE-502/SpringExporterUnsafeDeserialization.qlref
+++ /dev/null
@@ -1 +0,0 @@
-experimental/Security/CWE/CWE-502/SpringExporterUnsafeDeserialization.ql
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeSpringExporterInConfigurationClass.expected b/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeSpringExporterInConfigurationClass.expected
new file mode 100644
index 00000000000..926eff89403
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeSpringExporterInConfigurationClass.expected
@@ -0,0 +1,2 @@
+| SpringExporterUnsafeDeserialization.java:10:32:10:63 | unsafeHttpInvokerServiceExporter | Unsafe deserialization in a Spring exporter bean '/unsafeHttpInvokerServiceExporter' |
+| SpringExporterUnsafeDeserialization.java:18:41:18:88 | unsafeCustomeRemoteInvocationSerializingExporter | Unsafe deserialization in a Spring exporter bean '/unsafeCustomeRemoteInvocationSerializingExporter' |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeSpringExporterInConfigurationClass.qlref b/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeSpringExporterInConfigurationClass.qlref
new file mode 100644
index 00000000000..823c7735ec5
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeSpringExporterInConfigurationClass.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-502/UnsafeSpringExporterInConfigurationClass.ql
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeSpringExporterInXMLConfiguration.expected b/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeSpringExporterInXMLConfiguration.expected
new file mode 100644
index 00000000000..edcb3668557
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeSpringExporterInXMLConfiguration.expected
@@ -0,0 +1,2 @@
+| beans.xml:10:5:13:12 | /unsafeBooking | Unsafe deserialization in a Spring exporter bean '/unsafeBooking' |
+| beans.xml:15:5:18:12 | org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter | Unsafe deserialization in a Spring exporter bean 'org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter' |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeSpringExporterInXMLConfiguration.qlref b/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeSpringExporterInXMLConfiguration.qlref
new file mode 100644
index 00000000000..46024a0b6b3
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeSpringExporterInXMLConfiguration.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-502/UnsafeSpringExporterInXMLConfiguration.ql
\ No newline at end of file
From 48975fa7d220b7debe5d0c8a0b84c8ec64351435 Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Wed, 10 Mar 2021 00:17:26 +0000
Subject: [PATCH 095/725] Replace sanitizers
---
.../CWE-1004/SensitiveCookieNotHttpOnly.ql | 105 +++++++-----------
1 file changed, 41 insertions(+), 64 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
index 229a9d50325..716975d0486 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
@@ -50,23 +50,6 @@ class CookieClass extends RefType {
}
}
-/** The method call `toString` to get a stringified cookie representation. */
-class CookieInstanceExpr extends TaintPreservingCallable {
- CookieInstanceExpr() {
- this.getDeclaringType() instanceof CookieClass and
- this.hasName("toString")
- }
-
- override predicate returnsTaintFrom(int arg) { arg = -1 }
-}
-
-/** The cookie constructor. */
-class CookieTaintPreservingConstructor extends Constructor, TaintPreservingCallable {
- CookieTaintPreservingConstructor() { this.getDeclaringType() instanceof CookieClass }
-
- override predicate returnsTaintFrom(int arg) { arg = 0 }
-}
-
/** Sensitive cookie name used in a `Cookie` constructor or a `Set-Cookie` call. */
class SensitiveCookieNameExpr extends Expr {
SensitiveCookieNameExpr() { isSensitiveCookieNameExpr(this) }
@@ -78,55 +61,58 @@ class CookieResponseSink extends DataFlow::ExprNode {
exists(MethodAccess ma |
(
ma.getMethod() instanceof ResponseAddCookieMethod and
- this.getExpr() = ma.getArgument(0)
+ this.getExpr() = ma.getArgument(0) and
+ not exists(
+ MethodAccess ma2 // cookie.setHttpOnly(true)
+ |
+ ma2.getMethod().getName() = "setHttpOnly" and
+ ma2.getArgument(0).(BooleanLiteral).getBooleanValue() = true and
+ DataFlow::localExprFlow(ma2.getQualifier(), this.getExpr())
+ )
or
ma instanceof SetCookieMethodAccess and
- this.getExpr() = ma.getArgument(1)
+ this.getExpr() = ma.getArgument(1) and
+ not hasHttpOnlyExpr(this.getExpr()) // response.addHeader("Set-Cookie", "token=" +authId + ";HttpOnly;Secure")
)
)
}
}
-/**
- * Holds if `node` is an access to a variable which has `setHttpOnly(true)` called on it and is also
- * the first argument to a call to the method `addCookie` of `javax.servlet.http.HttpServletResponse`.
- */
-predicate setHttpOnlyMethodAccess(DataFlow::Node node) {
- exists(
- MethodAccess addCookie, Variable cookie, MethodAccess m // jwtCookie.setHttpOnly(true)
- |
- addCookie.getMethod() instanceof ResponseAddCookieMethod and
- addCookie.getArgument(0) = cookie.getAnAccess() and
- m.getMethod().getName() = "setHttpOnly" and
- m.getArgument(0).(BooleanLiteral).getBooleanValue() = true and
- m.getQualifier() = cookie.getAnAccess() and
- node.asExpr() = cookie.getAnAccess()
- )
+/** A JAX-RS `NewCookie` constructor that sets `HttpOnly` to true. */
+class HttpOnlyNewCookie extends ClassInstanceExpr {
+ HttpOnlyNewCookie() {
+ this.getConstructedType()
+ .hasQualifiedName(["javax.ws.rs.core", "jakarta.ws.rs.core"], "NewCookie") and
+ (
+ this.getNumArgument() = 6 and this.getArgument(5).(BooleanLiteral).getBooleanValue() = true // NewCookie(Cookie cookie, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly)
+ or
+ this.getNumArgument() = 8 and
+ this.getArgument(6).getType() instanceof BooleanType and
+ this.getArgument(7).(BooleanLiteral).getBooleanValue() = true // NewCookie(String name, String value, String path, String domain, String comment, int maxAge, boolean secure, boolean httpOnly)
+ or
+ this.getNumArgument() = 10 and this.getArgument(9).(BooleanLiteral).getBooleanValue() = true // NewCookie(String name, String value, String path, String domain, int version, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly)
+ )
+ }
}
-/**
- * Holds if `node` is a string that contains `httponly` and which flows to the second argument
- * of a method to set a cookie.
- */
-predicate setHttpOnlyInSetCookie(DataFlow::Node node) {
- exists(SetCookieMethodAccess sa |
- hasHttpOnlyExpr(node.asExpr()) and
- DataFlow::localExprFlow(node.asExpr(), sa.getArgument(1))
- )
+/** The cookie constructor. */
+class CookieTaintPreservingConstructor extends Constructor, TaintPreservingCallable {
+ CookieTaintPreservingConstructor() {
+ this.getDeclaringType() instanceof CookieClass and
+ not exists(HttpOnlyNewCookie hie | hie.getConstructor() = this)
+ }
+
+ override predicate returnsTaintFrom(int arg) { arg = 0 }
}
-/** Holds if `cie` is an invocation of a JAX-RS `NewCookie` constructor that sets `HttpOnly` to true. */
-predicate setHttpOnlyInNewCookie(ClassInstanceExpr cie) {
- cie.getConstructedType().hasQualifiedName(["javax.ws.rs.core", "jakarta.ws.rs.core"], "NewCookie") and
- (
- cie.getNumArgument() = 6 and cie.getArgument(5).(BooleanLiteral).getBooleanValue() = true // NewCookie(Cookie cookie, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly)
- or
- cie.getNumArgument() = 8 and
- cie.getArgument(6).getType() instanceof BooleanType and
- cie.getArgument(7).(BooleanLiteral).getBooleanValue() = true // NewCookie(String name, String value, String path, String domain, String comment, int maxAge, boolean secure, boolean httpOnly)
- or
- cie.getNumArgument() = 10 and cie.getArgument(9).(BooleanLiteral).getBooleanValue() = true // NewCookie(String name, String value, String path, String domain, int version, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly)
- )
+/** The method call `toString` to get a stringified cookie representation. */
+class CookieInstanceExpr extends TaintPreservingCallable {
+ CookieInstanceExpr() {
+ this.getDeclaringType() instanceof CookieClass and
+ this.hasName("toString")
+ }
+
+ override predicate returnsTaintFrom(int arg) { arg = -1 }
}
/**
@@ -163,15 +149,6 @@ class MissingHttpOnlyConfiguration extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) { sink instanceof CookieResponseSink }
override predicate isSanitizer(DataFlow::Node node) {
- // cookie.setHttpOnly(true)
- setHttpOnlyMethodAccess(node)
- or
- // response.addHeader("Set-Cookie", "token=" +authId + ";HttpOnly;Secure")
- setHttpOnlyInSetCookie(node)
- or
- // new NewCookie("session-access-key", accessKey, "/", null, null, 0, true, true)
- setHttpOnlyInNewCookie(node.asExpr())
- or
// Test class or method
isTestMethod(node)
}
From df60268023493fe0fa6a4aa723f22435fb769451 Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Wed, 10 Mar 2021 10:38:08 +0300
Subject: [PATCH 096/725] Split qhelp files
---
...feSpringExporterInConfigurationClass.qhelp | 86 ++-----------------
...orterInConfigurationClassExample.inc.qhelp | 14 +++
...safeSpringExporterInXMLConfiguration.qhelp | 85 ++----------------
...xporterInXMLConfigurationExample.inc.qhelp | 13 +++
.../UnsafeSpringExporterQuery.inc.qhelp | 41 +++++++++
.../UnsafeSpringExporterReferences.inc.qhelp | 37 ++++++++
6 files changed, 117 insertions(+), 159 deletions(-)
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInConfigurationClassExample.inc.qhelp
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInXMLConfigurationExample.inc.qhelp
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterQuery.inc.qhelp
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterReferences.inc.qhelp
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInConfigurationClass.qhelp b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInConfigurationClass.qhelp
index 87a57b0a43c..8580092b8a1 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInConfigurationClass.qhelp
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInConfigurationClass.qhelp
@@ -1,82 +1,8 @@
-
+
-
-
-
-The Spring Framework provides an abstract base class RemoteInvocationSerializingExporter
-for creating remote service exporters.
-A Spring exporter, which is based on this class, deserializes incoming data using ObjectInputStream.
-Deserializing untrusted data is easily exploitable and in many cases allows an attacker
-to execute arbitrary code.
-
-
-The Spring Framework also provides two classes that extend RemoteInvocationSerializingExporter:
-
-HttpInvokerServiceExporter
-
-
-SimpleHttpInvokerServiceExporter
-
-
-
-These classes export specified beans as HTTP endpoints that deserialize data from an HTTP request
-using unsafe ObjectInputStream. If a remote attacker can reach such endpoints,
-it results in remote code execution in the worst case.
-
-
-CVE-2016-1000027 has been assigned to this issue in the Spring Framework.
-It is regarded as a design limitation, and can be mitigated but not fixed outright.
-
-
-
-
-
-Avoid using HttpInvokerServiceExporter, SimpleHttpInvokerServiceExporter
-and any other exporter that is based on RemoteInvocationSerializingExporter.
-Instead, use other message formats for API endpoints (for example, JSON),
-but make sure that the underlying deserialization mechanism is properly configured
-so that deserialization attacks are not possible. If the vulnerable exporters can not be replaced,
-consider using global deserialization filters introduced in JEP 290.
-
-
-
-
-
-The following example shows how a vulnerable HTTP endpoint can be defined
-using HttpInvokerServiceExporter and Spring annotations:
-
-
-
-
-
-
-OWASP:
-Deserialization of untrusted data.
-
-
-Spring Framework API documentation:
-RemoteInvocationSerializingExporter class
-
-
-Spring Framework API documentation:
-HttpInvokerServiceExporter class
-
-
-National Vulnerability Database:
-CVE-2016-1000027
-
-
-Tenable Research Advisory:
-[R2] Pivotal Spring Framework HttpInvokerServiceExporter readRemoteInvocation Method Untrusted Java Deserialization
-
-
-Spring Framework bug tracker:
-Sonatype vulnerability CVE-2016-1000027 in Spring-web project
-
-
-OpenJDK:
-JEP 290: Filter Incoming Serialization Data
-
-
-
+
+
+
\ No newline at end of file
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInConfigurationClassExample.inc.qhelp b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInConfigurationClassExample.inc.qhelp
new file mode 100644
index 00000000000..ed33a03fabe
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInConfigurationClassExample.inc.qhelp
@@ -0,0 +1,14 @@
+
+
+
+
+
+The following example shows how a vulnerable HTTP endpoint can be defined
+using HttpInvokerServiceExporter and Spring annotations:
+
+
+
+
+
\ No newline at end of file
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInXMLConfiguration.qhelp b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInXMLConfiguration.qhelp
index dcefdfb97b0..76dda5842b3 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInXMLConfiguration.qhelp
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInXMLConfiguration.qhelp
@@ -1,81 +1,8 @@
-
+
-
-
-
-The Spring Framework provides an abstract base class RemoteInvocationSerializingExporter
-for creating remote service exporters.
-A Spring exporter, which is based on this class, deserializes incoming data using ObjectInputStream.
-Deserializing untrusted data is easily exploitable and in many cases allows an attacker
-to execute arbitrary code.
-
-
-The Spring Framework also provides two classes that extend RemoteInvocationSerializingExporter:
-
-HttpInvokerServiceExporter
-
-
-SimpleHttpInvokerServiceExporter
-
-
-
-These classes export specified beans as HTTP endpoints that deserialize data from an HTTP request
-using unsafe ObjectInputStream. If a remote attacker can reach such endpoints,
-it results in remote code execution in the worst case.
-
-
-CVE-2016-1000027 has been assigned to this issue in the Spring Framework.
-It is regarded as a design limitation, and can be mitigated but not fixed outright.
-
-
-
-
-
-Avoid using HttpInvokerServiceExporter, SimpleHttpInvokerServiceExporter
-and any other exporter that is based on RemoteInvocationSerializingExporter.
-Instead, use other message formats for API endpoints (for example, JSON),
-but make sure that the underlying deserialization mechanism is properly configured
-so that deserialization attacks are not possible. If the vulnerable exporters can not be replaced,
-consider using global deserialization filters introduced in JEP 290.
-
-
-
-
-
-The following examples shows how a vulnerable HTTP endpoint can be defined in a Spring XML config:
-
-
-
-
-
-
-OWASP:
-Deserialization of untrusted data.
-
-
-Spring Framework API documentation:
-RemoteInvocationSerializingExporter class
-
-
-Spring Framework API documentation:
-HttpInvokerServiceExporter class
-
-
-National Vulnerability Database:
-CVE-2016-1000027
-
-
-Tenable Research Advisory:
-[R2] Pivotal Spring Framework HttpInvokerServiceExporter readRemoteInvocation Method Untrusted Java Deserialization
-
-
-Spring Framework bug tracker:
-Sonatype vulnerability CVE-2016-1000027 in Spring-web project
-
-
-OpenJDK:
-JEP 290: Filter Incoming Serialization Data
-
-
-
+
+
+
\ No newline at end of file
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInXMLConfigurationExample.inc.qhelp b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInXMLConfigurationExample.inc.qhelp
new file mode 100644
index 00000000000..bc18f4dc233
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInXMLConfigurationExample.inc.qhelp
@@ -0,0 +1,13 @@
+
+
+
+
+
+The following examples shows how a vulnerable HTTP endpoint can be defined in a Spring XML config:
+
+
+
+
+
\ No newline at end of file
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterQuery.inc.qhelp b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterQuery.inc.qhelp
new file mode 100644
index 00000000000..732c5c7e545
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterQuery.inc.qhelp
@@ -0,0 +1,41 @@
+
+
+
+
+
+The Spring Framework provides an abstract base class RemoteInvocationSerializingExporter
+for creating remote service exporters.
+A Spring exporter, which is based on this class, deserializes incoming data using ObjectInputStream.
+Deserializing untrusted data is easily exploitable and in many cases allows an attacker
+to execute arbitrary code.
+
+
+The Spring Framework also provides HttpInvokerServiceExporter
+and SimpleHttpInvokerServiceExporter classes
+that extend RemoteInvocationSerializingExporter.
+
+
+These classes export specified beans as HTTP endpoints that deserialize data from an HTTP request
+using unsafe ObjectInputStream. If a remote attacker can reach such endpoints,
+it results in remote code execution in the worst case.
+
+
+CVE-2016-1000027 has been assigned to this issue in the Spring Framework.
+It is regarded as a design limitation, and can be mitigated but not fixed outright.
+
+
+
+
+
+Avoid using HttpInvokerServiceExporter, SimpleHttpInvokerServiceExporter
+and any other exporter that is based on RemoteInvocationSerializingExporter.
+Instead, use other message formats for API endpoints (for example, JSON),
+but make sure that the underlying deserialization mechanism is properly configured
+so that deserialization attacks are not possible. If the vulnerable exporters can not be replaced,
+consider using global deserialization filters introduced in JEP 290.
+
+
+
+
\ No newline at end of file
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterReferences.inc.qhelp b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterReferences.inc.qhelp
new file mode 100644
index 00000000000..94d269e35d0
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterReferences.inc.qhelp
@@ -0,0 +1,37 @@
+
+
+
+
+
+OWASP:
+Deserialization of untrusted data.
+
+
+Spring Framework API documentation:
+RemoteInvocationSerializingExporter class
+
+
+Spring Framework API documentation:
+HttpInvokerServiceExporter class
+
+
+National Vulnerability Database:
+CVE-2016-1000027
+
+
+Tenable Research Advisory:
+[R2] Pivotal Spring Framework HttpInvokerServiceExporter readRemoteInvocation Method Untrusted Java Deserialization
+
+
+Spring Framework bug tracker:
+Sonatype vulnerability CVE-2016-1000027 in Spring-web project
+
+
+OpenJDK:
+JEP 290: Filter Incoming Serialization Data
+
+
+
+
\ No newline at end of file
From 4941d9b7bf97fee023afa5f0b9f4061a66e95cf9 Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Wed, 10 Mar 2021 12:03:44 +0100
Subject: [PATCH 097/725] Java: Add query for CSV framework coverage.
---
java/ql/src/meta/frameworks/Coverage.ql | 14 +++++++++++
.../code/java/dataflow/ExternalFlow.qll | 23 +++++++++++++++++++
2 files changed, 37 insertions(+)
create mode 100644 java/ql/src/meta/frameworks/Coverage.ql
diff --git a/java/ql/src/meta/frameworks/Coverage.ql b/java/ql/src/meta/frameworks/Coverage.ql
new file mode 100644
index 00000000000..d945d7b4002
--- /dev/null
+++ b/java/ql/src/meta/frameworks/Coverage.ql
@@ -0,0 +1,14 @@
+/**
+ * @name Framework coverage
+ * @description The number of API endpoints covered by CSV models sorted by
+ * package and source-, sink-, and summary-kind.
+ * @kind metric
+ * @id java/meta/framework-coverage
+ */
+
+import java
+import semmle.code.java.dataflow.ExternalFlow
+
+from string package, string kind, string part, int n
+where modelCoverage(package, kind, part, n)
+select package, kind, part, n
diff --git a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll
index 7197caea351..2f285d5a115 100644
--- a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll
+++ b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll
@@ -204,6 +204,29 @@ private predicate summaryModel(
)
}
+/**
+ * Holds if CSV framework coverage of `package` is `n` api endpoints of the
+ * kind `(kind, part)`.
+ */
+predicate modelCoverage(string package, string kind, string part, int n) {
+ part = "source" and
+ n =
+ strictcount(string type, boolean subtypes, string name, string signature, string ext,
+ string output | sourceModel(package, type, subtypes, name, signature, ext, output, kind))
+ or
+ part = "sink" and
+ n =
+ strictcount(string type, boolean subtypes, string name, string signature, string ext,
+ string input | sinkModel(package, type, subtypes, name, signature, ext, input, kind))
+ or
+ part = "summary" and
+ n =
+ strictcount(string type, boolean subtypes, string name, string signature, string ext,
+ string input, string output |
+ summaryModel(package, type, subtypes, name, signature, ext, input, output, kind)
+ )
+}
+
/** Provides a query predicate to check the CSV data for validation errors. */
module CsvValidation {
/** Holds if some row in a CSV-based flow model appears to contain typos. */
From 16a3dfa30a4fb3fb176398a8374e24653a13c650 Mon Sep 17 00:00:00 2001
From: Cornelius Riemenschneider
Date: Wed, 10 Mar 2021 11:15:55 +0000
Subject: [PATCH 098/725] C++: Update summary metrics query format.
---
cpp/ql/src/Summary/Files.ql | 3 ++-
cpp/ql/src/Summary/Lines.ql | 3 ++-
cpp/ql/src/Summary/LinesOfCode.ql | 3 ++-
cpp/ql/src/Summary/LinesOfCodePerFile.ql | 3 ++-
cpp/ql/src/Summary/LinesPerFile.ql | 3 ++-
5 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/cpp/ql/src/Summary/Files.ql b/cpp/ql/src/Summary/Files.ql
index 5ea01b25631..83007c2fec6 100644
--- a/cpp/ql/src/Summary/Files.ql
+++ b/cpp/ql/src/Summary/Files.ql
@@ -1,8 +1,9 @@
/**
+ * @id cpp/summary/files
* @name Total source files
* @description The total number of source files.
* @kind metric
- * @id cpp/summary/files
+ * @tags summary
*/
import cpp
diff --git a/cpp/ql/src/Summary/Lines.ql b/cpp/ql/src/Summary/Lines.ql
index 87feb1f7d6f..dec466c5468 100644
--- a/cpp/ql/src/Summary/Lines.ql
+++ b/cpp/ql/src/Summary/Lines.ql
@@ -1,8 +1,9 @@
/**
+ * @id cpp/summary/lines
* @name Total lines of text
* @description The total number of lines of text across all source files.
* @kind metric
- * @id cpp/summary/lines
+ * @tags summary
*/
import cpp
diff --git a/cpp/ql/src/Summary/LinesOfCode.ql b/cpp/ql/src/Summary/LinesOfCode.ql
index 7d8ae36f79e..4dc7dc88c05 100644
--- a/cpp/ql/src/Summary/LinesOfCode.ql
+++ b/cpp/ql/src/Summary/LinesOfCode.ql
@@ -1,8 +1,9 @@
/**
+ * @id cpp/summary/lines-of-code
* @name Total lines of code
* @description The total number of lines of code across all source files.
* @kind metric
- * @id cpp/summary/lines-of-code
+ * @tags summary
*/
import cpp
diff --git a/cpp/ql/src/Summary/LinesOfCodePerFile.ql b/cpp/ql/src/Summary/LinesOfCodePerFile.ql
index 818e4312dcf..c98d8a3d196 100644
--- a/cpp/ql/src/Summary/LinesOfCodePerFile.ql
+++ b/cpp/ql/src/Summary/LinesOfCodePerFile.ql
@@ -1,8 +1,9 @@
/**
+ * @id cpp/summary/lines-of-code-per-file
* @name Lines of code per source file
* @description The number of lines of code for each source file.
* @kind metric
- * @id cpp/summary/lines-of-code-per-file
+ * @tags summary
*/
import cpp
diff --git a/cpp/ql/src/Summary/LinesPerFile.ql b/cpp/ql/src/Summary/LinesPerFile.ql
index 54a0a75d2e0..bfc78e4d284 100644
--- a/cpp/ql/src/Summary/LinesPerFile.ql
+++ b/cpp/ql/src/Summary/LinesPerFile.ql
@@ -1,8 +1,9 @@
/**
+ * @id cpp/summary/lines-per-file
* @name Lines of text per source file
* @description The number of lines of text for each source file.
* @kind metric
- * @id cpp/summary/lines-per-file
+ * @tags summary
*/
import cpp
From 72f28513eb6478024cb184f9affe71490c8217d3 Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Wed, 10 Mar 2021 12:12:27 +0000
Subject: [PATCH 099/725] Move test check to the sink
---
.../CWE-1004/SensitiveCookieNotHttpOnly.ql | 44 ++++++++-----------
1 file changed, 19 insertions(+), 25 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
index 716975d0486..eee4d8c89d1 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
@@ -73,34 +73,29 @@ class CookieResponseSink extends DataFlow::ExprNode {
ma instanceof SetCookieMethodAccess and
this.getExpr() = ma.getArgument(1) and
not hasHttpOnlyExpr(this.getExpr()) // response.addHeader("Set-Cookie", "token=" +authId + ";HttpOnly;Secure")
- )
+ ) and
+ not isTestMethod(ma) // Test class or method
)
}
}
-/** A JAX-RS `NewCookie` constructor that sets `HttpOnly` to true. */
-class HttpOnlyNewCookie extends ClassInstanceExpr {
- HttpOnlyNewCookie() {
- this.getConstructedType()
- .hasQualifiedName(["javax.ws.rs.core", "jakarta.ws.rs.core"], "NewCookie") and
- (
- this.getNumArgument() = 6 and this.getArgument(5).(BooleanLiteral).getBooleanValue() = true // NewCookie(Cookie cookie, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly)
- or
- this.getNumArgument() = 8 and
- this.getArgument(6).getType() instanceof BooleanType and
- this.getArgument(7).(BooleanLiteral).getBooleanValue() = true // NewCookie(String name, String value, String path, String domain, String comment, int maxAge, boolean secure, boolean httpOnly)
- or
- this.getNumArgument() = 10 and this.getArgument(9).(BooleanLiteral).getBooleanValue() = true // NewCookie(String name, String value, String path, String domain, int version, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly)
- )
- }
+/** Holds if `cie` is an invocation of a JAX-RS `NewCookie` constructor that sets `HttpOnly` to true. */
+predicate setHttpOnlyInNewCookie(ClassInstanceExpr cie) {
+ cie.getConstructedType().hasQualifiedName(["javax.ws.rs.core", "jakarta.ws.rs.core"], "NewCookie") and
+ (
+ cie.getNumArgument() = 6 and cie.getArgument(5).(BooleanLiteral).getBooleanValue() = true // NewCookie(Cookie cookie, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly)
+ or
+ cie.getNumArgument() = 8 and
+ cie.getArgument(6).getType() instanceof BooleanType and
+ cie.getArgument(7).(BooleanLiteral).getBooleanValue() = true // NewCookie(String name, String value, String path, String domain, String comment, int maxAge, boolean secure, boolean httpOnly)
+ or
+ cie.getNumArgument() = 10 and cie.getArgument(9).(BooleanLiteral).getBooleanValue() = true // NewCookie(String name, String value, String path, String domain, int version, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly)
+ )
}
/** The cookie constructor. */
class CookieTaintPreservingConstructor extends Constructor, TaintPreservingCallable {
- CookieTaintPreservingConstructor() {
- this.getDeclaringType() instanceof CookieClass and
- not exists(HttpOnlyNewCookie hie | hie.getConstructor() = this)
- }
+ CookieTaintPreservingConstructor() { this.getDeclaringType() instanceof CookieClass }
override predicate returnsTaintFrom(int arg) { arg = 0 }
}
@@ -122,9 +117,8 @@ class CookieInstanceExpr extends TaintPreservingCallable {
* c) in a test class whose name has the word `test`
* d) in a test class implementing a test framework such as JUnit or TestNG
*/
-predicate isTestMethod(DataFlow::Node node) {
- exists(MethodAccess ma, Method m |
- node.asExpr() = ma.getAnArgument() and
+predicate isTestMethod(MethodAccess ma) {
+ exists(Method m |
m = ma.getEnclosingCallable() and
(
m.getDeclaringType().getName().toLowerCase().matches("%test%") or // Simple check to exclude test classes to reduce FPs
@@ -149,8 +143,8 @@ class MissingHttpOnlyConfiguration extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) { sink instanceof CookieResponseSink }
override predicate isSanitizer(DataFlow::Node node) {
- // Test class or method
- isTestMethod(node)
+ // new NewCookie("session-access-key", accessKey, "/", null, null, 0, true, true)
+ setHttpOnlyInNewCookie(node.asExpr())
}
}
From f0ddfc9283607f01603a93e6a05357aa27a579ce Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Wed, 10 Mar 2021 12:18:55 +0000
Subject: [PATCH 100/725] Minor qldoc changes
---
.../Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
index eee4d8c89d1..c3f9349dbc8 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
@@ -79,7 +79,10 @@ class CookieResponseSink extends DataFlow::ExprNode {
}
}
-/** Holds if `cie` is an invocation of a JAX-RS `NewCookie` constructor that sets `HttpOnly` to true. */
+/**
+ * Holds if `ClassInstanceExpr` cie is an invocation of a JAX-RS `NewCookie` constructor
+ * that sets `HttpOnly` to true.
+ */
predicate setHttpOnlyInNewCookie(ClassInstanceExpr cie) {
cie.getConstructedType().hasQualifiedName(["javax.ws.rs.core", "jakarta.ws.rs.core"], "NewCookie") and
(
@@ -111,7 +114,7 @@ class CookieInstanceExpr extends TaintPreservingCallable {
}
/**
- * Holds if the node is a test method indicated by:
+ * Holds if the MethodAccess `ma` is a test method call indicated by:
* a) in a test directory such as `src/test/java`
* b) in a test package whose name has the word `test`
* c) in a test class whose name has the word `test`
From 0b6589c8bea669afe492a0308f7decbf23f68eb4 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Wed, 10 Mar 2021 15:47:06 +0100
Subject: [PATCH 101/725] C++: Accept test changes.
---
.../annotate_sinks_only/stl.cpp | 4 +-
.../security-taint/tainted_diff.expected | 2 -
.../security-taint/tainted_ir.expected | 2 +
.../dataflow/taint-tests/string.cpp | 4 +-
.../dataflow/taint-tests/stringstream.cpp | 6 +--
.../CWE-022/semmle/tests/TaintedPath.expected | 4 ++
.../CWE-089/SqlTainted/SqlTainted.expected | 4 ++
.../semmle/tests/UnboundedWrite.expected | 20 +++++++++
.../CWE-134/semmle/funcs/funcsLocal.expected | 24 ++++++++++
.../CWE/CWE-134/semmle/ifs/ifs.expected | 44 +++++++++++++++++++
.../semmle/TaintedAllocationSize/test.cpp | 2 +-
.../AuthenticationBypass.expected | 12 +++++
.../tests/CleartextBufferWrite.expected | 4 ++
13 files changed, 122 insertions(+), 10 deletions(-)
diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/stl.cpp b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/stl.cpp
index 0264f29f4af..8a15071ef3c 100644
--- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/stl.cpp
+++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/stl.cpp
@@ -91,12 +91,12 @@ void test_stringstream()
sink(ss2); // $ ir MISSING: ast
sink(ss3); // $ MISSING: ast,ir
sink(ss4); // $ ir MISSING: ast
- sink(ss5); // $ MISSING: ast,ir
+ sink(ss5); // $ ir MISSING: ast
sink(ss1.str());
sink(ss2.str()); // $ ir MISSING: ast
sink(ss3.str()); // $ MISSING: ast,ir
sink(ss4.str()); // $ ir MISSING: ast
- sink(ss5.str()); // $ MISSING: ast,ir
+ sink(ss5.str()); // $ ir MISSING: ast
}
void test_stringstream_int(int source)
diff --git a/cpp/ql/test/library-tests/dataflow/security-taint/tainted_diff.expected b/cpp/ql/test/library-tests/dataflow/security-taint/tainted_diff.expected
index 9050ad322f8..2b1b021716b 100644
--- a/cpp/ql/test/library-tests/dataflow/security-taint/tainted_diff.expected
+++ b/cpp/ql/test/library-tests/dataflow/security-taint/tainted_diff.expected
@@ -28,7 +28,6 @@
| test.cpp:83:28:83:33 | call to getenv | test.cpp:85:8:85:11 | copy | AST only |
| test.cpp:83:28:83:33 | call to getenv | test.cpp:86:2:86:7 | call to strcpy | AST only |
| test.cpp:83:28:83:33 | call to getenv | test.cpp:86:9:86:12 | copy | AST only |
-| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | (const char *)... | AST only |
| test.cpp:100:12:100:15 | call to gets | test.cpp:98:8:98:14 | pointer | AST only |
| test.cpp:100:12:100:15 | call to gets | test.cpp:100:2:100:8 | pointer | AST only |
| test.cpp:100:17:100:22 | buffer | test.cpp:93:18:93:18 | s | AST only |
@@ -41,4 +40,3 @@
| test.cpp:106:28:106:33 | call to getenv | test.cpp:108:8:108:11 | copy | AST only |
| test.cpp:106:28:106:33 | call to getenv | test.cpp:109:2:109:7 | call to strcpy | AST only |
| test.cpp:106:28:106:33 | call to getenv | test.cpp:109:9:109:12 | copy | AST only |
-| test.cpp:106:28:106:33 | call to getenv | test.cpp:111:14:111:17 | (const char *)... | AST only |
diff --git a/cpp/ql/test/library-tests/dataflow/security-taint/tainted_ir.expected b/cpp/ql/test/library-tests/dataflow/security-taint/tainted_ir.expected
index 7f3044e6715..fb19ea7fc9f 100644
--- a/cpp/ql/test/library-tests/dataflow/security-taint/tainted_ir.expected
+++ b/cpp/ql/test/library-tests/dataflow/security-taint/tainted_ir.expected
@@ -33,6 +33,7 @@
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:6:88:27 | ! ... |
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:7:88:12 | call to strcmp |
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:7:88:27 | (bool)... |
+| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | (const char *)... |
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | copy |
| test.cpp:100:12:100:15 | call to gets | test.cpp:100:12:100:15 | call to gets |
| test.cpp:100:17:100:22 | buffer | test.cpp:100:17:100:22 | array to pointer conversion |
@@ -43,4 +44,5 @@
| test.cpp:106:28:106:33 | call to getenv | test.cpp:111:6:111:27 | ! ... |
| test.cpp:106:28:106:33 | call to getenv | test.cpp:111:7:111:12 | call to strcmp |
| test.cpp:106:28:106:33 | call to getenv | test.cpp:111:7:111:27 | (bool)... |
+| test.cpp:106:28:106:33 | call to getenv | test.cpp:111:14:111:17 | (const char *)... |
| test.cpp:106:28:106:33 | call to getenv | test.cpp:111:14:111:17 | copy |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp
index 43b6e9bc53b..0e29d314887 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp
@@ -406,9 +406,9 @@ void test_string_iterators() {
i6--;
sink(*i6); // $ ast,ir
i7 = i2;
- sink(*(i7+=1)); // $ ast MISSING: ir
+ sink(*(i7+=1)); // $ ast,ir
i8 = i2;
- sink(*(i8-=1)); // $ ast MISSING: ir
+ sink(*(i8-=1)); // $ ast,ir
i9 = s2.end();
--i9;
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp
index 7a6c5e51820..4d28955aaa0 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp
@@ -32,18 +32,18 @@ void test_stringstream_string(int amount)
sink(ss2 << source()); // $ ast,ir
sink(ss3 << "123" << source()); // $ ast,ir
sink(ss4 << source() << "456"); // $ ast,ir
- sink(ss5 << t); // $ ast MISSING: ir
+ sink(ss5 << t); // $ ast,ir
sink(ss1);
sink(ss2); // $ ast,ir
sink(ss3); // $ ast MISSING: ir
sink(ss4); // $ ast,ir
- sink(ss5); // $ ast MISSING: ir
+ sink(ss5); // $ ast MISSIN,ir
sink(ss1.str());
sink(ss2.str()); // $ ast,ir
sink(ss3.str()); // $ ast MISSING: ir
sink(ss4.str()); // $ ast,ir
- sink(ss5.str()); // $ ast MISSING: ir
+ sink(ss5.str()); // $ ast,ir
ss6.str("abc");
ss6.str(source()); // (overwrites)
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected
index 7383fbbd215..60898414224 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected
@@ -1,4 +1,6 @@
edges
+| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | (const char *)... |
+| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | (const char *)... |
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | Argument 0 indirection |
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | Argument 0 indirection |
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName |
@@ -6,6 +8,8 @@ edges
nodes
| test.c:9:23:9:26 | argv | semmle.label | argv |
| test.c:9:23:9:26 | argv | semmle.label | argv |
+| test.c:17:11:17:18 | (const char *)... | semmle.label | (const char *)... |
+| test.c:17:11:17:18 | (const char *)... | semmle.label | (const char *)... |
| test.c:17:11:17:18 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| test.c:17:11:17:18 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| test.c:17:11:17:18 | fileName | semmle.label | fileName |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/SqlTainted.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/SqlTainted.expected
index af50f184740..fe84a80654c 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/SqlTainted.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/SqlTainted.expected
@@ -1,6 +1,8 @@
edges
| test.c:15:20:15:23 | argv | test.c:21:18:21:23 | (const char *)... |
| test.c:15:20:15:23 | argv | test.c:21:18:21:23 | (const char *)... |
+| test.c:15:20:15:23 | argv | test.c:21:18:21:23 | Argument 1 indirection |
+| test.c:15:20:15:23 | argv | test.c:21:18:21:23 | Argument 1 indirection |
| test.c:15:20:15:23 | argv | test.c:21:18:21:23 | query1 |
| test.c:15:20:15:23 | argv | test.c:21:18:21:23 | query1 |
nodes
@@ -8,6 +10,8 @@ nodes
| test.c:15:20:15:23 | argv | semmle.label | argv |
| test.c:21:18:21:23 | (const char *)... | semmle.label | (const char *)... |
| test.c:21:18:21:23 | (const char *)... | semmle.label | (const char *)... |
+| test.c:21:18:21:23 | Argument 1 indirection | semmle.label | Argument 1 indirection |
+| test.c:21:18:21:23 | Argument 1 indirection | semmle.label | Argument 1 indirection |
| test.c:21:18:21:23 | query1 | semmle.label | query1 |
#select
| test.c:21:18:21:23 | query1 | test.c:15:20:15:23 | argv | test.c:21:18:21:23 | query1 | This argument to a SQL query function is derived from $@ and then passed to mysql_query(sqlArg) | test.c:15:20:15:23 | argv | user input (argv) |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-120/semmle/tests/UnboundedWrite.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-120/semmle/tests/UnboundedWrite.expected
index cc314aa5d0e..a0db8029b2b 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-120/semmle/tests/UnboundedWrite.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-120/semmle/tests/UnboundedWrite.expected
@@ -1,6 +1,8 @@
edges
| tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | (const char *)... |
| tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | (const char *)... |
+| tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | Argument 1 indirection |
+| tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | Argument 1 indirection |
| tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | access to array |
| tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | access to array |
| tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | access to array |
@@ -13,6 +15,8 @@ edges
| tests.c:28:22:28:25 | argv | tests.c:33:21:33:29 | Argument 2 indirection |
| tests.c:28:22:28:25 | argv | tests.c:33:21:33:29 | buffer100 |
| tests.c:28:22:28:25 | argv | tests.c:33:21:33:29 | buffer100 |
+| tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | Argument 2 indirection |
+| tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | Argument 2 indirection |
| tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | access to array |
| tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | access to array |
| tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | access to array |
@@ -25,12 +29,22 @@ edges
| tests.c:29:28:29:31 | argv | tests.c:33:21:33:29 | Argument 2 indirection |
| tests.c:29:28:29:31 | argv | tests.c:33:21:33:29 | buffer100 |
| tests.c:29:28:29:31 | argv | tests.c:33:21:33:29 | buffer100 |
+| tests.c:31:15:31:23 | array to pointer conversion | tests.c:31:15:31:23 | Argument 1 indirection |
+| tests.c:31:15:31:23 | array to pointer conversion | tests.c:31:15:31:23 | buffer100 |
+| tests.c:31:15:31:23 | buffer100 | tests.c:31:15:31:23 | Argument 1 indirection |
+| tests.c:31:15:31:23 | buffer100 | tests.c:31:15:31:23 | buffer100 |
| tests.c:31:15:31:23 | buffer100 | tests.c:33:21:33:29 | Argument 2 indirection |
| tests.c:31:15:31:23 | buffer100 | tests.c:33:21:33:29 | buffer100 |
| tests.c:31:15:31:23 | scanf output argument | tests.c:33:21:33:29 | Argument 2 indirection |
| tests.c:31:15:31:23 | scanf output argument | tests.c:33:21:33:29 | buffer100 |
+| tests.c:33:21:33:29 | array to pointer conversion | tests.c:33:21:33:29 | Argument 2 indirection |
+| tests.c:33:21:33:29 | array to pointer conversion | tests.c:33:21:33:29 | buffer100 |
+| tests.c:33:21:33:29 | buffer100 | tests.c:33:21:33:29 | Argument 2 indirection |
+| tests.c:33:21:33:29 | buffer100 | tests.c:33:21:33:29 | buffer100 |
| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | (const char *)... |
| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | (const char *)... |
+| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | Argument 0 indirection |
+| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | Argument 0 indirection |
| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | access to array |
| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | access to array |
| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | access to array |
@@ -40,11 +54,15 @@ nodes
| tests.c:28:22:28:25 | argv | semmle.label | argv |
| tests.c:28:22:28:28 | (const char *)... | semmle.label | (const char *)... |
| tests.c:28:22:28:28 | (const char *)... | semmle.label | (const char *)... |
+| tests.c:28:22:28:28 | Argument 1 indirection | semmle.label | Argument 1 indirection |
+| tests.c:28:22:28:28 | Argument 1 indirection | semmle.label | Argument 1 indirection |
| tests.c:28:22:28:28 | access to array | semmle.label | access to array |
| tests.c:28:22:28:28 | access to array | semmle.label | access to array |
| tests.c:28:22:28:28 | access to array | semmle.label | access to array |
| tests.c:29:28:29:31 | argv | semmle.label | argv |
| tests.c:29:28:29:31 | argv | semmle.label | argv |
+| tests.c:29:28:29:34 | Argument 2 indirection | semmle.label | Argument 2 indirection |
+| tests.c:29:28:29:34 | Argument 2 indirection | semmle.label | Argument 2 indirection |
| tests.c:29:28:29:34 | access to array | semmle.label | access to array |
| tests.c:29:28:29:34 | access to array | semmle.label | access to array |
| tests.c:29:28:29:34 | access to array | semmle.label | access to array |
@@ -67,6 +85,8 @@ nodes
| tests.c:34:10:34:13 | argv | semmle.label | argv |
| tests.c:34:10:34:16 | (const char *)... | semmle.label | (const char *)... |
| tests.c:34:10:34:16 | (const char *)... | semmle.label | (const char *)... |
+| tests.c:34:10:34:16 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| tests.c:34:10:34:16 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| tests.c:34:10:34:16 | access to array | semmle.label | access to array |
| tests.c:34:10:34:16 | access to array | semmle.label | access to array |
| tests.c:34:10:34:16 | access to array | semmle.label | access to array |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/funcs/funcsLocal.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/funcs/funcsLocal.expected
index 5d04ac6149d..963c37bef48 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/funcs/funcsLocal.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/funcs/funcsLocal.expected
@@ -1,48 +1,68 @@
edges
+| funcsLocal.c:16:8:16:9 | fread output argument | funcsLocal.c:17:9:17:10 | (const char *)... |
| funcsLocal.c:16:8:16:9 | fread output argument | funcsLocal.c:17:9:17:10 | Argument 0 indirection |
| funcsLocal.c:16:8:16:9 | fread output argument | funcsLocal.c:17:9:17:10 | i1 |
+| funcsLocal.c:16:8:16:9 | fread output argument | funcsLocal.c:58:9:58:10 | (const char *)... |
| funcsLocal.c:16:8:16:9 | fread output argument | funcsLocal.c:58:9:58:10 | Argument 0 indirection |
| funcsLocal.c:16:8:16:9 | fread output argument | funcsLocal.c:58:9:58:10 | e1 |
+| funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:17:9:17:10 | (const char *)... |
| funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:17:9:17:10 | Argument 0 indirection |
| funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:17:9:17:10 | i1 |
+| funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:58:9:58:10 | (const char *)... |
| funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:58:9:58:10 | Argument 0 indirection |
| funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:58:9:58:10 | e1 |
+| funcsLocal.c:26:8:26:9 | fgets output argument | funcsLocal.c:27:9:27:10 | (const char *)... |
| funcsLocal.c:26:8:26:9 | fgets output argument | funcsLocal.c:27:9:27:10 | Argument 0 indirection |
| funcsLocal.c:26:8:26:9 | fgets output argument | funcsLocal.c:27:9:27:10 | i3 |
+| funcsLocal.c:26:8:26:9 | i3 | funcsLocal.c:27:9:27:10 | (const char *)... |
| funcsLocal.c:26:8:26:9 | i3 | funcsLocal.c:27:9:27:10 | Argument 0 indirection |
| funcsLocal.c:26:8:26:9 | i3 | funcsLocal.c:27:9:27:10 | i3 |
| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | (const char *)... |
| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | (const char *)... |
+| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | Argument 0 indirection |
+| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | Argument 0 indirection |
| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | i4 |
| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | i4 |
| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | i4 |
| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | i4 |
+| funcsLocal.c:31:19:31:21 | fgets output argument | funcsLocal.c:32:9:32:10 | (const char *)... |
| funcsLocal.c:31:19:31:21 | fgets output argument | funcsLocal.c:32:9:32:10 | Argument 0 indirection |
| funcsLocal.c:31:19:31:21 | fgets output argument | funcsLocal.c:32:9:32:10 | i4 |
+| funcsLocal.c:31:19:31:21 | i41 | funcsLocal.c:32:9:32:10 | (const char *)... |
| funcsLocal.c:31:19:31:21 | i41 | funcsLocal.c:32:9:32:10 | Argument 0 indirection |
| funcsLocal.c:31:19:31:21 | i41 | funcsLocal.c:32:9:32:10 | i4 |
+| funcsLocal.c:36:7:36:8 | gets output argument | funcsLocal.c:37:9:37:10 | (const char *)... |
| funcsLocal.c:36:7:36:8 | gets output argument | funcsLocal.c:37:9:37:10 | Argument 0 indirection |
| funcsLocal.c:36:7:36:8 | gets output argument | funcsLocal.c:37:9:37:10 | i5 |
+| funcsLocal.c:36:7:36:8 | i5 | funcsLocal.c:37:9:37:10 | (const char *)... |
| funcsLocal.c:36:7:36:8 | i5 | funcsLocal.c:37:9:37:10 | Argument 0 indirection |
| funcsLocal.c:36:7:36:8 | i5 | funcsLocal.c:37:9:37:10 | i5 |
| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | (const char *)... |
| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | (const char *)... |
+| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | Argument 0 indirection |
+| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | Argument 0 indirection |
| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | i6 |
| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | i6 |
| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | i6 |
| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | i6 |
+| funcsLocal.c:41:18:41:20 | gets output argument | funcsLocal.c:42:9:42:10 | (const char *)... |
| funcsLocal.c:41:18:41:20 | gets output argument | funcsLocal.c:42:9:42:10 | Argument 0 indirection |
| funcsLocal.c:41:18:41:20 | gets output argument | funcsLocal.c:42:9:42:10 | i6 |
+| funcsLocal.c:41:18:41:20 | i61 | funcsLocal.c:42:9:42:10 | (const char *)... |
| funcsLocal.c:41:18:41:20 | i61 | funcsLocal.c:42:9:42:10 | Argument 0 indirection |
| funcsLocal.c:41:18:41:20 | i61 | funcsLocal.c:42:9:42:10 | i6 |
nodes
| funcsLocal.c:16:8:16:9 | fread output argument | semmle.label | fread output argument |
| funcsLocal.c:16:8:16:9 | i1 | semmle.label | i1 |
+| funcsLocal.c:17:9:17:10 | (const char *)... | semmle.label | (const char *)... |
+| funcsLocal.c:17:9:17:10 | (const char *)... | semmle.label | (const char *)... |
| funcsLocal.c:17:9:17:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| funcsLocal.c:17:9:17:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| funcsLocal.c:17:9:17:10 | i1 | semmle.label | i1 |
| funcsLocal.c:26:8:26:9 | fgets output argument | semmle.label | fgets output argument |
| funcsLocal.c:26:8:26:9 | i3 | semmle.label | i3 |
+| funcsLocal.c:27:9:27:10 | (const char *)... | semmle.label | (const char *)... |
+| funcsLocal.c:27:9:27:10 | (const char *)... | semmle.label | (const char *)... |
| funcsLocal.c:27:9:27:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| funcsLocal.c:27:9:27:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| funcsLocal.c:27:9:27:10 | i3 | semmle.label | i3 |
@@ -59,6 +79,8 @@ nodes
| funcsLocal.c:32:9:32:10 | i4 | semmle.label | i4 |
| funcsLocal.c:36:7:36:8 | gets output argument | semmle.label | gets output argument |
| funcsLocal.c:36:7:36:8 | i5 | semmle.label | i5 |
+| funcsLocal.c:37:9:37:10 | (const char *)... | semmle.label | (const char *)... |
+| funcsLocal.c:37:9:37:10 | (const char *)... | semmle.label | (const char *)... |
| funcsLocal.c:37:9:37:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| funcsLocal.c:37:9:37:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| funcsLocal.c:37:9:37:10 | i5 | semmle.label | i5 |
@@ -73,6 +95,8 @@ nodes
| funcsLocal.c:42:9:42:10 | i6 | semmle.label | i6 |
| funcsLocal.c:42:9:42:10 | i6 | semmle.label | i6 |
| funcsLocal.c:42:9:42:10 | i6 | semmle.label | i6 |
+| funcsLocal.c:58:9:58:10 | (const char *)... | semmle.label | (const char *)... |
+| funcsLocal.c:58:9:58:10 | (const char *)... | semmle.label | (const char *)... |
| funcsLocal.c:58:9:58:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| funcsLocal.c:58:9:58:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| funcsLocal.c:58:9:58:10 | e1 | semmle.label | e1 |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.expected
index 62c36d0192d..e769c072f84 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.expected
@@ -1,66 +1,88 @@
edges
| ifs.c:61:8:61:11 | argv | ifs.c:62:9:62:10 | (const char *)... |
| ifs.c:61:8:61:11 | argv | ifs.c:62:9:62:10 | (const char *)... |
+| ifs.c:61:8:61:11 | argv | ifs.c:62:9:62:10 | Argument 0 indirection |
+| ifs.c:61:8:61:11 | argv | ifs.c:62:9:62:10 | Argument 0 indirection |
| ifs.c:61:8:61:11 | argv | ifs.c:62:9:62:10 | c7 |
| ifs.c:61:8:61:11 | argv | ifs.c:62:9:62:10 | c7 |
| ifs.c:61:8:61:11 | argv | ifs.c:62:9:62:10 | c7 |
| ifs.c:61:8:61:11 | argv | ifs.c:62:9:62:10 | c7 |
| ifs.c:68:8:68:11 | argv | ifs.c:69:9:69:10 | (const char *)... |
| ifs.c:68:8:68:11 | argv | ifs.c:69:9:69:10 | (const char *)... |
+| ifs.c:68:8:68:11 | argv | ifs.c:69:9:69:10 | Argument 0 indirection |
+| ifs.c:68:8:68:11 | argv | ifs.c:69:9:69:10 | Argument 0 indirection |
| ifs.c:68:8:68:11 | argv | ifs.c:69:9:69:10 | c8 |
| ifs.c:68:8:68:11 | argv | ifs.c:69:9:69:10 | c8 |
| ifs.c:68:8:68:11 | argv | ifs.c:69:9:69:10 | c8 |
| ifs.c:68:8:68:11 | argv | ifs.c:69:9:69:10 | c8 |
| ifs.c:74:8:74:11 | argv | ifs.c:75:9:75:10 | (const char *)... |
| ifs.c:74:8:74:11 | argv | ifs.c:75:9:75:10 | (const char *)... |
+| ifs.c:74:8:74:11 | argv | ifs.c:75:9:75:10 | Argument 0 indirection |
+| ifs.c:74:8:74:11 | argv | ifs.c:75:9:75:10 | Argument 0 indirection |
| ifs.c:74:8:74:11 | argv | ifs.c:75:9:75:10 | i1 |
| ifs.c:74:8:74:11 | argv | ifs.c:75:9:75:10 | i1 |
| ifs.c:74:8:74:11 | argv | ifs.c:75:9:75:10 | i1 |
| ifs.c:74:8:74:11 | argv | ifs.c:75:9:75:10 | i1 |
| ifs.c:80:8:80:11 | argv | ifs.c:81:9:81:10 | (const char *)... |
| ifs.c:80:8:80:11 | argv | ifs.c:81:9:81:10 | (const char *)... |
+| ifs.c:80:8:80:11 | argv | ifs.c:81:9:81:10 | Argument 0 indirection |
+| ifs.c:80:8:80:11 | argv | ifs.c:81:9:81:10 | Argument 0 indirection |
| ifs.c:80:8:80:11 | argv | ifs.c:81:9:81:10 | i2 |
| ifs.c:80:8:80:11 | argv | ifs.c:81:9:81:10 | i2 |
| ifs.c:80:8:80:11 | argv | ifs.c:81:9:81:10 | i2 |
| ifs.c:80:8:80:11 | argv | ifs.c:81:9:81:10 | i2 |
| ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | (const char *)... |
| ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | (const char *)... |
+| ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | Argument 0 indirection |
+| ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | Argument 0 indirection |
| ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | i3 |
| ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | i3 |
| ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | i3 |
| ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | i3 |
| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | (const char *)... |
| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | (const char *)... |
+| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | Argument 0 indirection |
+| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | Argument 0 indirection |
| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 |
| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 |
| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 |
| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 |
| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | (const char *)... |
| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | (const char *)... |
+| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | Argument 0 indirection |
+| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | Argument 0 indirection |
| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 |
| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 |
| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 |
| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 |
| ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | (const char *)... |
| ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | (const char *)... |
+| ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | Argument 0 indirection |
+| ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | Argument 0 indirection |
| ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | i6 |
| ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | i6 |
| ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | i6 |
| ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | i6 |
| ifs.c:111:8:111:11 | argv | ifs.c:112:9:112:10 | (const char *)... |
| ifs.c:111:8:111:11 | argv | ifs.c:112:9:112:10 | (const char *)... |
+| ifs.c:111:8:111:11 | argv | ifs.c:112:9:112:10 | Argument 0 indirection |
+| ifs.c:111:8:111:11 | argv | ifs.c:112:9:112:10 | Argument 0 indirection |
| ifs.c:111:8:111:11 | argv | ifs.c:112:9:112:10 | i7 |
| ifs.c:111:8:111:11 | argv | ifs.c:112:9:112:10 | i7 |
| ifs.c:111:8:111:11 | argv | ifs.c:112:9:112:10 | i7 |
| ifs.c:111:8:111:11 | argv | ifs.c:112:9:112:10 | i7 |
| ifs.c:117:8:117:11 | argv | ifs.c:118:9:118:10 | (const char *)... |
| ifs.c:117:8:117:11 | argv | ifs.c:118:9:118:10 | (const char *)... |
+| ifs.c:117:8:117:11 | argv | ifs.c:118:9:118:10 | Argument 0 indirection |
+| ifs.c:117:8:117:11 | argv | ifs.c:118:9:118:10 | Argument 0 indirection |
| ifs.c:117:8:117:11 | argv | ifs.c:118:9:118:10 | i8 |
| ifs.c:117:8:117:11 | argv | ifs.c:118:9:118:10 | i8 |
| ifs.c:117:8:117:11 | argv | ifs.c:118:9:118:10 | i8 |
| ifs.c:117:8:117:11 | argv | ifs.c:118:9:118:10 | i8 |
| ifs.c:123:8:123:11 | argv | ifs.c:124:9:124:10 | (const char *)... |
| ifs.c:123:8:123:11 | argv | ifs.c:124:9:124:10 | (const char *)... |
+| ifs.c:123:8:123:11 | argv | ifs.c:124:9:124:10 | Argument 0 indirection |
+| ifs.c:123:8:123:11 | argv | ifs.c:124:9:124:10 | Argument 0 indirection |
| ifs.c:123:8:123:11 | argv | ifs.c:124:9:124:10 | i9 |
| ifs.c:123:8:123:11 | argv | ifs.c:124:9:124:10 | i9 |
| ifs.c:123:8:123:11 | argv | ifs.c:124:9:124:10 | i9 |
@@ -70,6 +92,8 @@ nodes
| ifs.c:61:8:61:11 | argv | semmle.label | argv |
| ifs.c:62:9:62:10 | (const char *)... | semmle.label | (const char *)... |
| ifs.c:62:9:62:10 | (const char *)... | semmle.label | (const char *)... |
+| ifs.c:62:9:62:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| ifs.c:62:9:62:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| ifs.c:62:9:62:10 | c7 | semmle.label | c7 |
| ifs.c:62:9:62:10 | c7 | semmle.label | c7 |
| ifs.c:62:9:62:10 | c7 | semmle.label | c7 |
@@ -77,6 +101,8 @@ nodes
| ifs.c:68:8:68:11 | argv | semmle.label | argv |
| ifs.c:69:9:69:10 | (const char *)... | semmle.label | (const char *)... |
| ifs.c:69:9:69:10 | (const char *)... | semmle.label | (const char *)... |
+| ifs.c:69:9:69:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| ifs.c:69:9:69:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| ifs.c:69:9:69:10 | c8 | semmle.label | c8 |
| ifs.c:69:9:69:10 | c8 | semmle.label | c8 |
| ifs.c:69:9:69:10 | c8 | semmle.label | c8 |
@@ -84,6 +110,8 @@ nodes
| ifs.c:74:8:74:11 | argv | semmle.label | argv |
| ifs.c:75:9:75:10 | (const char *)... | semmle.label | (const char *)... |
| ifs.c:75:9:75:10 | (const char *)... | semmle.label | (const char *)... |
+| ifs.c:75:9:75:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| ifs.c:75:9:75:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| ifs.c:75:9:75:10 | i1 | semmle.label | i1 |
| ifs.c:75:9:75:10 | i1 | semmle.label | i1 |
| ifs.c:75:9:75:10 | i1 | semmle.label | i1 |
@@ -91,6 +119,8 @@ nodes
| ifs.c:80:8:80:11 | argv | semmle.label | argv |
| ifs.c:81:9:81:10 | (const char *)... | semmle.label | (const char *)... |
| ifs.c:81:9:81:10 | (const char *)... | semmle.label | (const char *)... |
+| ifs.c:81:9:81:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| ifs.c:81:9:81:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| ifs.c:81:9:81:10 | i2 | semmle.label | i2 |
| ifs.c:81:9:81:10 | i2 | semmle.label | i2 |
| ifs.c:81:9:81:10 | i2 | semmle.label | i2 |
@@ -98,6 +128,8 @@ nodes
| ifs.c:86:8:86:11 | argv | semmle.label | argv |
| ifs.c:87:9:87:10 | (const char *)... | semmle.label | (const char *)... |
| ifs.c:87:9:87:10 | (const char *)... | semmle.label | (const char *)... |
+| ifs.c:87:9:87:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| ifs.c:87:9:87:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| ifs.c:87:9:87:10 | i3 | semmle.label | i3 |
| ifs.c:87:9:87:10 | i3 | semmle.label | i3 |
| ifs.c:87:9:87:10 | i3 | semmle.label | i3 |
@@ -105,6 +137,8 @@ nodes
| ifs.c:92:8:92:11 | argv | semmle.label | argv |
| ifs.c:93:9:93:10 | (const char *)... | semmle.label | (const char *)... |
| ifs.c:93:9:93:10 | (const char *)... | semmle.label | (const char *)... |
+| ifs.c:93:9:93:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| ifs.c:93:9:93:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| ifs.c:93:9:93:10 | i4 | semmle.label | i4 |
| ifs.c:93:9:93:10 | i4 | semmle.label | i4 |
| ifs.c:93:9:93:10 | i4 | semmle.label | i4 |
@@ -112,6 +146,8 @@ nodes
| ifs.c:98:8:98:11 | argv | semmle.label | argv |
| ifs.c:99:9:99:10 | (const char *)... | semmle.label | (const char *)... |
| ifs.c:99:9:99:10 | (const char *)... | semmle.label | (const char *)... |
+| ifs.c:99:9:99:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| ifs.c:99:9:99:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| ifs.c:99:9:99:10 | i5 | semmle.label | i5 |
| ifs.c:99:9:99:10 | i5 | semmle.label | i5 |
| ifs.c:99:9:99:10 | i5 | semmle.label | i5 |
@@ -119,6 +155,8 @@ nodes
| ifs.c:105:8:105:11 | argv | semmle.label | argv |
| ifs.c:106:9:106:10 | (const char *)... | semmle.label | (const char *)... |
| ifs.c:106:9:106:10 | (const char *)... | semmle.label | (const char *)... |
+| ifs.c:106:9:106:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| ifs.c:106:9:106:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| ifs.c:106:9:106:10 | i6 | semmle.label | i6 |
| ifs.c:106:9:106:10 | i6 | semmle.label | i6 |
| ifs.c:106:9:106:10 | i6 | semmle.label | i6 |
@@ -126,6 +164,8 @@ nodes
| ifs.c:111:8:111:11 | argv | semmle.label | argv |
| ifs.c:112:9:112:10 | (const char *)... | semmle.label | (const char *)... |
| ifs.c:112:9:112:10 | (const char *)... | semmle.label | (const char *)... |
+| ifs.c:112:9:112:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| ifs.c:112:9:112:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| ifs.c:112:9:112:10 | i7 | semmle.label | i7 |
| ifs.c:112:9:112:10 | i7 | semmle.label | i7 |
| ifs.c:112:9:112:10 | i7 | semmle.label | i7 |
@@ -133,6 +173,8 @@ nodes
| ifs.c:117:8:117:11 | argv | semmle.label | argv |
| ifs.c:118:9:118:10 | (const char *)... | semmle.label | (const char *)... |
| ifs.c:118:9:118:10 | (const char *)... | semmle.label | (const char *)... |
+| ifs.c:118:9:118:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| ifs.c:118:9:118:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| ifs.c:118:9:118:10 | i8 | semmle.label | i8 |
| ifs.c:118:9:118:10 | i8 | semmle.label | i8 |
| ifs.c:118:9:118:10 | i8 | semmle.label | i8 |
@@ -140,6 +182,8 @@ nodes
| ifs.c:123:8:123:11 | argv | semmle.label | argv |
| ifs.c:124:9:124:10 | (const char *)... | semmle.label | (const char *)... |
| ifs.c:124:9:124:10 | (const char *)... | semmle.label | (const char *)... |
+| ifs.c:124:9:124:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| ifs.c:124:9:124:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| ifs.c:124:9:124:10 | i9 | semmle.label | i9 |
| ifs.c:124:9:124:10 | i9 | semmle.label | i9 |
| ifs.c:124:9:124:10 | i9 | semmle.label | i9 |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp
index 0683f7211e3..943bc3b1214 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp
@@ -76,7 +76,7 @@ void processData2(char *start, char *end)
{
char *copy;
- copy = new char[end - start]; // GOOD
+ copy = new char[end - start]; // GOOD [FALSE POSITIVE]
// ...
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-290/semmle/AuthenticationBypass/AuthenticationBypass.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-290/semmle/AuthenticationBypass/AuthenticationBypass.expected
index e2671c07952..dfbb01bf264 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-290/semmle/AuthenticationBypass/AuthenticationBypass.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-290/semmle/AuthenticationBypass/AuthenticationBypass.expected
@@ -1,29 +1,41 @@
edges
+| test.cpp:16:25:16:30 | call to getenv | test.cpp:20:14:20:20 | Argument 0 indirection |
| test.cpp:16:25:16:30 | call to getenv | test.cpp:20:14:20:20 | address |
| test.cpp:16:25:16:30 | call to getenv | test.cpp:20:14:20:20 | address |
+| test.cpp:16:25:16:42 | (const char *)... | test.cpp:20:14:20:20 | Argument 0 indirection |
| test.cpp:16:25:16:42 | (const char *)... | test.cpp:20:14:20:20 | address |
| test.cpp:16:25:16:42 | (const char *)... | test.cpp:20:14:20:20 | address |
+| test.cpp:27:25:27:30 | call to getenv | test.cpp:31:14:31:20 | Argument 0 indirection |
| test.cpp:27:25:27:30 | call to getenv | test.cpp:31:14:31:20 | address |
| test.cpp:27:25:27:30 | call to getenv | test.cpp:31:14:31:20 | address |
+| test.cpp:27:25:27:42 | (const char *)... | test.cpp:31:14:31:20 | Argument 0 indirection |
| test.cpp:27:25:27:42 | (const char *)... | test.cpp:31:14:31:20 | address |
| test.cpp:27:25:27:42 | (const char *)... | test.cpp:31:14:31:20 | address |
+| test.cpp:38:25:38:30 | call to getenv | test.cpp:42:14:42:20 | Argument 0 indirection |
| test.cpp:38:25:38:30 | call to getenv | test.cpp:42:14:42:20 | address |
| test.cpp:38:25:38:30 | call to getenv | test.cpp:42:14:42:20 | address |
+| test.cpp:38:25:38:42 | (const char *)... | test.cpp:42:14:42:20 | Argument 0 indirection |
| test.cpp:38:25:38:42 | (const char *)... | test.cpp:42:14:42:20 | address |
| test.cpp:38:25:38:42 | (const char *)... | test.cpp:42:14:42:20 | address |
nodes
| test.cpp:16:25:16:30 | call to getenv | semmle.label | call to getenv |
| test.cpp:16:25:16:42 | (const char *)... | semmle.label | (const char *)... |
+| test.cpp:20:14:20:20 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| test.cpp:20:14:20:20 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| test.cpp:20:14:20:20 | address | semmle.label | address |
| test.cpp:20:14:20:20 | address | semmle.label | address |
| test.cpp:20:14:20:20 | address | semmle.label | address |
| test.cpp:27:25:27:30 | call to getenv | semmle.label | call to getenv |
| test.cpp:27:25:27:42 | (const char *)... | semmle.label | (const char *)... |
+| test.cpp:31:14:31:20 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| test.cpp:31:14:31:20 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| test.cpp:31:14:31:20 | address | semmle.label | address |
| test.cpp:31:14:31:20 | address | semmle.label | address |
| test.cpp:31:14:31:20 | address | semmle.label | address |
| test.cpp:38:25:38:30 | call to getenv | semmle.label | call to getenv |
| test.cpp:38:25:38:42 | (const char *)... | semmle.label | (const char *)... |
+| test.cpp:42:14:42:20 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| test.cpp:42:14:42:20 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| test.cpp:42:14:42:20 | address | semmle.label | address |
| test.cpp:42:14:42:20 | address | semmle.label | address |
| test.cpp:42:14:42:20 | address | semmle.label | address |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/CleartextBufferWrite.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/CleartextBufferWrite.expected
index f78d75eeed1..559e491417b 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/CleartextBufferWrite.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/CleartextBufferWrite.expected
@@ -1,4 +1,6 @@
edges
+| test.cpp:54:17:54:20 | argv | test.cpp:58:25:58:29 | Argument 2 indirection |
+| test.cpp:54:17:54:20 | argv | test.cpp:58:25:58:29 | Argument 2 indirection |
| test.cpp:54:17:54:20 | argv | test.cpp:58:25:58:29 | input |
| test.cpp:54:17:54:20 | argv | test.cpp:58:25:58:29 | input |
| test.cpp:54:17:54:20 | argv | test.cpp:58:25:58:29 | input |
@@ -6,6 +8,8 @@ edges
nodes
| test.cpp:54:17:54:20 | argv | semmle.label | argv |
| test.cpp:54:17:54:20 | argv | semmle.label | argv |
+| test.cpp:58:25:58:29 | Argument 2 indirection | semmle.label | Argument 2 indirection |
+| test.cpp:58:25:58:29 | Argument 2 indirection | semmle.label | Argument 2 indirection |
| test.cpp:58:25:58:29 | input | semmle.label | input |
| test.cpp:58:25:58:29 | input | semmle.label | input |
| test.cpp:58:25:58:29 | input | semmle.label | input |
From bc36e0db43ad67b83eec48d7ba8f0b35e2630995 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Wed, 10 Mar 2021 16:51:13 +0100
Subject: [PATCH 102/725] C++: Accept more test changes.
---
.../TaintedAllocationSize.expected | 34 +++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected
index 7c2f1491281..35228fc70b2 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected
@@ -27,6 +27,24 @@ edges
| test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... |
| test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... |
| test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... |
+| test.cpp:75:25:75:29 | start | test.cpp:79:18:79:28 | ... - ... |
+| test.cpp:75:25:75:29 | start | test.cpp:79:18:79:28 | ... - ... |
+| test.cpp:75:38:75:40 | end | test.cpp:79:18:79:28 | ... - ... |
+| test.cpp:75:38:75:40 | end | test.cpp:79:18:79:28 | ... - ... |
+| test.cpp:97:18:97:23 | buffer | test.cpp:100:4:100:15 | Argument 0 |
+| test.cpp:97:18:97:23 | buffer | test.cpp:100:17:100:22 | Argument 0 indirection |
+| test.cpp:97:18:97:23 | buffer | test.cpp:101:4:101:15 | Argument 0 |
+| test.cpp:97:18:97:23 | buffer | test.cpp:101:4:101:15 | Argument 1 |
+| test.cpp:97:18:97:23 | fread output argument | test.cpp:100:4:100:15 | Argument 0 |
+| test.cpp:97:18:97:23 | fread output argument | test.cpp:100:17:100:22 | Argument 0 indirection |
+| test.cpp:97:18:97:23 | fread output argument | test.cpp:101:4:101:15 | Argument 0 |
+| test.cpp:97:18:97:23 | fread output argument | test.cpp:101:4:101:15 | Argument 1 |
+| test.cpp:100:4:100:15 | Argument 0 | test.cpp:100:17:100:22 | processData1 output argument |
+| test.cpp:100:17:100:22 | Argument 0 indirection | test.cpp:100:17:100:22 | processData1 output argument |
+| test.cpp:100:17:100:22 | processData1 output argument | test.cpp:101:4:101:15 | Argument 0 |
+| test.cpp:100:17:100:22 | processData1 output argument | test.cpp:101:4:101:15 | Argument 1 |
+| test.cpp:101:4:101:15 | Argument 0 | test.cpp:75:25:75:29 | start |
+| test.cpp:101:4:101:15 | Argument 1 | test.cpp:75:38:75:40 | end |
| test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... |
| test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... |
| test.cpp:123:18:123:31 | (const char *)... | test.cpp:127:24:127:41 | ... * ... |
@@ -106,6 +124,21 @@ nodes
| test.cpp:52:35:52:60 | ... * ... | semmle.label | ... * ... |
| test.cpp:52:35:52:60 | ... * ... | semmle.label | ... * ... |
| test.cpp:52:35:52:60 | ... * ... | semmle.label | ... * ... |
+| test.cpp:64:25:64:30 | *buffer | semmle.label | *buffer |
+| test.cpp:64:25:64:30 | *buffer | semmle.label | *buffer |
+| test.cpp:64:25:64:30 | buffer | semmle.label | buffer |
+| test.cpp:75:25:75:29 | start | semmle.label | start |
+| test.cpp:75:38:75:40 | end | semmle.label | end |
+| test.cpp:79:18:79:28 | ... - ... | semmle.label | ... - ... |
+| test.cpp:79:18:79:28 | ... - ... | semmle.label | ... - ... |
+| test.cpp:79:18:79:28 | ... - ... | semmle.label | ... - ... |
+| test.cpp:97:18:97:23 | buffer | semmle.label | buffer |
+| test.cpp:97:18:97:23 | fread output argument | semmle.label | fread output argument |
+| test.cpp:100:4:100:15 | Argument 0 | semmle.label | Argument 0 |
+| test.cpp:100:17:100:22 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| test.cpp:100:17:100:22 | processData1 output argument | semmle.label | processData1 output argument |
+| test.cpp:101:4:101:15 | Argument 0 | semmle.label | Argument 0 |
+| test.cpp:101:4:101:15 | Argument 1 | semmle.label | Argument 1 |
| test.cpp:123:18:123:23 | call to getenv | semmle.label | call to getenv |
| test.cpp:123:18:123:31 | (const char *)... | semmle.label | (const char *)... |
| test.cpp:127:24:127:41 | ... * ... | semmle.label | ... * ... |
@@ -180,6 +213,7 @@ nodes
| test.cpp:48:25:48:30 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | size | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
| test.cpp:49:17:49:30 | new[] | test.cpp:39:21:39:24 | argv | test.cpp:49:26:49:29 | size | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
| test.cpp:52:21:52:27 | call to realloc | test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
+| test.cpp:79:9:79:29 | new[] | test.cpp:97:18:97:23 | buffer | test.cpp:79:18:79:28 | ... - ... | This allocation size is derived from $@ and might overflow | test.cpp:97:18:97:23 | buffer | user input (fread) |
| test.cpp:127:17:127:22 | call to malloc | test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:123:18:123:23 | call to getenv | user input (getenv) |
| test.cpp:134:3:134:8 | call to malloc | test.cpp:132:19:132:24 | call to getenv | test.cpp:134:10:134:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:132:19:132:24 | call to getenv | user input (getenv) |
| test.cpp:142:4:142:9 | call to malloc | test.cpp:138:19:138:24 | call to getenv | test.cpp:142:11:142:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:138:19:138:24 | call to getenv | user input (getenv) |
From a0a1ddee86382c183b774e9cb88509a1b4aac1d9 Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Wed, 10 Mar 2021 17:07:31 +0000
Subject: [PATCH 103/725] Update class name
---
.../Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
index c3f9349dbc8..985c546b555 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
@@ -104,8 +104,8 @@ class CookieTaintPreservingConstructor extends Constructor, TaintPreservingCalla
}
/** The method call `toString` to get a stringified cookie representation. */
-class CookieInstanceExpr extends TaintPreservingCallable {
- CookieInstanceExpr() {
+class CookieToString extends TaintPreservingCallable {
+ CookieToString() {
this.getDeclaringType() instanceof CookieClass and
this.hasName("toString")
}
From 0a5d58ed8a15288a19394d74d39610a87869d864 Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Wed, 10 Mar 2021 21:15:19 +0300
Subject: [PATCH 104/725] Cover more configurations in
UnsafeSpringExporterInConfigurationClass.ql
---
...nsafeSpringExporterInConfigurationClass.ql | 24 +++++++++++++----
.../SpringExporterUnsafeDeserialization.java | 26 +++++++++++++++++++
...pringExporterInConfigurationClass.expected | 6 +++--
.../boot/SpringBootConfiguration.java | 10 +++++++
.../autoconfigure/SpringBootApplication.java | 12 +++++++++
5 files changed, 71 insertions(+), 7 deletions(-)
create mode 100644 java/ql/test/stubs/springframework-5.2.3/org/springframework/boot/SpringBootConfiguration.java
create mode 100644 java/ql/test/stubs/springframework-5.2.3/org/springframework/boot/autoconfigure/SpringBootApplication.java
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInConfigurationClass.ql b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInConfigurationClass.ql
index 34605d16fbd..4b8fee3c83f 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInConfigurationClass.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInConfigurationClass.ql
@@ -14,6 +14,22 @@
import java
import UnsafeSpringExporterLib
+/**
+ * Holds if `type` is a Spring configuration that declares beans.
+ */
+private predicate isConfiguration(RefType type) {
+ type.hasAnnotation("org.springframework.context.annotation", "Configuration") or
+ isConfigurationAnnotation(type.getAnAnnotation())
+}
+
+/**
+ * Holds if `annotation` is a Java annotations that declares a Spring configuration.
+ */
+private predicate isConfigurationAnnotation(Annotation annotation) {
+ isConfiguration(annotation.getType()) or
+ isConfigurationAnnotation(annotation.getType().getAnAnnotation())
+}
+
/**
* A method that initializes a unsafe bean based on `RemoteInvocationSerializingExporter`.
*/
@@ -22,11 +38,9 @@ private class UnsafeBeanInitMethod extends Method {
UnsafeBeanInitMethod() {
isRemoteInvocationSerializingExporter(this.getReturnType()) and
- this.getDeclaringType().hasAnnotation("org.springframework.context.annotation", "Configuration") and
- exists(Annotation a |
- a.getType().hasQualifiedName("org.springframework.context.annotation", "Bean")
- |
- this.getAnAnnotation() = a and
+ isConfiguration(this.getDeclaringType()) and
+ exists(Annotation a | this.getAnAnnotation() = a |
+ a.getType().hasQualifiedName("org.springframework.context.annotation", "Bean") and
if a.getValue("name") instanceof StringLiteral
then identifier = a.getValue("name").(StringLiteral).getRepresentedString()
else identifier = this.getName()
diff --git a/java/ql/test/experimental/query-tests/security/CWE-502/SpringExporterUnsafeDeserialization.java b/java/ql/test/experimental/query-tests/security/CWE-502/SpringExporterUnsafeDeserialization.java
index c89668fc6bb..2bca24ee370 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-502/SpringExporterUnsafeDeserialization.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-502/SpringExporterUnsafeDeserialization.java
@@ -1,3 +1,5 @@
+import org.springframework.boot.SpringBootConfiguration;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter;
@@ -27,6 +29,30 @@ public class SpringExporterUnsafeDeserialization {
}
}
+@SpringBootApplication
+class SpringBootTestApplication {
+
+ @Bean(name = "/unsafeHttpInvokerServiceExporter")
+ HttpInvokerServiceExporter unsafeHttpInvokerServiceExporter() {
+ HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter();
+ exporter.setService(new AccountServiceImpl());
+ exporter.setServiceInterface(AccountService.class);
+ return exporter;
+ }
+}
+
+@SpringBootConfiguration
+class SpringBootTestConfiguration {
+
+ @Bean(name = "/unsafeHttpInvokerServiceExporter")
+ HttpInvokerServiceExporter unsafeHttpInvokerServiceExporter() {
+ HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter();
+ exporter.setService(new AccountServiceImpl());
+ exporter.setServiceInterface(AccountService.class);
+ return exporter;
+ }
+}
+
class CustomeRemoteInvocationSerializingExporter extends RemoteInvocationSerializingExporter {}
class NotAConfiguration {
diff --git a/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeSpringExporterInConfigurationClass.expected b/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeSpringExporterInConfigurationClass.expected
index 926eff89403..2155d805e80 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeSpringExporterInConfigurationClass.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-502/UnsafeSpringExporterInConfigurationClass.expected
@@ -1,2 +1,4 @@
-| SpringExporterUnsafeDeserialization.java:10:32:10:63 | unsafeHttpInvokerServiceExporter | Unsafe deserialization in a Spring exporter bean '/unsafeHttpInvokerServiceExporter' |
-| SpringExporterUnsafeDeserialization.java:18:41:18:88 | unsafeCustomeRemoteInvocationSerializingExporter | Unsafe deserialization in a Spring exporter bean '/unsafeCustomeRemoteInvocationSerializingExporter' |
+| SpringExporterUnsafeDeserialization.java:12:32:12:63 | unsafeHttpInvokerServiceExporter | Unsafe deserialization in a Spring exporter bean '/unsafeHttpInvokerServiceExporter' |
+| SpringExporterUnsafeDeserialization.java:20:41:20:88 | unsafeCustomeRemoteInvocationSerializingExporter | Unsafe deserialization in a Spring exporter bean '/unsafeCustomeRemoteInvocationSerializingExporter' |
+| SpringExporterUnsafeDeserialization.java:36:32:36:63 | unsafeHttpInvokerServiceExporter | Unsafe deserialization in a Spring exporter bean '/unsafeHttpInvokerServiceExporter' |
+| SpringExporterUnsafeDeserialization.java:48:32:48:63 | unsafeHttpInvokerServiceExporter | Unsafe deserialization in a Spring exporter bean '/unsafeHttpInvokerServiceExporter' |
diff --git a/java/ql/test/stubs/springframework-5.2.3/org/springframework/boot/SpringBootConfiguration.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/boot/SpringBootConfiguration.java
new file mode 100644
index 00000000000..483c5cc5606
--- /dev/null
+++ b/java/ql/test/stubs/springframework-5.2.3/org/springframework/boot/SpringBootConfiguration.java
@@ -0,0 +1,10 @@
+package org.springframework.boot;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+import org.springframework.context.annotation.Configuration;
+
+@Target(ElementType.TYPE)
+@Configuration
+public @interface SpringBootConfiguration {}
\ No newline at end of file
diff --git a/java/ql/test/stubs/springframework-5.2.3/org/springframework/boot/autoconfigure/SpringBootApplication.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/boot/autoconfigure/SpringBootApplication.java
new file mode 100644
index 00000000000..38bbc35733e
--- /dev/null
+++ b/java/ql/test/stubs/springframework-5.2.3/org/springframework/boot/autoconfigure/SpringBootApplication.java
@@ -0,0 +1,12 @@
+package org.springframework.boot.autoconfigure;
+
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+
+import org.springframework.boot.SpringBootConfiguration;
+
+@Target(ElementType.TYPE)
+@Inherited
+@SpringBootConfiguration
+public @interface SpringBootApplication {}
\ No newline at end of file
From 4b7c57c077ff17eae1b94f69d90e67b1eb28e7ea Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Thu, 11 Mar 2021 11:52:07 +0100
Subject: [PATCH 105/725] Added a comment for getBeanIdentifier()
Co-authored-by: Chris Smowton
---
.../CWE/CWE-502/UnsafeSpringExporterInConfigurationClass.ql | 3 +++
1 file changed, 3 insertions(+)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInConfigurationClass.ql b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInConfigurationClass.ql
index 4b8fee3c83f..81b56ce0e52 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInConfigurationClass.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeSpringExporterInConfigurationClass.ql
@@ -47,6 +47,9 @@ private class UnsafeBeanInitMethod extends Method {
)
}
+ /**
+ * Gets this bean's name if given by the `Bean` annotation, or this method's identifier otherwise.
+ */
string getBeanIdentifier() { result = identifier }
}
From 97ab842010eda1f8fb2ef922227f37b2168bf670 Mon Sep 17 00:00:00 2001
From: Cornelius Riemenschneider
Date: Thu, 11 Mar 2021 12:44:30 +0000
Subject: [PATCH 106/725] C++: Update summary queries.
---
cpp/ql/src/Summary/Files.ql | 11 -----------
cpp/ql/src/Summary/Lines.ql | 11 -----------
cpp/ql/src/Summary/LinesOfCode.ql | 6 +++---
cpp/ql/src/Summary/LinesOfCodePerFile.ql | 6 +++---
cpp/ql/src/Summary/LinesOfUserCode.ql | 11 +++++++++++
cpp/ql/src/Summary/LinesOfUserCodePerFile.ql | 13 +++++++++++++
cpp/ql/src/Summary/LinesPerFile.ql | 13 -------------
cpp/ql/test/query-tests/Summary/Files.expected | 1 -
cpp/ql/test/query-tests/Summary/Files.qlref | 1 -
cpp/ql/test/query-tests/Summary/Lines.qlref | 1 -
.../test/query-tests/Summary/LinesOfCode.expected | 2 +-
cpp/ql/test/query-tests/Summary/LinesOfCode.qlref | 2 +-
.../query-tests/Summary/LinesOfCodePerFile.expected | 2 +-
.../query-tests/Summary/LinesOfCodePerFile.qlref | 2 +-
.../{Lines.expected => LinesOfUserCode.expected} | 0
.../test/query-tests/Summary/LinesOfUserCode.qlref | 1 +
...ile.expected => LinesOfUserCodePerFile.expected} | 0
.../Summary/LinesOfUserCodePerFile.qlref | 1 +
cpp/ql/test/query-tests/Summary/LinesPerFile.qlref | 1 -
19 files changed, 36 insertions(+), 49 deletions(-)
delete mode 100644 cpp/ql/src/Summary/Files.ql
delete mode 100644 cpp/ql/src/Summary/Lines.ql
create mode 100644 cpp/ql/src/Summary/LinesOfUserCode.ql
create mode 100644 cpp/ql/src/Summary/LinesOfUserCodePerFile.ql
delete mode 100644 cpp/ql/src/Summary/LinesPerFile.ql
delete mode 100644 cpp/ql/test/query-tests/Summary/Files.expected
delete mode 100644 cpp/ql/test/query-tests/Summary/Files.qlref
delete mode 100644 cpp/ql/test/query-tests/Summary/Lines.qlref
rename cpp/ql/test/query-tests/Summary/{Lines.expected => LinesOfUserCode.expected} (100%)
create mode 100644 cpp/ql/test/query-tests/Summary/LinesOfUserCode.qlref
rename cpp/ql/test/query-tests/Summary/{LinesPerFile.expected => LinesOfUserCodePerFile.expected} (100%)
create mode 100644 cpp/ql/test/query-tests/Summary/LinesOfUserCodePerFile.qlref
delete mode 100644 cpp/ql/test/query-tests/Summary/LinesPerFile.qlref
diff --git a/cpp/ql/src/Summary/Files.ql b/cpp/ql/src/Summary/Files.ql
deleted file mode 100644
index 83007c2fec6..00000000000
--- a/cpp/ql/src/Summary/Files.ql
+++ /dev/null
@@ -1,11 +0,0 @@
-/**
- * @id cpp/summary/files
- * @name Total source files
- * @description The total number of source files.
- * @kind metric
- * @tags summary
- */
-
-import cpp
-
-select count(File f | f.fromSource())
diff --git a/cpp/ql/src/Summary/Lines.ql b/cpp/ql/src/Summary/Lines.ql
deleted file mode 100644
index dec466c5468..00000000000
--- a/cpp/ql/src/Summary/Lines.ql
+++ /dev/null
@@ -1,11 +0,0 @@
-/**
- * @id cpp/summary/lines
- * @name Total lines of text
- * @description The total number of lines of text across all source files.
- * @kind metric
- * @tags summary
- */
-
-import cpp
-
-select sum(File f | f.fromSource() | f.getMetrics().getNumberOfLines())
diff --git a/cpp/ql/src/Summary/LinesOfCode.ql b/cpp/ql/src/Summary/LinesOfCode.ql
index 4dc7dc88c05..2d6fdf4b67b 100644
--- a/cpp/ql/src/Summary/LinesOfCode.ql
+++ b/cpp/ql/src/Summary/LinesOfCode.ql
@@ -1,11 +1,11 @@
/**
* @id cpp/summary/lines-of-code
- * @name Total lines of code
- * @description The total number of lines of code across all source files.
+ * @name Total lines of C/C++ code
+ * @description The total number of lines of C/C++ code across all files, including system headers and libraries.
* @kind metric
* @tags summary
*/
import cpp
-select sum(File f | f.fromSource() | f.getMetrics().getNumberOfLinesOfCode())
+select sum(File f | f.fromSource() | f.getMetrics().getNumberOfLines())
diff --git a/cpp/ql/src/Summary/LinesOfCodePerFile.ql b/cpp/ql/src/Summary/LinesOfCodePerFile.ql
index c98d8a3d196..c94f337744a 100644
--- a/cpp/ql/src/Summary/LinesOfCodePerFile.ql
+++ b/cpp/ql/src/Summary/LinesOfCodePerFile.ql
@@ -1,7 +1,7 @@
/**
* @id cpp/summary/lines-of-code-per-file
- * @name Lines of code per source file
- * @description The number of lines of code for each source file.
+ * @name Lines of C/C++ code per source file
+ * @description The number of lines of C/C++ code for each file in the database, including system headers and libraries.
* @kind metric
* @tags summary
*/
@@ -10,4 +10,4 @@ import cpp
from File f
where f.fromSource()
-select f, f.getMetrics().getNumberOfLinesOfCode()
+select f, f.getMetrics().getNumberOfLines()
diff --git a/cpp/ql/src/Summary/LinesOfUserCode.ql b/cpp/ql/src/Summary/LinesOfUserCode.ql
new file mode 100644
index 00000000000..e6466c6ddf6
--- /dev/null
+++ b/cpp/ql/src/Summary/LinesOfUserCode.ql
@@ -0,0 +1,11 @@
+/**
+ * @id cpp/summary/lines-of-user-code
+ * @name Total lines of C/C++ source code
+ * @description The total number of lines of C/C++ code across all files, excluding system headers and libraries.
+ * @kind metric
+ * @tags summary
+ */
+
+import cpp
+
+select sum(File f | exists(f.getRelativePath()) | f.getMetrics().getNumberOfLines())
diff --git a/cpp/ql/src/Summary/LinesOfUserCodePerFile.ql b/cpp/ql/src/Summary/LinesOfUserCodePerFile.ql
new file mode 100644
index 00000000000..a10e48c0359
--- /dev/null
+++ b/cpp/ql/src/Summary/LinesOfUserCodePerFile.ql
@@ -0,0 +1,13 @@
+/**
+ * @id cpp/summary/lines-of-user-code-per-file
+ * @name Lines of C/C++ code per source file
+ * @description The number of lines of C/C++ code for each file in the source directory.
+ * @kind metric
+ * @tags summary
+ */
+
+import cpp
+
+from File f
+where exists(f.getRelativePath())
+select f, f.getMetrics().getNumberOfLines()
diff --git a/cpp/ql/src/Summary/LinesPerFile.ql b/cpp/ql/src/Summary/LinesPerFile.ql
deleted file mode 100644
index bfc78e4d284..00000000000
--- a/cpp/ql/src/Summary/LinesPerFile.ql
+++ /dev/null
@@ -1,13 +0,0 @@
-/**
- * @id cpp/summary/lines-per-file
- * @name Lines of text per source file
- * @description The number of lines of text for each source file.
- * @kind metric
- * @tags summary
- */
-
-import cpp
-
-from File f
-where f.fromSource()
-select f, f.getMetrics().getNumberOfLines()
diff --git a/cpp/ql/test/query-tests/Summary/Files.expected b/cpp/ql/test/query-tests/Summary/Files.expected
deleted file mode 100644
index 7621cebdd5f..00000000000
--- a/cpp/ql/test/query-tests/Summary/Files.expected
+++ /dev/null
@@ -1 +0,0 @@
-| 3 |
diff --git a/cpp/ql/test/query-tests/Summary/Files.qlref b/cpp/ql/test/query-tests/Summary/Files.qlref
deleted file mode 100644
index f3bea020f4e..00000000000
--- a/cpp/ql/test/query-tests/Summary/Files.qlref
+++ /dev/null
@@ -1 +0,0 @@
-Summary/Files.ql
\ No newline at end of file
diff --git a/cpp/ql/test/query-tests/Summary/Lines.qlref b/cpp/ql/test/query-tests/Summary/Lines.qlref
deleted file mode 100644
index 62b3a59a14b..00000000000
--- a/cpp/ql/test/query-tests/Summary/Lines.qlref
+++ /dev/null
@@ -1 +0,0 @@
-Summary/Lines.ql
diff --git a/cpp/ql/test/query-tests/Summary/LinesOfCode.expected b/cpp/ql/test/query-tests/Summary/LinesOfCode.expected
index a75c288c151..d847b050658 100644
--- a/cpp/ql/test/query-tests/Summary/LinesOfCode.expected
+++ b/cpp/ql/test/query-tests/Summary/LinesOfCode.expected
@@ -1 +1 @@
-| 93 |
+| 122 |
diff --git a/cpp/ql/test/query-tests/Summary/LinesOfCode.qlref b/cpp/ql/test/query-tests/Summary/LinesOfCode.qlref
index ac8650d6dcc..b60eb791722 100644
--- a/cpp/ql/test/query-tests/Summary/LinesOfCode.qlref
+++ b/cpp/ql/test/query-tests/Summary/LinesOfCode.qlref
@@ -1 +1 @@
-Summary/LinesOfCode.ql
\ No newline at end of file
+Summary/LinesOfCode.ql
diff --git a/cpp/ql/test/query-tests/Summary/LinesOfCodePerFile.expected b/cpp/ql/test/query-tests/Summary/LinesOfCodePerFile.expected
index 076ee4121a4..e02d0591ab2 100644
--- a/cpp/ql/test/query-tests/Summary/LinesOfCodePerFile.expected
+++ b/cpp/ql/test/query-tests/Summary/LinesOfCodePerFile.expected
@@ -1,3 +1,3 @@
| empty-file.cpp:0:0:0:0 | empty-file.cpp | 0 |
-| large-file.cpp:0:0:0:0 | large-file.cpp | 90 |
+| large-file.cpp:0:0:0:0 | large-file.cpp | 119 |
| short-file.cpp:0:0:0:0 | short-file.cpp | 3 |
diff --git a/cpp/ql/test/query-tests/Summary/LinesOfCodePerFile.qlref b/cpp/ql/test/query-tests/Summary/LinesOfCodePerFile.qlref
index eb64c0d8a46..33edfd15c7c 100644
--- a/cpp/ql/test/query-tests/Summary/LinesOfCodePerFile.qlref
+++ b/cpp/ql/test/query-tests/Summary/LinesOfCodePerFile.qlref
@@ -1 +1 @@
-Summary/LinesOfCodePerFile.ql
\ No newline at end of file
+Summary/LinesOfCodePerFile.ql
diff --git a/cpp/ql/test/query-tests/Summary/Lines.expected b/cpp/ql/test/query-tests/Summary/LinesOfUserCode.expected
similarity index 100%
rename from cpp/ql/test/query-tests/Summary/Lines.expected
rename to cpp/ql/test/query-tests/Summary/LinesOfUserCode.expected
diff --git a/cpp/ql/test/query-tests/Summary/LinesOfUserCode.qlref b/cpp/ql/test/query-tests/Summary/LinesOfUserCode.qlref
new file mode 100644
index 00000000000..baaa947e6af
--- /dev/null
+++ b/cpp/ql/test/query-tests/Summary/LinesOfUserCode.qlref
@@ -0,0 +1 @@
+Summary/LinesOfUserCode.ql
diff --git a/cpp/ql/test/query-tests/Summary/LinesPerFile.expected b/cpp/ql/test/query-tests/Summary/LinesOfUserCodePerFile.expected
similarity index 100%
rename from cpp/ql/test/query-tests/Summary/LinesPerFile.expected
rename to cpp/ql/test/query-tests/Summary/LinesOfUserCodePerFile.expected
diff --git a/cpp/ql/test/query-tests/Summary/LinesOfUserCodePerFile.qlref b/cpp/ql/test/query-tests/Summary/LinesOfUserCodePerFile.qlref
new file mode 100644
index 00000000000..bb3fc603c4d
--- /dev/null
+++ b/cpp/ql/test/query-tests/Summary/LinesOfUserCodePerFile.qlref
@@ -0,0 +1 @@
+Summary/LinesOfUserCodePerFile.ql
diff --git a/cpp/ql/test/query-tests/Summary/LinesPerFile.qlref b/cpp/ql/test/query-tests/Summary/LinesPerFile.qlref
deleted file mode 100644
index d967fcb9a8c..00000000000
--- a/cpp/ql/test/query-tests/Summary/LinesPerFile.qlref
+++ /dev/null
@@ -1 +0,0 @@
-Summary/LinesPerFile.ql
\ No newline at end of file
From eeac7e322ad0d450f8dac1f0dad9195e4d3b8c33 Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Thu, 11 Mar 2021 13:46:32 +0000
Subject: [PATCH 107/725] Query to detect insecure configuration of Spring Boot
Actuator
---
.../InsecureSpringActuatorConfig.qhelp | 47 ++++++++
.../CWE-016/InsecureSpringActuatorConfig.ql | 112 ++++++++++++++++++
.../CWE/CWE-016/application.properties | 22 ++++
.../Security/CWE/CWE-016/pom_bad.xml | 50 ++++++++
.../Security/CWE/CWE-016/pom_good.xml | 50 ++++++++
.../InsecureSpringActuatorConfig.expected | 1 +
.../InsecureSpringActuatorConfig.qlref | 1 +
.../security/CWE-016/SensitiveInfo.java | 13 ++
.../security/CWE-016/application.properties | 14 +++
.../query-tests/security/CWE-016/pom.xml | 47 ++++++++
10 files changed, 357 insertions(+)
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.qhelp
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-016/application.properties
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-016/pom_bad.xml
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-016/pom_good.xml
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-016/InsecureSpringActuatorConfig.expected
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-016/InsecureSpringActuatorConfig.qlref
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-016/SensitiveInfo.java
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-016/application.properties
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-016/pom.xml
diff --git a/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.qhelp b/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.qhelp
new file mode 100644
index 00000000000..e201156728a
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.qhelp
@@ -0,0 +1,47 @@
+
+
+
+ Spring Boot is a popular framework that facilitates the development of stand-alone applications
+and micro services. Spring Boot Actuator helps to expose production-ready support features against
+Spring Boot applications.
+
+ Endpoints of Spring Boot Actuator allow to monitor and interact with a Spring Boot application.
+Exposing unprotected actuator endpoints through configuration files can lead to information disclosure
+or even remote code execution vulnerability.
+
+ Rather than programmatically permitting endpoint requests or enforcing access control, frequently
+developers simply leave management endpoints publicly accessible in the application configuration file
+application.properties without enforcing access control through Spring Security.
+
+
+
+ Declare the Spring Boot Starter Security module in XML configuration or programmatically enforce
+security checks on management endpoints using Spring Security. Otherwise accessing management endpoints
+on a different HTTP port other than the port that the web application is listening on also helps to
+improve the security.
+
+
+
+ The following examples show both 'BAD' and 'GOOD' configurations. In the 'BAD' configuration,
+no security module is declared and sensitive management endpoints are exposed. In the 'GOOD' configuration,
+security is enforced and only endpoints requiring exposure are exposed.
+
+
+
+
+
+
+
+ Spring Boot documentation:
+ Spring Boot Actuator: Production-ready Features
+
+
+ VERACODE Blog:
+ Exploiting Spring Boot Actuators
+
+
+ HackerOne Report:
+ Spring Actuator endpoints publicly available, leading to account takeover
+
+
+
diff --git a/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql b/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql
new file mode 100644
index 00000000000..2dc11e8e38e
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql
@@ -0,0 +1,112 @@
+/**
+ * @name Insecure Spring Boot Actuator Configuration
+ * @description Exposed Spring Boot Actuator through configuration files without declarative or procedural security enforcement leads to information leak or even remote code execution.
+ * @kind problem
+ * @id java/insecure-spring-actuator-config
+ * @tags security
+ * external/cwe-016
+ */
+
+import java
+import semmle.code.configfiles.ConfigFiles
+import semmle.code.java.security.SensitiveActions
+import semmle.code.xml.MavenPom
+
+/** The parent node of the `org.springframework.boot` group. */
+class SpringBootParent extends Parent {
+ SpringBootParent() { this.getGroup().getValue() = "org.springframework.boot" }
+}
+
+/** Class of Spring Boot dependencies. */
+class SpringBootPom extends Pom {
+ SpringBootPom() { this.getParentElement() instanceof SpringBootParent }
+
+ /** Holds if the Spring Boot Actuator module `spring-boot-starter-actuator` is used in the project. */
+ predicate isSpringBootActuatorUsed() {
+ this.getADependency().getArtifact().getValue() = "spring-boot-starter-actuator"
+ }
+
+ /** Holds if the Spring Boot Security module is used in the project, which brings in other security related libraries. */
+ predicate isSpringBootSecurityUsed() {
+ this.getADependency().getArtifact().getValue() = "spring-boot-starter-security"
+ }
+}
+
+/** The properties file `application.properties`. */
+class ApplicationProperties extends ConfigPair {
+ ApplicationProperties() { this.getFile().getBaseName() = "application.properties" }
+}
+
+/** The configuration property `management.security.enabled`. */
+class ManagementSecurityEnabled extends ApplicationProperties {
+ ManagementSecurityEnabled() { this.getNameElement().getName() = "management.security.enabled" }
+
+ string getManagementSecurityEnabled() { result = this.getValueElement().getValue() }
+
+ predicate hasSecurityDisabled() { getManagementSecurityEnabled() = "false" }
+
+ predicate hasSecurityEnabled() { getManagementSecurityEnabled() = "true" }
+}
+
+/** The configuration property `management.endpoints.web.exposure.include`. */
+class ManagementEndPointInclude extends ApplicationProperties {
+ ManagementEndPointInclude() {
+ this.getNameElement().getName() = "management.endpoints.web.exposure.include"
+ }
+
+ string getManagementEndPointInclude() { result = this.getValueElement().getValue().trim() }
+}
+
+/** The configuration property `management.endpoints.web.exposure.exclude`. */
+class ManagementEndPointExclude extends ApplicationProperties {
+ ManagementEndPointExclude() {
+ this.getNameElement().getName() = "management.endpoints.web.exposure.exclude"
+ }
+
+ string getManagementEndPointExclude() { result = this.getValueElement().getValue().trim() }
+}
+
+/** Holds if an application handles sensitive information judging by its variable names. */
+predicate isProtectedApp() {
+ exists(VarAccess va | va.getVariable().getName().regexpMatch(getCommonSensitiveInfoRegex()))
+}
+
+from SpringBootPom pom, ApplicationProperties ap, Dependency d
+where
+ isProtectedApp() and
+ pom.isSpringBootActuatorUsed() and
+ not pom.isSpringBootSecurityUsed() and
+ ap.getFile()
+ .getParentContainer()
+ .getAbsolutePath()
+ .matches(pom.getFile().getParentContainer().getAbsolutePath() + "%") and // in the same sub-directory
+ exists(string s | s = pom.getParentElement().getVersionString() |
+ s.regexpMatch("1\\.[0|1|2|3|4].*") and
+ not exists(ManagementSecurityEnabled me |
+ me.hasSecurityEnabled() and me.getFile() = ap.getFile()
+ )
+ or
+ s.regexpMatch("1\\.5.*") and
+ exists(ManagementSecurityEnabled me | me.hasSecurityDisabled() and me.getFile() = ap.getFile())
+ or
+ s.regexpMatch("2.*") and
+ exists(ManagementEndPointInclude mi |
+ mi.getFile() = ap.getFile() and
+ (
+ mi.getManagementEndPointInclude() = "*" // all endpoints are enabled
+ or
+ mi.getManagementEndPointInclude()
+ .matches([
+ "%dump%", "%trace%", "%logfile%", "%shutdown%", "%startup%", "%mappings%", "%env%",
+ "%beans%", "%sessions%"
+ ]) // all endpoints apart from '/health' and '/info' are considered sensitive
+ ) and
+ not exists(ManagementEndPointExclude mx |
+ mx.getFile() = ap.getFile() and
+ mx.getManagementEndPointExclude() = mi.getManagementEndPointInclude()
+ )
+ )
+ ) and
+ d = pom.getADependency() and
+ d.getArtifact().getValue() = "spring-boot-starter-actuator"
+select d, "Insecure configuration of Spring Boot Actuator exposes sensitive endpoints."
diff --git a/java/ql/src/experimental/Security/CWE/CWE-016/application.properties b/java/ql/src/experimental/Security/CWE/CWE-016/application.properties
new file mode 100644
index 00000000000..aa489435a12
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-016/application.properties
@@ -0,0 +1,22 @@
+#management.endpoints.web.base-path=/admin
+
+
+#### BAD: All management endpoints are accessible ####
+# vulnerable configuration (spring boot 1.0 - 1.4): exposes actuators by default
+
+# vulnerable configuration (spring boot 1.5+): requires value false to expose sensitive actuators
+management.security.enabled=false
+
+# vulnerable configuration (spring boot 2+): exposes health and info only by default
+management.endpoints.web.exposure.include=*
+
+
+#### GOOD: All management endpoints have access control ####
+# safe configuration (spring boot 1.0 - 1.4): exposes actuators by default
+management.security.enabled=true
+
+# safe configuration (spring boot 1.5+): requires value false to expose sensitive actuators
+management.security.enabled=true
+
+# safe configuration (spring boot 2+): exposes health and info only by default
+management.endpoints.web.exposure.include=beans,info,health
diff --git a/java/ql/src/experimental/Security/CWE/CWE-016/pom_bad.xml b/java/ql/src/experimental/Security/CWE/CWE-016/pom_bad.xml
new file mode 100644
index 00000000000..9dd5c9c188b
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-016/pom_bad.xml
@@ -0,0 +1,50 @@
+
+
+ 4.0.0
+
+ spring-boot-actuator-app
+ spring-boot-actuator-app
+ 1.0-SNAPSHOT
+
+
+ UTF-8
+ 1.8
+ 1.8
+
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.3.8.RELEASE
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.springframework.boot
+ spring-boot-devtools
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-test
+
+
+
+
\ No newline at end of file
diff --git a/java/ql/src/experimental/Security/CWE/CWE-016/pom_good.xml b/java/ql/src/experimental/Security/CWE/CWE-016/pom_good.xml
new file mode 100644
index 00000000000..89f577f21e5
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-016/pom_good.xml
@@ -0,0 +1,50 @@
+
+
+ 4.0.0
+
+ spring-boot-actuator-app
+ spring-boot-actuator-app
+ 1.0-SNAPSHOT
+
+
+ UTF-8
+ 1.8
+ 1.8
+
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.3.8.RELEASE
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.springframework.boot
+ spring-boot-devtools
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+
+ org.springframework.boot
+ spring-boot-test
+
+
+
+
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-016/InsecureSpringActuatorConfig.expected b/java/ql/test/experimental/query-tests/security/CWE-016/InsecureSpringActuatorConfig.expected
new file mode 100644
index 00000000000..48630293985
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-016/InsecureSpringActuatorConfig.expected
@@ -0,0 +1 @@
+| pom.xml:29:9:32:22 | dependency | Insecure configuration of Spring Boot Actuator exposes sensitive endpoints. |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-016/InsecureSpringActuatorConfig.qlref b/java/ql/test/experimental/query-tests/security/CWE-016/InsecureSpringActuatorConfig.qlref
new file mode 100644
index 00000000000..9cd12d5e4fb
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-016/InsecureSpringActuatorConfig.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-016/SensitiveInfo.java b/java/ql/test/experimental/query-tests/security/CWE-016/SensitiveInfo.java
new file mode 100644
index 00000000000..a3ff69c1b81
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-016/SensitiveInfo.java
@@ -0,0 +1,13 @@
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+@Controller
+public class SensitiveInfo {
+ @RequestMapping
+ public void handleLogin(@RequestParam String username, @RequestParam String password) throws Exception {
+ if (!username.equals("") && password.equals("")) {
+ //Blank processing
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-016/application.properties b/java/ql/test/experimental/query-tests/security/CWE-016/application.properties
new file mode 100644
index 00000000000..95e704f3a1a
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-016/application.properties
@@ -0,0 +1,14 @@
+#management.endpoints.web.base-path=/admin
+
+# vulnerable configuration (spring boot 1.0 - 1.4): exposes actuators by default
+
+# vulnerable configuration (spring boot 1.5+): requires value false to expose sensitive actuators
+management.security.enabled=false
+
+# vulnerable configuration (spring boot 2+): exposes health and info only by default
+management.endpoints.web.exposure.include=*
+management.endpoints.web.exposure.exclude=beans
+
+management.endpoint.shutdown.enabled=true
+
+management.endpoint.health.show-details=when_authorized
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-016/pom.xml b/java/ql/test/experimental/query-tests/security/CWE-016/pom.xml
new file mode 100644
index 00000000000..a9d5fa920c8
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-016/pom.xml
@@ -0,0 +1,47 @@
+
+
+ 4.0.0
+
+ spring-boot-actuator-app
+ spring-boot-actuator-app
+ 1.0-SNAPSHOT
+
+
+ UTF-8
+ 1.8
+ 1.8
+
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.3.8.RELEASE
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.springframework.boot
+ spring-boot-devtools
+
+
+
+ org.springframework.boot
+ spring-boot-test
+
+
+
+
\ No newline at end of file
From 57953c523c453c7be78a15f22d954e5c6cb78c59 Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Thu, 11 Mar 2021 17:16:36 +0000
Subject: [PATCH 108/725] Update qldoc
---
.../CWE/CWE-297/InsecureLdapEndpoint.qhelp | 21 ++++++++++++-------
.../CWE/CWE-297/InsecureLdapEndpoint.ql | 11 +++++++---
2 files changed, 22 insertions(+), 10 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.qhelp b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.qhelp
index cb43f52515b..50e1febf894 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.qhelp
+++ b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.qhelp
@@ -4,20 +4,27 @@
-Java versions 8u181 or greater have enabled LDAPS endpoint identification by default. Nowadays infrastructure services like LDAP are commonly deployed behind load balancers therefore the LDAP server name can be different from the FQDN of the LDAPS endpoint. If a service certificate does not properly contain a matching DNS name as part of the certificate, Java will reject it by default.
-Instead of addressing the issue properly by having a compliant certificate deployed, frequently developers simply disable the LDAPS endpoint check.
-Failing to validate the certificate makes the SSL session susceptible to a man-in-the-middle attack. This query checks whether the LDAPS endpoint check is disabled in system properties.
+Java versions 8u181 or greater have enabled LDAPS endpoint identification by default. Nowadays
+ infrastructure services like LDAP are commonly deployed behind load balancers therefore the LDAP
+ server name can be different from the FQDN of the LDAPS endpoint. If a service certificate does not
+ properly contain a matching DNS name as part of the certificate, Java will reject it by default.
+Instead of addressing the issue properly by having a compliant certificate deployed, frequently
+ developers simply disable the LDAPS endpoint check.
+Failing to validate the certificate makes the SSL session susceptible to a man-in-the-middle attack.
+ This query checks whether the LDAPS endpoint check is disabled in system properties.
-Replace any non-conforming LDAP server certificates to include a DNS name in the subjectAltName field of the certificate that matches the FQDN of the service.
+Replace any non-conforming LDAP server certificates to include a DNS name in the subjectAltName field
+ of the certificate that matches the FQDN of the service.
-The following two examples show two ways of configuring LDAPS endpoint. In the 'BAD' case,
-endpoint check is disabled. In the 'GOOD' case, endpoint check is left enabled through the default Java configuration.
+The following two examples show two ways of configuring LDAPS endpoint. In the 'BAD' case,
+ endpoint check is disabled. In the 'GOOD' case, endpoint check is left enabled through the
+ default Java configuration.
->
+
diff --git a/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.ql b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.ql
index c83aeb4a6a5..7780d2a0248 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.ql
@@ -1,6 +1,7 @@
/**
* @name Insecure LDAPS Endpoint Configuration
- * @description Java application configured to disable LDAPS endpoint identification does not validate the SSL certificate to properly ensure that it is actually associated with that host.
+ * @description Java application configured to disable LDAPS endpoint identification does not validate
+ * the SSL certificate to properly ensure that it is actually associated with that host.
* @kind problem
* @id java/insecure-ldaps-endpoint
* @tags security
@@ -23,7 +24,8 @@ class TypeHashtable extends Class {
}
/**
- * The method to set Java properties either through `setProperty` declared in the class `Properties` or `put` declared in its parent class `HashTable`.
+ * The method to set Java properties either through `setProperty` declared in the class `Properties`
+ * or `put` declared in its parent class `HashTable`.
*/
class SetPropertyMethod extends Method {
SetPropertyMethod() {
@@ -40,7 +42,10 @@ class SetSystemPropertiesMethod extends Method {
}
}
-/** Holds if `expr` is evaluated to the string literal `com.sun.jndi.ldap.object.disableEndpointIdentification`. */
+/**
+ * Holds if `Expr` expr is evaluated to the string literal
+ * `com.sun.jndi.ldap.object.disableEndpointIdentification`.
+ */
predicate isPropertyDisableLdapEndpointId(Expr expr) {
expr.(CompileTimeConstantExpr).getStringValue() =
"com.sun.jndi.ldap.object.disableEndpointIdentification"
From 0a35feef766249b88abfc78f8a9dd552664ab083 Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Thu, 11 Mar 2021 17:28:07 +0000
Subject: [PATCH 109/725] Exclude CSRF cookies to reduce FPs
---
.../CWE-1004/SensitiveCookieNotHttpOnly.ql | 16 +++--
.../SensitiveCookieNotHttpOnly.expected | 60 +++++++++----------
.../CWE-1004/SensitiveCookieNotHttpOnly.java | 22 ++++++-
.../query-tests/security/CWE-1004/options | 2 +-
.../security/web/csrf/CsrfToken.java | 50 ++++++++++++++++
5 files changed, 113 insertions(+), 37 deletions(-)
create mode 100644 java/ql/test/stubs/springframework-5.2.3/org/springframework/security/web/csrf/CsrfToken.java
diff --git a/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
index 985c546b555..693dad68082 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql
@@ -16,12 +16,18 @@ import DataFlow::PathGraph
/** Gets a regular expression for matching common names of sensitive cookies. */
string getSensitiveCookieNameRegex() { result = "(?i).*(auth|session|token|key|credential).*" }
-/** Holds if a string is concatenated with the name of a sensitive cookie. */
+/** Gets a regular expression for matching CSRF cookies. */
+string getCsrfCookieNameRegex() { result = "(?i).*(csrf).*" }
+
+/**
+ * Holds if a string is concatenated with the name of a sensitive cookie. Excludes CSRF cookies since
+ * they are special cookies implementing the Synchronizer Token Pattern that can be used in JavaScript.
+ */
predicate isSensitiveCookieNameExpr(Expr expr) {
- expr.(CompileTimeConstantExpr)
- .getStringValue()
- .toLowerCase()
- .regexpMatch(getSensitiveCookieNameRegex()) or
+ exists(string s | s = expr.(CompileTimeConstantExpr).getStringValue().toLowerCase() |
+ s.regexpMatch(getSensitiveCookieNameRegex()) and not s.regexpMatch(getCsrfCookieNameRegex())
+ )
+ or
isSensitiveCookieNameExpr(expr.(AddExpr).getAnOperand())
}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-1004/SensitiveCookieNotHttpOnly.expected b/java/ql/test/experimental/query-tests/security/CWE-1004/SensitiveCookieNotHttpOnly.expected
index c9fe15d4082..8fa688bef2a 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-1004/SensitiveCookieNotHttpOnly.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-1004/SensitiveCookieNotHttpOnly.expected
@@ -1,33 +1,33 @@
edges
-| SensitiveCookieNotHttpOnly.java:22:33:22:43 | "jwt_token" : String | SensitiveCookieNotHttpOnly.java:29:28:29:36 | jwtCookie |
-| SensitiveCookieNotHttpOnly.java:40:42:40:49 | "token=" : String | SensitiveCookieNotHttpOnly.java:40:42:40:69 | ... + ... |
-| SensitiveCookieNotHttpOnly.java:40:42:40:57 | ... + ... : String | SensitiveCookieNotHttpOnly.java:40:42:40:69 | ... + ... |
-| SensitiveCookieNotHttpOnly.java:50:56:50:75 | "session-access-key" : String | SensitiveCookieNotHttpOnly.java:50:42:50:124 | toString(...) |
-| SensitiveCookieNotHttpOnly.java:61:51:61:70 | "session-access-key" : String | SensitiveCookieNotHttpOnly.java:63:42:63:47 | keyStr |
-| SensitiveCookieNotHttpOnly.java:68:28:68:35 | "token=" : String | SensitiveCookieNotHttpOnly.java:69:42:69:50 | secString |
-| SensitiveCookieNotHttpOnly.java:68:28:68:43 | ... + ... : String | SensitiveCookieNotHttpOnly.java:69:42:69:50 | secString |
-| SensitiveCookieNotHttpOnly.java:68:28:68:55 | ... + ... : String | SensitiveCookieNotHttpOnly.java:69:42:69:50 | secString |
+| SensitiveCookieNotHttpOnly.java:24:33:24:43 | "jwt_token" : String | SensitiveCookieNotHttpOnly.java:31:28:31:36 | jwtCookie |
+| SensitiveCookieNotHttpOnly.java:42:42:42:49 | "token=" : String | SensitiveCookieNotHttpOnly.java:42:42:42:69 | ... + ... |
+| SensitiveCookieNotHttpOnly.java:42:42:42:57 | ... + ... : String | SensitiveCookieNotHttpOnly.java:42:42:42:69 | ... + ... |
+| SensitiveCookieNotHttpOnly.java:52:56:52:75 | "session-access-key" : String | SensitiveCookieNotHttpOnly.java:52:42:52:124 | toString(...) |
+| SensitiveCookieNotHttpOnly.java:63:51:63:70 | "session-access-key" : String | SensitiveCookieNotHttpOnly.java:65:42:65:47 | keyStr |
+| SensitiveCookieNotHttpOnly.java:70:28:70:35 | "token=" : String | SensitiveCookieNotHttpOnly.java:71:42:71:50 | secString |
+| SensitiveCookieNotHttpOnly.java:70:28:70:43 | ... + ... : String | SensitiveCookieNotHttpOnly.java:71:42:71:50 | secString |
+| SensitiveCookieNotHttpOnly.java:70:28:70:55 | ... + ... : String | SensitiveCookieNotHttpOnly.java:71:42:71:50 | secString |
nodes
-| SensitiveCookieNotHttpOnly.java:22:33:22:43 | "jwt_token" : String | semmle.label | "jwt_token" : String |
-| SensitiveCookieNotHttpOnly.java:29:28:29:36 | jwtCookie | semmle.label | jwtCookie |
-| SensitiveCookieNotHttpOnly.java:40:42:40:49 | "token=" : String | semmle.label | "token=" : String |
-| SensitiveCookieNotHttpOnly.java:40:42:40:57 | ... + ... : String | semmle.label | ... + ... : String |
-| SensitiveCookieNotHttpOnly.java:40:42:40:69 | ... + ... | semmle.label | ... + ... |
-| SensitiveCookieNotHttpOnly.java:50:42:50:124 | toString(...) | semmle.label | toString(...) |
-| SensitiveCookieNotHttpOnly.java:50:56:50:75 | "session-access-key" : String | semmle.label | "session-access-key" : String |
-| SensitiveCookieNotHttpOnly.java:61:51:61:70 | "session-access-key" : String | semmle.label | "session-access-key" : String |
-| SensitiveCookieNotHttpOnly.java:63:42:63:47 | keyStr | semmle.label | keyStr |
-| SensitiveCookieNotHttpOnly.java:68:28:68:35 | "token=" : String | semmle.label | "token=" : String |
-| SensitiveCookieNotHttpOnly.java:68:28:68:43 | ... + ... : String | semmle.label | ... + ... : String |
-| SensitiveCookieNotHttpOnly.java:68:28:68:55 | ... + ... : String | semmle.label | ... + ... : String |
-| SensitiveCookieNotHttpOnly.java:69:42:69:50 | secString | semmle.label | secString |
+| SensitiveCookieNotHttpOnly.java:24:33:24:43 | "jwt_token" : String | semmle.label | "jwt_token" : String |
+| SensitiveCookieNotHttpOnly.java:31:28:31:36 | jwtCookie | semmle.label | jwtCookie |
+| SensitiveCookieNotHttpOnly.java:42:42:42:49 | "token=" : String | semmle.label | "token=" : String |
+| SensitiveCookieNotHttpOnly.java:42:42:42:57 | ... + ... : String | semmle.label | ... + ... : String |
+| SensitiveCookieNotHttpOnly.java:42:42:42:69 | ... + ... | semmle.label | ... + ... |
+| SensitiveCookieNotHttpOnly.java:52:42:52:124 | toString(...) | semmle.label | toString(...) |
+| SensitiveCookieNotHttpOnly.java:52:56:52:75 | "session-access-key" : String | semmle.label | "session-access-key" : String |
+| SensitiveCookieNotHttpOnly.java:63:51:63:70 | "session-access-key" : String | semmle.label | "session-access-key" : String |
+| SensitiveCookieNotHttpOnly.java:65:42:65:47 | keyStr | semmle.label | keyStr |
+| SensitiveCookieNotHttpOnly.java:70:28:70:35 | "token=" : String | semmle.label | "token=" : String |
+| SensitiveCookieNotHttpOnly.java:70:28:70:43 | ... + ... : String | semmle.label | ... + ... : String |
+| SensitiveCookieNotHttpOnly.java:70:28:70:55 | ... + ... : String | semmle.label | ... + ... : String |
+| SensitiveCookieNotHttpOnly.java:71:42:71:50 | secString | semmle.label | secString |
#select
-| SensitiveCookieNotHttpOnly.java:29:28:29:36 | jwtCookie | SensitiveCookieNotHttpOnly.java:22:33:22:43 | "jwt_token" : String | SensitiveCookieNotHttpOnly.java:29:28:29:36 | jwtCookie | $@ doesn't have the HttpOnly flag set. | SensitiveCookieNotHttpOnly.java:22:33:22:43 | "jwt_token" | This sensitive cookie |
-| SensitiveCookieNotHttpOnly.java:40:42:40:69 | ... + ... | SensitiveCookieNotHttpOnly.java:40:42:40:49 | "token=" : String | SensitiveCookieNotHttpOnly.java:40:42:40:69 | ... + ... | $@ doesn't have the HttpOnly flag set. | SensitiveCookieNotHttpOnly.java:40:42:40:49 | "token=" | This sensitive cookie |
-| SensitiveCookieNotHttpOnly.java:40:42:40:69 | ... + ... | SensitiveCookieNotHttpOnly.java:40:42:40:57 | ... + ... : String | SensitiveCookieNotHttpOnly.java:40:42:40:69 | ... + ... | $@ doesn't have the HttpOnly flag set. | SensitiveCookieNotHttpOnly.java:40:42:40:57 | ... + ... | This sensitive cookie |
-| SensitiveCookieNotHttpOnly.java:40:42:40:69 | ... + ... | SensitiveCookieNotHttpOnly.java:40:42:40:69 | ... + ... | SensitiveCookieNotHttpOnly.java:40:42:40:69 | ... + ... | $@ doesn't have the HttpOnly flag set. | SensitiveCookieNotHttpOnly.java:40:42:40:69 | ... + ... | This sensitive cookie |
-| SensitiveCookieNotHttpOnly.java:50:42:50:124 | toString(...) | SensitiveCookieNotHttpOnly.java:50:56:50:75 | "session-access-key" : String | SensitiveCookieNotHttpOnly.java:50:42:50:124 | toString(...) | $@ doesn't have the HttpOnly flag set. | SensitiveCookieNotHttpOnly.java:50:56:50:75 | "session-access-key" | This sensitive cookie |
-| SensitiveCookieNotHttpOnly.java:63:42:63:47 | keyStr | SensitiveCookieNotHttpOnly.java:61:51:61:70 | "session-access-key" : String | SensitiveCookieNotHttpOnly.java:63:42:63:47 | keyStr | $@ doesn't have the HttpOnly flag set. | SensitiveCookieNotHttpOnly.java:61:51:61:70 | "session-access-key" | This sensitive cookie |
-| SensitiveCookieNotHttpOnly.java:69:42:69:50 | secString | SensitiveCookieNotHttpOnly.java:68:28:68:35 | "token=" : String | SensitiveCookieNotHttpOnly.java:69:42:69:50 | secString | $@ doesn't have the HttpOnly flag set. | SensitiveCookieNotHttpOnly.java:68:28:68:35 | "token=" | This sensitive cookie |
-| SensitiveCookieNotHttpOnly.java:69:42:69:50 | secString | SensitiveCookieNotHttpOnly.java:68:28:68:43 | ... + ... : String | SensitiveCookieNotHttpOnly.java:69:42:69:50 | secString | $@ doesn't have the HttpOnly flag set. | SensitiveCookieNotHttpOnly.java:68:28:68:43 | ... + ... | This sensitive cookie |
-| SensitiveCookieNotHttpOnly.java:69:42:69:50 | secString | SensitiveCookieNotHttpOnly.java:68:28:68:55 | ... + ... : String | SensitiveCookieNotHttpOnly.java:69:42:69:50 | secString | $@ doesn't have the HttpOnly flag set. | SensitiveCookieNotHttpOnly.java:68:28:68:55 | ... + ... | This sensitive cookie |
+| SensitiveCookieNotHttpOnly.java:31:28:31:36 | jwtCookie | SensitiveCookieNotHttpOnly.java:24:33:24:43 | "jwt_token" : String | SensitiveCookieNotHttpOnly.java:31:28:31:36 | jwtCookie | $@ doesn't have the HttpOnly flag set. | SensitiveCookieNotHttpOnly.java:24:33:24:43 | "jwt_token" | This sensitive cookie |
+| SensitiveCookieNotHttpOnly.java:42:42:42:69 | ... + ... | SensitiveCookieNotHttpOnly.java:42:42:42:49 | "token=" : String | SensitiveCookieNotHttpOnly.java:42:42:42:69 | ... + ... | $@ doesn't have the HttpOnly flag set. | SensitiveCookieNotHttpOnly.java:42:42:42:49 | "token=" | This sensitive cookie |
+| SensitiveCookieNotHttpOnly.java:42:42:42:69 | ... + ... | SensitiveCookieNotHttpOnly.java:42:42:42:57 | ... + ... : String | SensitiveCookieNotHttpOnly.java:42:42:42:69 | ... + ... | $@ doesn't have the HttpOnly flag set. | SensitiveCookieNotHttpOnly.java:42:42:42:57 | ... + ... | This sensitive cookie |
+| SensitiveCookieNotHttpOnly.java:42:42:42:69 | ... + ... | SensitiveCookieNotHttpOnly.java:42:42:42:69 | ... + ... | SensitiveCookieNotHttpOnly.java:42:42:42:69 | ... + ... | $@ doesn't have the HttpOnly flag set. | SensitiveCookieNotHttpOnly.java:42:42:42:69 | ... + ... | This sensitive cookie |
+| SensitiveCookieNotHttpOnly.java:52:42:52:124 | toString(...) | SensitiveCookieNotHttpOnly.java:52:56:52:75 | "session-access-key" : String | SensitiveCookieNotHttpOnly.java:52:42:52:124 | toString(...) | $@ doesn't have the HttpOnly flag set. | SensitiveCookieNotHttpOnly.java:52:56:52:75 | "session-access-key" | This sensitive cookie |
+| SensitiveCookieNotHttpOnly.java:65:42:65:47 | keyStr | SensitiveCookieNotHttpOnly.java:63:51:63:70 | "session-access-key" : String | SensitiveCookieNotHttpOnly.java:65:42:65:47 | keyStr | $@ doesn't have the HttpOnly flag set. | SensitiveCookieNotHttpOnly.java:63:51:63:70 | "session-access-key" | This sensitive cookie |
+| SensitiveCookieNotHttpOnly.java:71:42:71:50 | secString | SensitiveCookieNotHttpOnly.java:70:28:70:35 | "token=" : String | SensitiveCookieNotHttpOnly.java:71:42:71:50 | secString | $@ doesn't have the HttpOnly flag set. | SensitiveCookieNotHttpOnly.java:70:28:70:35 | "token=" | This sensitive cookie |
+| SensitiveCookieNotHttpOnly.java:71:42:71:50 | secString | SensitiveCookieNotHttpOnly.java:70:28:70:43 | ... + ... : String | SensitiveCookieNotHttpOnly.java:71:42:71:50 | secString | $@ doesn't have the HttpOnly flag set. | SensitiveCookieNotHttpOnly.java:70:28:70:43 | ... + ... | This sensitive cookie |
+| SensitiveCookieNotHttpOnly.java:71:42:71:50 | secString | SensitiveCookieNotHttpOnly.java:70:28:70:55 | ... + ... : String | SensitiveCookieNotHttpOnly.java:71:42:71:50 | secString | $@ doesn't have the HttpOnly flag set. | SensitiveCookieNotHttpOnly.java:70:28:70:55 | ... + ... | This sensitive cookie |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-1004/SensitiveCookieNotHttpOnly.java b/java/ql/test/experimental/query-tests/security/CWE-1004/SensitiveCookieNotHttpOnly.java
index 6572577a697..337a99cc096 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-1004/SensitiveCookieNotHttpOnly.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-1004/SensitiveCookieNotHttpOnly.java
@@ -7,6 +7,8 @@ import javax.servlet.ServletException;
import javax.ws.rs.core.NewCookie;
+import org.springframework.security.web.csrf.CsrfToken;
+
class SensitiveCookieNotHttpOnly {
// GOOD - Tests adding a sensitive cookie with the `HttpOnly` flag set.
public void addCookie(String jwt_token, HttpServletRequest request, HttpServletResponse response) {
@@ -67,5 +69,23 @@ class SensitiveCookieNotHttpOnly {
public void addCookie9(String authId, HttpServletRequest request, HttpServletResponse response) {
String secString = "token=" +authId + ";Secure";
response.addHeader("Set-Cookie", secString);
- }
+ }
+
+ // GOOD - CSRF token doesn't need to have the `HttpOnly` flag set.
+ public void addCsrfCookie(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ // Spring put the CSRF token in session attribute "_csrf"
+ CsrfToken csrfToken = (CsrfToken) request.getAttribute("_csrf");
+
+ // Send the cookie only if the token has changed
+ String actualToken = request.getHeader("X-CSRF-TOKEN");
+ if (actualToken == null || !actualToken.equals(csrfToken.getToken())) {
+ // Session cookie that can be used by AngularJS
+ String pCookieName = "CSRF-TOKEN";
+ Cookie cookie = new Cookie(pCookieName, csrfToken.getToken());
+ cookie.setMaxAge(-1);
+ cookie.setHttpOnly(false);
+ cookie.setPath("/");
+ response.addCookie(cookie);
+ }
+ }
}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-1004/options b/java/ql/test/experimental/query-tests/security/CWE-1004/options
index 7f2b253fb20..d61a358d97f 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-1004/options
+++ b/java/ql/test/experimental/query-tests/security/CWE-1004/options
@@ -1 +1 @@
-// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/jsr311-api-1.1.1
\ No newline at end of file
+// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/jsr311-api-1.1.1:${testdir}/../../../../stubs/springframework-5.2.3
\ No newline at end of file
diff --git a/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/web/csrf/CsrfToken.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/web/csrf/CsrfToken.java
new file mode 100644
index 00000000000..bc59a2e496a
--- /dev/null
+++ b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/web/csrf/CsrfToken.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2002-2013 the original author or authors.
+ *
+ * Licensed 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
+ *
+ * https://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.springframework.security.web.csrf;
+
+import java.io.Serializable;
+
+/**
+ * Provides the information about an expected CSRF token.
+ *
+ * @author Rob Winch
+ * @since 3.2
+ * @see DefaultCsrfToken
+ */
+public interface CsrfToken extends Serializable {
+
+ /**
+ * Gets the HTTP header that the CSRF is populated on the response and can be placed
+ * on requests instead of the parameter. Cannot be null.
+ * @return the HTTP header that the CSRF is populated on the response and can be
+ * placed on requests instead of the parameter
+ */
+ String getHeaderName();
+
+ /**
+ * Gets the HTTP parameter name that should contain the token. Cannot be null.
+ * @return the HTTP parameter name that should contain the token.
+ */
+ String getParameterName();
+
+ /**
+ * Gets the token value. Cannot be null.
+ * @return the token value
+ */
+ String getToken();
+
+}
From 5667901a2a2a92fac5e79b3be67d44d24e6a5531 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Thu, 11 Mar 2021 21:16:57 +0100
Subject: [PATCH 110/725] C++: Accept test changes after merge from main (which
changed the path explanations).
---
.../CWE-022/semmle/tests/TaintedPath.expected | 8 +-
.../CWE/CWE-079/semmle/CgiXss/CgiXss.expected | 16 ++--
.../CWE-089/SqlTainted/SqlTainted.expected | 8 +-
.../UncontrolledProcessOperation.expected | 48 +++++-----
.../semmle/tests/UnboundedWrite.expected | 60 ++++++-------
.../CWE-134/semmle/funcs/funcsLocal.expected | 56 ++++++------
...olledFormatStringThroughGlobalVar.expected | 30 +++----
.../CWE/CWE-134/semmle/ifs/ifs.expected | 88 +++++++++----------
.../TaintedAllocationSize.expected | 36 ++++----
.../AuthenticationBypass.expected | 24 ++---
.../tests/CleartextBufferWrite.expected | 8 +-
11 files changed, 191 insertions(+), 191 deletions(-)
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected
index 60898414224..7b513c574ad 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected
@@ -1,17 +1,17 @@
edges
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | (const char *)... |
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | (const char *)... |
-| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | Argument 0 indirection |
-| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | Argument 0 indirection |
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName |
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName |
+| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName indirection |
+| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName indirection |
nodes
| test.c:9:23:9:26 | argv | semmle.label | argv |
| test.c:9:23:9:26 | argv | semmle.label | argv |
| test.c:17:11:17:18 | (const char *)... | semmle.label | (const char *)... |
| test.c:17:11:17:18 | (const char *)... | semmle.label | (const char *)... |
-| test.c:17:11:17:18 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| test.c:17:11:17:18 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| test.c:17:11:17:18 | fileName | semmle.label | fileName |
+| test.c:17:11:17:18 | fileName indirection | semmle.label | fileName indirection |
+| test.c:17:11:17:18 | fileName indirection | semmle.label | fileName indirection |
#select
| test.c:17:11:17:18 | fileName | test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName | This argument to a file access function is derived from $@ and then passed to fopen(filename) | test.c:9:23:9:26 | argv | user input (argv) |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-079/semmle/CgiXss/CgiXss.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-079/semmle/CgiXss/CgiXss.expected
index 70615e0b606..d9fa0d7029f 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-079/semmle/CgiXss/CgiXss.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-079/semmle/CgiXss/CgiXss.expected
@@ -1,17 +1,17 @@
edges
| search.c:14:24:14:28 | *query | search.c:17:8:17:12 | (const char *)... |
-| search.c:14:24:14:28 | *query | search.c:17:8:17:12 | Argument 0 indirection |
| search.c:14:24:14:28 | *query | search.c:17:8:17:12 | query |
+| search.c:14:24:14:28 | *query | search.c:17:8:17:12 | query indirection |
| search.c:14:24:14:28 | query | search.c:17:8:17:12 | (const char *)... |
-| search.c:14:24:14:28 | query | search.c:17:8:17:12 | Argument 0 indirection |
| search.c:14:24:14:28 | query | search.c:17:8:17:12 | query |
| search.c:14:24:14:28 | query | search.c:17:8:17:12 | query |
-| search.c:22:24:22:28 | *query | search.c:23:39:23:43 | Argument 1 indirection |
+| search.c:14:24:14:28 | query | search.c:17:8:17:12 | query indirection |
| search.c:22:24:22:28 | *query | search.c:23:39:23:43 | query |
| search.c:22:24:22:28 | *query | search.c:23:39:23:43 | query |
-| search.c:22:24:22:28 | query | search.c:23:39:23:43 | Argument 1 indirection |
+| search.c:22:24:22:28 | *query | search.c:23:39:23:43 | query indirection |
| search.c:22:24:22:28 | query | search.c:23:39:23:43 | query |
| search.c:22:24:22:28 | query | search.c:23:39:23:43 | query |
+| search.c:22:24:22:28 | query | search.c:23:39:23:43 | query indirection |
| search.c:51:21:51:26 | call to getenv | search.c:55:5:55:15 | raw_query |
| search.c:51:21:51:26 | call to getenv | search.c:55:5:55:15 | raw_query |
| search.c:51:21:51:26 | call to getenv | search.c:55:17:55:25 | raw_query indirection |
@@ -29,18 +29,18 @@ nodes
| search.c:14:24:14:28 | query | semmle.label | query |
| search.c:17:8:17:12 | (const char *)... | semmle.label | (const char *)... |
| search.c:17:8:17:12 | (const char *)... | semmle.label | (const char *)... |
-| search.c:17:8:17:12 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| search.c:17:8:17:12 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| search.c:17:8:17:12 | query | semmle.label | query |
| search.c:17:8:17:12 | query | semmle.label | query |
| search.c:17:8:17:12 | query | semmle.label | query |
+| search.c:17:8:17:12 | query indirection | semmle.label | query indirection |
+| search.c:17:8:17:12 | query indirection | semmle.label | query indirection |
| search.c:22:24:22:28 | *query | semmle.label | *query |
| search.c:22:24:22:28 | query | semmle.label | query |
-| search.c:23:39:23:43 | Argument 1 indirection | semmle.label | Argument 1 indirection |
-| search.c:23:39:23:43 | Argument 1 indirection | semmle.label | Argument 1 indirection |
| search.c:23:39:23:43 | query | semmle.label | query |
| search.c:23:39:23:43 | query | semmle.label | query |
| search.c:23:39:23:43 | query | semmle.label | query |
+| search.c:23:39:23:43 | query indirection | semmle.label | query indirection |
+| search.c:23:39:23:43 | query indirection | semmle.label | query indirection |
| search.c:51:21:51:26 | call to getenv | semmle.label | call to getenv |
| search.c:51:21:51:26 | call to getenv | semmle.label | call to getenv |
| search.c:55:5:55:15 | raw_query | semmle.label | raw_query |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/SqlTainted.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/SqlTainted.expected
index fe84a80654c..e267dd48bba 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/SqlTainted.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/SqlTainted.expected
@@ -1,17 +1,17 @@
edges
| test.c:15:20:15:23 | argv | test.c:21:18:21:23 | (const char *)... |
| test.c:15:20:15:23 | argv | test.c:21:18:21:23 | (const char *)... |
-| test.c:15:20:15:23 | argv | test.c:21:18:21:23 | Argument 1 indirection |
-| test.c:15:20:15:23 | argv | test.c:21:18:21:23 | Argument 1 indirection |
| test.c:15:20:15:23 | argv | test.c:21:18:21:23 | query1 |
| test.c:15:20:15:23 | argv | test.c:21:18:21:23 | query1 |
+| test.c:15:20:15:23 | argv | test.c:21:18:21:23 | query1 indirection |
+| test.c:15:20:15:23 | argv | test.c:21:18:21:23 | query1 indirection |
nodes
| test.c:15:20:15:23 | argv | semmle.label | argv |
| test.c:15:20:15:23 | argv | semmle.label | argv |
| test.c:21:18:21:23 | (const char *)... | semmle.label | (const char *)... |
| test.c:21:18:21:23 | (const char *)... | semmle.label | (const char *)... |
-| test.c:21:18:21:23 | Argument 1 indirection | semmle.label | Argument 1 indirection |
-| test.c:21:18:21:23 | Argument 1 indirection | semmle.label | Argument 1 indirection |
| test.c:21:18:21:23 | query1 | semmle.label | query1 |
+| test.c:21:18:21:23 | query1 indirection | semmle.label | query1 indirection |
+| test.c:21:18:21:23 | query1 indirection | semmle.label | query1 indirection |
#select
| test.c:21:18:21:23 | query1 | test.c:15:20:15:23 | argv | test.c:21:18:21:23 | query1 | This argument to a SQL query function is derived from $@ and then passed to mysql_query(sqlArg) | test.c:15:20:15:23 | argv | user input (argv) |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected
index 062a59d2907..14f234f5999 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected
@@ -1,16 +1,16 @@
edges
-| test.cpp:24:30:24:36 | *command | test.cpp:26:10:26:16 | Argument 0 indirection |
| test.cpp:24:30:24:36 | *command | test.cpp:26:10:26:16 | command |
| test.cpp:24:30:24:36 | *command | test.cpp:26:10:26:16 | command |
-| test.cpp:24:30:24:36 | command | test.cpp:26:10:26:16 | Argument 0 indirection |
+| test.cpp:24:30:24:36 | *command | test.cpp:26:10:26:16 | command indirection |
| test.cpp:24:30:24:36 | command | test.cpp:26:10:26:16 | command |
| test.cpp:24:30:24:36 | command | test.cpp:26:10:26:16 | command |
-| test.cpp:29:30:29:36 | *command | test.cpp:31:10:31:16 | Argument 0 indirection |
+| test.cpp:24:30:24:36 | command | test.cpp:26:10:26:16 | command indirection |
| test.cpp:29:30:29:36 | *command | test.cpp:31:10:31:16 | command |
| test.cpp:29:30:29:36 | *command | test.cpp:31:10:31:16 | command |
-| test.cpp:29:30:29:36 | command | test.cpp:31:10:31:16 | Argument 0 indirection |
+| test.cpp:29:30:29:36 | *command | test.cpp:31:10:31:16 | command indirection |
| test.cpp:29:30:29:36 | command | test.cpp:31:10:31:16 | command |
| test.cpp:29:30:29:36 | command | test.cpp:31:10:31:16 | command |
+| test.cpp:29:30:29:36 | command | test.cpp:31:10:31:16 | command indirection |
| test.cpp:42:7:42:16 | call to getenv | test.cpp:24:30:24:36 | command |
| test.cpp:42:18:42:23 | call to getenv | test.cpp:42:7:42:16 | call to getenv |
| test.cpp:42:18:42:23 | call to getenv | test.cpp:42:18:42:34 | call to getenv indirection |
@@ -24,44 +24,44 @@ edges
| test.cpp:43:18:43:34 | (const char *)... | test.cpp:43:18:43:34 | call to getenv indirection |
| test.cpp:43:18:43:34 | call to getenv indirection | test.cpp:29:30:29:36 | *command |
| test.cpp:56:12:56:17 | buffer | test.cpp:62:10:62:15 | (const char *)... |
-| test.cpp:56:12:56:17 | buffer | test.cpp:62:10:62:15 | Argument 0 indirection |
| test.cpp:56:12:56:17 | buffer | test.cpp:62:10:62:15 | buffer |
+| test.cpp:56:12:56:17 | buffer | test.cpp:62:10:62:15 | buffer indirection |
| test.cpp:56:12:56:17 | buffer | test.cpp:63:10:63:13 | (const char *)... |
-| test.cpp:56:12:56:17 | buffer | test.cpp:63:10:63:13 | Argument 0 indirection |
| test.cpp:56:12:56:17 | buffer | test.cpp:63:10:63:13 | data |
+| test.cpp:56:12:56:17 | buffer | test.cpp:63:10:63:13 | data indirection |
| test.cpp:56:12:56:17 | fgets output argument | test.cpp:62:10:62:15 | (const char *)... |
-| test.cpp:56:12:56:17 | fgets output argument | test.cpp:62:10:62:15 | Argument 0 indirection |
| test.cpp:56:12:56:17 | fgets output argument | test.cpp:62:10:62:15 | buffer |
+| test.cpp:56:12:56:17 | fgets output argument | test.cpp:62:10:62:15 | buffer indirection |
| test.cpp:56:12:56:17 | fgets output argument | test.cpp:63:10:63:13 | (const char *)... |
-| test.cpp:56:12:56:17 | fgets output argument | test.cpp:63:10:63:13 | Argument 0 indirection |
| test.cpp:56:12:56:17 | fgets output argument | test.cpp:63:10:63:13 | data |
+| test.cpp:56:12:56:17 | fgets output argument | test.cpp:63:10:63:13 | data indirection |
| test.cpp:76:12:76:17 | buffer | test.cpp:78:10:78:15 | (const char *)... |
-| test.cpp:76:12:76:17 | buffer | test.cpp:78:10:78:15 | Argument 0 indirection |
| test.cpp:76:12:76:17 | buffer | test.cpp:78:10:78:15 | buffer |
+| test.cpp:76:12:76:17 | buffer | test.cpp:78:10:78:15 | buffer indirection |
| test.cpp:76:12:76:17 | buffer | test.cpp:79:10:79:13 | (const char *)... |
-| test.cpp:76:12:76:17 | buffer | test.cpp:79:10:79:13 | Argument 0 indirection |
| test.cpp:76:12:76:17 | buffer | test.cpp:79:10:79:13 | data |
+| test.cpp:76:12:76:17 | buffer | test.cpp:79:10:79:13 | data indirection |
| test.cpp:76:12:76:17 | fgets output argument | test.cpp:78:10:78:15 | (const char *)... |
-| test.cpp:76:12:76:17 | fgets output argument | test.cpp:78:10:78:15 | Argument 0 indirection |
| test.cpp:76:12:76:17 | fgets output argument | test.cpp:78:10:78:15 | buffer |
+| test.cpp:76:12:76:17 | fgets output argument | test.cpp:78:10:78:15 | buffer indirection |
| test.cpp:76:12:76:17 | fgets output argument | test.cpp:79:10:79:13 | (const char *)... |
-| test.cpp:76:12:76:17 | fgets output argument | test.cpp:79:10:79:13 | Argument 0 indirection |
| test.cpp:76:12:76:17 | fgets output argument | test.cpp:79:10:79:13 | data |
+| test.cpp:76:12:76:17 | fgets output argument | test.cpp:79:10:79:13 | data indirection |
nodes
| test.cpp:24:30:24:36 | *command | semmle.label | *command |
| test.cpp:24:30:24:36 | command | semmle.label | command |
-| test.cpp:26:10:26:16 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| test.cpp:26:10:26:16 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| test.cpp:26:10:26:16 | command | semmle.label | command |
| test.cpp:26:10:26:16 | command | semmle.label | command |
| test.cpp:26:10:26:16 | command | semmle.label | command |
+| test.cpp:26:10:26:16 | command indirection | semmle.label | command indirection |
+| test.cpp:26:10:26:16 | command indirection | semmle.label | command indirection |
| test.cpp:29:30:29:36 | *command | semmle.label | *command |
| test.cpp:29:30:29:36 | command | semmle.label | command |
-| test.cpp:31:10:31:16 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| test.cpp:31:10:31:16 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| test.cpp:31:10:31:16 | command | semmle.label | command |
| test.cpp:31:10:31:16 | command | semmle.label | command |
| test.cpp:31:10:31:16 | command | semmle.label | command |
+| test.cpp:31:10:31:16 | command indirection | semmle.label | command indirection |
+| test.cpp:31:10:31:16 | command indirection | semmle.label | command indirection |
| test.cpp:42:7:42:16 | call to getenv | semmle.label | call to getenv |
| test.cpp:42:18:42:23 | call to getenv | semmle.label | call to getenv |
| test.cpp:42:18:42:34 | (const char *)... | semmle.label | (const char *)... |
@@ -74,26 +74,26 @@ nodes
| test.cpp:56:12:56:17 | fgets output argument | semmle.label | fgets output argument |
| test.cpp:62:10:62:15 | (const char *)... | semmle.label | (const char *)... |
| test.cpp:62:10:62:15 | (const char *)... | semmle.label | (const char *)... |
-| test.cpp:62:10:62:15 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| test.cpp:62:10:62:15 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| test.cpp:62:10:62:15 | buffer | semmle.label | buffer |
+| test.cpp:62:10:62:15 | buffer indirection | semmle.label | buffer indirection |
+| test.cpp:62:10:62:15 | buffer indirection | semmle.label | buffer indirection |
| test.cpp:63:10:63:13 | (const char *)... | semmle.label | (const char *)... |
| test.cpp:63:10:63:13 | (const char *)... | semmle.label | (const char *)... |
-| test.cpp:63:10:63:13 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| test.cpp:63:10:63:13 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| test.cpp:63:10:63:13 | data | semmle.label | data |
+| test.cpp:63:10:63:13 | data indirection | semmle.label | data indirection |
+| test.cpp:63:10:63:13 | data indirection | semmle.label | data indirection |
| test.cpp:76:12:76:17 | buffer | semmle.label | buffer |
| test.cpp:76:12:76:17 | fgets output argument | semmle.label | fgets output argument |
| test.cpp:78:10:78:15 | (const char *)... | semmle.label | (const char *)... |
| test.cpp:78:10:78:15 | (const char *)... | semmle.label | (const char *)... |
-| test.cpp:78:10:78:15 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| test.cpp:78:10:78:15 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| test.cpp:78:10:78:15 | buffer | semmle.label | buffer |
+| test.cpp:78:10:78:15 | buffer indirection | semmle.label | buffer indirection |
+| test.cpp:78:10:78:15 | buffer indirection | semmle.label | buffer indirection |
| test.cpp:79:10:79:13 | (const char *)... | semmle.label | (const char *)... |
| test.cpp:79:10:79:13 | (const char *)... | semmle.label | (const char *)... |
-| test.cpp:79:10:79:13 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| test.cpp:79:10:79:13 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| test.cpp:79:10:79:13 | data | semmle.label | data |
+| test.cpp:79:10:79:13 | data indirection | semmle.label | data indirection |
+| test.cpp:79:10:79:13 | data indirection | semmle.label | data indirection |
#select
| test.cpp:26:10:26:16 | command | test.cpp:42:18:42:23 | call to getenv | test.cpp:26:10:26:16 | command | The value of this argument may come from $@ and is being passed to system | test.cpp:42:18:42:23 | call to getenv | call to getenv |
| test.cpp:31:10:31:16 | command | test.cpp:43:18:43:23 | call to getenv | test.cpp:31:10:31:16 | command | The value of this argument may come from $@ and is being passed to system | test.cpp:43:18:43:23 | call to getenv | call to getenv |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-120/semmle/tests/UnboundedWrite.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-120/semmle/tests/UnboundedWrite.expected
index a0db8029b2b..5255753b235 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-120/semmle/tests/UnboundedWrite.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-120/semmle/tests/UnboundedWrite.expected
@@ -1,95 +1,95 @@
edges
| tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | (const char *)... |
| tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | (const char *)... |
-| tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | Argument 1 indirection |
-| tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | Argument 1 indirection |
| tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | access to array |
| tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | access to array |
| tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | access to array |
| tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | access to array |
-| tests.c:28:22:28:25 | argv | tests.c:31:15:31:23 | Argument 1 indirection |
-| tests.c:28:22:28:25 | argv | tests.c:31:15:31:23 | Argument 1 indirection |
+| tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | access to array indirection |
+| tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | access to array indirection |
| tests.c:28:22:28:25 | argv | tests.c:31:15:31:23 | buffer100 |
| tests.c:28:22:28:25 | argv | tests.c:31:15:31:23 | buffer100 |
-| tests.c:28:22:28:25 | argv | tests.c:33:21:33:29 | Argument 2 indirection |
-| tests.c:28:22:28:25 | argv | tests.c:33:21:33:29 | Argument 2 indirection |
+| tests.c:28:22:28:25 | argv | tests.c:31:15:31:23 | buffer100 indirection |
+| tests.c:28:22:28:25 | argv | tests.c:31:15:31:23 | buffer100 indirection |
| tests.c:28:22:28:25 | argv | tests.c:33:21:33:29 | buffer100 |
| tests.c:28:22:28:25 | argv | tests.c:33:21:33:29 | buffer100 |
-| tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | Argument 2 indirection |
-| tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | Argument 2 indirection |
+| tests.c:28:22:28:25 | argv | tests.c:33:21:33:29 | buffer100 indirection |
+| tests.c:28:22:28:25 | argv | tests.c:33:21:33:29 | buffer100 indirection |
| tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | access to array |
| tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | access to array |
| tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | access to array |
| tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | access to array |
-| tests.c:29:28:29:31 | argv | tests.c:31:15:31:23 | Argument 1 indirection |
-| tests.c:29:28:29:31 | argv | tests.c:31:15:31:23 | Argument 1 indirection |
+| tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | access to array indirection |
+| tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | access to array indirection |
| tests.c:29:28:29:31 | argv | tests.c:31:15:31:23 | buffer100 |
| tests.c:29:28:29:31 | argv | tests.c:31:15:31:23 | buffer100 |
-| tests.c:29:28:29:31 | argv | tests.c:33:21:33:29 | Argument 2 indirection |
-| tests.c:29:28:29:31 | argv | tests.c:33:21:33:29 | Argument 2 indirection |
+| tests.c:29:28:29:31 | argv | tests.c:31:15:31:23 | buffer100 indirection |
+| tests.c:29:28:29:31 | argv | tests.c:31:15:31:23 | buffer100 indirection |
| tests.c:29:28:29:31 | argv | tests.c:33:21:33:29 | buffer100 |
| tests.c:29:28:29:31 | argv | tests.c:33:21:33:29 | buffer100 |
-| tests.c:31:15:31:23 | array to pointer conversion | tests.c:31:15:31:23 | Argument 1 indirection |
+| tests.c:29:28:29:31 | argv | tests.c:33:21:33:29 | buffer100 indirection |
+| tests.c:29:28:29:31 | argv | tests.c:33:21:33:29 | buffer100 indirection |
| tests.c:31:15:31:23 | array to pointer conversion | tests.c:31:15:31:23 | buffer100 |
-| tests.c:31:15:31:23 | buffer100 | tests.c:31:15:31:23 | Argument 1 indirection |
+| tests.c:31:15:31:23 | array to pointer conversion | tests.c:31:15:31:23 | buffer100 indirection |
| tests.c:31:15:31:23 | buffer100 | tests.c:31:15:31:23 | buffer100 |
-| tests.c:31:15:31:23 | buffer100 | tests.c:33:21:33:29 | Argument 2 indirection |
+| tests.c:31:15:31:23 | buffer100 | tests.c:31:15:31:23 | buffer100 indirection |
| tests.c:31:15:31:23 | buffer100 | tests.c:33:21:33:29 | buffer100 |
-| tests.c:31:15:31:23 | scanf output argument | tests.c:33:21:33:29 | Argument 2 indirection |
+| tests.c:31:15:31:23 | buffer100 | tests.c:33:21:33:29 | buffer100 indirection |
| tests.c:31:15:31:23 | scanf output argument | tests.c:33:21:33:29 | buffer100 |
-| tests.c:33:21:33:29 | array to pointer conversion | tests.c:33:21:33:29 | Argument 2 indirection |
+| tests.c:31:15:31:23 | scanf output argument | tests.c:33:21:33:29 | buffer100 indirection |
| tests.c:33:21:33:29 | array to pointer conversion | tests.c:33:21:33:29 | buffer100 |
-| tests.c:33:21:33:29 | buffer100 | tests.c:33:21:33:29 | Argument 2 indirection |
+| tests.c:33:21:33:29 | array to pointer conversion | tests.c:33:21:33:29 | buffer100 indirection |
| tests.c:33:21:33:29 | buffer100 | tests.c:33:21:33:29 | buffer100 |
+| tests.c:33:21:33:29 | buffer100 | tests.c:33:21:33:29 | buffer100 indirection |
| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | (const char *)... |
| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | (const char *)... |
-| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | Argument 0 indirection |
-| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | Argument 0 indirection |
| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | access to array |
| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | access to array |
| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | access to array |
| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | access to array |
+| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | access to array indirection |
+| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | access to array indirection |
nodes
| tests.c:28:22:28:25 | argv | semmle.label | argv |
| tests.c:28:22:28:25 | argv | semmle.label | argv |
| tests.c:28:22:28:28 | (const char *)... | semmle.label | (const char *)... |
| tests.c:28:22:28:28 | (const char *)... | semmle.label | (const char *)... |
-| tests.c:28:22:28:28 | Argument 1 indirection | semmle.label | Argument 1 indirection |
-| tests.c:28:22:28:28 | Argument 1 indirection | semmle.label | Argument 1 indirection |
| tests.c:28:22:28:28 | access to array | semmle.label | access to array |
| tests.c:28:22:28:28 | access to array | semmle.label | access to array |
| tests.c:28:22:28:28 | access to array | semmle.label | access to array |
+| tests.c:28:22:28:28 | access to array indirection | semmle.label | access to array indirection |
+| tests.c:28:22:28:28 | access to array indirection | semmle.label | access to array indirection |
| tests.c:29:28:29:31 | argv | semmle.label | argv |
| tests.c:29:28:29:31 | argv | semmle.label | argv |
-| tests.c:29:28:29:34 | Argument 2 indirection | semmle.label | Argument 2 indirection |
-| tests.c:29:28:29:34 | Argument 2 indirection | semmle.label | Argument 2 indirection |
| tests.c:29:28:29:34 | access to array | semmle.label | access to array |
| tests.c:29:28:29:34 | access to array | semmle.label | access to array |
| tests.c:29:28:29:34 | access to array | semmle.label | access to array |
-| tests.c:31:15:31:23 | Argument 1 indirection | semmle.label | Argument 1 indirection |
-| tests.c:31:15:31:23 | Argument 1 indirection | semmle.label | Argument 1 indirection |
+| tests.c:29:28:29:34 | access to array indirection | semmle.label | access to array indirection |
+| tests.c:29:28:29:34 | access to array indirection | semmle.label | access to array indirection |
| tests.c:31:15:31:23 | array to pointer conversion | semmle.label | array to pointer conversion |
| tests.c:31:15:31:23 | array to pointer conversion | semmle.label | array to pointer conversion |
| tests.c:31:15:31:23 | buffer100 | semmle.label | buffer100 |
| tests.c:31:15:31:23 | buffer100 | semmle.label | buffer100 |
| tests.c:31:15:31:23 | buffer100 | semmle.label | buffer100 |
+| tests.c:31:15:31:23 | buffer100 indirection | semmle.label | buffer100 indirection |
+| tests.c:31:15:31:23 | buffer100 indirection | semmle.label | buffer100 indirection |
| tests.c:31:15:31:23 | scanf output argument | semmle.label | scanf output argument |
-| tests.c:33:21:33:29 | Argument 2 indirection | semmle.label | Argument 2 indirection |
-| tests.c:33:21:33:29 | Argument 2 indirection | semmle.label | Argument 2 indirection |
| tests.c:33:21:33:29 | array to pointer conversion | semmle.label | array to pointer conversion |
| tests.c:33:21:33:29 | array to pointer conversion | semmle.label | array to pointer conversion |
| tests.c:33:21:33:29 | buffer100 | semmle.label | buffer100 |
| tests.c:33:21:33:29 | buffer100 | semmle.label | buffer100 |
| tests.c:33:21:33:29 | buffer100 | semmle.label | buffer100 |
+| tests.c:33:21:33:29 | buffer100 indirection | semmle.label | buffer100 indirection |
+| tests.c:33:21:33:29 | buffer100 indirection | semmle.label | buffer100 indirection |
| tests.c:34:10:34:13 | argv | semmle.label | argv |
| tests.c:34:10:34:13 | argv | semmle.label | argv |
| tests.c:34:10:34:16 | (const char *)... | semmle.label | (const char *)... |
| tests.c:34:10:34:16 | (const char *)... | semmle.label | (const char *)... |
-| tests.c:34:10:34:16 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| tests.c:34:10:34:16 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| tests.c:34:10:34:16 | access to array | semmle.label | access to array |
| tests.c:34:10:34:16 | access to array | semmle.label | access to array |
| tests.c:34:10:34:16 | access to array | semmle.label | access to array |
+| tests.c:34:10:34:16 | access to array indirection | semmle.label | access to array indirection |
+| tests.c:34:10:34:16 | access to array indirection | semmle.label | access to array indirection |
#select
| tests.c:28:3:28:9 | call to sprintf | tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | access to array | This 'call to sprintf' with input from $@ may overflow the destination. | tests.c:28:22:28:25 | argv | argv |
| tests.c:29:3:29:9 | call to sprintf | tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | access to array | This 'call to sprintf' with input from $@ may overflow the destination. | tests.c:29:28:29:31 | argv | argv |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/funcs/funcsLocal.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/funcs/funcsLocal.expected
index 963c37bef48..ceaf0489ec6 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/funcs/funcsLocal.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/funcs/funcsLocal.expected
@@ -1,105 +1,105 @@
edges
| funcsLocal.c:16:8:16:9 | fread output argument | funcsLocal.c:17:9:17:10 | (const char *)... |
-| funcsLocal.c:16:8:16:9 | fread output argument | funcsLocal.c:17:9:17:10 | Argument 0 indirection |
| funcsLocal.c:16:8:16:9 | fread output argument | funcsLocal.c:17:9:17:10 | i1 |
+| funcsLocal.c:16:8:16:9 | fread output argument | funcsLocal.c:17:9:17:10 | i1 indirection |
| funcsLocal.c:16:8:16:9 | fread output argument | funcsLocal.c:58:9:58:10 | (const char *)... |
-| funcsLocal.c:16:8:16:9 | fread output argument | funcsLocal.c:58:9:58:10 | Argument 0 indirection |
| funcsLocal.c:16:8:16:9 | fread output argument | funcsLocal.c:58:9:58:10 | e1 |
+| funcsLocal.c:16:8:16:9 | fread output argument | funcsLocal.c:58:9:58:10 | e1 indirection |
| funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:17:9:17:10 | (const char *)... |
-| funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:17:9:17:10 | Argument 0 indirection |
| funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:17:9:17:10 | i1 |
+| funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:17:9:17:10 | i1 indirection |
| funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:58:9:58:10 | (const char *)... |
-| funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:58:9:58:10 | Argument 0 indirection |
| funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:58:9:58:10 | e1 |
+| funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:58:9:58:10 | e1 indirection |
| funcsLocal.c:26:8:26:9 | fgets output argument | funcsLocal.c:27:9:27:10 | (const char *)... |
-| funcsLocal.c:26:8:26:9 | fgets output argument | funcsLocal.c:27:9:27:10 | Argument 0 indirection |
| funcsLocal.c:26:8:26:9 | fgets output argument | funcsLocal.c:27:9:27:10 | i3 |
+| funcsLocal.c:26:8:26:9 | fgets output argument | funcsLocal.c:27:9:27:10 | i3 indirection |
| funcsLocal.c:26:8:26:9 | i3 | funcsLocal.c:27:9:27:10 | (const char *)... |
-| funcsLocal.c:26:8:26:9 | i3 | funcsLocal.c:27:9:27:10 | Argument 0 indirection |
| funcsLocal.c:26:8:26:9 | i3 | funcsLocal.c:27:9:27:10 | i3 |
+| funcsLocal.c:26:8:26:9 | i3 | funcsLocal.c:27:9:27:10 | i3 indirection |
| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | (const char *)... |
| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | (const char *)... |
-| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | Argument 0 indirection |
-| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | Argument 0 indirection |
| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | i4 |
| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | i4 |
| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | i4 |
| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | i4 |
+| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | i4 indirection |
+| funcsLocal.c:31:13:31:17 | call to fgets | funcsLocal.c:32:9:32:10 | i4 indirection |
| funcsLocal.c:31:19:31:21 | fgets output argument | funcsLocal.c:32:9:32:10 | (const char *)... |
-| funcsLocal.c:31:19:31:21 | fgets output argument | funcsLocal.c:32:9:32:10 | Argument 0 indirection |
| funcsLocal.c:31:19:31:21 | fgets output argument | funcsLocal.c:32:9:32:10 | i4 |
+| funcsLocal.c:31:19:31:21 | fgets output argument | funcsLocal.c:32:9:32:10 | i4 indirection |
| funcsLocal.c:31:19:31:21 | i41 | funcsLocal.c:32:9:32:10 | (const char *)... |
-| funcsLocal.c:31:19:31:21 | i41 | funcsLocal.c:32:9:32:10 | Argument 0 indirection |
| funcsLocal.c:31:19:31:21 | i41 | funcsLocal.c:32:9:32:10 | i4 |
+| funcsLocal.c:31:19:31:21 | i41 | funcsLocal.c:32:9:32:10 | i4 indirection |
| funcsLocal.c:36:7:36:8 | gets output argument | funcsLocal.c:37:9:37:10 | (const char *)... |
-| funcsLocal.c:36:7:36:8 | gets output argument | funcsLocal.c:37:9:37:10 | Argument 0 indirection |
| funcsLocal.c:36:7:36:8 | gets output argument | funcsLocal.c:37:9:37:10 | i5 |
+| funcsLocal.c:36:7:36:8 | gets output argument | funcsLocal.c:37:9:37:10 | i5 indirection |
| funcsLocal.c:36:7:36:8 | i5 | funcsLocal.c:37:9:37:10 | (const char *)... |
-| funcsLocal.c:36:7:36:8 | i5 | funcsLocal.c:37:9:37:10 | Argument 0 indirection |
| funcsLocal.c:36:7:36:8 | i5 | funcsLocal.c:37:9:37:10 | i5 |
+| funcsLocal.c:36:7:36:8 | i5 | funcsLocal.c:37:9:37:10 | i5 indirection |
| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | (const char *)... |
| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | (const char *)... |
-| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | Argument 0 indirection |
-| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | Argument 0 indirection |
| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | i6 |
| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | i6 |
| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | i6 |
| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | i6 |
+| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | i6 indirection |
+| funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | i6 indirection |
| funcsLocal.c:41:18:41:20 | gets output argument | funcsLocal.c:42:9:42:10 | (const char *)... |
-| funcsLocal.c:41:18:41:20 | gets output argument | funcsLocal.c:42:9:42:10 | Argument 0 indirection |
| funcsLocal.c:41:18:41:20 | gets output argument | funcsLocal.c:42:9:42:10 | i6 |
+| funcsLocal.c:41:18:41:20 | gets output argument | funcsLocal.c:42:9:42:10 | i6 indirection |
| funcsLocal.c:41:18:41:20 | i61 | funcsLocal.c:42:9:42:10 | (const char *)... |
-| funcsLocal.c:41:18:41:20 | i61 | funcsLocal.c:42:9:42:10 | Argument 0 indirection |
| funcsLocal.c:41:18:41:20 | i61 | funcsLocal.c:42:9:42:10 | i6 |
+| funcsLocal.c:41:18:41:20 | i61 | funcsLocal.c:42:9:42:10 | i6 indirection |
nodes
| funcsLocal.c:16:8:16:9 | fread output argument | semmle.label | fread output argument |
| funcsLocal.c:16:8:16:9 | i1 | semmle.label | i1 |
| funcsLocal.c:17:9:17:10 | (const char *)... | semmle.label | (const char *)... |
| funcsLocal.c:17:9:17:10 | (const char *)... | semmle.label | (const char *)... |
-| funcsLocal.c:17:9:17:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| funcsLocal.c:17:9:17:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| funcsLocal.c:17:9:17:10 | i1 | semmle.label | i1 |
+| funcsLocal.c:17:9:17:10 | i1 indirection | semmle.label | i1 indirection |
+| funcsLocal.c:17:9:17:10 | i1 indirection | semmle.label | i1 indirection |
| funcsLocal.c:26:8:26:9 | fgets output argument | semmle.label | fgets output argument |
| funcsLocal.c:26:8:26:9 | i3 | semmle.label | i3 |
| funcsLocal.c:27:9:27:10 | (const char *)... | semmle.label | (const char *)... |
| funcsLocal.c:27:9:27:10 | (const char *)... | semmle.label | (const char *)... |
-| funcsLocal.c:27:9:27:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| funcsLocal.c:27:9:27:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| funcsLocal.c:27:9:27:10 | i3 | semmle.label | i3 |
+| funcsLocal.c:27:9:27:10 | i3 indirection | semmle.label | i3 indirection |
+| funcsLocal.c:27:9:27:10 | i3 indirection | semmle.label | i3 indirection |
| funcsLocal.c:31:13:31:17 | call to fgets | semmle.label | call to fgets |
| funcsLocal.c:31:13:31:17 | call to fgets | semmle.label | call to fgets |
| funcsLocal.c:31:19:31:21 | fgets output argument | semmle.label | fgets output argument |
| funcsLocal.c:31:19:31:21 | i41 | semmle.label | i41 |
| funcsLocal.c:32:9:32:10 | (const char *)... | semmle.label | (const char *)... |
| funcsLocal.c:32:9:32:10 | (const char *)... | semmle.label | (const char *)... |
-| funcsLocal.c:32:9:32:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| funcsLocal.c:32:9:32:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| funcsLocal.c:32:9:32:10 | i4 | semmle.label | i4 |
| funcsLocal.c:32:9:32:10 | i4 | semmle.label | i4 |
| funcsLocal.c:32:9:32:10 | i4 | semmle.label | i4 |
+| funcsLocal.c:32:9:32:10 | i4 indirection | semmle.label | i4 indirection |
+| funcsLocal.c:32:9:32:10 | i4 indirection | semmle.label | i4 indirection |
| funcsLocal.c:36:7:36:8 | gets output argument | semmle.label | gets output argument |
| funcsLocal.c:36:7:36:8 | i5 | semmle.label | i5 |
| funcsLocal.c:37:9:37:10 | (const char *)... | semmle.label | (const char *)... |
| funcsLocal.c:37:9:37:10 | (const char *)... | semmle.label | (const char *)... |
-| funcsLocal.c:37:9:37:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| funcsLocal.c:37:9:37:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| funcsLocal.c:37:9:37:10 | i5 | semmle.label | i5 |
+| funcsLocal.c:37:9:37:10 | i5 indirection | semmle.label | i5 indirection |
+| funcsLocal.c:37:9:37:10 | i5 indirection | semmle.label | i5 indirection |
| funcsLocal.c:41:13:41:16 | call to gets | semmle.label | call to gets |
| funcsLocal.c:41:13:41:16 | call to gets | semmle.label | call to gets |
| funcsLocal.c:41:18:41:20 | gets output argument | semmle.label | gets output argument |
| funcsLocal.c:41:18:41:20 | i61 | semmle.label | i61 |
| funcsLocal.c:42:9:42:10 | (const char *)... | semmle.label | (const char *)... |
| funcsLocal.c:42:9:42:10 | (const char *)... | semmle.label | (const char *)... |
-| funcsLocal.c:42:9:42:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| funcsLocal.c:42:9:42:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| funcsLocal.c:42:9:42:10 | i6 | semmle.label | i6 |
| funcsLocal.c:42:9:42:10 | i6 | semmle.label | i6 |
| funcsLocal.c:42:9:42:10 | i6 | semmle.label | i6 |
+| funcsLocal.c:42:9:42:10 | i6 indirection | semmle.label | i6 indirection |
+| funcsLocal.c:42:9:42:10 | i6 indirection | semmle.label | i6 indirection |
| funcsLocal.c:58:9:58:10 | (const char *)... | semmle.label | (const char *)... |
| funcsLocal.c:58:9:58:10 | (const char *)... | semmle.label | (const char *)... |
-| funcsLocal.c:58:9:58:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| funcsLocal.c:58:9:58:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| funcsLocal.c:58:9:58:10 | e1 | semmle.label | e1 |
+| funcsLocal.c:58:9:58:10 | e1 indirection | semmle.label | e1 indirection |
+| funcsLocal.c:58:9:58:10 | e1 indirection | semmle.label | e1 indirection |
#select
| funcsLocal.c:17:9:17:10 | i1 | funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:17:9:17:10 | i1 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:16:8:16:9 | i1 | fread |
| funcsLocal.c:27:9:27:10 | i3 | funcsLocal.c:26:8:26:9 | i3 | funcsLocal.c:27:9:27:10 | i3 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:26:8:26:9 | i3 | fgets |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected
index 9c2ac36476e..f095153f39c 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected
@@ -28,24 +28,24 @@ edges
| globalVars.c:24:11:24:14 | argv | globalVars.c:24:11:24:14 | argv indirection |
| globalVars.c:24:11:24:14 | argv indirection | globalVars.c:11:22:11:25 | *argv |
| globalVars.c:27:9:27:12 | copy | globalVars.c:27:9:27:12 | (const char *)... |
-| globalVars.c:27:9:27:12 | copy | globalVars.c:27:9:27:12 | Argument 0 indirection |
| globalVars.c:27:9:27:12 | copy | globalVars.c:27:9:27:12 | copy |
-| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | Argument 0 indirection |
+| globalVars.c:27:9:27:12 | copy | globalVars.c:27:9:27:12 | copy indirection |
| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy |
| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy |
| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy |
+| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy indirection |
| globalVars.c:35:2:35:9 | copy | globalVars.c:15:21:15:23 | val |
| globalVars.c:35:11:35:14 | copy | globalVars.c:35:2:35:9 | copy |
| globalVars.c:38:9:38:13 | copy2 | globalVars.c:38:9:38:13 | (const char *)... |
-| globalVars.c:38:9:38:13 | copy2 | globalVars.c:38:9:38:13 | Argument 0 indirection |
| globalVars.c:38:9:38:13 | copy2 | globalVars.c:38:9:38:13 | copy2 |
-| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | Argument 0 indirection |
+| globalVars.c:38:9:38:13 | copy2 | globalVars.c:38:9:38:13 | copy2 indirection |
| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 |
| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 |
| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 indirection |
| globalVars.c:50:9:50:13 | copy2 | globalVars.c:50:9:50:13 | (const char *)... |
-| globalVars.c:50:9:50:13 | copy2 | globalVars.c:50:9:50:13 | Argument 0 indirection |
| globalVars.c:50:9:50:13 | copy2 | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:50:9:50:13 | copy2 | globalVars.c:50:9:50:13 | copy2 indirection |
nodes
| globalVars.c:8:7:8:10 | copy | semmle.label | copy |
| globalVars.c:9:7:9:11 | copy2 | semmle.label | copy2 |
@@ -60,37 +60,37 @@ nodes
| globalVars.c:24:11:24:14 | argv indirection | semmle.label | argv indirection |
| globalVars.c:27:9:27:12 | (const char *)... | semmle.label | (const char *)... |
| globalVars.c:27:9:27:12 | (const char *)... | semmle.label | (const char *)... |
-| globalVars.c:27:9:27:12 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| globalVars.c:27:9:27:12 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| globalVars.c:27:9:27:12 | copy | semmle.label | copy |
| globalVars.c:27:9:27:12 | copy | semmle.label | copy |
| globalVars.c:27:9:27:12 | copy | semmle.label | copy |
-| globalVars.c:30:15:30:18 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| globalVars.c:30:15:30:18 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| globalVars.c:27:9:27:12 | copy indirection | semmle.label | copy indirection |
+| globalVars.c:27:9:27:12 | copy indirection | semmle.label | copy indirection |
| globalVars.c:30:15:30:18 | copy | semmle.label | copy |
| globalVars.c:30:15:30:18 | copy | semmle.label | copy |
| globalVars.c:30:15:30:18 | copy | semmle.label | copy |
+| globalVars.c:30:15:30:18 | copy indirection | semmle.label | copy indirection |
+| globalVars.c:30:15:30:18 | copy indirection | semmle.label | copy indirection |
| globalVars.c:35:2:35:9 | copy | semmle.label | copy |
| globalVars.c:35:11:35:14 | copy | semmle.label | copy |
| globalVars.c:38:9:38:13 | (const char *)... | semmle.label | (const char *)... |
| globalVars.c:38:9:38:13 | (const char *)... | semmle.label | (const char *)... |
-| globalVars.c:38:9:38:13 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| globalVars.c:38:9:38:13 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| globalVars.c:38:9:38:13 | copy2 | semmle.label | copy2 |
| globalVars.c:38:9:38:13 | copy2 | semmle.label | copy2 |
| globalVars.c:38:9:38:13 | copy2 | semmle.label | copy2 |
-| globalVars.c:41:15:41:19 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| globalVars.c:41:15:41:19 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| globalVars.c:38:9:38:13 | copy2 indirection | semmle.label | copy2 indirection |
+| globalVars.c:38:9:38:13 | copy2 indirection | semmle.label | copy2 indirection |
| globalVars.c:41:15:41:19 | copy2 | semmle.label | copy2 |
| globalVars.c:41:15:41:19 | copy2 | semmle.label | copy2 |
| globalVars.c:41:15:41:19 | copy2 | semmle.label | copy2 |
+| globalVars.c:41:15:41:19 | copy2 indirection | semmle.label | copy2 indirection |
+| globalVars.c:41:15:41:19 | copy2 indirection | semmle.label | copy2 indirection |
| globalVars.c:50:9:50:13 | (const char *)... | semmle.label | (const char *)... |
| globalVars.c:50:9:50:13 | (const char *)... | semmle.label | (const char *)... |
-| globalVars.c:50:9:50:13 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| globalVars.c:50:9:50:13 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| globalVars.c:50:9:50:13 | copy2 | semmle.label | copy2 |
| globalVars.c:50:9:50:13 | copy2 | semmle.label | copy2 |
| globalVars.c:50:9:50:13 | copy2 | semmle.label | copy2 |
+| globalVars.c:50:9:50:13 | copy2 indirection | semmle.label | copy2 indirection |
+| globalVars.c:50:9:50:13 | copy2 indirection | semmle.label | copy2 indirection |
#select
| globalVars.c:27:9:27:12 | copy | globalVars.c:24:11:24:14 | argv | globalVars.c:27:9:27:12 | copy | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | globalVars.c:24:11:24:14 | argv | argv |
| globalVars.c:30:15:30:18 | copy | globalVars.c:24:11:24:14 | argv | globalVars.c:30:15:30:18 | copy | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(str), which calls printf(format) | globalVars.c:24:11:24:14 | argv | argv |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.expected
index e769c072f84..50ae940400a 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.expected
@@ -1,192 +1,192 @@
edges
| ifs.c:61:8:61:11 | argv | ifs.c:62:9:62:10 | (const char *)... |
| ifs.c:61:8:61:11 | argv | ifs.c:62:9:62:10 | (const char *)... |
-| ifs.c:61:8:61:11 | argv | ifs.c:62:9:62:10 | Argument 0 indirection |
-| ifs.c:61:8:61:11 | argv | ifs.c:62:9:62:10 | Argument 0 indirection |
| ifs.c:61:8:61:11 | argv | ifs.c:62:9:62:10 | c7 |
| ifs.c:61:8:61:11 | argv | ifs.c:62:9:62:10 | c7 |
| ifs.c:61:8:61:11 | argv | ifs.c:62:9:62:10 | c7 |
| ifs.c:61:8:61:11 | argv | ifs.c:62:9:62:10 | c7 |
+| ifs.c:61:8:61:11 | argv | ifs.c:62:9:62:10 | c7 indirection |
+| ifs.c:61:8:61:11 | argv | ifs.c:62:9:62:10 | c7 indirection |
| ifs.c:68:8:68:11 | argv | ifs.c:69:9:69:10 | (const char *)... |
| ifs.c:68:8:68:11 | argv | ifs.c:69:9:69:10 | (const char *)... |
-| ifs.c:68:8:68:11 | argv | ifs.c:69:9:69:10 | Argument 0 indirection |
-| ifs.c:68:8:68:11 | argv | ifs.c:69:9:69:10 | Argument 0 indirection |
| ifs.c:68:8:68:11 | argv | ifs.c:69:9:69:10 | c8 |
| ifs.c:68:8:68:11 | argv | ifs.c:69:9:69:10 | c8 |
| ifs.c:68:8:68:11 | argv | ifs.c:69:9:69:10 | c8 |
| ifs.c:68:8:68:11 | argv | ifs.c:69:9:69:10 | c8 |
+| ifs.c:68:8:68:11 | argv | ifs.c:69:9:69:10 | c8 indirection |
+| ifs.c:68:8:68:11 | argv | ifs.c:69:9:69:10 | c8 indirection |
| ifs.c:74:8:74:11 | argv | ifs.c:75:9:75:10 | (const char *)... |
| ifs.c:74:8:74:11 | argv | ifs.c:75:9:75:10 | (const char *)... |
-| ifs.c:74:8:74:11 | argv | ifs.c:75:9:75:10 | Argument 0 indirection |
-| ifs.c:74:8:74:11 | argv | ifs.c:75:9:75:10 | Argument 0 indirection |
| ifs.c:74:8:74:11 | argv | ifs.c:75:9:75:10 | i1 |
| ifs.c:74:8:74:11 | argv | ifs.c:75:9:75:10 | i1 |
| ifs.c:74:8:74:11 | argv | ifs.c:75:9:75:10 | i1 |
| ifs.c:74:8:74:11 | argv | ifs.c:75:9:75:10 | i1 |
+| ifs.c:74:8:74:11 | argv | ifs.c:75:9:75:10 | i1 indirection |
+| ifs.c:74:8:74:11 | argv | ifs.c:75:9:75:10 | i1 indirection |
| ifs.c:80:8:80:11 | argv | ifs.c:81:9:81:10 | (const char *)... |
| ifs.c:80:8:80:11 | argv | ifs.c:81:9:81:10 | (const char *)... |
-| ifs.c:80:8:80:11 | argv | ifs.c:81:9:81:10 | Argument 0 indirection |
-| ifs.c:80:8:80:11 | argv | ifs.c:81:9:81:10 | Argument 0 indirection |
| ifs.c:80:8:80:11 | argv | ifs.c:81:9:81:10 | i2 |
| ifs.c:80:8:80:11 | argv | ifs.c:81:9:81:10 | i2 |
| ifs.c:80:8:80:11 | argv | ifs.c:81:9:81:10 | i2 |
| ifs.c:80:8:80:11 | argv | ifs.c:81:9:81:10 | i2 |
+| ifs.c:80:8:80:11 | argv | ifs.c:81:9:81:10 | i2 indirection |
+| ifs.c:80:8:80:11 | argv | ifs.c:81:9:81:10 | i2 indirection |
| ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | (const char *)... |
| ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | (const char *)... |
-| ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | Argument 0 indirection |
-| ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | Argument 0 indirection |
| ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | i3 |
| ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | i3 |
| ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | i3 |
| ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | i3 |
+| ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | i3 indirection |
+| ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | i3 indirection |
| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | (const char *)... |
| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | (const char *)... |
-| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | Argument 0 indirection |
-| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | Argument 0 indirection |
| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 |
| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 |
| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 |
| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 |
+| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 indirection |
+| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 indirection |
| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | (const char *)... |
| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | (const char *)... |
-| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | Argument 0 indirection |
-| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | Argument 0 indirection |
| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 |
| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 |
| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 |
| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 |
+| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 indirection |
+| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 indirection |
| ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | (const char *)... |
| ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | (const char *)... |
-| ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | Argument 0 indirection |
-| ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | Argument 0 indirection |
| ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | i6 |
| ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | i6 |
| ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | i6 |
| ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | i6 |
+| ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | i6 indirection |
+| ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | i6 indirection |
| ifs.c:111:8:111:11 | argv | ifs.c:112:9:112:10 | (const char *)... |
| ifs.c:111:8:111:11 | argv | ifs.c:112:9:112:10 | (const char *)... |
-| ifs.c:111:8:111:11 | argv | ifs.c:112:9:112:10 | Argument 0 indirection |
-| ifs.c:111:8:111:11 | argv | ifs.c:112:9:112:10 | Argument 0 indirection |
| ifs.c:111:8:111:11 | argv | ifs.c:112:9:112:10 | i7 |
| ifs.c:111:8:111:11 | argv | ifs.c:112:9:112:10 | i7 |
| ifs.c:111:8:111:11 | argv | ifs.c:112:9:112:10 | i7 |
| ifs.c:111:8:111:11 | argv | ifs.c:112:9:112:10 | i7 |
+| ifs.c:111:8:111:11 | argv | ifs.c:112:9:112:10 | i7 indirection |
+| ifs.c:111:8:111:11 | argv | ifs.c:112:9:112:10 | i7 indirection |
| ifs.c:117:8:117:11 | argv | ifs.c:118:9:118:10 | (const char *)... |
| ifs.c:117:8:117:11 | argv | ifs.c:118:9:118:10 | (const char *)... |
-| ifs.c:117:8:117:11 | argv | ifs.c:118:9:118:10 | Argument 0 indirection |
-| ifs.c:117:8:117:11 | argv | ifs.c:118:9:118:10 | Argument 0 indirection |
| ifs.c:117:8:117:11 | argv | ifs.c:118:9:118:10 | i8 |
| ifs.c:117:8:117:11 | argv | ifs.c:118:9:118:10 | i8 |
| ifs.c:117:8:117:11 | argv | ifs.c:118:9:118:10 | i8 |
| ifs.c:117:8:117:11 | argv | ifs.c:118:9:118:10 | i8 |
+| ifs.c:117:8:117:11 | argv | ifs.c:118:9:118:10 | i8 indirection |
+| ifs.c:117:8:117:11 | argv | ifs.c:118:9:118:10 | i8 indirection |
| ifs.c:123:8:123:11 | argv | ifs.c:124:9:124:10 | (const char *)... |
| ifs.c:123:8:123:11 | argv | ifs.c:124:9:124:10 | (const char *)... |
-| ifs.c:123:8:123:11 | argv | ifs.c:124:9:124:10 | Argument 0 indirection |
-| ifs.c:123:8:123:11 | argv | ifs.c:124:9:124:10 | Argument 0 indirection |
| ifs.c:123:8:123:11 | argv | ifs.c:124:9:124:10 | i9 |
| ifs.c:123:8:123:11 | argv | ifs.c:124:9:124:10 | i9 |
| ifs.c:123:8:123:11 | argv | ifs.c:124:9:124:10 | i9 |
| ifs.c:123:8:123:11 | argv | ifs.c:124:9:124:10 | i9 |
+| ifs.c:123:8:123:11 | argv | ifs.c:124:9:124:10 | i9 indirection |
+| ifs.c:123:8:123:11 | argv | ifs.c:124:9:124:10 | i9 indirection |
nodes
| ifs.c:61:8:61:11 | argv | semmle.label | argv |
| ifs.c:61:8:61:11 | argv | semmle.label | argv |
| ifs.c:62:9:62:10 | (const char *)... | semmle.label | (const char *)... |
| ifs.c:62:9:62:10 | (const char *)... | semmle.label | (const char *)... |
-| ifs.c:62:9:62:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| ifs.c:62:9:62:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| ifs.c:62:9:62:10 | c7 | semmle.label | c7 |
| ifs.c:62:9:62:10 | c7 | semmle.label | c7 |
| ifs.c:62:9:62:10 | c7 | semmle.label | c7 |
+| ifs.c:62:9:62:10 | c7 indirection | semmle.label | c7 indirection |
+| ifs.c:62:9:62:10 | c7 indirection | semmle.label | c7 indirection |
| ifs.c:68:8:68:11 | argv | semmle.label | argv |
| ifs.c:68:8:68:11 | argv | semmle.label | argv |
| ifs.c:69:9:69:10 | (const char *)... | semmle.label | (const char *)... |
| ifs.c:69:9:69:10 | (const char *)... | semmle.label | (const char *)... |
-| ifs.c:69:9:69:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| ifs.c:69:9:69:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| ifs.c:69:9:69:10 | c8 | semmle.label | c8 |
| ifs.c:69:9:69:10 | c8 | semmle.label | c8 |
| ifs.c:69:9:69:10 | c8 | semmle.label | c8 |
+| ifs.c:69:9:69:10 | c8 indirection | semmle.label | c8 indirection |
+| ifs.c:69:9:69:10 | c8 indirection | semmle.label | c8 indirection |
| ifs.c:74:8:74:11 | argv | semmle.label | argv |
| ifs.c:74:8:74:11 | argv | semmle.label | argv |
| ifs.c:75:9:75:10 | (const char *)... | semmle.label | (const char *)... |
| ifs.c:75:9:75:10 | (const char *)... | semmle.label | (const char *)... |
-| ifs.c:75:9:75:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| ifs.c:75:9:75:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| ifs.c:75:9:75:10 | i1 | semmle.label | i1 |
| ifs.c:75:9:75:10 | i1 | semmle.label | i1 |
| ifs.c:75:9:75:10 | i1 | semmle.label | i1 |
+| ifs.c:75:9:75:10 | i1 indirection | semmle.label | i1 indirection |
+| ifs.c:75:9:75:10 | i1 indirection | semmle.label | i1 indirection |
| ifs.c:80:8:80:11 | argv | semmle.label | argv |
| ifs.c:80:8:80:11 | argv | semmle.label | argv |
| ifs.c:81:9:81:10 | (const char *)... | semmle.label | (const char *)... |
| ifs.c:81:9:81:10 | (const char *)... | semmle.label | (const char *)... |
-| ifs.c:81:9:81:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| ifs.c:81:9:81:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| ifs.c:81:9:81:10 | i2 | semmle.label | i2 |
| ifs.c:81:9:81:10 | i2 | semmle.label | i2 |
| ifs.c:81:9:81:10 | i2 | semmle.label | i2 |
+| ifs.c:81:9:81:10 | i2 indirection | semmle.label | i2 indirection |
+| ifs.c:81:9:81:10 | i2 indirection | semmle.label | i2 indirection |
| ifs.c:86:8:86:11 | argv | semmle.label | argv |
| ifs.c:86:8:86:11 | argv | semmle.label | argv |
| ifs.c:87:9:87:10 | (const char *)... | semmle.label | (const char *)... |
| ifs.c:87:9:87:10 | (const char *)... | semmle.label | (const char *)... |
-| ifs.c:87:9:87:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| ifs.c:87:9:87:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| ifs.c:87:9:87:10 | i3 | semmle.label | i3 |
| ifs.c:87:9:87:10 | i3 | semmle.label | i3 |
| ifs.c:87:9:87:10 | i3 | semmle.label | i3 |
+| ifs.c:87:9:87:10 | i3 indirection | semmle.label | i3 indirection |
+| ifs.c:87:9:87:10 | i3 indirection | semmle.label | i3 indirection |
| ifs.c:92:8:92:11 | argv | semmle.label | argv |
| ifs.c:92:8:92:11 | argv | semmle.label | argv |
| ifs.c:93:9:93:10 | (const char *)... | semmle.label | (const char *)... |
| ifs.c:93:9:93:10 | (const char *)... | semmle.label | (const char *)... |
-| ifs.c:93:9:93:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| ifs.c:93:9:93:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| ifs.c:93:9:93:10 | i4 | semmle.label | i4 |
| ifs.c:93:9:93:10 | i4 | semmle.label | i4 |
| ifs.c:93:9:93:10 | i4 | semmle.label | i4 |
+| ifs.c:93:9:93:10 | i4 indirection | semmle.label | i4 indirection |
+| ifs.c:93:9:93:10 | i4 indirection | semmle.label | i4 indirection |
| ifs.c:98:8:98:11 | argv | semmle.label | argv |
| ifs.c:98:8:98:11 | argv | semmle.label | argv |
| ifs.c:99:9:99:10 | (const char *)... | semmle.label | (const char *)... |
| ifs.c:99:9:99:10 | (const char *)... | semmle.label | (const char *)... |
-| ifs.c:99:9:99:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| ifs.c:99:9:99:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| ifs.c:99:9:99:10 | i5 | semmle.label | i5 |
| ifs.c:99:9:99:10 | i5 | semmle.label | i5 |
| ifs.c:99:9:99:10 | i5 | semmle.label | i5 |
+| ifs.c:99:9:99:10 | i5 indirection | semmle.label | i5 indirection |
+| ifs.c:99:9:99:10 | i5 indirection | semmle.label | i5 indirection |
| ifs.c:105:8:105:11 | argv | semmle.label | argv |
| ifs.c:105:8:105:11 | argv | semmle.label | argv |
| ifs.c:106:9:106:10 | (const char *)... | semmle.label | (const char *)... |
| ifs.c:106:9:106:10 | (const char *)... | semmle.label | (const char *)... |
-| ifs.c:106:9:106:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| ifs.c:106:9:106:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| ifs.c:106:9:106:10 | i6 | semmle.label | i6 |
| ifs.c:106:9:106:10 | i6 | semmle.label | i6 |
| ifs.c:106:9:106:10 | i6 | semmle.label | i6 |
+| ifs.c:106:9:106:10 | i6 indirection | semmle.label | i6 indirection |
+| ifs.c:106:9:106:10 | i6 indirection | semmle.label | i6 indirection |
| ifs.c:111:8:111:11 | argv | semmle.label | argv |
| ifs.c:111:8:111:11 | argv | semmle.label | argv |
| ifs.c:112:9:112:10 | (const char *)... | semmle.label | (const char *)... |
| ifs.c:112:9:112:10 | (const char *)... | semmle.label | (const char *)... |
-| ifs.c:112:9:112:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| ifs.c:112:9:112:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| ifs.c:112:9:112:10 | i7 | semmle.label | i7 |
| ifs.c:112:9:112:10 | i7 | semmle.label | i7 |
| ifs.c:112:9:112:10 | i7 | semmle.label | i7 |
+| ifs.c:112:9:112:10 | i7 indirection | semmle.label | i7 indirection |
+| ifs.c:112:9:112:10 | i7 indirection | semmle.label | i7 indirection |
| ifs.c:117:8:117:11 | argv | semmle.label | argv |
| ifs.c:117:8:117:11 | argv | semmle.label | argv |
| ifs.c:118:9:118:10 | (const char *)... | semmle.label | (const char *)... |
| ifs.c:118:9:118:10 | (const char *)... | semmle.label | (const char *)... |
-| ifs.c:118:9:118:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| ifs.c:118:9:118:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| ifs.c:118:9:118:10 | i8 | semmle.label | i8 |
| ifs.c:118:9:118:10 | i8 | semmle.label | i8 |
| ifs.c:118:9:118:10 | i8 | semmle.label | i8 |
+| ifs.c:118:9:118:10 | i8 indirection | semmle.label | i8 indirection |
+| ifs.c:118:9:118:10 | i8 indirection | semmle.label | i8 indirection |
| ifs.c:123:8:123:11 | argv | semmle.label | argv |
| ifs.c:123:8:123:11 | argv | semmle.label | argv |
| ifs.c:124:9:124:10 | (const char *)... | semmle.label | (const char *)... |
| ifs.c:124:9:124:10 | (const char *)... | semmle.label | (const char *)... |
-| ifs.c:124:9:124:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| ifs.c:124:9:124:10 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| ifs.c:124:9:124:10 | i9 | semmle.label | i9 |
| ifs.c:124:9:124:10 | i9 | semmle.label | i9 |
| ifs.c:124:9:124:10 | i9 | semmle.label | i9 |
+| ifs.c:124:9:124:10 | i9 indirection | semmle.label | i9 indirection |
+| ifs.c:124:9:124:10 | i9 indirection | semmle.label | i9 indirection |
#select
| ifs.c:62:9:62:10 | c7 | ifs.c:61:8:61:11 | argv | ifs.c:62:9:62:10 | c7 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:61:8:61:11 | argv | argv |
| ifs.c:69:9:69:10 | c8 | ifs.c:68:8:68:11 | argv | ifs.c:69:9:69:10 | c8 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:68:8:68:11 | argv | argv |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected
index 704c511a6fa..25ff3162973 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected
@@ -31,20 +31,20 @@ edges
| test.cpp:75:25:75:29 | start | test.cpp:79:18:79:28 | ... - ... |
| test.cpp:75:38:75:40 | end | test.cpp:79:18:79:28 | ... - ... |
| test.cpp:75:38:75:40 | end | test.cpp:79:18:79:28 | ... - ... |
-| test.cpp:97:18:97:23 | buffer | test.cpp:100:4:100:15 | Argument 0 |
-| test.cpp:97:18:97:23 | buffer | test.cpp:100:17:100:22 | Argument 0 indirection |
-| test.cpp:97:18:97:23 | buffer | test.cpp:101:4:101:15 | Argument 0 |
-| test.cpp:97:18:97:23 | buffer | test.cpp:101:4:101:15 | Argument 1 |
-| test.cpp:97:18:97:23 | fread output argument | test.cpp:100:4:100:15 | Argument 0 |
-| test.cpp:97:18:97:23 | fread output argument | test.cpp:100:17:100:22 | Argument 0 indirection |
-| test.cpp:97:18:97:23 | fread output argument | test.cpp:101:4:101:15 | Argument 0 |
-| test.cpp:97:18:97:23 | fread output argument | test.cpp:101:4:101:15 | Argument 1 |
-| test.cpp:100:4:100:15 | Argument 0 | test.cpp:100:17:100:22 | processData1 output argument |
-| test.cpp:100:17:100:22 | Argument 0 indirection | test.cpp:100:17:100:22 | processData1 output argument |
-| test.cpp:100:17:100:22 | processData1 output argument | test.cpp:101:4:101:15 | Argument 0 |
-| test.cpp:100:17:100:22 | processData1 output argument | test.cpp:101:4:101:15 | Argument 1 |
-| test.cpp:101:4:101:15 | Argument 0 | test.cpp:75:25:75:29 | start |
-| test.cpp:101:4:101:15 | Argument 1 | test.cpp:75:38:75:40 | end |
+| test.cpp:97:18:97:23 | buffer | test.cpp:100:4:100:15 | buffer |
+| test.cpp:97:18:97:23 | buffer | test.cpp:100:17:100:22 | buffer indirection |
+| test.cpp:97:18:97:23 | buffer | test.cpp:101:4:101:15 | ... + ... |
+| test.cpp:97:18:97:23 | buffer | test.cpp:101:4:101:15 | buffer |
+| test.cpp:97:18:97:23 | fread output argument | test.cpp:100:4:100:15 | buffer |
+| test.cpp:97:18:97:23 | fread output argument | test.cpp:100:17:100:22 | buffer indirection |
+| test.cpp:97:18:97:23 | fread output argument | test.cpp:101:4:101:15 | ... + ... |
+| test.cpp:97:18:97:23 | fread output argument | test.cpp:101:4:101:15 | buffer |
+| test.cpp:100:4:100:15 | buffer | test.cpp:100:17:100:22 | processData1 output argument |
+| test.cpp:100:17:100:22 | buffer indirection | test.cpp:100:17:100:22 | processData1 output argument |
+| test.cpp:100:17:100:22 | processData1 output argument | test.cpp:101:4:101:15 | ... + ... |
+| test.cpp:100:17:100:22 | processData1 output argument | test.cpp:101:4:101:15 | buffer |
+| test.cpp:101:4:101:15 | ... + ... | test.cpp:75:38:75:40 | end |
+| test.cpp:101:4:101:15 | buffer | test.cpp:75:25:75:29 | start |
| test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... |
| test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... |
| test.cpp:123:18:123:31 | (const char *)... | test.cpp:127:24:127:41 | ... * ... |
@@ -134,11 +134,11 @@ nodes
| test.cpp:79:18:79:28 | ... - ... | semmle.label | ... - ... |
| test.cpp:97:18:97:23 | buffer | semmle.label | buffer |
| test.cpp:97:18:97:23 | fread output argument | semmle.label | fread output argument |
-| test.cpp:100:4:100:15 | Argument 0 | semmle.label | Argument 0 |
-| test.cpp:100:17:100:22 | Argument 0 indirection | semmle.label | Argument 0 indirection |
+| test.cpp:100:4:100:15 | buffer | semmle.label | buffer |
+| test.cpp:100:17:100:22 | buffer indirection | semmle.label | buffer indirection |
| test.cpp:100:17:100:22 | processData1 output argument | semmle.label | processData1 output argument |
-| test.cpp:101:4:101:15 | Argument 0 | semmle.label | Argument 0 |
-| test.cpp:101:4:101:15 | Argument 1 | semmle.label | Argument 1 |
+| test.cpp:101:4:101:15 | ... + ... | semmle.label | ... + ... |
+| test.cpp:101:4:101:15 | buffer | semmle.label | buffer |
| test.cpp:123:18:123:23 | call to getenv | semmle.label | call to getenv |
| test.cpp:123:18:123:31 | (const char *)... | semmle.label | (const char *)... |
| test.cpp:127:24:127:41 | ... * ... | semmle.label | ... * ... |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-290/semmle/AuthenticationBypass/AuthenticationBypass.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-290/semmle/AuthenticationBypass/AuthenticationBypass.expected
index dfbb01bf264..1ad31c3f9f7 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-290/semmle/AuthenticationBypass/AuthenticationBypass.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-290/semmle/AuthenticationBypass/AuthenticationBypass.expected
@@ -1,44 +1,44 @@
edges
-| test.cpp:16:25:16:30 | call to getenv | test.cpp:20:14:20:20 | Argument 0 indirection |
| test.cpp:16:25:16:30 | call to getenv | test.cpp:20:14:20:20 | address |
| test.cpp:16:25:16:30 | call to getenv | test.cpp:20:14:20:20 | address |
-| test.cpp:16:25:16:42 | (const char *)... | test.cpp:20:14:20:20 | Argument 0 indirection |
+| test.cpp:16:25:16:30 | call to getenv | test.cpp:20:14:20:20 | address indirection |
| test.cpp:16:25:16:42 | (const char *)... | test.cpp:20:14:20:20 | address |
| test.cpp:16:25:16:42 | (const char *)... | test.cpp:20:14:20:20 | address |
-| test.cpp:27:25:27:30 | call to getenv | test.cpp:31:14:31:20 | Argument 0 indirection |
+| test.cpp:16:25:16:42 | (const char *)... | test.cpp:20:14:20:20 | address indirection |
| test.cpp:27:25:27:30 | call to getenv | test.cpp:31:14:31:20 | address |
| test.cpp:27:25:27:30 | call to getenv | test.cpp:31:14:31:20 | address |
-| test.cpp:27:25:27:42 | (const char *)... | test.cpp:31:14:31:20 | Argument 0 indirection |
+| test.cpp:27:25:27:30 | call to getenv | test.cpp:31:14:31:20 | address indirection |
| test.cpp:27:25:27:42 | (const char *)... | test.cpp:31:14:31:20 | address |
| test.cpp:27:25:27:42 | (const char *)... | test.cpp:31:14:31:20 | address |
-| test.cpp:38:25:38:30 | call to getenv | test.cpp:42:14:42:20 | Argument 0 indirection |
+| test.cpp:27:25:27:42 | (const char *)... | test.cpp:31:14:31:20 | address indirection |
| test.cpp:38:25:38:30 | call to getenv | test.cpp:42:14:42:20 | address |
| test.cpp:38:25:38:30 | call to getenv | test.cpp:42:14:42:20 | address |
-| test.cpp:38:25:38:42 | (const char *)... | test.cpp:42:14:42:20 | Argument 0 indirection |
+| test.cpp:38:25:38:30 | call to getenv | test.cpp:42:14:42:20 | address indirection |
| test.cpp:38:25:38:42 | (const char *)... | test.cpp:42:14:42:20 | address |
| test.cpp:38:25:38:42 | (const char *)... | test.cpp:42:14:42:20 | address |
+| test.cpp:38:25:38:42 | (const char *)... | test.cpp:42:14:42:20 | address indirection |
nodes
| test.cpp:16:25:16:30 | call to getenv | semmle.label | call to getenv |
| test.cpp:16:25:16:42 | (const char *)... | semmle.label | (const char *)... |
-| test.cpp:20:14:20:20 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| test.cpp:20:14:20:20 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| test.cpp:20:14:20:20 | address | semmle.label | address |
| test.cpp:20:14:20:20 | address | semmle.label | address |
| test.cpp:20:14:20:20 | address | semmle.label | address |
+| test.cpp:20:14:20:20 | address indirection | semmle.label | address indirection |
+| test.cpp:20:14:20:20 | address indirection | semmle.label | address indirection |
| test.cpp:27:25:27:30 | call to getenv | semmle.label | call to getenv |
| test.cpp:27:25:27:42 | (const char *)... | semmle.label | (const char *)... |
-| test.cpp:31:14:31:20 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| test.cpp:31:14:31:20 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| test.cpp:31:14:31:20 | address | semmle.label | address |
| test.cpp:31:14:31:20 | address | semmle.label | address |
| test.cpp:31:14:31:20 | address | semmle.label | address |
+| test.cpp:31:14:31:20 | address indirection | semmle.label | address indirection |
+| test.cpp:31:14:31:20 | address indirection | semmle.label | address indirection |
| test.cpp:38:25:38:30 | call to getenv | semmle.label | call to getenv |
| test.cpp:38:25:38:42 | (const char *)... | semmle.label | (const char *)... |
-| test.cpp:42:14:42:20 | Argument 0 indirection | semmle.label | Argument 0 indirection |
-| test.cpp:42:14:42:20 | Argument 0 indirection | semmle.label | Argument 0 indirection |
| test.cpp:42:14:42:20 | address | semmle.label | address |
| test.cpp:42:14:42:20 | address | semmle.label | address |
| test.cpp:42:14:42:20 | address | semmle.label | address |
+| test.cpp:42:14:42:20 | address indirection | semmle.label | address indirection |
+| test.cpp:42:14:42:20 | address indirection | semmle.label | address indirection |
#select
| test.cpp:20:7:20:12 | call to strcmp | test.cpp:16:25:16:30 | call to getenv | test.cpp:20:14:20:20 | address | Untrusted input $@ might be vulnerable to a spoofing attack. | test.cpp:16:25:16:30 | call to getenv | call to getenv |
| test.cpp:31:7:31:12 | call to strcmp | test.cpp:27:25:27:30 | call to getenv | test.cpp:31:14:31:20 | address | Untrusted input $@ might be vulnerable to a spoofing attack. | test.cpp:27:25:27:30 | call to getenv | call to getenv |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/CleartextBufferWrite.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/CleartextBufferWrite.expected
index 559e491417b..e4f9bc918a4 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/CleartextBufferWrite.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/CleartextBufferWrite.expected
@@ -1,17 +1,17 @@
edges
-| test.cpp:54:17:54:20 | argv | test.cpp:58:25:58:29 | Argument 2 indirection |
-| test.cpp:54:17:54:20 | argv | test.cpp:58:25:58:29 | Argument 2 indirection |
| test.cpp:54:17:54:20 | argv | test.cpp:58:25:58:29 | input |
| test.cpp:54:17:54:20 | argv | test.cpp:58:25:58:29 | input |
| test.cpp:54:17:54:20 | argv | test.cpp:58:25:58:29 | input |
| test.cpp:54:17:54:20 | argv | test.cpp:58:25:58:29 | input |
+| test.cpp:54:17:54:20 | argv | test.cpp:58:25:58:29 | input indirection |
+| test.cpp:54:17:54:20 | argv | test.cpp:58:25:58:29 | input indirection |
nodes
| test.cpp:54:17:54:20 | argv | semmle.label | argv |
| test.cpp:54:17:54:20 | argv | semmle.label | argv |
-| test.cpp:58:25:58:29 | Argument 2 indirection | semmle.label | Argument 2 indirection |
-| test.cpp:58:25:58:29 | Argument 2 indirection | semmle.label | Argument 2 indirection |
| test.cpp:58:25:58:29 | input | semmle.label | input |
| test.cpp:58:25:58:29 | input | semmle.label | input |
| test.cpp:58:25:58:29 | input | semmle.label | input |
+| test.cpp:58:25:58:29 | input indirection | semmle.label | input indirection |
+| test.cpp:58:25:58:29 | input indirection | semmle.label | input indirection |
#select
| test.cpp:58:3:58:9 | call to sprintf | test.cpp:54:17:54:20 | argv | test.cpp:58:25:58:29 | input | This write into buffer 'passwd' may contain unencrypted data from $@ | test.cpp:54:17:54:20 | argv | user input (argv) |
From c8b1bc3a89b6f0c6b006449f38f4966899f0bc3d Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Thu, 11 Mar 2021 21:41:34 +0000
Subject: [PATCH 111/725] Enhance the query
---
.../CWE-016/InsecureSpringActuatorConfig.ql | 58 +++++++------------
.../CWE/CWE-016/application.properties | 4 +-
.../security/CWE-016/application.properties | 2 +-
3 files changed, 24 insertions(+), 40 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql b/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql
index 2dc11e8e38e..06ba0d8a288 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql
@@ -1,6 +1,7 @@
/**
* @name Insecure Spring Boot Actuator Configuration
- * @description Exposed Spring Boot Actuator through configuration files without declarative or procedural security enforcement leads to information leak or even remote code execution.
+ * @description Exposed Spring Boot Actuator through configuration files without declarative or procedural
+ * security enforcement leads to information leak or even remote code execution.
* @kind problem
* @id java/insecure-spring-actuator-config
* @tags security
@@ -9,7 +10,6 @@
import java
import semmle.code.configfiles.ConfigFiles
-import semmle.code.java.security.SensitiveActions
import semmle.code.xml.MavenPom
/** The parent node of the `org.springframework.boot` group. */
@@ -26,7 +26,10 @@ class SpringBootPom extends Pom {
this.getADependency().getArtifact().getValue() = "spring-boot-starter-actuator"
}
- /** Holds if the Spring Boot Security module is used in the project, which brings in other security related libraries. */
+ /**
+ * Holds if the Spring Boot Security module is used in the project, which brings in other security
+ * related libraries.
+ */
predicate isSpringBootSecurityUsed() {
this.getADependency().getArtifact().getValue() = "spring-boot-starter-security"
}
@@ -38,14 +41,14 @@ class ApplicationProperties extends ConfigPair {
}
/** The configuration property `management.security.enabled`. */
-class ManagementSecurityEnabled extends ApplicationProperties {
- ManagementSecurityEnabled() { this.getNameElement().getName() = "management.security.enabled" }
+class ManagementSecurityConfig extends ApplicationProperties {
+ ManagementSecurityConfig() { this.getNameElement().getName() = "management.security.enabled" }
- string getManagementSecurityEnabled() { result = this.getValueElement().getValue() }
+ string getValue() { result = this.getValueElement().getValue().trim() }
- predicate hasSecurityDisabled() { getManagementSecurityEnabled() = "false" }
+ predicate hasSecurityDisabled() { getValue() = "false" }
- predicate hasSecurityEnabled() { getManagementSecurityEnabled() = "true" }
+ predicate hasSecurityEnabled() { getValue() = "true" }
}
/** The configuration property `management.endpoints.web.exposure.include`. */
@@ -54,56 +57,37 @@ class ManagementEndPointInclude extends ApplicationProperties {
this.getNameElement().getName() = "management.endpoints.web.exposure.include"
}
- string getManagementEndPointInclude() { result = this.getValueElement().getValue().trim() }
-}
-
-/** The configuration property `management.endpoints.web.exposure.exclude`. */
-class ManagementEndPointExclude extends ApplicationProperties {
- ManagementEndPointExclude() {
- this.getNameElement().getName() = "management.endpoints.web.exposure.exclude"
- }
-
- string getManagementEndPointExclude() { result = this.getValueElement().getValue().trim() }
-}
-
-/** Holds if an application handles sensitive information judging by its variable names. */
-predicate isProtectedApp() {
- exists(VarAccess va | va.getVariable().getName().regexpMatch(getCommonSensitiveInfoRegex()))
+ string getValue() { result = this.getValueElement().getValue().trim() }
}
from SpringBootPom pom, ApplicationProperties ap, Dependency d
where
- isProtectedApp() and
pom.isSpringBootActuatorUsed() and
not pom.isSpringBootSecurityUsed() and
ap.getFile()
.getParentContainer()
.getAbsolutePath()
.matches(pom.getFile().getParentContainer().getAbsolutePath() + "%") and // in the same sub-directory
- exists(string s | s = pom.getParentElement().getVersionString() |
- s.regexpMatch("1\\.[0|1|2|3|4].*") and
- not exists(ManagementSecurityEnabled me |
+ exists(string springBootVersion | springBootVersion = pom.getParentElement().getVersionString() |
+ springBootVersion.regexpMatch("1\\.[0-4].*") and // version 1.0, 1.1, ..., 1.4
+ not exists(ManagementSecurityConfig me |
me.hasSecurityEnabled() and me.getFile() = ap.getFile()
)
or
- s.regexpMatch("1\\.5.*") and
- exists(ManagementSecurityEnabled me | me.hasSecurityDisabled() and me.getFile() = ap.getFile())
+ springBootVersion.matches("1.5%") and // version 1.5
+ exists(ManagementSecurityConfig me | me.hasSecurityDisabled() and me.getFile() = ap.getFile())
or
- s.regexpMatch("2.*") and
+ springBootVersion.matches("2.%") and //version 2.x
exists(ManagementEndPointInclude mi |
mi.getFile() = ap.getFile() and
(
- mi.getManagementEndPointInclude() = "*" // all endpoints are enabled
+ mi.getValue() = "*" // all endpoints are enabled
or
- mi.getManagementEndPointInclude()
+ mi.getValue()
.matches([
"%dump%", "%trace%", "%logfile%", "%shutdown%", "%startup%", "%mappings%", "%env%",
"%beans%", "%sessions%"
- ]) // all endpoints apart from '/health' and '/info' are considered sensitive
- ) and
- not exists(ManagementEndPointExclude mx |
- mx.getFile() = ap.getFile() and
- mx.getManagementEndPointExclude() = mi.getManagementEndPointInclude()
+ ]) // confidential endpoints to check although all endpoints apart from '/health' and '/info' are considered sensitive by Spring
)
)
) and
diff --git a/java/ql/src/experimental/Security/CWE/CWE-016/application.properties b/java/ql/src/experimental/Security/CWE/CWE-016/application.properties
index aa489435a12..4f5defdd948 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-016/application.properties
+++ b/java/ql/src/experimental/Security/CWE/CWE-016/application.properties
@@ -7,7 +7,7 @@
# vulnerable configuration (spring boot 1.5+): requires value false to expose sensitive actuators
management.security.enabled=false
-# vulnerable configuration (spring boot 2+): exposes health and info only by default
+# vulnerable configuration (spring boot 2+): exposes health and info only by default, here overridden to expose everything
management.endpoints.web.exposure.include=*
@@ -18,5 +18,5 @@ management.security.enabled=true
# safe configuration (spring boot 1.5+): requires value false to expose sensitive actuators
management.security.enabled=true
-# safe configuration (spring boot 2+): exposes health and info only by default
+# safe configuration (spring boot 2+): exposes health and info only by default, here overridden to expose one additional endpoint which we assume is intentional and safe.
management.endpoints.web.exposure.include=beans,info,health
diff --git a/java/ql/test/experimental/query-tests/security/CWE-016/application.properties b/java/ql/test/experimental/query-tests/security/CWE-016/application.properties
index 95e704f3a1a..797906a3ca3 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-016/application.properties
+++ b/java/ql/test/experimental/query-tests/security/CWE-016/application.properties
@@ -5,7 +5,7 @@
# vulnerable configuration (spring boot 1.5+): requires value false to expose sensitive actuators
management.security.enabled=false
-# vulnerable configuration (spring boot 2+): exposes health and info only by default
+# vulnerable configuration (spring boot 2+): exposes health and info only by default, here overridden to expose everything
management.endpoints.web.exposure.include=*
management.endpoints.web.exposure.exclude=beans
From fe3824c202b6689e6cd3ea58c9e5a8dae9d5ff75 Mon Sep 17 00:00:00 2001
From: Taus Brock-Nannestad
Date: Thu, 11 Mar 2021 21:22:33 +0100
Subject: [PATCH 112/725] Python: Document API graphs
---
.../using-api-graphs-in-python.rst | 164 ++++++++++++++++++
1 file changed, 164 insertions(+)
create mode 100644 docs/codeql/codeql-language-guides/using-api-graphs-in-python.rst
diff --git a/docs/codeql/codeql-language-guides/using-api-graphs-in-python.rst b/docs/codeql/codeql-language-guides/using-api-graphs-in-python.rst
new file mode 100644
index 00000000000..ebfd70fe1fc
--- /dev/null
+++ b/docs/codeql/codeql-language-guides/using-api-graphs-in-python.rst
@@ -0,0 +1,164 @@
+.. _using-api-graphs-in-python:
+
+Using API graphs in Python
+==========================
+
+API graphs are a uniform interface for referring to functions, classes, and methods defined in
+external libraries.
+
+About this article
+------------------
+
+This article describes how to use API graphs to reference classes and functions defined in library
+code. This can be used to conveniently refer to external library functions when defining things like
+remote flow sources.
+
+
+Module imports
+--------------
+
+The most common entry point into the API graph will be the point where an external module or package is
+imported. The API graph node corresponding to the ``re`` library, for instance, can be accessed
+using the ``API::moduleImport`` method defined in the ``semmle.python.ApiGraphs`` module, as the
+following snippet demonstrates.
+
+.. code-block:: ql
+
+ import python
+ import semmle.python.ApiGraphs
+
+ select API::moduleImport("re")
+
+➤ `See this in the query console on LGTM.com `__.
+
+On its own, this only selects the API graph node corresponding to the ``re`` module. To find
+where this module is referenced, we use the ``getAUse`` method. Thus, the following query selects
+all references to the ``re`` module in the current database.
+
+.. code-block:: ql
+
+ import python
+ import semmle.python.ApiGraphs
+
+ select API::moduleImport("re").getAUse()
+
+➤ `See this in the query console on LGTM.com `__.
+
+Note that the ``getAUse`` method accounts for local flow, so that ``my_re_compile``
+in the following snippet is
+correctly recognized as a reference to the ``re.compile`` function.
+
+.. code-block:: python
+
+ from re import compile as re_compile
+
+ my_re_compile = re_compile
+
+ r = my_re_compile(".*")
+
+If only immediate uses are required, without taking local flow into account, then the method
+``getAnImmediateUse`` may be used instead.
+
+Note that the given module name *must not* contain any dots. Thus, something like
+``API::moduleImport("flask.views")`` will not do what you expect. Instead, this should be decomposed
+into an access of the ``views`` member of the API graph node for ``flask``, as described in the next
+section.
+
+Accessing attributes
+--------------------
+
+Given a node in the API graph, we may access its attributes by using the ``getMember`` method. Using
+the above ``re.compile`` example, we may now find references to ``re.compile`` by doing
+
+.. code-block:: ql
+
+ import python
+ import semmle.python.ApiGraphs
+
+ select API::moduleImport("re").getMember("compile").getAUse()
+
+➤ `See this in the query console on LGTM.com `__.
+
+In addition to ``getMember``, the method ``getUnknownMember`` can be used to find references to API
+components where the name is not known statically, and the ``getAMember`` method can be used to
+access all members, both known and unknown.
+
+Calls and class instantiations
+------------------------------
+
+To track instances of classes defined in external libraries, or the results of calling externally
+defined functions, we may use the ``getReturn`` method. Thus, the following snippet finds all places
+where the return value of ``re.compile`` is used:
+
+.. code-block:: ql
+
+ import python
+ import semmle.python.ApiGraphs
+
+ select API::moduleImport("re").getMember("compile").getReturn().getAUse()
+
+➤ `See this in the query console on LGTM.com `__.
+
+Note that this includes all uses of the result of ``re.compile``, including those reachable via
+local flow. To get just the *calls* to ``re.compile``, we can use ``getAnImmediateUse`` instead of
+``getAUse``. As this is a common occurrence, the method ``getACall`` can be used instead of
+``getReturn`` followed by ``getAnImmediateUse``.
+
+➤ `See this in the query console on LGTM.com `__.
+
+Note that the API graph does not distinguish between class instantiations and function calls. As far
+as it's concerned, both are simply places where an API graph node is called.
+
+Subclasses
+----------
+
+For many libraries, the main mode of usage is to extend one or more library classes. To track this
+in the API graph, we can use the ``getASubclass`` method to get the API graph node corresponding to
+all the immediate subclasses of this node. To find *all* subclasses, use ``*`` or ``+`` to apply the
+method repeatedly, as in `getASubclass*`.
+
+Note that ``getASubclass`` does not account for any subclassing that takes place in library code
+that has not been extracted. Thus, it may be necessary to account for this in the models you write.
+For example, the ``flask.views.View`` class has a predefined subclass ``MethodView``, and so to find
+all subclasses of ``View``, we must explicitly include the subclasses of ``MethodView`` as well.
+
+.. code-block:: ql
+
+ import python
+ import semmle.python.ApiGraphs
+
+ API::Node viewClass() {
+ result =
+ API::moduleImport("flask").getMember("views").getMember(["View", "MethodView"]).getASubclass*()
+ }
+
+ select viewClass()
+
+➤ `See this in the query console on LGTM.com `__.
+
+Note the use of the set literal ``["View", "MethodView"]`` to match both classes simultaneously.
+
+Built-in functions and classes
+------------------------------
+
+Built-in functions and classes can be accessed using the ``API::builtin`` method, giving the name of
+the built-in as an argument.
+
+To find all calls to the built-in ``open`` function, for instance, can be done using the following snippet
+
+.. code-block:: ql
+
+ import python
+ import semmle.python.ApiGraphs
+
+ select API::builtin("open").getACall()
+
+
+
+
+Further reading
+---------------
+
+
+.. include:: ../reusables/python-further-reading.rst
+.. include:: ../reusables/codeql-ref-tools-further-reading.rst
From 1a2e341b7c767fc4b6e21e9dc38d25110aa8b50e Mon Sep 17 00:00:00 2001
From: luchua-bc
Date: Fri, 12 Mar 2021 12:19:37 +0000
Subject: [PATCH 113/725] Refactor the business logic of the query into a
separate predicate
---
.../CWE/CWE-016/InsecureSpringActuatorConfig.ql | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql b/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql
index 06ba0d8a288..3acd22e767a 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql
@@ -60,8 +60,11 @@ class ManagementEndPointInclude extends ApplicationProperties {
string getValue() { result = this.getValueElement().getValue().trim() }
}
-from SpringBootPom pom, ApplicationProperties ap, Dependency d
-where
+/**
+ * Holds if `ApplicationProperties` ap of a repository managed by `SpringBootPom` pom
+ * has a vulnerable configuration of Spring Boot Actuator management endpoints.
+ */
+predicate hasConfidentialEndPointExposed(SpringBootPom pom, ApplicationProperties ap) {
pom.isSpringBootActuatorUsed() and
not pom.isSpringBootSecurityUsed() and
ap.getFile()
@@ -90,7 +93,12 @@ where
]) // confidential endpoints to check although all endpoints apart from '/health' and '/info' are considered sensitive by Spring
)
)
- ) and
+ )
+}
+
+from SpringBootPom pom, ApplicationProperties ap, Dependency d
+where
+ hasConfidentialEndPointExposed(pom, ap) and
d = pom.getADependency() and
d.getArtifact().getValue() = "spring-boot-starter-actuator"
select d, "Insecure configuration of Spring Boot Actuator exposes sensitive endpoints."
From f75b969ffcfa047b8d47664aaf99e05f2b751741 Mon Sep 17 00:00:00 2001
From: Cornelius Riemenschneider
Date: Mon, 15 Mar 2021 11:32:10 +0000
Subject: [PATCH 114/725] C++: Only include sum of LoC in the new non-alert
summary queries for now.
---
cpp/ql/src/Summary/LinesOfCode.ql | 4 ++--
cpp/ql/src/Summary/LinesOfCodePerFile.ql | 13 -------------
cpp/ql/src/Summary/LinesOfUserCode.ql | 11 -----------
cpp/ql/src/Summary/LinesOfUserCodePerFile.ql | 13 -------------
.../query-tests/Summary/LinesOfCodePerFile.expected | 3 ---
.../query-tests/Summary/LinesOfCodePerFile.qlref | 1 -
.../query-tests/Summary/LinesOfUserCode.expected | 1 -
.../test/query-tests/Summary/LinesOfUserCode.qlref | 1 -
.../Summary/LinesOfUserCodePerFile.expected | 3 ---
.../Summary/LinesOfUserCodePerFile.qlref | 1 -
10 files changed, 2 insertions(+), 49 deletions(-)
delete mode 100644 cpp/ql/src/Summary/LinesOfCodePerFile.ql
delete mode 100644 cpp/ql/src/Summary/LinesOfUserCode.ql
delete mode 100644 cpp/ql/src/Summary/LinesOfUserCodePerFile.ql
delete mode 100644 cpp/ql/test/query-tests/Summary/LinesOfCodePerFile.expected
delete mode 100644 cpp/ql/test/query-tests/Summary/LinesOfCodePerFile.qlref
delete mode 100644 cpp/ql/test/query-tests/Summary/LinesOfUserCode.expected
delete mode 100644 cpp/ql/test/query-tests/Summary/LinesOfUserCode.qlref
delete mode 100644 cpp/ql/test/query-tests/Summary/LinesOfUserCodePerFile.expected
delete mode 100644 cpp/ql/test/query-tests/Summary/LinesOfUserCodePerFile.qlref
diff --git a/cpp/ql/src/Summary/LinesOfCode.ql b/cpp/ql/src/Summary/LinesOfCode.ql
index 2d6fdf4b67b..6ba027dbd14 100644
--- a/cpp/ql/src/Summary/LinesOfCode.ql
+++ b/cpp/ql/src/Summary/LinesOfCode.ql
@@ -1,7 +1,7 @@
/**
* @id cpp/summary/lines-of-code
- * @name Total lines of C/C++ code
- * @description The total number of lines of C/C++ code across all files, including system headers and libraries.
+ * @name Total lines of C/C++ code in the database.
+ * @description The total number of lines of C/C++ code across all files, including system headers and libraries. This is a useful metric of the size of a database.
* @kind metric
* @tags summary
*/
diff --git a/cpp/ql/src/Summary/LinesOfCodePerFile.ql b/cpp/ql/src/Summary/LinesOfCodePerFile.ql
deleted file mode 100644
index c94f337744a..00000000000
--- a/cpp/ql/src/Summary/LinesOfCodePerFile.ql
+++ /dev/null
@@ -1,13 +0,0 @@
-/**
- * @id cpp/summary/lines-of-code-per-file
- * @name Lines of C/C++ code per source file
- * @description The number of lines of C/C++ code for each file in the database, including system headers and libraries.
- * @kind metric
- * @tags summary
- */
-
-import cpp
-
-from File f
-where f.fromSource()
-select f, f.getMetrics().getNumberOfLines()
diff --git a/cpp/ql/src/Summary/LinesOfUserCode.ql b/cpp/ql/src/Summary/LinesOfUserCode.ql
deleted file mode 100644
index e6466c6ddf6..00000000000
--- a/cpp/ql/src/Summary/LinesOfUserCode.ql
+++ /dev/null
@@ -1,11 +0,0 @@
-/**
- * @id cpp/summary/lines-of-user-code
- * @name Total lines of C/C++ source code
- * @description The total number of lines of C/C++ code across all files, excluding system headers and libraries.
- * @kind metric
- * @tags summary
- */
-
-import cpp
-
-select sum(File f | exists(f.getRelativePath()) | f.getMetrics().getNumberOfLines())
diff --git a/cpp/ql/src/Summary/LinesOfUserCodePerFile.ql b/cpp/ql/src/Summary/LinesOfUserCodePerFile.ql
deleted file mode 100644
index a10e48c0359..00000000000
--- a/cpp/ql/src/Summary/LinesOfUserCodePerFile.ql
+++ /dev/null
@@ -1,13 +0,0 @@
-/**
- * @id cpp/summary/lines-of-user-code-per-file
- * @name Lines of C/C++ code per source file
- * @description The number of lines of C/C++ code for each file in the source directory.
- * @kind metric
- * @tags summary
- */
-
-import cpp
-
-from File f
-where exists(f.getRelativePath())
-select f, f.getMetrics().getNumberOfLines()
diff --git a/cpp/ql/test/query-tests/Summary/LinesOfCodePerFile.expected b/cpp/ql/test/query-tests/Summary/LinesOfCodePerFile.expected
deleted file mode 100644
index e02d0591ab2..00000000000
--- a/cpp/ql/test/query-tests/Summary/LinesOfCodePerFile.expected
+++ /dev/null
@@ -1,3 +0,0 @@
-| empty-file.cpp:0:0:0:0 | empty-file.cpp | 0 |
-| large-file.cpp:0:0:0:0 | large-file.cpp | 119 |
-| short-file.cpp:0:0:0:0 | short-file.cpp | 3 |
diff --git a/cpp/ql/test/query-tests/Summary/LinesOfCodePerFile.qlref b/cpp/ql/test/query-tests/Summary/LinesOfCodePerFile.qlref
deleted file mode 100644
index 33edfd15c7c..00000000000
--- a/cpp/ql/test/query-tests/Summary/LinesOfCodePerFile.qlref
+++ /dev/null
@@ -1 +0,0 @@
-Summary/LinesOfCodePerFile.ql
diff --git a/cpp/ql/test/query-tests/Summary/LinesOfUserCode.expected b/cpp/ql/test/query-tests/Summary/LinesOfUserCode.expected
deleted file mode 100644
index d847b050658..00000000000
--- a/cpp/ql/test/query-tests/Summary/LinesOfUserCode.expected
+++ /dev/null
@@ -1 +0,0 @@
-| 122 |
diff --git a/cpp/ql/test/query-tests/Summary/LinesOfUserCode.qlref b/cpp/ql/test/query-tests/Summary/LinesOfUserCode.qlref
deleted file mode 100644
index baaa947e6af..00000000000
--- a/cpp/ql/test/query-tests/Summary/LinesOfUserCode.qlref
+++ /dev/null
@@ -1 +0,0 @@
-Summary/LinesOfUserCode.ql
diff --git a/cpp/ql/test/query-tests/Summary/LinesOfUserCodePerFile.expected b/cpp/ql/test/query-tests/Summary/LinesOfUserCodePerFile.expected
deleted file mode 100644
index e02d0591ab2..00000000000
--- a/cpp/ql/test/query-tests/Summary/LinesOfUserCodePerFile.expected
+++ /dev/null
@@ -1,3 +0,0 @@
-| empty-file.cpp:0:0:0:0 | empty-file.cpp | 0 |
-| large-file.cpp:0:0:0:0 | large-file.cpp | 119 |
-| short-file.cpp:0:0:0:0 | short-file.cpp | 3 |
diff --git a/cpp/ql/test/query-tests/Summary/LinesOfUserCodePerFile.qlref b/cpp/ql/test/query-tests/Summary/LinesOfUserCodePerFile.qlref
deleted file mode 100644
index bb3fc603c4d..00000000000
--- a/cpp/ql/test/query-tests/Summary/LinesOfUserCodePerFile.qlref
+++ /dev/null
@@ -1 +0,0 @@
-Summary/LinesOfUserCodePerFile.ql
From 662e17ff85cc75305fc849c0c02df9e36ea5cca3 Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Mon, 15 Mar 2021 15:09:03 +0100
Subject: [PATCH 115/725] Java: Bugfix dispatch to lambda in call context.
---
.../dataflow/internal/DataFlowDispatch.qll | 14 +++--
.../dataflow/lambda/Executor.java | 63 +++++++++++++++++++
.../dataflow/lambda/Processor.java | 9 +++
.../dataflow/lambda/StringProcessor.java | 30 +++++++++
.../dataflow/lambda/flow.expected | 8 +++
.../library-tests/dataflow/lambda/flow.ql | 25 ++++++++
6 files changed, 144 insertions(+), 5 deletions(-)
create mode 100644 java/ql/test/library-tests/dataflow/lambda/Executor.java
create mode 100644 java/ql/test/library-tests/dataflow/lambda/Processor.java
create mode 100644 java/ql/test/library-tests/dataflow/lambda/StringProcessor.java
create mode 100644 java/ql/test/library-tests/dataflow/lambda/flow.expected
create mode 100644 java/ql/test/library-tests/dataflow/lambda/flow.ql
diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowDispatch.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowDispatch.qll
index ba99bffbf5f..223af728fe4 100644
--- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowDispatch.qll
+++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowDispatch.qll
@@ -41,6 +41,12 @@ private module DispatchImpl {
)
}
+ private RefType getPreciseType(Expr e) {
+ result = e.(FunctionalExpr).getConstructedType()
+ or
+ not e instanceof FunctionalExpr and result = e.getType()
+ }
+
/**
* Holds if the `i`th argument of `ctx` has type `t` and `ctx` is a
* relevant call context.
@@ -55,7 +61,7 @@ private module DispatchImpl {
ctx.getArgument(i) = arg
|
src = variableTrack(arg) and
- srctype = src.getType() and
+ srctype = getPreciseType(src) and
if src instanceof ClassInstanceExpr then exact = true else exact = false
)
or
@@ -67,11 +73,9 @@ private module DispatchImpl {
if ctx instanceof ClassInstanceExpr then exact = true else exact = false
)
|
- exists(TypeVariable v | v = srctype |
- t = v.getUpperBoundType+() and not t instanceof TypeVariable
- )
+ t = srctype.(BoundedType).getAnUltimateUpperBoundType()
or
- t = srctype and not srctype instanceof TypeVariable
+ t = srctype and not srctype instanceof BoundedType
)
}
diff --git a/java/ql/test/library-tests/dataflow/lambda/Executor.java b/java/ql/test/library-tests/dataflow/lambda/Executor.java
new file mode 100644
index 00000000000..93f5e04221d
--- /dev/null
+++ b/java/ql/test/library-tests/dataflow/lambda/Executor.java
@@ -0,0 +1,63 @@
+import java.lang.Runtime;
+import java.util.function.Function;
+
+public class Executor {
+
+ private static final Processor processor = new Processor();
+
+ private static String source() { return "taint"; }
+
+ public static void main(String[] args) {
+ exec1(source());
+ exec2(source());
+ exec3(source());
+ exec4(source());
+ exec5(source());
+ }
+
+ private static void exec1(String command){
+ command = process(s->s.toUpperCase(),command);
+ exec(command);
+ }
+
+ private static void exec2(String command){
+ command = process(s->"Taint stops here.",command);
+ exec(command);
+ }
+
+ private static void exec3(String command){
+ command = processor.process(s->s.toUpperCase(),command);
+ exec(command);
+ }
+
+ private static void exec4(String command){
+ command = processor.process(s->"Taint stops here.",command);
+ exec_b(command);
+ }
+
+ private static void exec5(String command){
+ command = processor.process(s->s.toUpperCase(),command);
+ exec_b(command);
+ }
+
+ public static String process(Function fun, String command){
+ return processor.process(fun, command);
+ }
+
+ private static void exec(String command){
+ command = process(s->s.trim(),command);
+ try {
+ Runtime.getRuntime().exec(command);
+ }
+ catch(Exception e) {}
+ }
+
+ private static void exec_b(String command){
+ command = processor.process(s->s.trim(),command);
+ try {
+ Runtime.getRuntime().exec(command);
+ }
+ catch(Exception e) {}
+ }
+}
+
diff --git a/java/ql/test/library-tests/dataflow/lambda/Processor.java b/java/ql/test/library-tests/dataflow/lambda/Processor.java
new file mode 100644
index 00000000000..c632519041c
--- /dev/null
+++ b/java/ql/test/library-tests/dataflow/lambda/Processor.java
@@ -0,0 +1,9 @@
+import java.util.function.Function;
+
+public class Processor {
+
+ public R process(Function function, T arg) {
+ return function.apply(arg);
+ }
+}
+
diff --git a/java/ql/test/library-tests/dataflow/lambda/StringProcessor.java b/java/ql/test/library-tests/dataflow/lambda/StringProcessor.java
new file mode 100644
index 00000000000..abe92ce5ca7
--- /dev/null
+++ b/java/ql/test/library-tests/dataflow/lambda/StringProcessor.java
@@ -0,0 +1,30 @@
+import java.util.function.Function;
+
+public class StringProcessor {
+
+ private static final Processor processor = new Processor();
+
+ public static void main(String[] args) {
+ String command = args[0];
+ lambdaExec(command);
+ }
+
+ public static void lambdaExec(String command){
+ processor.process(s->exec(s), command);
+ }
+
+ public static String lambdaUnrelated(String command){
+ return processor.process(s->s+"not related to anything", command);
+ }
+
+ public static String exec(String command){
+ try {
+ command = processor.process(s->s.trim(), command);
+ Runtime.getRuntime().exec(command);
+ return "Executed: "+command;
+ } catch(Exception e) {
+ return null;
+ }
+ }
+}
+
diff --git a/java/ql/test/library-tests/dataflow/lambda/flow.expected b/java/ql/test/library-tests/dataflow/lambda/flow.expected
new file mode 100644
index 00000000000..3b467de204b
--- /dev/null
+++ b/java/ql/test/library-tests/dataflow/lambda/flow.expected
@@ -0,0 +1,8 @@
+| Executor.java:11:15:11:22 | source(...) | Executor.java:50:39:50:45 | command |
+| Executor.java:11:15:11:22 | source(...) | StringProcessor.java:23:39:23:45 | command |
+| Executor.java:12:15:12:22 | source(...) | Executor.java:50:39:50:45 | command |
+| Executor.java:12:15:12:22 | source(...) | StringProcessor.java:23:39:23:45 | command |
+| Executor.java:13:15:13:22 | source(...) | Executor.java:50:39:50:45 | command |
+| Executor.java:13:15:13:22 | source(...) | StringProcessor.java:23:39:23:45 | command |
+| Executor.java:15:15:15:22 | source(...) | Executor.java:58:39:58:45 | command |
+| StringProcessor.java:8:26:8:29 | args | StringProcessor.java:23:39:23:45 | command |
diff --git a/java/ql/test/library-tests/dataflow/lambda/flow.ql b/java/ql/test/library-tests/dataflow/lambda/flow.ql
new file mode 100644
index 00000000000..61bc7a33307
--- /dev/null
+++ b/java/ql/test/library-tests/dataflow/lambda/flow.ql
@@ -0,0 +1,25 @@
+import java
+import semmle.code.java.dataflow.TaintTracking
+
+class Conf extends TaintTracking::Configuration {
+ Conf() { this = "qltest lambda" }
+
+ override predicate isSource(DataFlow::Node src) {
+ src.asExpr().(VarAccess).getVariable().hasName("args")
+ or
+ src.asExpr().(MethodAccess).getMethod().hasName("source")
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ sink.asExpr().(Argument).getCall() =
+ any(MethodAccess ma |
+ ma.getMethod().hasName("exec") and
+ ma.getQualifier().(MethodAccess).getMethod().hasName("getRuntime")
+ )
+ }
+}
+
+from DataFlow::Node src, DataFlow::Node sink, Conf c
+where c.hasFlow(src, sink)
+select src, sink
+
From d1f30d91645e203f739605ef5daeee288b47c539 Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Mon, 15 Mar 2021 15:28:04 +0100
Subject: [PATCH 116/725] Java: Autoformat.
---
java/ql/test/library-tests/dataflow/lambda/flow.ql | 1 -
1 file changed, 1 deletion(-)
diff --git a/java/ql/test/library-tests/dataflow/lambda/flow.ql b/java/ql/test/library-tests/dataflow/lambda/flow.ql
index 61bc7a33307..dc8d1c7d4b0 100644
--- a/java/ql/test/library-tests/dataflow/lambda/flow.ql
+++ b/java/ql/test/library-tests/dataflow/lambda/flow.ql
@@ -22,4 +22,3 @@ class Conf extends TaintTracking::Configuration {
from DataFlow::Node src, DataFlow::Node sink, Conf c
where c.hasFlow(src, sink)
select src, sink
-
From fa3ac30894dba4a15868566d2e47dcd9d3036900 Mon Sep 17 00:00:00 2001
From: Cornelius Riemenschneider
Date: Tue, 16 Mar 2021 09:56:38 +0000
Subject: [PATCH 117/725] C++: Update query to latest spec.
---
cpp/ql/src/Summary/LinesOfCode.ql | 4 ++--
cpp/ql/test/query-tests/Summary/LinesOfCode.expected | 2 +-
cpp/ql/test/query-tests/Summary/empty-file.cpp | 1 +
cpp/ql/test/query-tests/Summary/large-file.cpp | 4 ++++
4 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/cpp/ql/src/Summary/LinesOfCode.ql b/cpp/ql/src/Summary/LinesOfCode.ql
index 6ba027dbd14..6dfb1d1d741 100644
--- a/cpp/ql/src/Summary/LinesOfCode.ql
+++ b/cpp/ql/src/Summary/LinesOfCode.ql
@@ -1,11 +1,11 @@
/**
* @id cpp/summary/lines-of-code
* @name Total lines of C/C++ code in the database.
- * @description The total number of lines of C/C++ code across all files, including system headers and libraries. This is a useful metric of the size of a database.
+ * @description The total number of lines of C/C++ code across all files, including system headers, libraries and auto-generated files. This is a useful metric of the size of a database. Lines of code are all lines in a file that was seen during the build that contain code, i.e. are not whitespace or comments.
* @kind metric
* @tags summary
*/
import cpp
-select sum(File f | f.fromSource() | f.getMetrics().getNumberOfLines())
+select sum(File f | f.fromSource() | f.getMetrics().getNumberOfLinesOfCode())
diff --git a/cpp/ql/test/query-tests/Summary/LinesOfCode.expected b/cpp/ql/test/query-tests/Summary/LinesOfCode.expected
index d847b050658..a75c288c151 100644
--- a/cpp/ql/test/query-tests/Summary/LinesOfCode.expected
+++ b/cpp/ql/test/query-tests/Summary/LinesOfCode.expected
@@ -1 +1 @@
-| 122 |
+| 93 |
diff --git a/cpp/ql/test/query-tests/Summary/empty-file.cpp b/cpp/ql/test/query-tests/Summary/empty-file.cpp
index e69de29bb2d..8b137891791 100644
--- a/cpp/ql/test/query-tests/Summary/empty-file.cpp
+++ b/cpp/ql/test/query-tests/Summary/empty-file.cpp
@@ -0,0 +1 @@
+
diff --git a/cpp/ql/test/query-tests/Summary/large-file.cpp b/cpp/ql/test/query-tests/Summary/large-file.cpp
index d6d06518b66..b4af2da0cde 100644
--- a/cpp/ql/test/query-tests/Summary/large-file.cpp
+++ b/cpp/ql/test/query-tests/Summary/large-file.cpp
@@ -26,10 +26,14 @@ int a06(float x) {
return (int)x;
}
+/**
+ * This is a multi-line comment
+ */
int a07(float x) {
return (int)x;
}
+// this is a single-line comment
int a08(float x) {
return (int)x;
}
From 2e8e04f73ef9ebec6113381b14ae781925dae62c Mon Sep 17 00:00:00 2001
From: Cornelius Riemenschneider
Date: Tue, 16 Mar 2021 10:48:04 +0000
Subject: [PATCH 118/725] C++: Move FailedExtractions.ql to
FailedCompilations.ql.
---
.../{FailedExtractions.ql => FailedCompilations.ql} | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
rename cpp/ql/src/Diagnostics/{FailedExtractions.ql => FailedCompilations.ql} (88%)
diff --git a/cpp/ql/src/Diagnostics/FailedExtractions.ql b/cpp/ql/src/Diagnostics/FailedCompilations.ql
similarity index 88%
rename from cpp/ql/src/Diagnostics/FailedExtractions.ql
rename to cpp/ql/src/Diagnostics/FailedCompilations.ql
index 513c833f14f..8af1b0250c0 100644
--- a/cpp/ql/src/Diagnostics/FailedExtractions.ql
+++ b/cpp/ql/src/Diagnostics/FailedCompilations.ql
@@ -1,8 +1,8 @@
/**
- * @name Failed extractions
+ * @name Failed compiler invocations.
* @description Gives the command-line of compilations for which extraction did not run to completion.
* @kind diagnostic
- * @id cpp/diagnostics/failed-extractions
+ * @id cpp/diagnostics/failed-compilations
*/
import cpp
From e092b317912dbabbe5682ef1ef12ae808a59f7b0 Mon Sep 17 00:00:00 2001
From: Tom Hvitved
Date: Thu, 4 Mar 2021 13:50:15 +0100
Subject: [PATCH 119/725] C#: Add test exposing missing phi flow
---
.../library-tests/dataflow/delegates/DelegateFlow.cs | 9 +++++++++
.../dataflow/delegates/DelegateFlow.expected | 1 +
2 files changed, 10 insertions(+)
diff --git a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.cs b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.cs
index 59c1c924a80..15d628aea9a 100644
--- a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.cs
+++ b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.cs
@@ -124,4 +124,13 @@ class DelegateFlow
delegate*, void> fnptr = &M2;
fnptr((i) => { });
}
+
+ void M19(Action a, bool b)
+ {
+ if (b)
+ a = () => {};
+ a();
+ }
+
+ void M20(bool b) => M19(() => {}, b);
}
diff --git a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected
index 80ae0d0fe89..c620a7cf22a 100644
--- a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected
+++ b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected
@@ -24,3 +24,4 @@ delegateCall
| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:65:10:65:12 | M11 | DelegateFlow.cs:91:13:91:47 | delegate creation of type MyDelegate |
| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:74:17:74:19 | M12 | DelegateFlow.cs:92:13:92:15 | delegate creation of type MyDelegate |
| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:93:13:93:21 | (...) => ... | DelegateFlow.cs:93:13:93:21 | (...) => ... |
+| DelegateFlow.cs:132:9:132:11 | delegate call | DelegateFlow.cs:131:17:131:24 | (...) => ... | file://:0:0:0:0 | |
From 25adcfc39d2afda92f1f62be272e041c44499ef5 Mon Sep 17 00:00:00 2001
From: Tom Hvitved
Date: Thu, 4 Mar 2021 10:22:11 +0100
Subject: [PATCH 120/725] C#: Fix missing phi flow
---
.../semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll | 4 ++--
.../library-tests/dataflow/delegates/DelegateFlow.expected | 1 +
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
index 5e3ac64f7c2..ebd8f51a8f1 100644
--- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
+++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
@@ -292,8 +292,8 @@ module LocalFlow {
*/
private predicate localFlowSsaInput(Node nodeFrom, Ssa::Definition def, Ssa::Definition next) {
exists(ControlFlow::BasicBlock bb, int i | SsaImpl::lastRefBeforeRedef(def, bb, i, next) |
- def = nodeFrom.(SsaDefinitionNode).getDefinition() and
- def.definesAt(_, bb, i)
+ def.definesAt(_, bb, i) and
+ def = getSsaDefinition(nodeFrom)
or
nodeFrom.asExprAtNode(bb.getNode(i)) instanceof AssignableRead
)
diff --git a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected
index c620a7cf22a..addcc3c0ff7 100644
--- a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected
+++ b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected
@@ -25,3 +25,4 @@ delegateCall
| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:74:17:74:19 | M12 | DelegateFlow.cs:92:13:92:15 | delegate creation of type MyDelegate |
| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:93:13:93:21 | (...) => ... | DelegateFlow.cs:93:13:93:21 | (...) => ... |
| DelegateFlow.cs:132:9:132:11 | delegate call | DelegateFlow.cs:131:17:131:24 | (...) => ... | file://:0:0:0:0 | |
+| DelegateFlow.cs:132:9:132:11 | delegate call | DelegateFlow.cs:135:29:135:36 | (...) => ... | DelegateFlow.cs:135:29:135:36 | (...) => ... |
From 29c6d221634e968943543df9c92b336e00352a36 Mon Sep 17 00:00:00 2001
From: Tom Hvitved
Date: Thu, 4 Mar 2021 13:50:38 +0100
Subject: [PATCH 121/725] C#: Add test exposing missing delegate flow
---
.../dataflow/global/GetAnOutNode.expected | 2 +-
.../library-tests/dataflow/global/GlobalDataFlow.cs | 12 ++++++++++++
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected b/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected
index 5b4957a19e9..a074c7f5a79 100644
--- a/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected
+++ b/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected
@@ -196,7 +196,7 @@
| GlobalDataFlow.cs:472:25:472:50 | call to method ConfigureAwait | return | GlobalDataFlow.cs:472:25:472:50 | call to method ConfigureAwait |
| GlobalDataFlow.cs:473:23:473:44 | call to method GetAwaiter | return | GlobalDataFlow.cs:473:23:473:44 | call to method GetAwaiter |
| GlobalDataFlow.cs:474:22:474:40 | call to method GetResult | return | GlobalDataFlow.cs:474:22:474:40 | call to method GetResult |
-| GlobalDataFlow.cs:486:44:486:47 | delegate call | return | GlobalDataFlow.cs:486:44:486:47 | delegate call |
+| GlobalDataFlow.cs:498:44:498:47 | delegate call | return | GlobalDataFlow.cs:498:44:498:47 | delegate call |
| Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return | return | Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return |
| Splitting.cs:8:17:8:31 | [b (line 3): true] call to method Return | return | Splitting.cs:8:17:8:31 | [b (line 3): true] call to method Return |
| Splitting.cs:20:22:20:30 | call to method Return | return | Splitting.cs:20:22:20:30 | call to method Return |
diff --git a/csharp/ql/test/library-tests/dataflow/global/GlobalDataFlow.cs b/csharp/ql/test/library-tests/dataflow/global/GlobalDataFlow.cs
index 4d072ca4d1c..5405acd5a6c 100644
--- a/csharp/ql/test/library-tests/dataflow/global/GlobalDataFlow.cs
+++ b/csharp/ql/test/library-tests/dataflow/global/GlobalDataFlow.cs
@@ -474,6 +474,18 @@ public class DataFlow
var sink45 = awaiter.GetResult();
Check(sink45);
}
+
+ void M5(bool b)
+ {
+ void Inner(Action a, bool b, string arg)
+ {
+ if (b)
+ a = s => Check(s);
+ a(arg);
+ }
+
+ Inner(_ => {}, b, "taint source");
+ }
}
static class IEnumerableExtensions
From e1e4016a5c7b4fb76c667fbced127b9480c1436a Mon Sep 17 00:00:00 2001
From: Tom Hvitved
Date: Thu, 4 Mar 2021 14:05:50 +0100
Subject: [PATCH 122/725] C#: Fix missing delegate flow
---
.../code/csharp/dataflow/internal/DataFlowDispatch.qll | 6 ++++++
.../library-tests/dataflow/global/DataFlow.expected | 1 +
.../dataflow/global/DataFlowPath.expected | 10 ++++++++++
.../dataflow/global/TaintTracking.expected | 1 +
.../dataflow/global/TaintTrackingPath.expected | 10 ++++++++++
5 files changed, 28 insertions(+)
diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll
index c68ba38e0b5..d58fda11922 100644
--- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll
+++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll
@@ -167,6 +167,12 @@ private module DispatchImpl {
cc.isArgument(ctx.getExpr(), _)
)
or
+ exists(DataFlowCallable enclosing |
+ mayBenefitFromCallContext(call, enclosing) and
+ ctx.getARuntimeTarget() = enclosing and
+ result = viableDelegateCallable(call, any(EmptyCallContext ecc))
+ )
+ or
result =
call.(NonDelegateDataFlowCall)
.getDispatchCall()
diff --git a/csharp/ql/test/library-tests/dataflow/global/DataFlow.expected b/csharp/ql/test/library-tests/dataflow/global/DataFlow.expected
index 9c382aaee62..b0506ab3e4e 100644
--- a/csharp/ql/test/library-tests/dataflow/global/DataFlow.expected
+++ b/csharp/ql/test/library-tests/dataflow/global/DataFlow.expected
@@ -52,6 +52,7 @@
| GlobalDataFlow.cs:401:15:401:20 | access to local variable sink11 |
| GlobalDataFlow.cs:424:41:424:46 | access to local variable sink20 |
| GlobalDataFlow.cs:475:15:475:20 | access to local variable sink45 |
+| GlobalDataFlow.cs:483:32:483:32 | access to parameter s |
| Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x |
| Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x |
| Splitting.cs:11:19:11:19 | access to local variable x |
diff --git a/csharp/ql/test/library-tests/dataflow/global/DataFlowPath.expected b/csharp/ql/test/library-tests/dataflow/global/DataFlowPath.expected
index a806a9a6256..d07fcd39403 100644
--- a/csharp/ql/test/library-tests/dataflow/global/DataFlowPath.expected
+++ b/csharp/ql/test/library-tests/dataflow/global/DataFlowPath.expected
@@ -229,6 +229,10 @@ edges
| GlobalDataFlow.cs:473:23:473:44 | call to method GetAwaiter [m_task, Result] : String | GlobalDataFlow.cs:474:22:474:28 | access to local variable awaiter [m_task, Result] : String |
| GlobalDataFlow.cs:474:22:474:28 | access to local variable awaiter [m_task, Result] : String | GlobalDataFlow.cs:474:22:474:40 | call to method GetResult : String |
| GlobalDataFlow.cs:474:22:474:40 | call to method GetResult : String | GlobalDataFlow.cs:475:15:475:20 | access to local variable sink45 |
+| GlobalDataFlow.cs:480:53:480:55 | arg : String | GlobalDataFlow.cs:484:15:484:17 | access to parameter arg : String |
+| GlobalDataFlow.cs:483:21:483:21 | s : String | GlobalDataFlow.cs:483:32:483:32 | access to parameter s |
+| GlobalDataFlow.cs:484:15:484:17 | access to parameter arg : String | GlobalDataFlow.cs:483:21:483:21 | s : String |
+| GlobalDataFlow.cs:487:27:487:40 | "taint source" : String | GlobalDataFlow.cs:480:53:480:55 | arg : String |
| Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:8:24:8:30 | [b (line 3): false] access to parameter tainted : String |
| Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:8:24:8:30 | [b (line 3): true] access to parameter tainted : String |
| Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return : String | Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x |
@@ -444,6 +448,11 @@ nodes
| GlobalDataFlow.cs:474:22:474:28 | access to local variable awaiter [m_task, Result] : String | semmle.label | access to local variable awaiter [m_task, Result] : String |
| GlobalDataFlow.cs:474:22:474:40 | call to method GetResult : String | semmle.label | call to method GetResult : String |
| GlobalDataFlow.cs:475:15:475:20 | access to local variable sink45 | semmle.label | access to local variable sink45 |
+| GlobalDataFlow.cs:480:53:480:55 | arg : String | semmle.label | arg : String |
+| GlobalDataFlow.cs:483:21:483:21 | s : String | semmle.label | s : String |
+| GlobalDataFlow.cs:483:32:483:32 | access to parameter s | semmle.label | access to parameter s |
+| GlobalDataFlow.cs:484:15:484:17 | access to parameter arg : String | semmle.label | access to parameter arg : String |
+| GlobalDataFlow.cs:487:27:487:40 | "taint source" : String | semmle.label | "taint source" : String |
| Splitting.cs:3:28:3:34 | tainted : String | semmle.label | tainted : String |
| Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return : String | semmle.label | [b (line 3): false] call to method Return : String |
| Splitting.cs:8:17:8:31 | [b (line 3): true] call to method Return : String | semmle.label | [b (line 3): true] call to method Return : String |
@@ -520,6 +529,7 @@ nodes
| GlobalDataFlow.cs:182:15:182:19 | access to local variable sink9 | GlobalDataFlow.cs:180:35:180:48 | "taint source" : String | GlobalDataFlow.cs:182:15:182:19 | access to local variable sink9 | access to local variable sink9 |
| Splitting.cs:11:19:11:19 | access to local variable x | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:11:19:11:19 | access to local variable x | access to local variable x |
| Splitting.cs:34:19:34:19 | access to local variable x | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:34:19:34:19 | access to local variable x | access to local variable x |
+| GlobalDataFlow.cs:483:32:483:32 | access to parameter s | GlobalDataFlow.cs:487:27:487:40 | "taint source" : String | GlobalDataFlow.cs:483:32:483:32 | access to parameter s | access to parameter s |
| Capture.cs:57:27:57:32 | access to parameter sink39 | Capture.cs:7:20:7:26 | tainted : String | Capture.cs:57:27:57:32 | access to parameter sink39 | access to parameter sink39 |
| GlobalDataFlow.cs:257:15:257:24 | access to parameter sinkParam0 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:257:15:257:24 | access to parameter sinkParam0 | access to parameter sinkParam0 |
| GlobalDataFlow.cs:262:15:262:24 | access to parameter sinkParam1 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:262:15:262:24 | access to parameter sinkParam1 | access to parameter sinkParam1 |
diff --git a/csharp/ql/test/library-tests/dataflow/global/TaintTracking.expected b/csharp/ql/test/library-tests/dataflow/global/TaintTracking.expected
index fd800913202..43e5ee21e42 100644
--- a/csharp/ql/test/library-tests/dataflow/global/TaintTracking.expected
+++ b/csharp/ql/test/library-tests/dataflow/global/TaintTracking.expected
@@ -58,6 +58,7 @@
| GlobalDataFlow.cs:453:15:453:20 | access to local variable sink43 |
| GlobalDataFlow.cs:463:15:463:20 | access to local variable sink44 |
| GlobalDataFlow.cs:475:15:475:20 | access to local variable sink45 |
+| GlobalDataFlow.cs:483:32:483:32 | access to parameter s |
| Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x |
| Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x |
| Splitting.cs:11:19:11:19 | access to local variable x |
diff --git a/csharp/ql/test/library-tests/dataflow/global/TaintTrackingPath.expected b/csharp/ql/test/library-tests/dataflow/global/TaintTrackingPath.expected
index 7297686b5e3..e9ba5973f50 100644
--- a/csharp/ql/test/library-tests/dataflow/global/TaintTrackingPath.expected
+++ b/csharp/ql/test/library-tests/dataflow/global/TaintTrackingPath.expected
@@ -249,6 +249,10 @@ edges
| GlobalDataFlow.cs:473:23:473:44 | call to method GetAwaiter [m_task, Result] : String | GlobalDataFlow.cs:474:22:474:28 | access to local variable awaiter [m_task, Result] : String |
| GlobalDataFlow.cs:474:22:474:28 | access to local variable awaiter [m_task, Result] : String | GlobalDataFlow.cs:474:22:474:40 | call to method GetResult : String |
| GlobalDataFlow.cs:474:22:474:40 | call to method GetResult : String | GlobalDataFlow.cs:475:15:475:20 | access to local variable sink45 |
+| GlobalDataFlow.cs:480:53:480:55 | arg : String | GlobalDataFlow.cs:484:15:484:17 | access to parameter arg : String |
+| GlobalDataFlow.cs:483:21:483:21 | s : String | GlobalDataFlow.cs:483:32:483:32 | access to parameter s |
+| GlobalDataFlow.cs:484:15:484:17 | access to parameter arg : String | GlobalDataFlow.cs:483:21:483:21 | s : String |
+| GlobalDataFlow.cs:487:27:487:40 | "taint source" : String | GlobalDataFlow.cs:480:53:480:55 | arg : String |
| Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:8:24:8:30 | [b (line 3): false] access to parameter tainted : String |
| Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:8:24:8:30 | [b (line 3): true] access to parameter tainted : String |
| Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return : String | Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x |
@@ -486,6 +490,11 @@ nodes
| GlobalDataFlow.cs:474:22:474:28 | access to local variable awaiter [m_task, Result] : String | semmle.label | access to local variable awaiter [m_task, Result] : String |
| GlobalDataFlow.cs:474:22:474:40 | call to method GetResult : String | semmle.label | call to method GetResult : String |
| GlobalDataFlow.cs:475:15:475:20 | access to local variable sink45 | semmle.label | access to local variable sink45 |
+| GlobalDataFlow.cs:480:53:480:55 | arg : String | semmle.label | arg : String |
+| GlobalDataFlow.cs:483:21:483:21 | s : String | semmle.label | s : String |
+| GlobalDataFlow.cs:483:32:483:32 | access to parameter s | semmle.label | access to parameter s |
+| GlobalDataFlow.cs:484:15:484:17 | access to parameter arg : String | semmle.label | access to parameter arg : String |
+| GlobalDataFlow.cs:487:27:487:40 | "taint source" : String | semmle.label | "taint source" : String |
| Splitting.cs:3:28:3:34 | tainted : String | semmle.label | tainted : String |
| Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return : String | semmle.label | [b (line 3): false] call to method Return : String |
| Splitting.cs:8:17:8:31 | [b (line 3): true] call to method Return : String | semmle.label | [b (line 3): true] call to method Return : String |
@@ -573,6 +582,7 @@ nodes
| GlobalDataFlow.cs:453:15:453:20 | access to local variable sink43 | GlobalDataFlow.cs:451:35:451:48 | "taint source" : String | GlobalDataFlow.cs:453:15:453:20 | access to local variable sink43 | access to local variable sink43 |
| GlobalDataFlow.cs:463:15:463:20 | access to local variable sink44 | GlobalDataFlow.cs:462:51:462:64 | "taint source" : String | GlobalDataFlow.cs:463:15:463:20 | access to local variable sink44 | access to local variable sink44 |
| GlobalDataFlow.cs:475:15:475:20 | access to local variable sink45 | GlobalDataFlow.cs:471:35:471:48 | "taint source" : String | GlobalDataFlow.cs:475:15:475:20 | access to local variable sink45 | access to local variable sink45 |
+| GlobalDataFlow.cs:483:32:483:32 | access to parameter s | GlobalDataFlow.cs:487:27:487:40 | "taint source" : String | GlobalDataFlow.cs:483:32:483:32 | access to parameter s | access to parameter s |
| Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x | [b (line 3): false] access to local variable x |
| Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x | [b (line 3): true] access to local variable x |
| Splitting.cs:11:19:11:19 | access to local variable x | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:11:19:11:19 | access to local variable x | access to local variable x |
From 755fec466f97c610e4546e88f39d5081b17062ea Mon Sep 17 00:00:00 2001
From: Cornelius Riemenschneider
Date: Tue, 16 Mar 2021 13:21:57 +0100
Subject: [PATCH 123/725] Apply suggestions from code review
Co-authored-by: Jonas Jensen
---
cpp/ql/src/Summary/LinesOfCode.ql | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/cpp/ql/src/Summary/LinesOfCode.ql b/cpp/ql/src/Summary/LinesOfCode.ql
index 6dfb1d1d741..79e4e7b16f2 100644
--- a/cpp/ql/src/Summary/LinesOfCode.ql
+++ b/cpp/ql/src/Summary/LinesOfCode.ql
@@ -1,7 +1,7 @@
/**
* @id cpp/summary/lines-of-code
- * @name Total lines of C/C++ code in the database.
- * @description The total number of lines of C/C++ code across all files, including system headers, libraries and auto-generated files. This is a useful metric of the size of a database. Lines of code are all lines in a file that was seen during the build that contain code, i.e. are not whitespace or comments.
+ * @name Total lines of C/C++ code in the database
+ * @description The total number of lines of C/C++ code across all files, including system headers, libraries, and auto-generated files. This is a useful metric of the size of a database. Lines of code are all lines in a file that was seen during the build that contain code, i.e. are not whitespace or comments.
* @kind metric
* @tags summary
*/
From a373a523f69c4aad9f526c1dabf5a7f547cf1409 Mon Sep 17 00:00:00 2001
From: Tom Hvitved
Date: Tue, 2 Mar 2021 20:12:08 +0100
Subject: [PATCH 124/725] Data flow: Move C# lambda flow logic into shared
library
---
.../code/csharp/dataflow/CallContext.qll | 31 +-
.../dataflow/internal/DataFlowDispatch.qll | 61 +---
.../dataflow/internal/DataFlowImplCommon.qll | 309 +++++++++++++++++-
.../dataflow/internal/DataFlowPrivate.qll | 69 +++-
.../dataflow/internal/DelegateDataFlow.qll | 24 +-
.../code/csharp/dataflow/internal/SsaImpl.qll | 1 -
.../ql/src/semmle/code/csharp/exprs/Call.qll | 23 +-
.../dataflow/delegates/DelegateFlow.expected | 81 +++--
.../dataflow/delegates/DelegateFlow.ql | 34 +-
.../FunctionPointerFlow.expected | 29 +-
.../functionpointers/FunctionPointerFlow.ql | 37 ++-
11 files changed, 552 insertions(+), 147 deletions(-)
diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/CallContext.qll b/csharp/ql/src/semmle/code/csharp/dataflow/CallContext.qll
index 8be2fc0939f..47ff7f96111 100755
--- a/csharp/ql/src/semmle/code/csharp/dataflow/CallContext.qll
+++ b/csharp/ql/src/semmle/code/csharp/dataflow/CallContext.qll
@@ -1,4 +1,6 @@
/**
+ * DEPRECATED.
+ *
* Provides classes for data flow call contexts.
*/
@@ -15,11 +17,13 @@ private newtype TCallContext =
TArgFunctionPointerCallContext(FunctionPointerCall fptrc, int i) { exists(fptrc.getArgument(i)) }
/**
+ * DEPRECATED.
+ *
* A call context.
*
* A call context records the origin of data flow into callables.
*/
-class CallContext extends TCallContext {
+deprecated class CallContext extends TCallContext {
/** Gets a textual representation of this call context. */
string toString() { none() }
@@ -27,18 +31,20 @@ class CallContext extends TCallContext {
Location getLocation() { none() }
}
-/** An empty call context. */
-class EmptyCallContext extends CallContext, TEmptyCallContext {
+/** DEPRECATED. An empty call context. */
+deprecated class EmptyCallContext extends CallContext, TEmptyCallContext {
override string toString() { result = "" }
override EmptyLocation getLocation() { any() }
}
/**
+ * DEPRECATED.
+ *
* An argument call context, that is a call argument through which data flows
* into a callable.
*/
-abstract class ArgumentCallContext extends CallContext {
+abstract deprecated class ArgumentCallContext extends CallContext {
/**
* Holds if this call context represents the argument at position `i` of the
* call expression `call`.
@@ -46,8 +52,9 @@ abstract class ArgumentCallContext extends CallContext {
abstract predicate isArgument(Expr call, int i);
}
-/** An argument of a non-delegate call. */
-class NonDelegateCallArgumentCallContext extends ArgumentCallContext, TArgNonDelegateCallContext {
+/** DEPRECATED. An argument of a non-delegate call. */
+deprecated class NonDelegateCallArgumentCallContext extends ArgumentCallContext,
+ TArgNonDelegateCallContext {
Expr arg;
NonDelegateCallArgumentCallContext() { this = TArgNonDelegateCallContext(arg) }
@@ -61,8 +68,8 @@ class NonDelegateCallArgumentCallContext extends ArgumentCallContext, TArgNonDel
override Location getLocation() { result = arg.getLocation() }
}
-/** An argument of a delegate or function pointer call. */
-class DelegateLikeCallArgumentCallContext extends ArgumentCallContext {
+/** DEPRECATED. An argument of a delegate or function pointer call. */
+deprecated class DelegateLikeCallArgumentCallContext extends ArgumentCallContext {
DelegateLikeCall dc;
int arg;
@@ -80,10 +87,10 @@ class DelegateLikeCallArgumentCallContext extends ArgumentCallContext {
override Location getLocation() { result = dc.getArgument(arg).getLocation() }
}
-/** An argument of a delegate call. */
-class DelegateCallArgumentCallContext extends DelegateLikeCallArgumentCallContext,
+/** DEPRECATED. An argument of a delegate call. */
+deprecated class DelegateCallArgumentCallContext extends DelegateLikeCallArgumentCallContext,
TArgDelegateCallContext { }
-/** An argument of a function pointer call. */
-class FunctionPointerCallArgumentCallContext extends DelegateLikeCallArgumentCallContext,
+/** DEPRECATED. An argument of a function pointer call. */
+deprecated class FunctionPointerCallArgumentCallContext extends DelegateLikeCallArgumentCallContext,
TArgFunctionPointerCallContext { }
diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll
index d58fda11922..5f5da527cde 100644
--- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll
+++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll
@@ -1,8 +1,8 @@
private import csharp
private import cil
private import dotnet
+private import DataFlowPublic
private import DataFlowPrivate
-private import DelegateDataFlow
private import FlowSummaryImpl as FlowSummaryImpl
private import semmle.code.csharp.dataflow.FlowSummary
private import semmle.code.csharp.dispatch.Dispatch
@@ -131,51 +131,24 @@ private module Cached {
import Cached
private module DispatchImpl {
- private import CallContext
-
- /**
- * Gets a viable run-time target for the delegate call `call`, requiring
- * call context `cc`.
- */
- private DataFlowCallable viableDelegateCallable(DataFlowCall call, CallContext cc) {
- result = call.(DelegateDataFlowCall).getARuntimeTarget(cc)
- }
-
/**
* Holds if the set of viable implementations that can be called by `call`
* might be improved by knowing the call context. This is the case if the
* call is a delegate call, or if the qualifier accesses a parameter of
* the enclosing callable `c` (including the implicit `this` parameter).
*/
- predicate mayBenefitFromCallContext(DataFlowCall call, Callable c) {
+ predicate mayBenefitFromCallContext(NonDelegateDataFlowCall call, Callable c) {
c = call.getEnclosingCallable() and
- (
- exists(CallContext cc | exists(viableDelegateCallable(call, cc)) |
- not cc instanceof EmptyCallContext
- )
- or
- call.(NonDelegateDataFlowCall).getDispatchCall().mayBenefitFromCallContext()
- )
+ call.getDispatchCall().mayBenefitFromCallContext()
}
/**
* Gets a viable dispatch target of `call` in the context `ctx`. This is
* restricted to those `call`s for which a context might make a difference.
*/
- DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
- exists(ArgumentCallContext cc | result = viableDelegateCallable(call, cc) |
- cc.isArgument(ctx.getExpr(), _)
- )
- or
- exists(DataFlowCallable enclosing |
- mayBenefitFromCallContext(call, enclosing) and
- ctx.getARuntimeTarget() = enclosing and
- result = viableDelegateCallable(call, any(EmptyCallContext ecc))
- )
- or
+ DataFlowCallable viableImplInCallContext(NonDelegateDataFlowCall call, DataFlowCall ctx) {
result =
- call.(NonDelegateDataFlowCall)
- .getDispatchCall()
+ call.getDispatchCall()
.getADynamicTargetInCallContext(ctx.(NonDelegateDataFlowCall).getDispatchCall())
.getUnboundDeclaration()
}
@@ -307,12 +280,7 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall {
}
/** A delegate call relevant for data flow. */
-abstract class DelegateDataFlowCall extends DataFlowCall {
- /** Gets a viable run-time target of this call requiring call context `cc`. */
- abstract DataFlowCallable getARuntimeTarget(CallContext::CallContext cc);
-
- override DataFlowCallable getARuntimeTarget() { result = this.getARuntimeTarget(_) }
-}
+abstract class DelegateDataFlowCall extends DataFlowCall { }
/** An explicit delegate or function pointer call relevant for data flow. */
class ExplicitDelegateLikeDataFlowCall extends DelegateDataFlowCall, TExplicitDelegateLikeCall {
@@ -321,8 +289,11 @@ class ExplicitDelegateLikeDataFlowCall extends DelegateDataFlowCall, TExplicitDe
ExplicitDelegateLikeDataFlowCall() { this = TExplicitDelegateLikeCall(cfn, dc) }
- override DataFlowCallable getARuntimeTarget(CallContext::CallContext cc) {
- result = getCallableForDataFlow(dc.getARuntimeTarget(cc))
+ /** Gets the underlying call. */
+ DelegateLikeCall getCall() { result = dc }
+
+ override DataFlowCallable getARuntimeTarget() {
+ none() // handled by the shared library
}
override ControlFlow::Nodes::ElementNode getControlFlowNode() { result = cfn }
@@ -395,11 +366,11 @@ class SummaryDelegateCall extends DelegateDataFlowCall, TSummaryDelegateCall {
SummaryDelegateCall() { this = TSummaryDelegateCall(c, pos) }
- override DataFlowCallable getARuntimeTarget(CallContext::CallContext cc) {
- exists(SummaryDelegateParameterSink p |
- p.isParameterOf(c, pos) and
- result = p.getARuntimeTarget(cc)
- )
+ /** Gets the parameter node that this delegate call targets. */
+ ParameterNode getParameterNode() { result.isParameterOf(c, pos) }
+
+ override DataFlowCallable getARuntimeTarget() {
+ none() // handled by the shared library
}
override ControlFlow::Nodes::ElementNode getControlFlowNode() { none() }
diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll
index 1319f972037..a51c20c2288 100644
--- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll
+++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll
@@ -26,15 +26,243 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) {
tupleLimit = 1000
}
+/**
+ * Provides a simple data-flow analysis for resolving lambda calls. The analysis
+ * currently excludes read-steps, store-steps, and flow-through.
+ *
+ * The analysis uses non-linear recursion: When computing a flow path in or out
+ * of a call, we use the results of the analysis recursively to resolve lamba
+ * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly.
+ */
+private module LambdaFlow {
+ private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNode p) {
+ p.isParameterOf(viableCallable(call), i)
+ }
+
+ private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) {
+ p.isParameterOf(viableCallableLambda(call, _), i)
+ }
+
+ private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
+ exists(int i |
+ viableParamNonLambda(call, i, p) and
+ arg.argumentOf(call, i)
+ )
+ }
+
+ private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
+ exists(int i |
+ viableParamLambda(call, i, p) and
+ arg.argumentOf(call, i)
+ )
+ }
+
+ private newtype TReturnPositionSimple =
+ TReturnPositionSimple0(DataFlowCallable c, ReturnKind kind) {
+ exists(ReturnNode ret |
+ c = getNodeEnclosingCallable(ret) and
+ kind = ret.getKind()
+ )
+ }
+
+ pragma[noinline]
+ private TReturnPositionSimple getReturnPositionSimple(ReturnNode ret, ReturnKind kind) {
+ result = TReturnPositionSimple0(getNodeEnclosingCallable(ret), kind)
+ }
+
+ pragma[nomagic]
+ private TReturnPositionSimple viableReturnPosNonLambda(DataFlowCall call, ReturnKind kind) {
+ result = TReturnPositionSimple0(viableCallable(call), kind)
+ }
+
+ pragma[nomagic]
+ private TReturnPositionSimple viableReturnPosLambda(
+ DataFlowCall call, DataFlowCallOption lastCall, ReturnKind kind
+ ) {
+ result = TReturnPositionSimple0(viableCallableLambda(call, lastCall), kind)
+ }
+
+ private predicate viableReturnPosOutNonLambda(
+ DataFlowCall call, TReturnPositionSimple pos, OutNode out
+ ) {
+ exists(ReturnKind kind |
+ pos = viableReturnPosNonLambda(call, kind) and
+ out = getAnOutNode(call, kind)
+ )
+ }
+
+ private predicate viableReturnPosOutLambda(
+ DataFlowCall call, DataFlowCallOption lastCall, TReturnPositionSimple pos, OutNode out
+ ) {
+ exists(ReturnKind kind |
+ pos = viableReturnPosLambda(call, lastCall, kind) and
+ out = getAnOutNode(call, kind)
+ )
+ }
+
+ /**
+ * Holds if data can flow (inter-procedurally) from `node` (of type `t`) to
+ * the lambda call `lambdaCall`.
+ *
+ * The parameter `toReturn` indicates whether the path from `node` to
+ * `lambdaCall` goes through a return, and `toJump` whether the path goes
+ * through a jump step.
+ *
+ * The call context `lastCall` records the last call on the path from `node`
+ * to `lambdaCall`, if any. That is, `lastCall` is able to target the enclosing
+ * callable of `lambdaCall`.
+ */
+ pragma[nomagic]
+ predicate revLambdaFlow(
+ DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn,
+ boolean toJump, DataFlowCallOption lastCall
+ ) {
+ revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and
+ if node instanceof CastNode or node instanceof ArgumentNode or node instanceof ReturnNode
+ then compatibleTypes(t, getNodeType(node))
+ else any()
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlow0(
+ DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn,
+ boolean toJump, DataFlowCallOption lastCall
+ ) {
+ lambdaCall(lambdaCall, kind, node) and
+ t = getNodeType(node) and
+ toReturn = false and
+ toJump = false and
+ lastCall = TDataFlowCallNone()
+ or
+ // local flow
+ exists(Node mid, DataFlowType t0 |
+ revLambdaFlow(lambdaCall, kind, mid, t0, toReturn, toJump, lastCall)
+ |
+ simpleLocalFlowStep(node, mid) and
+ t = t0
+ or
+ exists(boolean preservesValue |
+ additionalLambdaFlowStep(node, mid, preservesValue) and
+ getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid)
+ |
+ preservesValue = false and
+ t = getNodeType(node)
+ or
+ preservesValue = true and
+ t = t0
+ )
+ )
+ or
+ // jump step
+ exists(Node mid, DataFlowType t0 |
+ revLambdaFlow(lambdaCall, kind, mid, t0, _, _, _) and
+ toReturn = false and
+ toJump = true and
+ lastCall = TDataFlowCallNone()
+ |
+ jumpStep(node, mid) and
+ t = t0
+ or
+ exists(boolean preservesValue |
+ additionalLambdaFlowStep(node, mid, preservesValue) and
+ getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid)
+ |
+ preservesValue = false and
+ t = getNodeType(node)
+ or
+ preservesValue = true and
+ t = t0
+ )
+ )
+ or
+ // flow into a callable
+ exists(ParameterNode p, DataFlowCallOption lastCall0, DataFlowCall call |
+ revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and
+ (
+ if lastCall0 = TDataFlowCallNone() and toJump = false
+ then lastCall = TDataFlowCallSome(call)
+ else lastCall = lastCall0
+ ) and
+ toReturn = false
+ |
+ viableParamArgNonLambda(call, p, node)
+ or
+ viableParamArgLambda(call, p, node) // non-linear recursion
+ )
+ or
+ // flow out of a callable
+ exists(TReturnPositionSimple pos |
+ revLambdaFlowOut(lambdaCall, kind, pos, t, toJump, lastCall) and
+ getReturnPositionSimple(node, node.(ReturnNode).getKind()) = pos and
+ toReturn = true
+ )
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlowOutLambdaCall(
+ DataFlowCall lambdaCall, LambdaCallKind kind, OutNode out, DataFlowType t, boolean toJump,
+ DataFlowCall call, DataFlowCallOption lastCall
+ ) {
+ revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and
+ exists(ReturnKindExt rk |
+ out = rk.getAnOutNode(call) and
+ lambdaCall(call, _, _)
+ )
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlowOut(
+ DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t,
+ boolean toJump, DataFlowCallOption lastCall
+ ) {
+ exists(DataFlowCall call, OutNode out |
+ revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and
+ viableReturnPosOutNonLambda(call, pos, out)
+ or
+ // non-linear recursion
+ revLambdaFlowOutLambdaCall(lambdaCall, kind, out, t, toJump, call, lastCall) and
+ viableReturnPosOutLambda(call, _, pos, out)
+ )
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlowIn(
+ DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump,
+ DataFlowCallOption lastCall
+ ) {
+ revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall)
+ }
+}
+
+private DataFlowCallable viableCallableExt(DataFlowCall call) {
+ result = viableCallable(call)
+ or
+ result = viableCallableLambda(call, _)
+}
+
cached
private module Cached {
+ /**
+ * Gets a viable target for the lambda call `call`.
+ *
+ * `lastCall` records the call required to reach `call` in order for the result
+ * to be a viable target, if any.
+ */
+ cached
+ DataFlowCallable viableCallableLambda(DataFlowCall call, DataFlowCallOption lastCall) {
+ exists(Node creation, LambdaCallKind kind |
+ LambdaFlow::revLambdaFlow(call, kind, creation, _, _, _, lastCall) and
+ lambdaCreation(creation, kind, result)
+ )
+ }
+
/**
* Holds if `p` is the `i`th parameter of a viable dispatch target of `call`.
* The instance parameter is considered to have index `-1`.
*/
pragma[nomagic]
private predicate viableParam(DataFlowCall call, int i, ParameterNode p) {
- p.isParameterOf(viableCallable(call), i)
+ p.isParameterOf(viableCallableExt(call), i)
}
/**
@@ -52,7 +280,7 @@ private module Cached {
pragma[nomagic]
private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
- viableCallable(call) = result.getCallable() and
+ viableCallableExt(call) = result.getCallable() and
kind = result.getKind()
}
@@ -317,6 +545,35 @@ private module Cached {
cached
private module DispatchWithCallContext {
+ /**
+ * Holds if the set of viable implementations that can be called by `call`
+ * might be improved by knowing the call context.
+ */
+ pragma[nomagic]
+ private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) {
+ mayBenefitFromCallContext(call, callable)
+ or
+ callable = call.getEnclosingCallable() and
+ exists(viableCallableLambda(call, TDataFlowCallSome(_)))
+ }
+
+ /**
+ * Gets a viable dispatch target of `call` in the context `ctx`. This is
+ * restricted to those `call`s for which a context might make a difference.
+ */
+ pragma[nomagic]
+ private DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) {
+ result = viableImplInCallContext(call, ctx)
+ or
+ result = viableCallableLambda(call, TDataFlowCallSome(ctx))
+ or
+ exists(DataFlowCallable enclosing |
+ mayBenefitFromCallContextExt(call, enclosing) and
+ enclosing = viableCallableExt(ctx) and
+ result = viableCallableLambda(call, TDataFlowCallNone())
+ )
+ }
+
/**
* Holds if the call context `ctx` reduces the set of viable run-time
* dispatch targets of call `call` in `c`.
@@ -324,10 +581,10 @@ private module Cached {
cached
predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) {
exists(int tgts, int ctxtgts |
- mayBenefitFromCallContext(call, c) and
- c = viableCallable(ctx) and
- ctxtgts = count(viableImplInCallContext(call, ctx)) and
- tgts = strictcount(viableCallable(call)) and
+ mayBenefitFromCallContextExt(call, c) and
+ c = viableCallableExt(ctx) and
+ ctxtgts = count(viableImplInCallContextExt(call, ctx)) and
+ tgts = strictcount(viableCallableExt(call)) and
ctxtgts < tgts
)
}
@@ -339,7 +596,7 @@ private module Cached {
*/
cached
DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
- result = viableImplInCallContext(call, ctx) and
+ result = viableImplInCallContextExt(call, ctx) and
reducedViableImplInCallContext(call, _, ctx)
}
@@ -351,10 +608,10 @@ private module Cached {
cached
predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) {
exists(int tgts, int ctxtgts |
- mayBenefitFromCallContext(call, _) and
- c = viableCallable(call) and
- ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and
- tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and
+ mayBenefitFromCallContextExt(call, _) and
+ c = viableCallableExt(call) and
+ ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and
+ tgts = strictcount(DataFlowCall ctx | viableCallableExt(ctx) = call.getEnclosingCallable()) and
ctxtgts < tgts
)
}
@@ -367,7 +624,7 @@ private module Cached {
*/
cached
DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) {
- result = viableImplInCallContext(call, ctx) and
+ result = viableImplInCallContextExt(call, ctx) and
reducedViableImplInReturn(result, call)
}
}
@@ -481,6 +738,11 @@ private module Cached {
TBooleanNone() or
TBooleanSome(boolean b) { b = true or b = false }
+ cached
+ newtype TDataFlowCallOption =
+ TDataFlowCallNone() or
+ TDataFlowCallSome(DataFlowCall call)
+
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
@@ -777,7 +1039,7 @@ ReturnPosition getReturnPosition(ReturnNodeExt ret) {
bindingset[cc, callable]
predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) {
- cc instanceof CallContextAny and callable = viableCallable(call)
+ cc instanceof CallContextAny and callable = viableCallableExt(call)
or
exists(DataFlowCallable c0, DataFlowCall call0 |
call0.getEnclosingCallable() = callable and
@@ -791,14 +1053,14 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
exists(DataFlowCall ctx | cc = TSpecificCall(ctx) |
if reducedViableImplInCallContext(call, _, ctx)
then result = prunedViableImplInCallContext(call, ctx)
- else result = viableCallable(call)
+ else result = viableCallableExt(call)
)
or
- result = viableCallable(call) and cc instanceof CallContextSomeCall
+ result = viableCallableExt(call) and cc instanceof CallContextSomeCall
or
- result = viableCallable(call) and cc instanceof CallContextAny
+ result = viableCallableExt(call) and cc instanceof CallContextAny
or
- result = viableCallable(call) and cc instanceof CallContextReturn
+ result = viableCallableExt(call) and cc instanceof CallContextReturn
}
predicate read = readStep/3;
@@ -812,6 +1074,19 @@ class BooleanOption extends TBooleanOption {
}
}
+/** An optional `DataFlowCall`. */
+class DataFlowCallOption extends TDataFlowCallOption {
+ string toString() {
+ this = TDataFlowCallNone() and
+ result = "(none)"
+ or
+ exists(DataFlowCall call |
+ this = TDataFlowCallSome(call) and
+ result = call.toString()
+ )
+ }
+}
+
/** Content tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;
diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
index ebd8f51a8f1..a4591187c3d 100644
--- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
+++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
@@ -5,7 +5,6 @@ private import DataFlowPublic
private import DataFlowDispatch
private import DataFlowImplCommon
private import ControlFlowReachability
-private import DelegateDataFlow
private import FlowSummaryImpl as FlowSummaryImpl
private import semmle.code.csharp.dataflow.FlowSummary
private import semmle.code.csharp.Caching
@@ -433,7 +432,7 @@ private class Argument extends Expr {
this = dc.getQualifier() and arg = -1 and not dc.getAStaticTarget().(Modifiable).isStatic()
).getCall()
or
- this = call.(DelegateCall).getArgument(arg)
+ this = call.(DelegateLikeCall).getArgument(arg)
}
/**
@@ -2022,3 +2021,69 @@ class Unit extends TUnit {
* This predicate is only used for consistency checks.
*/
predicate isImmutableOrUnobservable(Node n) { none() }
+
+class LambdaCallKind = Unit;
+
+/** Holds if `creation` is an expression that creates a delegate for `c`. */
+predicate lambdaCreation(ExprNode creation, LambdaCallKind kind, DataFlowCallable c) {
+ exists(Expr e | e = creation.getExpr() |
+ c = e.(AnonymousFunctionExpr)
+ or
+ c = e.(CallableAccess).getTarget().getUnboundDeclaration()
+ or
+ c = e.(AddressOfExpr).getOperand().(CallableAccess).getTarget().getUnboundDeclaration()
+ ) and
+ kind = TMkUnit()
+}
+
+private class LambdaConfiguration extends ControlFlowReachabilityConfiguration {
+ LambdaConfiguration() { this = "LambdaConfiguration" }
+
+ override predicate candidate(
+ Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
+ ) {
+ e1 = e2.(DelegateLikeCall).getExpr() and
+ exactScope = false and
+ scope = e2 and
+ isSuccessor = true
+ or
+ e1 = e2.(DelegateCreation).getArgument() and
+ exactScope = false and
+ scope = e2 and
+ isSuccessor = true
+ }
+}
+
+/** Holds if `call` is a lambda call where `receiver` is the lambda expression. */
+predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
+ (
+ exists(LambdaConfiguration x, DelegateLikeCall dc |
+ x.hasExprPath(dc.getExpr(), receiver.(ExprNode).getControlFlowNode(), dc,
+ call.getControlFlowNode())
+ )
+ or
+ receiver = call.(SummaryDelegateCall).getParameterNode()
+ ) and
+ kind = TMkUnit()
+}
+
+/** Extra data-flow steps needed for lamba flow analysis. */
+predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) {
+ exists(Ssa::Definition def |
+ LocalFlow::localSsaFlowStep(def, nodeFrom, nodeTo) and
+ LocalFlow::usesInstanceField(def) and
+ preservesValue = true
+ )
+ or
+ exists(LambdaConfiguration x, DelegateCreation dc |
+ x.hasExprPath(dc.getArgument(), nodeFrom.(ExprNode).getControlFlowNode(), dc,
+ nodeTo.(ExprNode).getControlFlowNode()) and
+ preservesValue = false
+ )
+ or
+ exists(AddEventExpr aee |
+ nodeFrom.asExpr() = aee.getRValue() and
+ nodeTo.asExpr().(EventRead).getTarget() = aee.getTarget() and
+ preservesValue = false
+ )
+}
diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DelegateDataFlow.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DelegateDataFlow.qll
index e6b15b95d7f..df6f62a3975 100755
--- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DelegateDataFlow.qll
+++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DelegateDataFlow.qll
@@ -1,4 +1,6 @@
/**
+ * DEPRECATED.
+ *
* INTERNAL: Do not use.
*
* Provides classes for resolving delegate calls.
@@ -108,7 +110,7 @@ abstract private class DelegateLikeFlowSink extends DataFlow::Node {
* possible delegates resolved on line 6.
*/
cached
- Callable getARuntimeTarget(CallContext context) {
+ deprecated Callable getARuntimeTarget(CallContext context) {
exists(DelegateLikeFlowSource dfs |
flowsFrom(this, dfs, _, context) and
result = dfs.getCallable()
@@ -117,7 +119,7 @@ abstract private class DelegateLikeFlowSink extends DataFlow::Node {
}
/** A delegate or function pointer call expression. */
-class DelegateLikeCallExpr extends DelegateLikeFlowSink, DataFlow::ExprNode {
+deprecated class DelegateLikeCallExpr extends DelegateLikeFlowSink, DataFlow::ExprNode {
DelegateLikeCall dc;
DelegateLikeCallExpr() { this.getExpr() = dc.getExpr() }
@@ -126,16 +128,8 @@ class DelegateLikeCallExpr extends DelegateLikeFlowSink, DataFlow::ExprNode {
DelegateLikeCall getCall() { result = dc }
}
-/** A parameter of delegate type belonging to a callable with a flow summary. */
-class SummaryDelegateParameterSink extends DelegateLikeFlowSink, ParameterNode {
- SummaryDelegateParameterSink() {
- this.getType() instanceof SystemLinqExpressions::DelegateExtType and
- this.isParameterOf(any(SummarizedCallable c), _)
- }
-}
-
/** A delegate expression that is added to an event. */
-class AddEventSource extends DelegateLikeFlowSink, DataFlow::ExprNode {
+deprecated class AddEventSource extends DelegateLikeFlowSink, DataFlow::ExprNode {
AddEventExpr ae;
AddEventSource() { this.getExpr() = ae.getRValue() }
@@ -173,7 +167,7 @@ private class NormalReturnNode extends Node {
* `node` goes through a returned expression. The call context `lastCall`
* records the last call on the path from `node` to `sink`, if any.
*/
-private predicate flowsFrom(
+deprecated private predicate flowsFrom(
DelegateLikeFlowSink sink, DataFlow::Node node, boolean isReturned, CallContext lastCall
) {
// Base case
@@ -244,7 +238,7 @@ private predicate flowsFrom(
* previous call if it exists, otherwise `call` is the last call.
*/
bindingset[call, i]
-private CallContext getLastCall(CallContext prevLastCall, Expr call, int i) {
+deprecated private CallContext getLastCall(CallContext prevLastCall, Expr call, int i) {
prevLastCall instanceof EmptyCallContext and
result.(ArgumentCallContext).isArgument(call, i)
or
@@ -263,7 +257,7 @@ private predicate flowIntoNonDelegateCall(NonDelegateCall call, Expr arg, DotNet
}
pragma[noinline]
-private predicate flowIntoDelegateCall(DelegateLikeCall call, Callable c, Expr arg, int i) {
+deprecated private predicate flowIntoDelegateCall(DelegateLikeCall call, Callable c, Expr arg, int i) {
exists(DelegateLikeFlowSource dfs, DelegateLikeCallExpr dce |
// the call context is irrelevant because the delegate call
// itself will be the context
@@ -280,7 +274,7 @@ private predicate flowOutOfNonDelegateCall(NonDelegateCall call, NormalReturnNod
}
pragma[noinline]
-private predicate flowOutOfDelegateCall(
+deprecated private predicate flowOutOfDelegateCall(
DelegateLikeCall dc, NormalReturnNode ret, CallContext lastCall
) {
exists(DelegateLikeFlowSource dfs, DelegateLikeCallExpr dce, Callable c |
diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImpl.qll
index 7f54d3287d1..41da2b8f0b5 100644
--- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImpl.qll
+++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImpl.qll
@@ -235,7 +235,6 @@ private module CallGraph {
}
private module SimpleDelegateAnalysis {
- private import semmle.code.csharp.dataflow.internal.DelegateDataFlow
private import semmle.code.csharp.dataflow.internal.Steps
private import semmle.code.csharp.frameworks.system.linq.Expressions
diff --git a/csharp/ql/src/semmle/code/csharp/exprs/Call.qll b/csharp/ql/src/semmle/code/csharp/exprs/Call.qll
index 078ecd1a52a..6dc88e941ef 100644
--- a/csharp/ql/src/semmle/code/csharp/exprs/Call.qll
+++ b/csharp/ql/src/semmle/code/csharp/exprs/Call.qll
@@ -8,6 +8,8 @@ import Expr
import semmle.code.csharp.Callable
import semmle.code.csharp.dataflow.CallContext as CallContext
private import semmle.code.csharp.dataflow.internal.DelegateDataFlow
+private import semmle.code.csharp.dataflow.internal.DataFlowDispatch
+private import semmle.code.csharp.dataflow.internal.DataFlowImplCommon
private import semmle.code.csharp.dispatch.Dispatch
private import dotnet
@@ -536,10 +538,12 @@ class DelegateLikeCall extends Call, DelegateLikeCall_ {
override Callable getTarget() { none() }
/**
+ * DEPRECATED: Use `getARuntimeTarget/0` instead.
+ *
* Gets a potential run-time target of this delegate or function pointer call in the given
* call context `cc`.
*/
- Callable getARuntimeTarget(CallContext::CallContext cc) {
+ deprecated Callable getARuntimeTarget(CallContext::CallContext cc) {
exists(DelegateLikeCallExpr call |
this = call.getCall() and
result = call.getARuntimeTarget(cc)
@@ -562,7 +566,12 @@ class DelegateLikeCall extends Call, DelegateLikeCall_ {
*/
Expr getExpr() { result = this.getChild(-1) }
- override Callable getARuntimeTarget() { result = getARuntimeTarget(_) }
+ final override Callable getARuntimeTarget() {
+ exists(ExplicitDelegateLikeDataFlowCall call |
+ this = call.getCall() and
+ result = viableCallableLambda(call, _)
+ )
+ }
override Expr getRuntimeArgument(int i) { result = getArgument(i) }
}
@@ -582,10 +591,12 @@ class DelegateLikeCall extends Call, DelegateLikeCall_ {
*/
class DelegateCall extends DelegateLikeCall, @delegate_invocation_expr {
/**
+ * DEPRECATED: Use `getARuntimeTarget/0` instead.
+ *
* Gets a potential run-time target of this delegate call in the given
* call context `cc`.
*/
- override Callable getARuntimeTarget(CallContext::CallContext cc) {
+ deprecated override Callable getARuntimeTarget(CallContext::CallContext cc) {
result = DelegateLikeCall.super.getARuntimeTarget(cc)
or
exists(AddEventSource aes, CallContext::CallContext cc2 |
@@ -601,16 +612,16 @@ class DelegateCall extends DelegateLikeCall, @delegate_invocation_expr {
)
}
- private AddEventSource getAnAddEventSource(Callable enclosingCallable) {
+ deprecated private AddEventSource getAnAddEventSource(Callable enclosingCallable) {
this.getExpr().(EventAccess).getTarget() = result.getEvent() and
enclosingCallable = result.getExpr().getEnclosingCallable()
}
- private AddEventSource getAnAddEventSourceSameEnclosingCallable() {
+ deprecated private AddEventSource getAnAddEventSourceSameEnclosingCallable() {
result = getAnAddEventSource(this.getEnclosingCallable())
}
- private AddEventSource getAnAddEventSourceDifferentEnclosingCallable() {
+ deprecated private AddEventSource getAnAddEventSourceDifferentEnclosingCallable() {
exists(Callable c | result = getAnAddEventSource(c) | c != this.getEnclosingCallable())
}
diff --git a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected
index addcc3c0ff7..731de330946 100644
--- a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected
+++ b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected
@@ -1,28 +1,55 @@
-summaryDelegateCall
-| file://:0:0:0:0 | valueFactory | DelegateFlow.cs:104:23:104:30 | (...) => ... | DelegateFlow.cs:105:23:105:23 | access to local variable f |
-| file://:0:0:0:0 | valueFactory | DelegateFlow.cs:106:13:106:20 | (...) => ... | DelegateFlow.cs:107:23:107:23 | access to local variable f |
delegateCall
-| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:5:10:5:11 | M1 | DelegateFlow.cs:17:12:17:13 | delegate creation of type Action |
-| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:5:10:5:11 | M1 | DelegateFlow.cs:22:12:22:12 | access to parameter a |
-| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:16:12:16:19 | (...) => ... | DelegateFlow.cs:16:12:16:19 | (...) => ... |
-| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:27:12:27:19 | (...) => ... | DelegateFlow.cs:22:12:22:12 | access to parameter a |
-| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:98:9:98:37 | LocalFunction | DelegateFlow.cs:99:12:99:24 | delegate creation of type Action |
-| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:119:18:119:27 | (...) => ... | DelegateFlow.cs:114:15:114:15 | access to parameter a |
-| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:125:15:125:24 | (...) => ... | DelegateFlow.cs:125:15:125:24 | (...) => ... |
-| DelegateFlow.cs:11:9:11:12 | delegate call | DelegateFlow.cs:10:13:10:20 | (...) => ... | file://:0:0:0:0 | |
-| DelegateFlow.cs:33:9:33:13 | delegate call | DelegateFlow.cs:38:12:38:25 | (...) => ... | DelegateFlow.cs:38:12:38:25 | (...) => ... |
-| DelegateFlow.cs:38:19:38:22 | delegate call | DelegateFlow.cs:5:10:5:11 | M1 | DelegateFlow.cs:33:12:33:12 | access to parameter a |
-| DelegateFlow.cs:44:15:44:22 | delegate call | DelegateFlow.cs:43:22:43:29 | (...) => ... | DelegateFlow.cs:50:18:50:23 | dynamic access to member Prop |
-| DelegateFlow.cs:57:9:57:11 | delegate call | DelegateFlow.cs:53:34:53:47 | (...) => ... | file://:0:0:0:0 | |
-| DelegateFlow.cs:57:9:57:14 | delegate call | DelegateFlow.cs:53:40:53:47 | (...) => ... | file://:0:0:0:0 | |
-| DelegateFlow.cs:67:9:67:16 | delegate call | DelegateFlow.cs:62:16:62:23 | (...) => ... | file://:0:0:0:0 | |
-| DelegateFlow.cs:77:9:77:15 | delegate call | DelegateFlow.cs:55:10:55:11 | M9 | file://:0:0:0:0 | |
-| DelegateFlow.cs:77:9:77:15 | delegate call | DelegateFlow.cs:65:10:65:12 | M11 | file://:0:0:0:0 | |
-| DelegateFlow.cs:84:9:84:15 | delegate call | DelegateFlow.cs:55:10:55:11 | M9 | DelegateFlow.cs:78:13:78:14 | delegate creation of type EventHandler |
-| DelegateFlow.cs:84:9:84:15 | delegate call | DelegateFlow.cs:65:10:65:12 | M11 | file://:0:0:0:0 | |
-| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:55:10:55:11 | M9 | DelegateFlow.cs:90:13:90:30 | delegate creation of type MyDelegate |
-| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:65:10:65:12 | M11 | DelegateFlow.cs:91:13:91:47 | delegate creation of type MyDelegate |
-| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:74:17:74:19 | M12 | DelegateFlow.cs:92:13:92:15 | delegate creation of type MyDelegate |
-| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:93:13:93:21 | (...) => ... | DelegateFlow.cs:93:13:93:21 | (...) => ... |
-| DelegateFlow.cs:132:9:132:11 | delegate call | DelegateFlow.cs:131:17:131:24 | (...) => ... | file://:0:0:0:0 | |
-| DelegateFlow.cs:132:9:132:11 | delegate call | DelegateFlow.cs:135:29:135:36 | (...) => ... | DelegateFlow.cs:135:29:135:36 | (...) => ... |
+| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:5:10:5:11 | M1 |
+| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:16:12:16:19 | (...) => ... |
+| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:27:12:27:19 | (...) => ... |
+| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:98:9:98:37 | LocalFunction |
+| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:119:18:119:27 | (...) => ... |
+| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:125:15:125:24 | (...) => ... |
+| DelegateFlow.cs:11:9:11:12 | delegate call | DelegateFlow.cs:10:13:10:20 | (...) => ... |
+| DelegateFlow.cs:33:9:33:13 | delegate call | DelegateFlow.cs:38:12:38:25 | (...) => ... |
+| DelegateFlow.cs:38:19:38:22 | delegate call | DelegateFlow.cs:5:10:5:11 | M1 |
+| DelegateFlow.cs:44:15:44:22 | delegate call | DelegateFlow.cs:43:22:43:29 | (...) => ... |
+| DelegateFlow.cs:57:9:57:11 | delegate call | DelegateFlow.cs:53:34:53:47 | (...) => ... |
+| DelegateFlow.cs:57:9:57:14 | delegate call | DelegateFlow.cs:53:40:53:47 | (...) => ... |
+| DelegateFlow.cs:67:9:67:16 | delegate call | DelegateFlow.cs:62:16:62:23 | (...) => ... |
+| DelegateFlow.cs:77:9:77:15 | delegate call | DelegateFlow.cs:55:10:55:11 | M9 |
+| DelegateFlow.cs:77:9:77:15 | delegate call | DelegateFlow.cs:65:10:65:12 | M11 |
+| DelegateFlow.cs:84:9:84:15 | delegate call | DelegateFlow.cs:55:10:55:11 | M9 |
+| DelegateFlow.cs:84:9:84:15 | delegate call | DelegateFlow.cs:65:10:65:12 | M11 |
+| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:55:10:55:11 | M9 |
+| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:65:10:65:12 | M11 |
+| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:74:17:74:19 | M12 |
+| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:93:13:93:21 | (...) => ... |
+| DelegateFlow.cs:114:9:114:16 | function pointer call | DelegateFlow.cs:7:17:7:18 | M2 |
+| DelegateFlow.cs:125:9:125:25 | function pointer call | DelegateFlow.cs:7:17:7:18 | M2 |
+| DelegateFlow.cs:132:9:132:11 | delegate call | DelegateFlow.cs:131:17:131:24 | (...) => ... |
+| DelegateFlow.cs:132:9:132:11 | delegate call | DelegateFlow.cs:135:29:135:36 | (...) => ... |
+viableLambda
+| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:16:9:16:20 | call to method M2 | DelegateFlow.cs:16:12:16:19 | (...) => ... |
+| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:17:9:17:14 | call to method M2 | DelegateFlow.cs:5:10:5:11 | M1 |
+| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:22:9:22:13 | call to method M2 | DelegateFlow.cs:5:10:5:11 | M1 |
+| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:22:9:22:13 | call to method M2 | DelegateFlow.cs:27:12:27:19 | (...) => ... |
+| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:99:9:99:25 | call to method M2 | DelegateFlow.cs:98:9:98:37 | LocalFunction |
+| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:114:9:114:16 | function pointer call | DelegateFlow.cs:119:18:119:27 | (...) => ... |
+| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:125:9:125:25 | function pointer call | DelegateFlow.cs:125:15:125:24 | (...) => ... |
+| DelegateFlow.cs:11:9:11:12 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:10:13:10:20 | (...) => ... |
+| DelegateFlow.cs:33:9:33:13 | delegate call | DelegateFlow.cs:38:9:38:30 | call to method M6 | DelegateFlow.cs:38:12:38:25 | (...) => ... |
+| DelegateFlow.cs:38:19:38:22 | delegate call | DelegateFlow.cs:33:9:33:13 | delegate call | DelegateFlow.cs:5:10:5:11 | M1 |
+| DelegateFlow.cs:44:15:44:22 | delegate call | DelegateFlow.cs:50:9:50:14 | dynamic access to member Prop | DelegateFlow.cs:43:22:43:29 | (...) => ... |
+| DelegateFlow.cs:57:9:57:11 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:53:34:53:47 | (...) => ... |
+| DelegateFlow.cs:57:9:57:14 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:53:40:53:47 | (...) => ... |
+| DelegateFlow.cs:67:9:67:16 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:62:16:62:23 | (...) => ... |
+| DelegateFlow.cs:77:9:77:15 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:55:10:55:11 | M9 |
+| DelegateFlow.cs:77:9:77:15 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:65:10:65:12 | M11 |
+| DelegateFlow.cs:84:9:84:15 | delegate call | DelegateFlow.cs:78:9:78:15 | call to method M13 | DelegateFlow.cs:55:10:55:11 | M9 |
+| DelegateFlow.cs:84:9:84:15 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:65:10:65:12 | M11 |
+| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:90:9:90:31 | call to local function M14 | DelegateFlow.cs:55:10:55:11 | M9 |
+| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:91:9:91:48 | call to local function M14 | DelegateFlow.cs:65:10:65:12 | M11 |
+| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:92:9:92:16 | call to local function M14 | DelegateFlow.cs:74:17:74:19 | M12 |
+| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:93:9:93:22 | call to local function M14 | DelegateFlow.cs:93:13:93:21 | (...) => ... |
+| DelegateFlow.cs:114:9:114:16 | function pointer call | DelegateFlow.cs:119:9:119:28 | call to method M16 | DelegateFlow.cs:7:17:7:18 | M2 |
+| DelegateFlow.cs:125:9:125:25 | function pointer call | file://:0:0:0:0 | (none) | DelegateFlow.cs:7:17:7:18 | M2 |
+| DelegateFlow.cs:132:9:132:11 | delegate call | DelegateFlow.cs:135:25:135:40 | call to method M19 | DelegateFlow.cs:135:29:135:36 | (...) => ... |
+| DelegateFlow.cs:132:9:132:11 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:131:17:131:24 | (...) => ... |
+| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Lazy | DelegateFlow.cs:105:9:105:24 | object creation of type Lazy | DelegateFlow.cs:104:23:104:30 | (...) => ... |
+| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Lazy | DelegateFlow.cs:107:9:107:24 | object creation of type Lazy | DelegateFlow.cs:106:13:106:20 | (...) => ... |
diff --git a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.ql b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.ql
index c01feabc2f9..6ec7637bf6c 100644
--- a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.ql
+++ b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.ql
@@ -1,22 +1,34 @@
import csharp
-import semmle.code.csharp.dataflow.internal.DataFlowPrivate
-import semmle.code.csharp.dataflow.internal.DelegateDataFlow
+import semmle.code.csharp.dataflow.internal.DataFlowImplCommon
+import semmle.code.csharp.dataflow.internal.DataFlowDispatch
-private class NodeAdjusted extends TNode {
- string toString() { result = this.(DataFlow::Node).toString() }
+query predicate delegateCall(DelegateLikeCall dc, Callable c) { c = dc.getARuntimeTarget() }
+
+private class LocatableDataFlowCallOption extends DataFlowCallOption {
+ Location getLocation() {
+ this = TDataFlowCallNone() and
+ result instanceof EmptyLocation
+ or
+ exists(DataFlowCall call |
+ this = TDataFlowCallSome(call) and
+ result = call.getLocation()
+ )
+ }
+}
+
+private class LocatableDataFlowCall extends TDataFlowCall {
+ string toString() { result = this.(DataFlowCall).toString() }
Location getLocation() {
exists(Location l |
- l = this.(DataFlow::Node).getLocation() and
+ l = this.(DataFlowCall).getLocation() and
if l instanceof SourceLocation then result = l else result instanceof EmptyLocation
)
}
}
-query predicate summaryDelegateCall(NodeAdjusted sink, Callable c, CallContext::CallContext cc) {
- c = sink.(SummaryDelegateParameterSink).getARuntimeTarget(cc)
-}
-
-query predicate delegateCall(DelegateCall dc, Callable c, CallContext::CallContext cc) {
- c = dc.getARuntimeTarget(cc)
+query predicate viableLambda(
+ LocatableDataFlowCall call, LocatableDataFlowCallOption lastCall, DataFlowCallable target
+) {
+ target = viableCallableLambda(call, lastCall)
}
diff --git a/csharp/ql/test/library-tests/dataflow/functionpointers/FunctionPointerFlow.expected b/csharp/ql/test/library-tests/dataflow/functionpointers/FunctionPointerFlow.expected
index ad2bfb887c0..a25f9c14539 100644
--- a/csharp/ql/test/library-tests/dataflow/functionpointers/FunctionPointerFlow.expected
+++ b/csharp/ql/test/library-tests/dataflow/functionpointers/FunctionPointerFlow.expected
@@ -1,9 +1,20 @@
-| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:5:24:5:27 | Log1 | FunctionPointerFlow.cs:21:12:21:16 | &... |
-| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:6:24:6:27 | Log2 | FunctionPointerFlow.cs:26:12:26:12 | access to parameter a |
-| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:10:24:10:27 | Log6 | FunctionPointerFlow.cs:26:12:26:12 | access to parameter a |
-| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:46:9:46:44 | LocalFunction | FunctionPointerFlow.cs:47:12:47:25 | &... |
-| FunctionPointerFlow.cs:16:9:16:12 | function pointer call | FunctionPointerFlow.cs:7:24:7:27 | Log3 | file://:0:0:0:0 | |
-| FunctionPointerFlow.cs:41:9:41:15 | function pointer call | FunctionPointerFlow.cs:8:24:8:27 | Log4 | file://:0:0:0:0 | |
-| FunctionPointerFlow.cs:54:9:54:16 | function pointer call | FunctionPointerFlow.cs:9:24:9:27 | Log5 | file://:0:0:0:0 | |
-| FunctionPointerFlow.cs:59:9:59:13 | function pointer call | FunctionPointerFlow.cs:24:24:24:25 | M4 | FunctionPointerFlow.cs:64:13:64:15 | &... |
-| FunctionPointerFlow.cs:69:9:69:13 | function pointer call | FunctionPointerFlow.cs:72:24:72:26 | M17 | FunctionPointerFlow.cs:81:13:81:16 | &... |
+fptrCall
+| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:5:24:5:27 | Log1 |
+| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:6:24:6:27 | Log2 |
+| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:10:24:10:27 | Log6 |
+| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:46:9:46:44 | LocalFunction |
+| FunctionPointerFlow.cs:16:9:16:12 | function pointer call | FunctionPointerFlow.cs:7:24:7:27 | Log3 |
+| FunctionPointerFlow.cs:41:9:41:15 | function pointer call | FunctionPointerFlow.cs:8:24:8:27 | Log4 |
+| FunctionPointerFlow.cs:54:9:54:16 | function pointer call | FunctionPointerFlow.cs:9:24:9:27 | Log5 |
+| FunctionPointerFlow.cs:59:9:59:13 | function pointer call | FunctionPointerFlow.cs:24:24:24:25 | M4 |
+| FunctionPointerFlow.cs:69:9:69:13 | function pointer call | FunctionPointerFlow.cs:72:24:72:26 | M17 |
+fptrCallContext
+| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:21:9:21:17 | call to method M2 | FunctionPointerFlow.cs:5:24:5:27 | Log1 |
+| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:26:9:26:13 | call to method M2 | FunctionPointerFlow.cs:6:24:6:27 | Log2 |
+| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:26:9:26:13 | call to method M2 | FunctionPointerFlow.cs:10:24:10:27 | Log6 |
+| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:47:9:47:26 | call to method M2 | FunctionPointerFlow.cs:46:9:46:44 | LocalFunction |
+| FunctionPointerFlow.cs:16:9:16:12 | function pointer call | file://:0:0:0:0 | (none) | FunctionPointerFlow.cs:7:24:7:27 | Log3 |
+| FunctionPointerFlow.cs:41:9:41:15 | function pointer call | file://:0:0:0:0 | (none) | FunctionPointerFlow.cs:8:24:8:27 | Log4 |
+| FunctionPointerFlow.cs:54:9:54:16 | function pointer call | file://:0:0:0:0 | (none) | FunctionPointerFlow.cs:9:24:9:27 | Log5 |
+| FunctionPointerFlow.cs:59:9:59:13 | function pointer call | FunctionPointerFlow.cs:64:9:64:23 | call to method M10 | FunctionPointerFlow.cs:24:24:24:25 | M4 |
+| FunctionPointerFlow.cs:69:9:69:13 | function pointer call | FunctionPointerFlow.cs:81:9:81:29 | call to method M16 | FunctionPointerFlow.cs:72:24:72:26 | M17 |
diff --git a/csharp/ql/test/library-tests/dataflow/functionpointers/FunctionPointerFlow.ql b/csharp/ql/test/library-tests/dataflow/functionpointers/FunctionPointerFlow.ql
index 3434d068329..fced49bc9c6 100644
--- a/csharp/ql/test/library-tests/dataflow/functionpointers/FunctionPointerFlow.ql
+++ b/csharp/ql/test/library-tests/dataflow/functionpointers/FunctionPointerFlow.ql
@@ -1,5 +1,38 @@
import csharp
+import semmle.code.csharp.dataflow.internal.DataFlowImplCommon
+import semmle.code.csharp.dataflow.internal.DataFlowDispatch
-query predicate fptrCall(FunctionPointerCall fptrc, Callable c, CallContext::CallContext cc) {
- c = fptrc.getARuntimeTarget(cc)
+query predicate fptrCall(FunctionPointerCall dc, Callable c) { c = dc.getARuntimeTarget() }
+
+private class LocatableDataFlowCallOption extends DataFlowCallOption {
+ Location getLocation() {
+ this = TDataFlowCallNone() and
+ result instanceof EmptyLocation
+ or
+ exists(DataFlowCall call |
+ this = TDataFlowCallSome(call) and
+ result = call.getLocation()
+ )
+ }
+}
+
+private class LocatableDataFlowCall extends TDataFlowCall {
+ LocatableDataFlowCall() {
+ this.(ExplicitDelegateLikeDataFlowCall).getCall() instanceof FunctionPointerCall
+ }
+
+ string toString() { result = this.(DataFlowCall).toString() }
+
+ Location getLocation() {
+ exists(Location l |
+ l = this.(DataFlowCall).getLocation() and
+ if l instanceof SourceLocation then result = l else result instanceof EmptyLocation
+ )
+ }
+}
+
+query predicate fptrCallContext(
+ LocatableDataFlowCall call, LocatableDataFlowCallOption lastCall, DataFlowCallable target
+) {
+ target = viableCallableLambda(call, lastCall)
}
From b11e15154fc6aabd32c07b996920c0d514b36360 Mon Sep 17 00:00:00 2001
From: Tom Hvitved
Date: Thu, 4 Mar 2021 09:12:02 +0100
Subject: [PATCH 125/725] Data flow: Sync files and add stubs
---
.../dataflow/internal/DataFlowImplCommon.qll | 309 +++++++++++++++++-
.../cpp/dataflow/internal/DataFlowPrivate.qll | 11 +
.../dataflow/internal/DataFlowImplCommon.qll | 309 +++++++++++++++++-
.../ir/dataflow/internal/DataFlowPrivate.qll | 11 +
.../dataflow/internal/DataFlowImplCommon.qll | 309 +++++++++++++++++-
.../dataflow/internal/DataFlowPrivate.qll | 11 +
.../new/internal/DataFlowImplCommon.qll | 309 +++++++++++++++++-
.../dataflow/new/internal/DataFlowPrivate.qll | 11 +
8 files changed, 1212 insertions(+), 68 deletions(-)
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
index 1319f972037..a51c20c2288 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
@@ -26,15 +26,243 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) {
tupleLimit = 1000
}
+/**
+ * Provides a simple data-flow analysis for resolving lambda calls. The analysis
+ * currently excludes read-steps, store-steps, and flow-through.
+ *
+ * The analysis uses non-linear recursion: When computing a flow path in or out
+ * of a call, we use the results of the analysis recursively to resolve lamba
+ * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly.
+ */
+private module LambdaFlow {
+ private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNode p) {
+ p.isParameterOf(viableCallable(call), i)
+ }
+
+ private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) {
+ p.isParameterOf(viableCallableLambda(call, _), i)
+ }
+
+ private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
+ exists(int i |
+ viableParamNonLambda(call, i, p) and
+ arg.argumentOf(call, i)
+ )
+ }
+
+ private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
+ exists(int i |
+ viableParamLambda(call, i, p) and
+ arg.argumentOf(call, i)
+ )
+ }
+
+ private newtype TReturnPositionSimple =
+ TReturnPositionSimple0(DataFlowCallable c, ReturnKind kind) {
+ exists(ReturnNode ret |
+ c = getNodeEnclosingCallable(ret) and
+ kind = ret.getKind()
+ )
+ }
+
+ pragma[noinline]
+ private TReturnPositionSimple getReturnPositionSimple(ReturnNode ret, ReturnKind kind) {
+ result = TReturnPositionSimple0(getNodeEnclosingCallable(ret), kind)
+ }
+
+ pragma[nomagic]
+ private TReturnPositionSimple viableReturnPosNonLambda(DataFlowCall call, ReturnKind kind) {
+ result = TReturnPositionSimple0(viableCallable(call), kind)
+ }
+
+ pragma[nomagic]
+ private TReturnPositionSimple viableReturnPosLambda(
+ DataFlowCall call, DataFlowCallOption lastCall, ReturnKind kind
+ ) {
+ result = TReturnPositionSimple0(viableCallableLambda(call, lastCall), kind)
+ }
+
+ private predicate viableReturnPosOutNonLambda(
+ DataFlowCall call, TReturnPositionSimple pos, OutNode out
+ ) {
+ exists(ReturnKind kind |
+ pos = viableReturnPosNonLambda(call, kind) and
+ out = getAnOutNode(call, kind)
+ )
+ }
+
+ private predicate viableReturnPosOutLambda(
+ DataFlowCall call, DataFlowCallOption lastCall, TReturnPositionSimple pos, OutNode out
+ ) {
+ exists(ReturnKind kind |
+ pos = viableReturnPosLambda(call, lastCall, kind) and
+ out = getAnOutNode(call, kind)
+ )
+ }
+
+ /**
+ * Holds if data can flow (inter-procedurally) from `node` (of type `t`) to
+ * the lambda call `lambdaCall`.
+ *
+ * The parameter `toReturn` indicates whether the path from `node` to
+ * `lambdaCall` goes through a return, and `toJump` whether the path goes
+ * through a jump step.
+ *
+ * The call context `lastCall` records the last call on the path from `node`
+ * to `lambdaCall`, if any. That is, `lastCall` is able to target the enclosing
+ * callable of `lambdaCall`.
+ */
+ pragma[nomagic]
+ predicate revLambdaFlow(
+ DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn,
+ boolean toJump, DataFlowCallOption lastCall
+ ) {
+ revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and
+ if node instanceof CastNode or node instanceof ArgumentNode or node instanceof ReturnNode
+ then compatibleTypes(t, getNodeType(node))
+ else any()
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlow0(
+ DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn,
+ boolean toJump, DataFlowCallOption lastCall
+ ) {
+ lambdaCall(lambdaCall, kind, node) and
+ t = getNodeType(node) and
+ toReturn = false and
+ toJump = false and
+ lastCall = TDataFlowCallNone()
+ or
+ // local flow
+ exists(Node mid, DataFlowType t0 |
+ revLambdaFlow(lambdaCall, kind, mid, t0, toReturn, toJump, lastCall)
+ |
+ simpleLocalFlowStep(node, mid) and
+ t = t0
+ or
+ exists(boolean preservesValue |
+ additionalLambdaFlowStep(node, mid, preservesValue) and
+ getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid)
+ |
+ preservesValue = false and
+ t = getNodeType(node)
+ or
+ preservesValue = true and
+ t = t0
+ )
+ )
+ or
+ // jump step
+ exists(Node mid, DataFlowType t0 |
+ revLambdaFlow(lambdaCall, kind, mid, t0, _, _, _) and
+ toReturn = false and
+ toJump = true and
+ lastCall = TDataFlowCallNone()
+ |
+ jumpStep(node, mid) and
+ t = t0
+ or
+ exists(boolean preservesValue |
+ additionalLambdaFlowStep(node, mid, preservesValue) and
+ getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid)
+ |
+ preservesValue = false and
+ t = getNodeType(node)
+ or
+ preservesValue = true and
+ t = t0
+ )
+ )
+ or
+ // flow into a callable
+ exists(ParameterNode p, DataFlowCallOption lastCall0, DataFlowCall call |
+ revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and
+ (
+ if lastCall0 = TDataFlowCallNone() and toJump = false
+ then lastCall = TDataFlowCallSome(call)
+ else lastCall = lastCall0
+ ) and
+ toReturn = false
+ |
+ viableParamArgNonLambda(call, p, node)
+ or
+ viableParamArgLambda(call, p, node) // non-linear recursion
+ )
+ or
+ // flow out of a callable
+ exists(TReturnPositionSimple pos |
+ revLambdaFlowOut(lambdaCall, kind, pos, t, toJump, lastCall) and
+ getReturnPositionSimple(node, node.(ReturnNode).getKind()) = pos and
+ toReturn = true
+ )
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlowOutLambdaCall(
+ DataFlowCall lambdaCall, LambdaCallKind kind, OutNode out, DataFlowType t, boolean toJump,
+ DataFlowCall call, DataFlowCallOption lastCall
+ ) {
+ revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and
+ exists(ReturnKindExt rk |
+ out = rk.getAnOutNode(call) and
+ lambdaCall(call, _, _)
+ )
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlowOut(
+ DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t,
+ boolean toJump, DataFlowCallOption lastCall
+ ) {
+ exists(DataFlowCall call, OutNode out |
+ revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and
+ viableReturnPosOutNonLambda(call, pos, out)
+ or
+ // non-linear recursion
+ revLambdaFlowOutLambdaCall(lambdaCall, kind, out, t, toJump, call, lastCall) and
+ viableReturnPosOutLambda(call, _, pos, out)
+ )
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlowIn(
+ DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump,
+ DataFlowCallOption lastCall
+ ) {
+ revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall)
+ }
+}
+
+private DataFlowCallable viableCallableExt(DataFlowCall call) {
+ result = viableCallable(call)
+ or
+ result = viableCallableLambda(call, _)
+}
+
cached
private module Cached {
+ /**
+ * Gets a viable target for the lambda call `call`.
+ *
+ * `lastCall` records the call required to reach `call` in order for the result
+ * to be a viable target, if any.
+ */
+ cached
+ DataFlowCallable viableCallableLambda(DataFlowCall call, DataFlowCallOption lastCall) {
+ exists(Node creation, LambdaCallKind kind |
+ LambdaFlow::revLambdaFlow(call, kind, creation, _, _, _, lastCall) and
+ lambdaCreation(creation, kind, result)
+ )
+ }
+
/**
* Holds if `p` is the `i`th parameter of a viable dispatch target of `call`.
* The instance parameter is considered to have index `-1`.
*/
pragma[nomagic]
private predicate viableParam(DataFlowCall call, int i, ParameterNode p) {
- p.isParameterOf(viableCallable(call), i)
+ p.isParameterOf(viableCallableExt(call), i)
}
/**
@@ -52,7 +280,7 @@ private module Cached {
pragma[nomagic]
private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
- viableCallable(call) = result.getCallable() and
+ viableCallableExt(call) = result.getCallable() and
kind = result.getKind()
}
@@ -317,6 +545,35 @@ private module Cached {
cached
private module DispatchWithCallContext {
+ /**
+ * Holds if the set of viable implementations that can be called by `call`
+ * might be improved by knowing the call context.
+ */
+ pragma[nomagic]
+ private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) {
+ mayBenefitFromCallContext(call, callable)
+ or
+ callable = call.getEnclosingCallable() and
+ exists(viableCallableLambda(call, TDataFlowCallSome(_)))
+ }
+
+ /**
+ * Gets a viable dispatch target of `call` in the context `ctx`. This is
+ * restricted to those `call`s for which a context might make a difference.
+ */
+ pragma[nomagic]
+ private DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) {
+ result = viableImplInCallContext(call, ctx)
+ or
+ result = viableCallableLambda(call, TDataFlowCallSome(ctx))
+ or
+ exists(DataFlowCallable enclosing |
+ mayBenefitFromCallContextExt(call, enclosing) and
+ enclosing = viableCallableExt(ctx) and
+ result = viableCallableLambda(call, TDataFlowCallNone())
+ )
+ }
+
/**
* Holds if the call context `ctx` reduces the set of viable run-time
* dispatch targets of call `call` in `c`.
@@ -324,10 +581,10 @@ private module Cached {
cached
predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) {
exists(int tgts, int ctxtgts |
- mayBenefitFromCallContext(call, c) and
- c = viableCallable(ctx) and
- ctxtgts = count(viableImplInCallContext(call, ctx)) and
- tgts = strictcount(viableCallable(call)) and
+ mayBenefitFromCallContextExt(call, c) and
+ c = viableCallableExt(ctx) and
+ ctxtgts = count(viableImplInCallContextExt(call, ctx)) and
+ tgts = strictcount(viableCallableExt(call)) and
ctxtgts < tgts
)
}
@@ -339,7 +596,7 @@ private module Cached {
*/
cached
DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
- result = viableImplInCallContext(call, ctx) and
+ result = viableImplInCallContextExt(call, ctx) and
reducedViableImplInCallContext(call, _, ctx)
}
@@ -351,10 +608,10 @@ private module Cached {
cached
predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) {
exists(int tgts, int ctxtgts |
- mayBenefitFromCallContext(call, _) and
- c = viableCallable(call) and
- ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and
- tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and
+ mayBenefitFromCallContextExt(call, _) and
+ c = viableCallableExt(call) and
+ ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and
+ tgts = strictcount(DataFlowCall ctx | viableCallableExt(ctx) = call.getEnclosingCallable()) and
ctxtgts < tgts
)
}
@@ -367,7 +624,7 @@ private module Cached {
*/
cached
DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) {
- result = viableImplInCallContext(call, ctx) and
+ result = viableImplInCallContextExt(call, ctx) and
reducedViableImplInReturn(result, call)
}
}
@@ -481,6 +738,11 @@ private module Cached {
TBooleanNone() or
TBooleanSome(boolean b) { b = true or b = false }
+ cached
+ newtype TDataFlowCallOption =
+ TDataFlowCallNone() or
+ TDataFlowCallSome(DataFlowCall call)
+
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
@@ -777,7 +1039,7 @@ ReturnPosition getReturnPosition(ReturnNodeExt ret) {
bindingset[cc, callable]
predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) {
- cc instanceof CallContextAny and callable = viableCallable(call)
+ cc instanceof CallContextAny and callable = viableCallableExt(call)
or
exists(DataFlowCallable c0, DataFlowCall call0 |
call0.getEnclosingCallable() = callable and
@@ -791,14 +1053,14 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
exists(DataFlowCall ctx | cc = TSpecificCall(ctx) |
if reducedViableImplInCallContext(call, _, ctx)
then result = prunedViableImplInCallContext(call, ctx)
- else result = viableCallable(call)
+ else result = viableCallableExt(call)
)
or
- result = viableCallable(call) and cc instanceof CallContextSomeCall
+ result = viableCallableExt(call) and cc instanceof CallContextSomeCall
or
- result = viableCallable(call) and cc instanceof CallContextAny
+ result = viableCallableExt(call) and cc instanceof CallContextAny
or
- result = viableCallable(call) and cc instanceof CallContextReturn
+ result = viableCallableExt(call) and cc instanceof CallContextReturn
}
predicate read = readStep/3;
@@ -812,6 +1074,19 @@ class BooleanOption extends TBooleanOption {
}
}
+/** An optional `DataFlowCall`. */
+class DataFlowCallOption extends TDataFlowCallOption {
+ string toString() {
+ this = TDataFlowCallNone() and
+ result = "(none)"
+ or
+ exists(DataFlowCall call |
+ this = TDataFlowCallSome(call) and
+ result = call.toString()
+ )
+ }
+}
+
/** Content tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll
index da20528760f..835e6c46451 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll
@@ -312,3 +312,14 @@ predicate isImmutableOrUnobservable(Node n) {
/** Holds if `n` should be hidden from path explanations. */
predicate nodeIsHidden(Node n) { none() }
+
+class LambdaCallKind = Unit;
+
+/** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */
+predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) { none() }
+
+/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */
+predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { none() }
+
+/** Extra data-flow steps needed for lamba flow analysis. */
+predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
index 1319f972037..a51c20c2288 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
@@ -26,15 +26,243 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) {
tupleLimit = 1000
}
+/**
+ * Provides a simple data-flow analysis for resolving lambda calls. The analysis
+ * currently excludes read-steps, store-steps, and flow-through.
+ *
+ * The analysis uses non-linear recursion: When computing a flow path in or out
+ * of a call, we use the results of the analysis recursively to resolve lamba
+ * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly.
+ */
+private module LambdaFlow {
+ private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNode p) {
+ p.isParameterOf(viableCallable(call), i)
+ }
+
+ private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) {
+ p.isParameterOf(viableCallableLambda(call, _), i)
+ }
+
+ private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
+ exists(int i |
+ viableParamNonLambda(call, i, p) and
+ arg.argumentOf(call, i)
+ )
+ }
+
+ private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
+ exists(int i |
+ viableParamLambda(call, i, p) and
+ arg.argumentOf(call, i)
+ )
+ }
+
+ private newtype TReturnPositionSimple =
+ TReturnPositionSimple0(DataFlowCallable c, ReturnKind kind) {
+ exists(ReturnNode ret |
+ c = getNodeEnclosingCallable(ret) and
+ kind = ret.getKind()
+ )
+ }
+
+ pragma[noinline]
+ private TReturnPositionSimple getReturnPositionSimple(ReturnNode ret, ReturnKind kind) {
+ result = TReturnPositionSimple0(getNodeEnclosingCallable(ret), kind)
+ }
+
+ pragma[nomagic]
+ private TReturnPositionSimple viableReturnPosNonLambda(DataFlowCall call, ReturnKind kind) {
+ result = TReturnPositionSimple0(viableCallable(call), kind)
+ }
+
+ pragma[nomagic]
+ private TReturnPositionSimple viableReturnPosLambda(
+ DataFlowCall call, DataFlowCallOption lastCall, ReturnKind kind
+ ) {
+ result = TReturnPositionSimple0(viableCallableLambda(call, lastCall), kind)
+ }
+
+ private predicate viableReturnPosOutNonLambda(
+ DataFlowCall call, TReturnPositionSimple pos, OutNode out
+ ) {
+ exists(ReturnKind kind |
+ pos = viableReturnPosNonLambda(call, kind) and
+ out = getAnOutNode(call, kind)
+ )
+ }
+
+ private predicate viableReturnPosOutLambda(
+ DataFlowCall call, DataFlowCallOption lastCall, TReturnPositionSimple pos, OutNode out
+ ) {
+ exists(ReturnKind kind |
+ pos = viableReturnPosLambda(call, lastCall, kind) and
+ out = getAnOutNode(call, kind)
+ )
+ }
+
+ /**
+ * Holds if data can flow (inter-procedurally) from `node` (of type `t`) to
+ * the lambda call `lambdaCall`.
+ *
+ * The parameter `toReturn` indicates whether the path from `node` to
+ * `lambdaCall` goes through a return, and `toJump` whether the path goes
+ * through a jump step.
+ *
+ * The call context `lastCall` records the last call on the path from `node`
+ * to `lambdaCall`, if any. That is, `lastCall` is able to target the enclosing
+ * callable of `lambdaCall`.
+ */
+ pragma[nomagic]
+ predicate revLambdaFlow(
+ DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn,
+ boolean toJump, DataFlowCallOption lastCall
+ ) {
+ revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and
+ if node instanceof CastNode or node instanceof ArgumentNode or node instanceof ReturnNode
+ then compatibleTypes(t, getNodeType(node))
+ else any()
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlow0(
+ DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn,
+ boolean toJump, DataFlowCallOption lastCall
+ ) {
+ lambdaCall(lambdaCall, kind, node) and
+ t = getNodeType(node) and
+ toReturn = false and
+ toJump = false and
+ lastCall = TDataFlowCallNone()
+ or
+ // local flow
+ exists(Node mid, DataFlowType t0 |
+ revLambdaFlow(lambdaCall, kind, mid, t0, toReturn, toJump, lastCall)
+ |
+ simpleLocalFlowStep(node, mid) and
+ t = t0
+ or
+ exists(boolean preservesValue |
+ additionalLambdaFlowStep(node, mid, preservesValue) and
+ getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid)
+ |
+ preservesValue = false and
+ t = getNodeType(node)
+ or
+ preservesValue = true and
+ t = t0
+ )
+ )
+ or
+ // jump step
+ exists(Node mid, DataFlowType t0 |
+ revLambdaFlow(lambdaCall, kind, mid, t0, _, _, _) and
+ toReturn = false and
+ toJump = true and
+ lastCall = TDataFlowCallNone()
+ |
+ jumpStep(node, mid) and
+ t = t0
+ or
+ exists(boolean preservesValue |
+ additionalLambdaFlowStep(node, mid, preservesValue) and
+ getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid)
+ |
+ preservesValue = false and
+ t = getNodeType(node)
+ or
+ preservesValue = true and
+ t = t0
+ )
+ )
+ or
+ // flow into a callable
+ exists(ParameterNode p, DataFlowCallOption lastCall0, DataFlowCall call |
+ revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and
+ (
+ if lastCall0 = TDataFlowCallNone() and toJump = false
+ then lastCall = TDataFlowCallSome(call)
+ else lastCall = lastCall0
+ ) and
+ toReturn = false
+ |
+ viableParamArgNonLambda(call, p, node)
+ or
+ viableParamArgLambda(call, p, node) // non-linear recursion
+ )
+ or
+ // flow out of a callable
+ exists(TReturnPositionSimple pos |
+ revLambdaFlowOut(lambdaCall, kind, pos, t, toJump, lastCall) and
+ getReturnPositionSimple(node, node.(ReturnNode).getKind()) = pos and
+ toReturn = true
+ )
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlowOutLambdaCall(
+ DataFlowCall lambdaCall, LambdaCallKind kind, OutNode out, DataFlowType t, boolean toJump,
+ DataFlowCall call, DataFlowCallOption lastCall
+ ) {
+ revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and
+ exists(ReturnKindExt rk |
+ out = rk.getAnOutNode(call) and
+ lambdaCall(call, _, _)
+ )
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlowOut(
+ DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t,
+ boolean toJump, DataFlowCallOption lastCall
+ ) {
+ exists(DataFlowCall call, OutNode out |
+ revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and
+ viableReturnPosOutNonLambda(call, pos, out)
+ or
+ // non-linear recursion
+ revLambdaFlowOutLambdaCall(lambdaCall, kind, out, t, toJump, call, lastCall) and
+ viableReturnPosOutLambda(call, _, pos, out)
+ )
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlowIn(
+ DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump,
+ DataFlowCallOption lastCall
+ ) {
+ revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall)
+ }
+}
+
+private DataFlowCallable viableCallableExt(DataFlowCall call) {
+ result = viableCallable(call)
+ or
+ result = viableCallableLambda(call, _)
+}
+
cached
private module Cached {
+ /**
+ * Gets a viable target for the lambda call `call`.
+ *
+ * `lastCall` records the call required to reach `call` in order for the result
+ * to be a viable target, if any.
+ */
+ cached
+ DataFlowCallable viableCallableLambda(DataFlowCall call, DataFlowCallOption lastCall) {
+ exists(Node creation, LambdaCallKind kind |
+ LambdaFlow::revLambdaFlow(call, kind, creation, _, _, _, lastCall) and
+ lambdaCreation(creation, kind, result)
+ )
+ }
+
/**
* Holds if `p` is the `i`th parameter of a viable dispatch target of `call`.
* The instance parameter is considered to have index `-1`.
*/
pragma[nomagic]
private predicate viableParam(DataFlowCall call, int i, ParameterNode p) {
- p.isParameterOf(viableCallable(call), i)
+ p.isParameterOf(viableCallableExt(call), i)
}
/**
@@ -52,7 +280,7 @@ private module Cached {
pragma[nomagic]
private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
- viableCallable(call) = result.getCallable() and
+ viableCallableExt(call) = result.getCallable() and
kind = result.getKind()
}
@@ -317,6 +545,35 @@ private module Cached {
cached
private module DispatchWithCallContext {
+ /**
+ * Holds if the set of viable implementations that can be called by `call`
+ * might be improved by knowing the call context.
+ */
+ pragma[nomagic]
+ private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) {
+ mayBenefitFromCallContext(call, callable)
+ or
+ callable = call.getEnclosingCallable() and
+ exists(viableCallableLambda(call, TDataFlowCallSome(_)))
+ }
+
+ /**
+ * Gets a viable dispatch target of `call` in the context `ctx`. This is
+ * restricted to those `call`s for which a context might make a difference.
+ */
+ pragma[nomagic]
+ private DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) {
+ result = viableImplInCallContext(call, ctx)
+ or
+ result = viableCallableLambda(call, TDataFlowCallSome(ctx))
+ or
+ exists(DataFlowCallable enclosing |
+ mayBenefitFromCallContextExt(call, enclosing) and
+ enclosing = viableCallableExt(ctx) and
+ result = viableCallableLambda(call, TDataFlowCallNone())
+ )
+ }
+
/**
* Holds if the call context `ctx` reduces the set of viable run-time
* dispatch targets of call `call` in `c`.
@@ -324,10 +581,10 @@ private module Cached {
cached
predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) {
exists(int tgts, int ctxtgts |
- mayBenefitFromCallContext(call, c) and
- c = viableCallable(ctx) and
- ctxtgts = count(viableImplInCallContext(call, ctx)) and
- tgts = strictcount(viableCallable(call)) and
+ mayBenefitFromCallContextExt(call, c) and
+ c = viableCallableExt(ctx) and
+ ctxtgts = count(viableImplInCallContextExt(call, ctx)) and
+ tgts = strictcount(viableCallableExt(call)) and
ctxtgts < tgts
)
}
@@ -339,7 +596,7 @@ private module Cached {
*/
cached
DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
- result = viableImplInCallContext(call, ctx) and
+ result = viableImplInCallContextExt(call, ctx) and
reducedViableImplInCallContext(call, _, ctx)
}
@@ -351,10 +608,10 @@ private module Cached {
cached
predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) {
exists(int tgts, int ctxtgts |
- mayBenefitFromCallContext(call, _) and
- c = viableCallable(call) and
- ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and
- tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and
+ mayBenefitFromCallContextExt(call, _) and
+ c = viableCallableExt(call) and
+ ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and
+ tgts = strictcount(DataFlowCall ctx | viableCallableExt(ctx) = call.getEnclosingCallable()) and
ctxtgts < tgts
)
}
@@ -367,7 +624,7 @@ private module Cached {
*/
cached
DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) {
- result = viableImplInCallContext(call, ctx) and
+ result = viableImplInCallContextExt(call, ctx) and
reducedViableImplInReturn(result, call)
}
}
@@ -481,6 +738,11 @@ private module Cached {
TBooleanNone() or
TBooleanSome(boolean b) { b = true or b = false }
+ cached
+ newtype TDataFlowCallOption =
+ TDataFlowCallNone() or
+ TDataFlowCallSome(DataFlowCall call)
+
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
@@ -777,7 +1039,7 @@ ReturnPosition getReturnPosition(ReturnNodeExt ret) {
bindingset[cc, callable]
predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) {
- cc instanceof CallContextAny and callable = viableCallable(call)
+ cc instanceof CallContextAny and callable = viableCallableExt(call)
or
exists(DataFlowCallable c0, DataFlowCall call0 |
call0.getEnclosingCallable() = callable and
@@ -791,14 +1053,14 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
exists(DataFlowCall ctx | cc = TSpecificCall(ctx) |
if reducedViableImplInCallContext(call, _, ctx)
then result = prunedViableImplInCallContext(call, ctx)
- else result = viableCallable(call)
+ else result = viableCallableExt(call)
)
or
- result = viableCallable(call) and cc instanceof CallContextSomeCall
+ result = viableCallableExt(call) and cc instanceof CallContextSomeCall
or
- result = viableCallable(call) and cc instanceof CallContextAny
+ result = viableCallableExt(call) and cc instanceof CallContextAny
or
- result = viableCallable(call) and cc instanceof CallContextReturn
+ result = viableCallableExt(call) and cc instanceof CallContextReturn
}
predicate read = readStep/3;
@@ -812,6 +1074,19 @@ class BooleanOption extends TBooleanOption {
}
}
+/** An optional `DataFlowCall`. */
+class DataFlowCallOption extends TDataFlowCallOption {
+ string toString() {
+ this = TDataFlowCallNone() and
+ result = "(none)"
+ or
+ exists(DataFlowCall call |
+ this = TDataFlowCallSome(call) and
+ result = call.toString()
+ )
+ }
+}
+
/** Content tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
index a2b1cae14d6..3b94e574de0 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
@@ -548,3 +548,14 @@ predicate isImmutableOrUnobservable(Node n) {
/** Holds if `n` should be hidden from path explanations. */
predicate nodeIsHidden(Node n) { n instanceof OperandNode and not n instanceof ArgumentNode }
+
+class LambdaCallKind = Unit;
+
+/** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */
+predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) { none() }
+
+/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */
+predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { none() }
+
+/** Extra data-flow steps needed for lamba flow analysis. */
+predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll
index 1319f972037..a51c20c2288 100644
--- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll
+++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll
@@ -26,15 +26,243 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) {
tupleLimit = 1000
}
+/**
+ * Provides a simple data-flow analysis for resolving lambda calls. The analysis
+ * currently excludes read-steps, store-steps, and flow-through.
+ *
+ * The analysis uses non-linear recursion: When computing a flow path in or out
+ * of a call, we use the results of the analysis recursively to resolve lamba
+ * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly.
+ */
+private module LambdaFlow {
+ private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNode p) {
+ p.isParameterOf(viableCallable(call), i)
+ }
+
+ private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) {
+ p.isParameterOf(viableCallableLambda(call, _), i)
+ }
+
+ private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
+ exists(int i |
+ viableParamNonLambda(call, i, p) and
+ arg.argumentOf(call, i)
+ )
+ }
+
+ private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
+ exists(int i |
+ viableParamLambda(call, i, p) and
+ arg.argumentOf(call, i)
+ )
+ }
+
+ private newtype TReturnPositionSimple =
+ TReturnPositionSimple0(DataFlowCallable c, ReturnKind kind) {
+ exists(ReturnNode ret |
+ c = getNodeEnclosingCallable(ret) and
+ kind = ret.getKind()
+ )
+ }
+
+ pragma[noinline]
+ private TReturnPositionSimple getReturnPositionSimple(ReturnNode ret, ReturnKind kind) {
+ result = TReturnPositionSimple0(getNodeEnclosingCallable(ret), kind)
+ }
+
+ pragma[nomagic]
+ private TReturnPositionSimple viableReturnPosNonLambda(DataFlowCall call, ReturnKind kind) {
+ result = TReturnPositionSimple0(viableCallable(call), kind)
+ }
+
+ pragma[nomagic]
+ private TReturnPositionSimple viableReturnPosLambda(
+ DataFlowCall call, DataFlowCallOption lastCall, ReturnKind kind
+ ) {
+ result = TReturnPositionSimple0(viableCallableLambda(call, lastCall), kind)
+ }
+
+ private predicate viableReturnPosOutNonLambda(
+ DataFlowCall call, TReturnPositionSimple pos, OutNode out
+ ) {
+ exists(ReturnKind kind |
+ pos = viableReturnPosNonLambda(call, kind) and
+ out = getAnOutNode(call, kind)
+ )
+ }
+
+ private predicate viableReturnPosOutLambda(
+ DataFlowCall call, DataFlowCallOption lastCall, TReturnPositionSimple pos, OutNode out
+ ) {
+ exists(ReturnKind kind |
+ pos = viableReturnPosLambda(call, lastCall, kind) and
+ out = getAnOutNode(call, kind)
+ )
+ }
+
+ /**
+ * Holds if data can flow (inter-procedurally) from `node` (of type `t`) to
+ * the lambda call `lambdaCall`.
+ *
+ * The parameter `toReturn` indicates whether the path from `node` to
+ * `lambdaCall` goes through a return, and `toJump` whether the path goes
+ * through a jump step.
+ *
+ * The call context `lastCall` records the last call on the path from `node`
+ * to `lambdaCall`, if any. That is, `lastCall` is able to target the enclosing
+ * callable of `lambdaCall`.
+ */
+ pragma[nomagic]
+ predicate revLambdaFlow(
+ DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn,
+ boolean toJump, DataFlowCallOption lastCall
+ ) {
+ revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and
+ if node instanceof CastNode or node instanceof ArgumentNode or node instanceof ReturnNode
+ then compatibleTypes(t, getNodeType(node))
+ else any()
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlow0(
+ DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn,
+ boolean toJump, DataFlowCallOption lastCall
+ ) {
+ lambdaCall(lambdaCall, kind, node) and
+ t = getNodeType(node) and
+ toReturn = false and
+ toJump = false and
+ lastCall = TDataFlowCallNone()
+ or
+ // local flow
+ exists(Node mid, DataFlowType t0 |
+ revLambdaFlow(lambdaCall, kind, mid, t0, toReturn, toJump, lastCall)
+ |
+ simpleLocalFlowStep(node, mid) and
+ t = t0
+ or
+ exists(boolean preservesValue |
+ additionalLambdaFlowStep(node, mid, preservesValue) and
+ getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid)
+ |
+ preservesValue = false and
+ t = getNodeType(node)
+ or
+ preservesValue = true and
+ t = t0
+ )
+ )
+ or
+ // jump step
+ exists(Node mid, DataFlowType t0 |
+ revLambdaFlow(lambdaCall, kind, mid, t0, _, _, _) and
+ toReturn = false and
+ toJump = true and
+ lastCall = TDataFlowCallNone()
+ |
+ jumpStep(node, mid) and
+ t = t0
+ or
+ exists(boolean preservesValue |
+ additionalLambdaFlowStep(node, mid, preservesValue) and
+ getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid)
+ |
+ preservesValue = false and
+ t = getNodeType(node)
+ or
+ preservesValue = true and
+ t = t0
+ )
+ )
+ or
+ // flow into a callable
+ exists(ParameterNode p, DataFlowCallOption lastCall0, DataFlowCall call |
+ revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and
+ (
+ if lastCall0 = TDataFlowCallNone() and toJump = false
+ then lastCall = TDataFlowCallSome(call)
+ else lastCall = lastCall0
+ ) and
+ toReturn = false
+ |
+ viableParamArgNonLambda(call, p, node)
+ or
+ viableParamArgLambda(call, p, node) // non-linear recursion
+ )
+ or
+ // flow out of a callable
+ exists(TReturnPositionSimple pos |
+ revLambdaFlowOut(lambdaCall, kind, pos, t, toJump, lastCall) and
+ getReturnPositionSimple(node, node.(ReturnNode).getKind()) = pos and
+ toReturn = true
+ )
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlowOutLambdaCall(
+ DataFlowCall lambdaCall, LambdaCallKind kind, OutNode out, DataFlowType t, boolean toJump,
+ DataFlowCall call, DataFlowCallOption lastCall
+ ) {
+ revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and
+ exists(ReturnKindExt rk |
+ out = rk.getAnOutNode(call) and
+ lambdaCall(call, _, _)
+ )
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlowOut(
+ DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t,
+ boolean toJump, DataFlowCallOption lastCall
+ ) {
+ exists(DataFlowCall call, OutNode out |
+ revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and
+ viableReturnPosOutNonLambda(call, pos, out)
+ or
+ // non-linear recursion
+ revLambdaFlowOutLambdaCall(lambdaCall, kind, out, t, toJump, call, lastCall) and
+ viableReturnPosOutLambda(call, _, pos, out)
+ )
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlowIn(
+ DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump,
+ DataFlowCallOption lastCall
+ ) {
+ revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall)
+ }
+}
+
+private DataFlowCallable viableCallableExt(DataFlowCall call) {
+ result = viableCallable(call)
+ or
+ result = viableCallableLambda(call, _)
+}
+
cached
private module Cached {
+ /**
+ * Gets a viable target for the lambda call `call`.
+ *
+ * `lastCall` records the call required to reach `call` in order for the result
+ * to be a viable target, if any.
+ */
+ cached
+ DataFlowCallable viableCallableLambda(DataFlowCall call, DataFlowCallOption lastCall) {
+ exists(Node creation, LambdaCallKind kind |
+ LambdaFlow::revLambdaFlow(call, kind, creation, _, _, _, lastCall) and
+ lambdaCreation(creation, kind, result)
+ )
+ }
+
/**
* Holds if `p` is the `i`th parameter of a viable dispatch target of `call`.
* The instance parameter is considered to have index `-1`.
*/
pragma[nomagic]
private predicate viableParam(DataFlowCall call, int i, ParameterNode p) {
- p.isParameterOf(viableCallable(call), i)
+ p.isParameterOf(viableCallableExt(call), i)
}
/**
@@ -52,7 +280,7 @@ private module Cached {
pragma[nomagic]
private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
- viableCallable(call) = result.getCallable() and
+ viableCallableExt(call) = result.getCallable() and
kind = result.getKind()
}
@@ -317,6 +545,35 @@ private module Cached {
cached
private module DispatchWithCallContext {
+ /**
+ * Holds if the set of viable implementations that can be called by `call`
+ * might be improved by knowing the call context.
+ */
+ pragma[nomagic]
+ private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) {
+ mayBenefitFromCallContext(call, callable)
+ or
+ callable = call.getEnclosingCallable() and
+ exists(viableCallableLambda(call, TDataFlowCallSome(_)))
+ }
+
+ /**
+ * Gets a viable dispatch target of `call` in the context `ctx`. This is
+ * restricted to those `call`s for which a context might make a difference.
+ */
+ pragma[nomagic]
+ private DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) {
+ result = viableImplInCallContext(call, ctx)
+ or
+ result = viableCallableLambda(call, TDataFlowCallSome(ctx))
+ or
+ exists(DataFlowCallable enclosing |
+ mayBenefitFromCallContextExt(call, enclosing) and
+ enclosing = viableCallableExt(ctx) and
+ result = viableCallableLambda(call, TDataFlowCallNone())
+ )
+ }
+
/**
* Holds if the call context `ctx` reduces the set of viable run-time
* dispatch targets of call `call` in `c`.
@@ -324,10 +581,10 @@ private module Cached {
cached
predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) {
exists(int tgts, int ctxtgts |
- mayBenefitFromCallContext(call, c) and
- c = viableCallable(ctx) and
- ctxtgts = count(viableImplInCallContext(call, ctx)) and
- tgts = strictcount(viableCallable(call)) and
+ mayBenefitFromCallContextExt(call, c) and
+ c = viableCallableExt(ctx) and
+ ctxtgts = count(viableImplInCallContextExt(call, ctx)) and
+ tgts = strictcount(viableCallableExt(call)) and
ctxtgts < tgts
)
}
@@ -339,7 +596,7 @@ private module Cached {
*/
cached
DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
- result = viableImplInCallContext(call, ctx) and
+ result = viableImplInCallContextExt(call, ctx) and
reducedViableImplInCallContext(call, _, ctx)
}
@@ -351,10 +608,10 @@ private module Cached {
cached
predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) {
exists(int tgts, int ctxtgts |
- mayBenefitFromCallContext(call, _) and
- c = viableCallable(call) and
- ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and
- tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and
+ mayBenefitFromCallContextExt(call, _) and
+ c = viableCallableExt(call) and
+ ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and
+ tgts = strictcount(DataFlowCall ctx | viableCallableExt(ctx) = call.getEnclosingCallable()) and
ctxtgts < tgts
)
}
@@ -367,7 +624,7 @@ private module Cached {
*/
cached
DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) {
- result = viableImplInCallContext(call, ctx) and
+ result = viableImplInCallContextExt(call, ctx) and
reducedViableImplInReturn(result, call)
}
}
@@ -481,6 +738,11 @@ private module Cached {
TBooleanNone() or
TBooleanSome(boolean b) { b = true or b = false }
+ cached
+ newtype TDataFlowCallOption =
+ TDataFlowCallNone() or
+ TDataFlowCallSome(DataFlowCall call)
+
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
@@ -777,7 +1039,7 @@ ReturnPosition getReturnPosition(ReturnNodeExt ret) {
bindingset[cc, callable]
predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) {
- cc instanceof CallContextAny and callable = viableCallable(call)
+ cc instanceof CallContextAny and callable = viableCallableExt(call)
or
exists(DataFlowCallable c0, DataFlowCall call0 |
call0.getEnclosingCallable() = callable and
@@ -791,14 +1053,14 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
exists(DataFlowCall ctx | cc = TSpecificCall(ctx) |
if reducedViableImplInCallContext(call, _, ctx)
then result = prunedViableImplInCallContext(call, ctx)
- else result = viableCallable(call)
+ else result = viableCallableExt(call)
)
or
- result = viableCallable(call) and cc instanceof CallContextSomeCall
+ result = viableCallableExt(call) and cc instanceof CallContextSomeCall
or
- result = viableCallable(call) and cc instanceof CallContextAny
+ result = viableCallableExt(call) and cc instanceof CallContextAny
or
- result = viableCallable(call) and cc instanceof CallContextReturn
+ result = viableCallableExt(call) and cc instanceof CallContextReturn
}
predicate read = readStep/3;
@@ -812,6 +1074,19 @@ class BooleanOption extends TBooleanOption {
}
}
+/** An optional `DataFlowCall`. */
+class DataFlowCallOption extends TDataFlowCallOption {
+ string toString() {
+ this = TDataFlowCallNone() and
+ result = "(none)"
+ or
+ exists(DataFlowCall call |
+ this = TDataFlowCallSome(call) and
+ result = call.toString()
+ )
+ }
+}
+
/** Content tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;
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 c38d0df4e57..f49ca8d75d9 100644
--- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll
+++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll
@@ -338,3 +338,14 @@ predicate isImmutableOrUnobservable(Node n) {
/** Holds if `n` should be hidden from path explanations. */
predicate nodeIsHidden(Node n) { none() }
+
+class LambdaCallKind = Unit;
+
+/** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */
+predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) { none() }
+
+/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */
+predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { none() }
+
+/** Extra data-flow steps needed for lamba flow analysis. */
+predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll
index 1319f972037..a51c20c2288 100644
--- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll
+++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll
@@ -26,15 +26,243 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) {
tupleLimit = 1000
}
+/**
+ * Provides a simple data-flow analysis for resolving lambda calls. The analysis
+ * currently excludes read-steps, store-steps, and flow-through.
+ *
+ * The analysis uses non-linear recursion: When computing a flow path in or out
+ * of a call, we use the results of the analysis recursively to resolve lamba
+ * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly.
+ */
+private module LambdaFlow {
+ private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNode p) {
+ p.isParameterOf(viableCallable(call), i)
+ }
+
+ private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) {
+ p.isParameterOf(viableCallableLambda(call, _), i)
+ }
+
+ private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
+ exists(int i |
+ viableParamNonLambda(call, i, p) and
+ arg.argumentOf(call, i)
+ )
+ }
+
+ private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
+ exists(int i |
+ viableParamLambda(call, i, p) and
+ arg.argumentOf(call, i)
+ )
+ }
+
+ private newtype TReturnPositionSimple =
+ TReturnPositionSimple0(DataFlowCallable c, ReturnKind kind) {
+ exists(ReturnNode ret |
+ c = getNodeEnclosingCallable(ret) and
+ kind = ret.getKind()
+ )
+ }
+
+ pragma[noinline]
+ private TReturnPositionSimple getReturnPositionSimple(ReturnNode ret, ReturnKind kind) {
+ result = TReturnPositionSimple0(getNodeEnclosingCallable(ret), kind)
+ }
+
+ pragma[nomagic]
+ private TReturnPositionSimple viableReturnPosNonLambda(DataFlowCall call, ReturnKind kind) {
+ result = TReturnPositionSimple0(viableCallable(call), kind)
+ }
+
+ pragma[nomagic]
+ private TReturnPositionSimple viableReturnPosLambda(
+ DataFlowCall call, DataFlowCallOption lastCall, ReturnKind kind
+ ) {
+ result = TReturnPositionSimple0(viableCallableLambda(call, lastCall), kind)
+ }
+
+ private predicate viableReturnPosOutNonLambda(
+ DataFlowCall call, TReturnPositionSimple pos, OutNode out
+ ) {
+ exists(ReturnKind kind |
+ pos = viableReturnPosNonLambda(call, kind) and
+ out = getAnOutNode(call, kind)
+ )
+ }
+
+ private predicate viableReturnPosOutLambda(
+ DataFlowCall call, DataFlowCallOption lastCall, TReturnPositionSimple pos, OutNode out
+ ) {
+ exists(ReturnKind kind |
+ pos = viableReturnPosLambda(call, lastCall, kind) and
+ out = getAnOutNode(call, kind)
+ )
+ }
+
+ /**
+ * Holds if data can flow (inter-procedurally) from `node` (of type `t`) to
+ * the lambda call `lambdaCall`.
+ *
+ * The parameter `toReturn` indicates whether the path from `node` to
+ * `lambdaCall` goes through a return, and `toJump` whether the path goes
+ * through a jump step.
+ *
+ * The call context `lastCall` records the last call on the path from `node`
+ * to `lambdaCall`, if any. That is, `lastCall` is able to target the enclosing
+ * callable of `lambdaCall`.
+ */
+ pragma[nomagic]
+ predicate revLambdaFlow(
+ DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn,
+ boolean toJump, DataFlowCallOption lastCall
+ ) {
+ revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and
+ if node instanceof CastNode or node instanceof ArgumentNode or node instanceof ReturnNode
+ then compatibleTypes(t, getNodeType(node))
+ else any()
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlow0(
+ DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn,
+ boolean toJump, DataFlowCallOption lastCall
+ ) {
+ lambdaCall(lambdaCall, kind, node) and
+ t = getNodeType(node) and
+ toReturn = false and
+ toJump = false and
+ lastCall = TDataFlowCallNone()
+ or
+ // local flow
+ exists(Node mid, DataFlowType t0 |
+ revLambdaFlow(lambdaCall, kind, mid, t0, toReturn, toJump, lastCall)
+ |
+ simpleLocalFlowStep(node, mid) and
+ t = t0
+ or
+ exists(boolean preservesValue |
+ additionalLambdaFlowStep(node, mid, preservesValue) and
+ getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid)
+ |
+ preservesValue = false and
+ t = getNodeType(node)
+ or
+ preservesValue = true and
+ t = t0
+ )
+ )
+ or
+ // jump step
+ exists(Node mid, DataFlowType t0 |
+ revLambdaFlow(lambdaCall, kind, mid, t0, _, _, _) and
+ toReturn = false and
+ toJump = true and
+ lastCall = TDataFlowCallNone()
+ |
+ jumpStep(node, mid) and
+ t = t0
+ or
+ exists(boolean preservesValue |
+ additionalLambdaFlowStep(node, mid, preservesValue) and
+ getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid)
+ |
+ preservesValue = false and
+ t = getNodeType(node)
+ or
+ preservesValue = true and
+ t = t0
+ )
+ )
+ or
+ // flow into a callable
+ exists(ParameterNode p, DataFlowCallOption lastCall0, DataFlowCall call |
+ revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and
+ (
+ if lastCall0 = TDataFlowCallNone() and toJump = false
+ then lastCall = TDataFlowCallSome(call)
+ else lastCall = lastCall0
+ ) and
+ toReturn = false
+ |
+ viableParamArgNonLambda(call, p, node)
+ or
+ viableParamArgLambda(call, p, node) // non-linear recursion
+ )
+ or
+ // flow out of a callable
+ exists(TReturnPositionSimple pos |
+ revLambdaFlowOut(lambdaCall, kind, pos, t, toJump, lastCall) and
+ getReturnPositionSimple(node, node.(ReturnNode).getKind()) = pos and
+ toReturn = true
+ )
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlowOutLambdaCall(
+ DataFlowCall lambdaCall, LambdaCallKind kind, OutNode out, DataFlowType t, boolean toJump,
+ DataFlowCall call, DataFlowCallOption lastCall
+ ) {
+ revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and
+ exists(ReturnKindExt rk |
+ out = rk.getAnOutNode(call) and
+ lambdaCall(call, _, _)
+ )
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlowOut(
+ DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t,
+ boolean toJump, DataFlowCallOption lastCall
+ ) {
+ exists(DataFlowCall call, OutNode out |
+ revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and
+ viableReturnPosOutNonLambda(call, pos, out)
+ or
+ // non-linear recursion
+ revLambdaFlowOutLambdaCall(lambdaCall, kind, out, t, toJump, call, lastCall) and
+ viableReturnPosOutLambda(call, _, pos, out)
+ )
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlowIn(
+ DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump,
+ DataFlowCallOption lastCall
+ ) {
+ revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall)
+ }
+}
+
+private DataFlowCallable viableCallableExt(DataFlowCall call) {
+ result = viableCallable(call)
+ or
+ result = viableCallableLambda(call, _)
+}
+
cached
private module Cached {
+ /**
+ * Gets a viable target for the lambda call `call`.
+ *
+ * `lastCall` records the call required to reach `call` in order for the result
+ * to be a viable target, if any.
+ */
+ cached
+ DataFlowCallable viableCallableLambda(DataFlowCall call, DataFlowCallOption lastCall) {
+ exists(Node creation, LambdaCallKind kind |
+ LambdaFlow::revLambdaFlow(call, kind, creation, _, _, _, lastCall) and
+ lambdaCreation(creation, kind, result)
+ )
+ }
+
/**
* Holds if `p` is the `i`th parameter of a viable dispatch target of `call`.
* The instance parameter is considered to have index `-1`.
*/
pragma[nomagic]
private predicate viableParam(DataFlowCall call, int i, ParameterNode p) {
- p.isParameterOf(viableCallable(call), i)
+ p.isParameterOf(viableCallableExt(call), i)
}
/**
@@ -52,7 +280,7 @@ private module Cached {
pragma[nomagic]
private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
- viableCallable(call) = result.getCallable() and
+ viableCallableExt(call) = result.getCallable() and
kind = result.getKind()
}
@@ -317,6 +545,35 @@ private module Cached {
cached
private module DispatchWithCallContext {
+ /**
+ * Holds if the set of viable implementations that can be called by `call`
+ * might be improved by knowing the call context.
+ */
+ pragma[nomagic]
+ private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) {
+ mayBenefitFromCallContext(call, callable)
+ or
+ callable = call.getEnclosingCallable() and
+ exists(viableCallableLambda(call, TDataFlowCallSome(_)))
+ }
+
+ /**
+ * Gets a viable dispatch target of `call` in the context `ctx`. This is
+ * restricted to those `call`s for which a context might make a difference.
+ */
+ pragma[nomagic]
+ private DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) {
+ result = viableImplInCallContext(call, ctx)
+ or
+ result = viableCallableLambda(call, TDataFlowCallSome(ctx))
+ or
+ exists(DataFlowCallable enclosing |
+ mayBenefitFromCallContextExt(call, enclosing) and
+ enclosing = viableCallableExt(ctx) and
+ result = viableCallableLambda(call, TDataFlowCallNone())
+ )
+ }
+
/**
* Holds if the call context `ctx` reduces the set of viable run-time
* dispatch targets of call `call` in `c`.
@@ -324,10 +581,10 @@ private module Cached {
cached
predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) {
exists(int tgts, int ctxtgts |
- mayBenefitFromCallContext(call, c) and
- c = viableCallable(ctx) and
- ctxtgts = count(viableImplInCallContext(call, ctx)) and
- tgts = strictcount(viableCallable(call)) and
+ mayBenefitFromCallContextExt(call, c) and
+ c = viableCallableExt(ctx) and
+ ctxtgts = count(viableImplInCallContextExt(call, ctx)) and
+ tgts = strictcount(viableCallableExt(call)) and
ctxtgts < tgts
)
}
@@ -339,7 +596,7 @@ private module Cached {
*/
cached
DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
- result = viableImplInCallContext(call, ctx) and
+ result = viableImplInCallContextExt(call, ctx) and
reducedViableImplInCallContext(call, _, ctx)
}
@@ -351,10 +608,10 @@ private module Cached {
cached
predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) {
exists(int tgts, int ctxtgts |
- mayBenefitFromCallContext(call, _) and
- c = viableCallable(call) and
- ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and
- tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and
+ mayBenefitFromCallContextExt(call, _) and
+ c = viableCallableExt(call) and
+ ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and
+ tgts = strictcount(DataFlowCall ctx | viableCallableExt(ctx) = call.getEnclosingCallable()) and
ctxtgts < tgts
)
}
@@ -367,7 +624,7 @@ private module Cached {
*/
cached
DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) {
- result = viableImplInCallContext(call, ctx) and
+ result = viableImplInCallContextExt(call, ctx) and
reducedViableImplInReturn(result, call)
}
}
@@ -481,6 +738,11 @@ private module Cached {
TBooleanNone() or
TBooleanSome(boolean b) { b = true or b = false }
+ cached
+ newtype TDataFlowCallOption =
+ TDataFlowCallNone() or
+ TDataFlowCallSome(DataFlowCall call)
+
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
@@ -777,7 +1039,7 @@ ReturnPosition getReturnPosition(ReturnNodeExt ret) {
bindingset[cc, callable]
predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) {
- cc instanceof CallContextAny and callable = viableCallable(call)
+ cc instanceof CallContextAny and callable = viableCallableExt(call)
or
exists(DataFlowCallable c0, DataFlowCall call0 |
call0.getEnclosingCallable() = callable and
@@ -791,14 +1053,14 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
exists(DataFlowCall ctx | cc = TSpecificCall(ctx) |
if reducedViableImplInCallContext(call, _, ctx)
then result = prunedViableImplInCallContext(call, ctx)
- else result = viableCallable(call)
+ else result = viableCallableExt(call)
)
or
- result = viableCallable(call) and cc instanceof CallContextSomeCall
+ result = viableCallableExt(call) and cc instanceof CallContextSomeCall
or
- result = viableCallable(call) and cc instanceof CallContextAny
+ result = viableCallableExt(call) and cc instanceof CallContextAny
or
- result = viableCallable(call) and cc instanceof CallContextReturn
+ result = viableCallableExt(call) and cc instanceof CallContextReturn
}
predicate read = readStep/3;
@@ -812,6 +1074,19 @@ class BooleanOption extends TBooleanOption {
}
}
+/** An optional `DataFlowCall`. */
+class DataFlowCallOption extends TDataFlowCallOption {
+ string toString() {
+ this = TDataFlowCallNone() and
+ result = "(none)"
+ or
+ exists(DataFlowCall call |
+ this = TDataFlowCallSome(call) and
+ result = call.toString()
+ )
+ }
+}
+
/** Content tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;
diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPrivate.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPrivate.qll
index 958a434eeb1..86ef69a87b9 100644
--- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPrivate.qll
+++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPrivate.qll
@@ -1605,3 +1605,14 @@ int accessPathLimit() { result = 5 }
/** Holds if `n` should be hidden from path explanations. */
predicate nodeIsHidden(Node n) { none() }
+
+class LambdaCallKind = Unit;
+
+/** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */
+predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) { none() }
+
+/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */
+predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { none() }
+
+/** Extra data-flow steps needed for lamba flow analysis. */
+predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
From c684b74b3df89d67f9dace66f0b29c6f123960ae Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Tue, 16 Mar 2021 14:46:16 +0100
Subject: [PATCH 126/725] C#: Add async dataflow tests
---
.../library-tests/dataflow/async/Async.cs | 50 +++++++++++++++++++
.../dataflow/async/Async.expected | 29 +++++++++++
.../library-tests/dataflow/async/Async.ql | 33 ++++++++++++
3 files changed, 112 insertions(+)
create mode 100644 csharp/ql/test/library-tests/dataflow/async/Async.cs
create mode 100644 csharp/ql/test/library-tests/dataflow/async/Async.expected
create mode 100644 csharp/ql/test/library-tests/dataflow/async/Async.ql
diff --git a/csharp/ql/test/library-tests/dataflow/async/Async.cs b/csharp/ql/test/library-tests/dataflow/async/Async.cs
new file mode 100644
index 00000000000..94adc7eb94a
--- /dev/null
+++ b/csharp/ql/test/library-tests/dataflow/async/Async.cs
@@ -0,0 +1,50 @@
+using System.Threading.Tasks;
+
+class Test
+{
+ private void Sink(string s)
+ {
+ }
+
+ public void TestNonAwait(string input)
+ {
+ Sink(Return(input)); // True positive
+ }
+
+ private string Return(string x)
+ {
+ return x;
+ }
+
+ public async Task TestAwait1(string input)
+ {
+ Sink(await ReturnAwait(input)); // False negative
+ }
+
+ public async Task TestAwait2(string input)
+ {
+ var x = await ReturnAwait(input);
+ Sink(x); // False negative
+ }
+
+ public void TestAwait3(string input)
+ {
+ Sink(ReturnAwait(input).Result); // False negative
+ }
+
+ private async Task ReturnAwait(string x)
+ {
+ await Task.Delay(1);
+ return x;
+ }
+
+ public void TestTask(string input)
+ {
+ Sink(ReturnTask(input).Result); // True positive
+ }
+
+ private Task ReturnTask(string x)
+ {
+ return Task.FromResult(x);
+ }
+}
\ No newline at end of file
diff --git a/csharp/ql/test/library-tests/dataflow/async/Async.expected b/csharp/ql/test/library-tests/dataflow/async/Async.expected
new file mode 100644
index 00000000000..bb186ed405b
--- /dev/null
+++ b/csharp/ql/test/library-tests/dataflow/async/Async.expected
@@ -0,0 +1,29 @@
+edges
+| Async.cs:9:37:9:41 | input : String | Async.cs:11:21:11:25 | access to parameter input : String |
+| Async.cs:11:21:11:25 | access to parameter input : String | Async.cs:11:14:11:26 | call to method Return |
+| Async.cs:14:34:14:34 | x : String | Async.cs:16:16:16:16 | access to parameter x : String |
+| Async.cs:16:16:16:16 | access to parameter x : String | Async.cs:11:14:11:26 | call to method Return |
+| Async.cs:41:33:41:37 | input : String | Async.cs:43:25:43:29 | access to parameter input : String |
+| Async.cs:43:14:43:30 | call to method ReturnTask [Result] : String | Async.cs:43:14:43:37 | access to property Result |
+| Async.cs:43:25:43:29 | access to parameter input : String | Async.cs:43:14:43:30 | call to method ReturnTask [Result] : String |
+| Async.cs:46:44:46:44 | x : String | Async.cs:48:32:48:32 | access to parameter x : String |
+| Async.cs:48:16:48:33 | call to method FromResult [Result] : String | Async.cs:43:14:43:30 | call to method ReturnTask [Result] : String |
+| Async.cs:48:32:48:32 | access to parameter x : String | Async.cs:48:16:48:33 | call to method FromResult [Result] : String |
+nodes
+| Async.cs:9:37:9:41 | input : String | semmle.label | input : String |
+| Async.cs:11:14:11:26 | call to method Return | semmle.label | call to method Return |
+| Async.cs:11:21:11:25 | access to parameter input : String | semmle.label | access to parameter input : String |
+| Async.cs:14:34:14:34 | x : String | semmle.label | x : String |
+| Async.cs:16:16:16:16 | access to parameter x : String | semmle.label | access to parameter x : String |
+| Async.cs:41:33:41:37 | input : String | semmle.label | input : String |
+| Async.cs:43:14:43:30 | call to method ReturnTask [Result] : String | semmle.label | call to method ReturnTask [Result] : String |
+| Async.cs:43:14:43:37 | access to property Result | semmle.label | access to property Result |
+| Async.cs:43:25:43:29 | access to parameter input : String | semmle.label | access to parameter input : String |
+| Async.cs:46:44:46:44 | x : String | semmle.label | x : String |
+| Async.cs:48:16:48:33 | call to method FromResult [Result] : String | semmle.label | call to method FromResult [Result] : String |
+| Async.cs:48:32:48:32 | access to parameter x : String | semmle.label | access to parameter x : String |
+#select
+| Async.cs:11:14:11:26 | call to method Return | Async.cs:9:37:9:41 | input : String | Async.cs:11:14:11:26 | call to method Return | $@ flows to here and is used. | Async.cs:9:37:9:41 | input | User-provided value |
+| Async.cs:11:14:11:26 | call to method Return | Async.cs:14:34:14:34 | x : String | Async.cs:11:14:11:26 | call to method Return | $@ flows to here and is used. | Async.cs:14:34:14:34 | x | User-provided value |
+| Async.cs:43:14:43:37 | access to property Result | Async.cs:41:33:41:37 | input : String | Async.cs:43:14:43:37 | access to property Result | $@ flows to here and is used. | Async.cs:41:33:41:37 | input | User-provided value |
+| Async.cs:43:14:43:37 | access to property Result | Async.cs:46:44:46:44 | x : String | Async.cs:43:14:43:37 | access to property Result | $@ flows to here and is used. | Async.cs:46:44:46:44 | x | User-provided value |
diff --git a/csharp/ql/test/library-tests/dataflow/async/Async.ql b/csharp/ql/test/library-tests/dataflow/async/Async.ql
new file mode 100644
index 00000000000..71799e22f8a
--- /dev/null
+++ b/csharp/ql/test/library-tests/dataflow/async/Async.ql
@@ -0,0 +1,33 @@
+import csharp
+import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
+
+class MySink extends DataFlow::ExprNode {
+ MySink() {
+ exists(Method m, MethodCall mc |
+ mc.getTarget() = m and
+ m.getName() = "Sink" and
+ this.getExpr() = mc.getArgumentForName("s")
+ )
+ }
+}
+
+class MySource extends DataFlow::ParameterNode {
+ MySource() {
+ exists(Parameter p | p = this.getParameter() |
+ p = any(Class c | c.hasQualifiedName("Test")).getAMethod().getAParameter()
+ )
+ }
+}
+
+class MyConfig extends TaintTracking::Configuration {
+ MyConfig() { this = "MyConfig" }
+
+ override predicate isSource(DataFlow::Node source) { source instanceof MySource }
+
+ override predicate isSink(DataFlow::Node sink) { sink instanceof MySink }
+}
+
+from MyConfig c, DataFlow::PathNode source, DataFlow::PathNode sink
+where c.hasFlowPath(source, sink)
+select sink.getNode(), source, sink, "$@ flows to here and is used.", source.getNode(),
+ "User-provided value"
From 732ef92830d9a3e148a3e1b06d10661677152eef Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Tue, 16 Mar 2021 15:18:00 +0100
Subject: [PATCH 127/725] C#: add store step for return statements inside async
methods
---
.../dataflow/internal/DataFlowPrivate.qll | 35 +++++++++++++++++++
.../library-tests/dataflow/async/Async.cs | 10 +++---
.../dataflow/async/Async.expected | 35 +++++++++++++++++++
3 files changed, 75 insertions(+), 5 deletions(-)
diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
index 5e3ac64f7c2..a140b48a423 100644
--- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
+++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
@@ -632,6 +632,9 @@ private module Cached {
TYieldReturnNode(ControlFlow::Nodes::ElementNode cfn) {
any(Callable c).canYieldReturn(cfn.getElement())
} or
+ TAsyncReturnNode(ControlFlow::Nodes::ElementNode cfn) {
+ any(Callable c | c.(Modifiable).isAsync()).canReturn(cfn.getElement())
+ } or
TImplicitCapturedArgumentNode(ControlFlow::Nodes::ElementNode cfn, LocalScopeVariable v) {
exists(Ssa::ExplicitDefinition def | def.isCapturedVariableDefinitionFlowIn(_, cfn, _) |
v = def.getSourceVariable().getAssignable()
@@ -783,6 +786,12 @@ private module Cached {
c instanceof ElementContent
)
or
+ exists(Expr e |
+ e = node1.asExpr() and
+ node2.(AsyncReturnNode).getReturnStmt().getExpr() = e and
+ c = getResultContent()
+ )
+ or
FlowSummaryImpl::Private::storeStep(node1, c, node2)
}
@@ -962,6 +971,8 @@ private module Cached {
or
n instanceof YieldReturnNode
or
+ n instanceof AsyncReturnNode
+ or
n instanceof ImplicitCapturedArgumentNode
or
n instanceof MallocNode
@@ -1397,6 +1408,30 @@ private module ReturnNodes {
override string toStringImpl() { result = yrs.toString() }
}
+ /**
+ * A synthesized `return` node for returned expressions inside `async` methods.
+ */
+ class AsyncReturnNode extends ReturnNode, NodeImpl, TAsyncReturnNode {
+ private ControlFlow::Nodes::ElementNode cfn;
+ private ReturnStmt rs;
+
+ AsyncReturnNode() { this = TAsyncReturnNode(cfn) and rs.getExpr().getAControlFlowNode() = cfn }
+
+ ReturnStmt getReturnStmt() { result = rs }
+
+ override NormalReturnKind getKind() { any() }
+
+ override Callable getEnclosingCallableImpl() { result = rs.getEnclosingCallable() }
+
+ override Type getTypeImpl() { result = rs.getEnclosingCallable().getReturnType() }
+
+ override ControlFlow::Node getControlFlowNodeImpl() { result = cfn }
+
+ override Location getLocationImpl() { result = rs.getLocation() }
+
+ override string toStringImpl() { result = rs.toString() }
+ }
+
/**
* The value of a captured variable as an implicit return from a call, viewed
* as a node in a data flow graph.
diff --git a/csharp/ql/test/library-tests/dataflow/async/Async.cs b/csharp/ql/test/library-tests/dataflow/async/Async.cs
index 94adc7eb94a..7040b5d4507 100644
--- a/csharp/ql/test/library-tests/dataflow/async/Async.cs
+++ b/csharp/ql/test/library-tests/dataflow/async/Async.cs
@@ -8,7 +8,7 @@ class Test
public void TestNonAwait(string input)
{
- Sink(Return(input)); // True positive
+ Sink(Return(input));
}
private string Return(string x)
@@ -18,18 +18,18 @@ class Test
public async Task TestAwait1(string input)
{
- Sink(await ReturnAwait(input)); // False negative
+ Sink(await ReturnAwait(input));
}
public async Task TestAwait2(string input)
{
var x = await ReturnAwait(input);
- Sink(x); // False negative
+ Sink(x);
}
public void TestAwait3(string input)
{
- Sink(ReturnAwait(input).Result); // False negative
+ Sink(ReturnAwait(input).Result);
}
private async Task ReturnAwait(string x)
@@ -40,7 +40,7 @@ class Test
public void TestTask(string input)
{
- Sink(ReturnTask(input).Result); // True positive
+ Sink(ReturnTask(input).Result);
}
private Task ReturnTask(string x)
diff --git a/csharp/ql/test/library-tests/dataflow/async/Async.expected b/csharp/ql/test/library-tests/dataflow/async/Async.expected
index bb186ed405b..7afc9cf0e9f 100644
--- a/csharp/ql/test/library-tests/dataflow/async/Async.expected
+++ b/csharp/ql/test/library-tests/dataflow/async/Async.expected
@@ -3,6 +3,20 @@ edges
| Async.cs:11:21:11:25 | access to parameter input : String | Async.cs:11:14:11:26 | call to method Return |
| Async.cs:14:34:14:34 | x : String | Async.cs:16:16:16:16 | access to parameter x : String |
| Async.cs:16:16:16:16 | access to parameter x : String | Async.cs:11:14:11:26 | call to method Return |
+| Async.cs:19:41:19:45 | input : String | Async.cs:21:32:21:36 | access to parameter input : String |
+| Async.cs:21:20:21:37 | call to method ReturnAwait [Result] : String | Async.cs:21:14:21:37 | await ... |
+| Async.cs:21:32:21:36 | access to parameter input : String | Async.cs:21:20:21:37 | call to method ReturnAwait [Result] : String |
+| Async.cs:24:41:24:45 | input : String | Async.cs:26:35:26:39 | access to parameter input : String |
+| Async.cs:26:17:26:40 | await ... : String | Async.cs:27:14:27:14 | access to local variable x |
+| Async.cs:26:23:26:40 | call to method ReturnAwait [Result] : String | Async.cs:26:17:26:40 | await ... : String |
+| Async.cs:26:35:26:39 | access to parameter input : String | Async.cs:26:23:26:40 | call to method ReturnAwait [Result] : String |
+| Async.cs:30:35:30:39 | input : String | Async.cs:32:26:32:30 | access to parameter input : String |
+| Async.cs:32:14:32:31 | call to method ReturnAwait [Result] : String | Async.cs:32:14:32:38 | access to property Result |
+| Async.cs:32:26:32:30 | access to parameter input : String | Async.cs:32:14:32:31 | call to method ReturnAwait [Result] : String |
+| Async.cs:35:51:35:51 | x : String | Async.cs:38:16:38:16 | access to parameter x : String |
+| Async.cs:38:16:38:16 | access to parameter x : String | Async.cs:21:20:21:37 | call to method ReturnAwait [Result] : String |
+| Async.cs:38:16:38:16 | access to parameter x : String | Async.cs:26:23:26:40 | call to method ReturnAwait [Result] : String |
+| Async.cs:38:16:38:16 | access to parameter x : String | Async.cs:32:14:32:31 | call to method ReturnAwait [Result] : String |
| Async.cs:41:33:41:37 | input : String | Async.cs:43:25:43:29 | access to parameter input : String |
| Async.cs:43:14:43:30 | call to method ReturnTask [Result] : String | Async.cs:43:14:43:37 | access to property Result |
| Async.cs:43:25:43:29 | access to parameter input : String | Async.cs:43:14:43:30 | call to method ReturnTask [Result] : String |
@@ -15,6 +29,21 @@ nodes
| Async.cs:11:21:11:25 | access to parameter input : String | semmle.label | access to parameter input : String |
| Async.cs:14:34:14:34 | x : String | semmle.label | x : String |
| Async.cs:16:16:16:16 | access to parameter x : String | semmle.label | access to parameter x : String |
+| Async.cs:19:41:19:45 | input : String | semmle.label | input : String |
+| Async.cs:21:14:21:37 | await ... | semmle.label | await ... |
+| Async.cs:21:20:21:37 | call to method ReturnAwait [Result] : String | semmle.label | call to method ReturnAwait [Result] : String |
+| Async.cs:21:32:21:36 | access to parameter input : String | semmle.label | access to parameter input : String |
+| Async.cs:24:41:24:45 | input : String | semmle.label | input : String |
+| Async.cs:26:17:26:40 | await ... : String | semmle.label | await ... : String |
+| Async.cs:26:23:26:40 | call to method ReturnAwait [Result] : String | semmle.label | call to method ReturnAwait [Result] : String |
+| Async.cs:26:35:26:39 | access to parameter input : String | semmle.label | access to parameter input : String |
+| Async.cs:27:14:27:14 | access to local variable x | semmle.label | access to local variable x |
+| Async.cs:30:35:30:39 | input : String | semmle.label | input : String |
+| Async.cs:32:14:32:31 | call to method ReturnAwait [Result] : String | semmle.label | call to method ReturnAwait [Result] : String |
+| Async.cs:32:14:32:38 | access to property Result | semmle.label | access to property Result |
+| Async.cs:32:26:32:30 | access to parameter input : String | semmle.label | access to parameter input : String |
+| Async.cs:35:51:35:51 | x : String | semmle.label | x : String |
+| Async.cs:38:16:38:16 | access to parameter x : String | semmle.label | access to parameter x : String |
| Async.cs:41:33:41:37 | input : String | semmle.label | input : String |
| Async.cs:43:14:43:30 | call to method ReturnTask [Result] : String | semmle.label | call to method ReturnTask [Result] : String |
| Async.cs:43:14:43:37 | access to property Result | semmle.label | access to property Result |
@@ -25,5 +54,11 @@ nodes
#select
| Async.cs:11:14:11:26 | call to method Return | Async.cs:9:37:9:41 | input : String | Async.cs:11:14:11:26 | call to method Return | $@ flows to here and is used. | Async.cs:9:37:9:41 | input | User-provided value |
| Async.cs:11:14:11:26 | call to method Return | Async.cs:14:34:14:34 | x : String | Async.cs:11:14:11:26 | call to method Return | $@ flows to here and is used. | Async.cs:14:34:14:34 | x | User-provided value |
+| Async.cs:21:14:21:37 | await ... | Async.cs:19:41:19:45 | input : String | Async.cs:21:14:21:37 | await ... | $@ flows to here and is used. | Async.cs:19:41:19:45 | input | User-provided value |
+| Async.cs:21:14:21:37 | await ... | Async.cs:35:51:35:51 | x : String | Async.cs:21:14:21:37 | await ... | $@ flows to here and is used. | Async.cs:35:51:35:51 | x | User-provided value |
+| Async.cs:27:14:27:14 | access to local variable x | Async.cs:24:41:24:45 | input : String | Async.cs:27:14:27:14 | access to local variable x | $@ flows to here and is used. | Async.cs:24:41:24:45 | input | User-provided value |
+| Async.cs:27:14:27:14 | access to local variable x | Async.cs:35:51:35:51 | x : String | Async.cs:27:14:27:14 | access to local variable x | $@ flows to here and is used. | Async.cs:35:51:35:51 | x | User-provided value |
+| Async.cs:32:14:32:38 | access to property Result | Async.cs:30:35:30:39 | input : String | Async.cs:32:14:32:38 | access to property Result | $@ flows to here and is used. | Async.cs:30:35:30:39 | input | User-provided value |
+| Async.cs:32:14:32:38 | access to property Result | Async.cs:35:51:35:51 | x : String | Async.cs:32:14:32:38 | access to property Result | $@ flows to here and is used. | Async.cs:35:51:35:51 | x | User-provided value |
| Async.cs:43:14:43:37 | access to property Result | Async.cs:41:33:41:37 | input : String | Async.cs:43:14:43:37 | access to property Result | $@ flows to here and is used. | Async.cs:41:33:41:37 | input | User-provided value |
| Async.cs:43:14:43:37 | access to property Result | Async.cs:46:44:46:44 | x : String | Async.cs:43:14:43:37 | access to property Result | $@ flows to here and is used. | Async.cs:46:44:46:44 | x | User-provided value |
From aa2abf76baae17b76b9a85cfc13443c15b55b475 Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Tue, 16 Mar 2021 16:17:27 +0100
Subject: [PATCH 128/725] Make ReturnNodes disjoint (normal, yield, async)
---
.../code/csharp/dataflow/internal/DataFlowPrivate.qll | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
index a140b48a423..cc39631f8b5 100644
--- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
+++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
@@ -1354,14 +1354,12 @@ abstract class ReturnNode extends Node {
private module ReturnNodes {
/**
* A data-flow node that represents an expression returned by a callable,
- * either using a (`yield`) `return` statement or an expression body (`=>`).
+ * either using a `return` statement or an expression body (`=>`).
*/
class ExprReturnNode extends ReturnNode, ExprNode {
ExprReturnNode() {
exists(DotNet::Callable c, DotNet::Expr e | e = this.getExpr() |
- c.canReturn(e)
- or
- c.(Callable).canYieldReturn(e)
+ c.canReturn(e) and not c.(Modifiable).isAsync()
)
}
From 048c72a0f230e28b19ac84b5c816e5e3f24276ba Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Tue, 16 Mar 2021 16:20:04 +0100
Subject: [PATCH 129/725] C#: Remove YieldReturnKind
---
.../dataflow/internal/DataFlowDispatch.qll | 6 ------
.../dataflow/internal/DataFlowPrivate.qll | 20 +------------------
2 files changed, 1 insertion(+), 25 deletions(-)
diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll
index c68ba38e0b5..ce6c9976ac8 100644
--- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll
+++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll
@@ -82,7 +82,6 @@ private module Cached {
cached
newtype TReturnKind =
TNormalReturnKind() { Stages::DataFlowStage::forceCachingInSameStage() } or
- TYieldReturnKind() or
TOutReturnKind(int i) {
exists(Parameter p | callableReturnsOutOrRef(_, p, _) and p.isOut() | i = p.getPosition())
} or
@@ -200,11 +199,6 @@ class NormalReturnKind extends ReturnKind, TNormalReturnKind {
override string toString() { result = "return" }
}
-/** A value returned from a callable using a `yield return` statement. */
-class YieldReturnKind extends ReturnKind, TYieldReturnKind {
- override string toString() { result = "yield return" }
-}
-
/** A value returned from a callable using an `out` or a `ref` parameter. */
abstract class OutRefReturnKind extends ReturnKind {
/** Gets the position of the `out`/`ref` parameter. */
diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
index cc39631f8b5..061858ab316 100644
--- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
+++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
@@ -1393,7 +1393,7 @@ private module ReturnNodes {
YieldReturnStmt getYieldReturnStmt() { result = yrs }
- override YieldReturnKind getKind() { any() }
+ override NormalReturnKind getKind() { any() }
override Callable getEnclosingCallableImpl() { result = yrs.getEnclosingCallable() }
@@ -1516,21 +1516,6 @@ private module OutNodes {
result = TExplicitDelegateLikeCall(cfn, e)
}
- /** A valid return type for a method that uses `yield return`. */
- private class YieldReturnType extends Type {
- YieldReturnType() {
- exists(Type t | t = this.getUnboundDeclaration() |
- t instanceof SystemCollectionsIEnumerableInterface
- or
- t instanceof SystemCollectionsIEnumeratorInterface
- or
- t instanceof SystemCollectionsGenericIEnumerableTInterface
- or
- t instanceof SystemCollectionsGenericIEnumeratorInterface
- )
- }
- }
-
/**
* A data-flow node that reads a value returned directly by a callable,
* either via a C# call or a CIL call.
@@ -1551,9 +1536,6 @@ private module OutNodes {
(
kind instanceof NormalReturnKind and
not call.getExpr().getType() instanceof VoidType
- or
- kind instanceof YieldReturnKind and
- call.getExpr().getType() instanceof YieldReturnType
)
}
}
From 2541e9cb6a9d97b159d15d8f8deddbb3aecef3e2 Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Tue, 16 Mar 2021 16:32:47 +0100
Subject: [PATCH 130/725] C#: Handle async data flow in expression bodied
callables
---
csharp/ql/src/semmle/code/csharp/Callable.qll | 7 ++++++-
.../dataflow/internal/DataFlowPrivate.qll | 16 +++++++-------
.../library-tests/dataflow/async/Async.cs | 4 +++-
.../dataflow/async/Async.expected | 21 +++++++++++--------
4 files changed, 29 insertions(+), 19 deletions(-)
diff --git a/csharp/ql/src/semmle/code/csharp/Callable.qll b/csharp/ql/src/semmle/code/csharp/Callable.qll
index 6dc7cfced9b..83e1739be89 100644
--- a/csharp/ql/src/semmle/code/csharp/Callable.qll
+++ b/csharp/ql/src/semmle/code/csharp/Callable.qll
@@ -11,6 +11,7 @@ private import dotnet
private import semmle.code.csharp.ExprOrStmtParent
private import semmle.code.csharp.metrics.Complexity
private import TypeRef
+private import semmle.code.csharp.frameworks.system.threading.Tasks
/**
* An element that can be called.
@@ -206,7 +207,11 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal
exists(ReturnStmt ret | ret.getEnclosingCallable() = this | e = ret.getExpr())
or
e = this.getExpressionBody() and
- not this.getReturnType() instanceof VoidType
+ not this.getReturnType() instanceof VoidType and
+ (
+ not this.(Modifiable).isAsync() or
+ not this.getReturnType() instanceof SystemThreadingTasksTaskClass
+ )
}
/** Holds if this callable can yield return the expression `e`. */
diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
index 061858ab316..e816d376799 100644
--- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
+++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
@@ -788,7 +788,7 @@ private module Cached {
or
exists(Expr e |
e = node1.asExpr() and
- node2.(AsyncReturnNode).getReturnStmt().getExpr() = e and
+ node2.(AsyncReturnNode).getExpr() = e and
c = getResultContent()
)
or
@@ -1411,23 +1411,23 @@ private module ReturnNodes {
*/
class AsyncReturnNode extends ReturnNode, NodeImpl, TAsyncReturnNode {
private ControlFlow::Nodes::ElementNode cfn;
- private ReturnStmt rs;
+ private Expr expr;
- AsyncReturnNode() { this = TAsyncReturnNode(cfn) and rs.getExpr().getAControlFlowNode() = cfn }
+ AsyncReturnNode() { this = TAsyncReturnNode(cfn) and expr = cfn.getElement() }
- ReturnStmt getReturnStmt() { result = rs }
+ Expr getExpr() { result = expr }
override NormalReturnKind getKind() { any() }
- override Callable getEnclosingCallableImpl() { result = rs.getEnclosingCallable() }
+ override Callable getEnclosingCallableImpl() { result = expr.getEnclosingCallable() }
- override Type getTypeImpl() { result = rs.getEnclosingCallable().getReturnType() }
+ override Type getTypeImpl() { result = expr.getEnclosingCallable().getReturnType() }
override ControlFlow::Node getControlFlowNodeImpl() { result = cfn }
- override Location getLocationImpl() { result = rs.getLocation() }
+ override Location getLocationImpl() { result = expr.getLocation() }
- override string toStringImpl() { result = rs.toString() }
+ override string toStringImpl() { result = expr.toString() }
}
/**
diff --git a/csharp/ql/test/library-tests/dataflow/async/Async.cs b/csharp/ql/test/library-tests/dataflow/async/Async.cs
index 7040b5d4507..feb30fbbc49 100644
--- a/csharp/ql/test/library-tests/dataflow/async/Async.cs
+++ b/csharp/ql/test/library-tests/dataflow/async/Async.cs
@@ -29,7 +29,7 @@ class Test
public void TestAwait3(string input)
{
- Sink(ReturnAwait(input).Result);
+ Sink(ReturnAwait2(input).Result);
}
private async Task ReturnAwait(string x)
@@ -47,4 +47,6 @@ class Test
{
return Task.FromResult(x);
}
+
+ private async Task ReturnAwait2(string x) => x;
}
\ No newline at end of file
diff --git a/csharp/ql/test/library-tests/dataflow/async/Async.expected b/csharp/ql/test/library-tests/dataflow/async/Async.expected
index 7afc9cf0e9f..1b7ec20d98e 100644
--- a/csharp/ql/test/library-tests/dataflow/async/Async.expected
+++ b/csharp/ql/test/library-tests/dataflow/async/Async.expected
@@ -10,19 +10,20 @@ edges
| Async.cs:26:17:26:40 | await ... : String | Async.cs:27:14:27:14 | access to local variable x |
| Async.cs:26:23:26:40 | call to method ReturnAwait [Result] : String | Async.cs:26:17:26:40 | await ... : String |
| Async.cs:26:35:26:39 | access to parameter input : String | Async.cs:26:23:26:40 | call to method ReturnAwait [Result] : String |
-| Async.cs:30:35:30:39 | input : String | Async.cs:32:26:32:30 | access to parameter input : String |
-| Async.cs:32:14:32:31 | call to method ReturnAwait [Result] : String | Async.cs:32:14:32:38 | access to property Result |
-| Async.cs:32:26:32:30 | access to parameter input : String | Async.cs:32:14:32:31 | call to method ReturnAwait [Result] : String |
+| Async.cs:30:35:30:39 | input : String | Async.cs:32:27:32:31 | access to parameter input : String |
+| Async.cs:32:14:32:32 | call to method ReturnAwait2 [Result] : String | Async.cs:32:14:32:39 | access to property Result |
+| Async.cs:32:27:32:31 | access to parameter input : String | Async.cs:32:14:32:32 | call to method ReturnAwait2 [Result] : String |
| Async.cs:35:51:35:51 | x : String | Async.cs:38:16:38:16 | access to parameter x : String |
| Async.cs:38:16:38:16 | access to parameter x : String | Async.cs:21:20:21:37 | call to method ReturnAwait [Result] : String |
| Async.cs:38:16:38:16 | access to parameter x : String | Async.cs:26:23:26:40 | call to method ReturnAwait [Result] : String |
-| Async.cs:38:16:38:16 | access to parameter x : String | Async.cs:32:14:32:31 | call to method ReturnAwait [Result] : String |
| Async.cs:41:33:41:37 | input : String | Async.cs:43:25:43:29 | access to parameter input : String |
| Async.cs:43:14:43:30 | call to method ReturnTask [Result] : String | Async.cs:43:14:43:37 | access to property Result |
| Async.cs:43:25:43:29 | access to parameter input : String | Async.cs:43:14:43:30 | call to method ReturnTask [Result] : String |
| Async.cs:46:44:46:44 | x : String | Async.cs:48:32:48:32 | access to parameter x : String |
| Async.cs:48:16:48:33 | call to method FromResult [Result] : String | Async.cs:43:14:43:30 | call to method ReturnTask [Result] : String |
| Async.cs:48:32:48:32 | access to parameter x : String | Async.cs:48:16:48:33 | call to method FromResult [Result] : String |
+| Async.cs:51:52:51:52 | x : String | Async.cs:51:58:51:58 | access to parameter x : String |
+| Async.cs:51:58:51:58 | access to parameter x : String | Async.cs:32:14:32:32 | call to method ReturnAwait2 [Result] : String |
nodes
| Async.cs:9:37:9:41 | input : String | semmle.label | input : String |
| Async.cs:11:14:11:26 | call to method Return | semmle.label | call to method Return |
@@ -39,9 +40,9 @@ nodes
| Async.cs:26:35:26:39 | access to parameter input : String | semmle.label | access to parameter input : String |
| Async.cs:27:14:27:14 | access to local variable x | semmle.label | access to local variable x |
| Async.cs:30:35:30:39 | input : String | semmle.label | input : String |
-| Async.cs:32:14:32:31 | call to method ReturnAwait [Result] : String | semmle.label | call to method ReturnAwait [Result] : String |
-| Async.cs:32:14:32:38 | access to property Result | semmle.label | access to property Result |
-| Async.cs:32:26:32:30 | access to parameter input : String | semmle.label | access to parameter input : String |
+| Async.cs:32:14:32:32 | call to method ReturnAwait2 [Result] : String | semmle.label | call to method ReturnAwait2 [Result] : String |
+| Async.cs:32:14:32:39 | access to property Result | semmle.label | access to property Result |
+| Async.cs:32:27:32:31 | access to parameter input : String | semmle.label | access to parameter input : String |
| Async.cs:35:51:35:51 | x : String | semmle.label | x : String |
| Async.cs:38:16:38:16 | access to parameter x : String | semmle.label | access to parameter x : String |
| Async.cs:41:33:41:37 | input : String | semmle.label | input : String |
@@ -51,6 +52,8 @@ nodes
| Async.cs:46:44:46:44 | x : String | semmle.label | x : String |
| Async.cs:48:16:48:33 | call to method FromResult [Result] : String | semmle.label | call to method FromResult [Result] : String |
| Async.cs:48:32:48:32 | access to parameter x : String | semmle.label | access to parameter x : String |
+| Async.cs:51:52:51:52 | x : String | semmle.label | x : String |
+| Async.cs:51:58:51:58 | access to parameter x : String | semmle.label | access to parameter x : String |
#select
| Async.cs:11:14:11:26 | call to method Return | Async.cs:9:37:9:41 | input : String | Async.cs:11:14:11:26 | call to method Return | $@ flows to here and is used. | Async.cs:9:37:9:41 | input | User-provided value |
| Async.cs:11:14:11:26 | call to method Return | Async.cs:14:34:14:34 | x : String | Async.cs:11:14:11:26 | call to method Return | $@ flows to here and is used. | Async.cs:14:34:14:34 | x | User-provided value |
@@ -58,7 +61,7 @@ nodes
| Async.cs:21:14:21:37 | await ... | Async.cs:35:51:35:51 | x : String | Async.cs:21:14:21:37 | await ... | $@ flows to here and is used. | Async.cs:35:51:35:51 | x | User-provided value |
| Async.cs:27:14:27:14 | access to local variable x | Async.cs:24:41:24:45 | input : String | Async.cs:27:14:27:14 | access to local variable x | $@ flows to here and is used. | Async.cs:24:41:24:45 | input | User-provided value |
| Async.cs:27:14:27:14 | access to local variable x | Async.cs:35:51:35:51 | x : String | Async.cs:27:14:27:14 | access to local variable x | $@ flows to here and is used. | Async.cs:35:51:35:51 | x | User-provided value |
-| Async.cs:32:14:32:38 | access to property Result | Async.cs:30:35:30:39 | input : String | Async.cs:32:14:32:38 | access to property Result | $@ flows to here and is used. | Async.cs:30:35:30:39 | input | User-provided value |
-| Async.cs:32:14:32:38 | access to property Result | Async.cs:35:51:35:51 | x : String | Async.cs:32:14:32:38 | access to property Result | $@ flows to here and is used. | Async.cs:35:51:35:51 | x | User-provided value |
+| Async.cs:32:14:32:39 | access to property Result | Async.cs:30:35:30:39 | input : String | Async.cs:32:14:32:39 | access to property Result | $@ flows to here and is used. | Async.cs:30:35:30:39 | input | User-provided value |
+| Async.cs:32:14:32:39 | access to property Result | Async.cs:51:52:51:52 | x : String | Async.cs:32:14:32:39 | access to property Result | $@ flows to here and is used. | Async.cs:51:52:51:52 | x | User-provided value |
| Async.cs:43:14:43:37 | access to property Result | Async.cs:41:33:41:37 | input : String | Async.cs:43:14:43:37 | access to property Result | $@ flows to here and is used. | Async.cs:41:33:41:37 | input | User-provided value |
| Async.cs:43:14:43:37 | access to property Result | Async.cs:46:44:46:44 | x : String | Async.cs:43:14:43:37 | access to property Result | $@ flows to here and is used. | Async.cs:46:44:46:44 | x | User-provided value |
From cd820917bc957497951fa9158fea990c6fb67025 Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Tue, 16 Mar 2021 21:28:58 +0100
Subject: [PATCH 131/725] Remove duplicate yield return entries from global
dataflow test
---
.../dataflow/global/GetAnOutNode.expected | 18 ------------------
1 file changed, 18 deletions(-)
diff --git a/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected b/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected
index 5b4957a19e9..318034455b7 100644
--- a/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected
+++ b/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected
@@ -1,11 +1,9 @@
| Capture.cs:33:9:33:40 | call to method Select | return | Capture.cs:33:9:33:40 | call to method Select |
-| Capture.cs:33:9:33:40 | call to method Select | yield return | Capture.cs:33:9:33:40 | call to method Select |
| Capture.cs:33:9:33:50 | call to method ToArray | return | Capture.cs:33:9:33:50 | call to method ToArray |
| Capture.cs:71:9:71:21 | call to local function CaptureOut1 | captured sink30 | Capture.cs:71:9:71:21 | SSA call def(sink30) |
| Capture.cs:83:9:83:21 | [transitive] call to local function CaptureOut2 | captured sink31 | Capture.cs:83:9:83:21 | SSA call def(sink31) |
| Capture.cs:92:9:92:41 | [transitive] call to method Select | captured sink32 | Capture.cs:92:9:92:41 | SSA call def(sink32) |
| Capture.cs:92:9:92:41 | call to method Select | return | Capture.cs:92:9:92:41 | call to method Select |
-| Capture.cs:92:9:92:41 | call to method Select | yield return | Capture.cs:92:9:92:41 | call to method Select |
| Capture.cs:92:9:92:51 | call to method ToArray | return | Capture.cs:92:9:92:51 | call to method ToArray |
| Capture.cs:121:9:121:35 | [transitive] call to local function CaptureOutMultipleLambdas | captured nonSink0 | Capture.cs:121:9:121:35 | SSA call def(nonSink0) |
| Capture.cs:121:9:121:35 | [transitive] call to local function CaptureOutMultipleLambdas | captured sink40 | Capture.cs:121:9:121:35 | SSA call def(sink40) |
@@ -13,7 +11,6 @@
| Capture.cs:144:9:144:25 | [transitive] call to local function CaptureThrough2 | captured sink34 | Capture.cs:144:9:144:25 | SSA call def(sink34) |
| Capture.cs:153:9:153:45 | [transitive] call to method Select | captured sink35 | Capture.cs:153:9:153:45 | SSA call def(sink35) |
| Capture.cs:153:9:153:45 | call to method Select | return | Capture.cs:153:9:153:45 | call to method Select |
-| Capture.cs:153:9:153:45 | call to method Select | yield return | Capture.cs:153:9:153:45 | call to method Select |
| Capture.cs:153:9:153:55 | call to method ToArray | return | Capture.cs:153:9:153:55 | call to method ToArray |
| Capture.cs:160:22:160:38 | call to local function CaptureThrough4 | return | Capture.cs:160:22:160:38 | call to local function CaptureThrough4 |
| Capture.cs:168:9:168:32 | call to local function CaptureThrough5 | captured sink37 | Capture.cs:168:9:168:32 | SSA call def(sink37) |
@@ -52,16 +49,12 @@
| GlobalDataFlow.cs:79:9:79:46 | call to method ReturnRef | out | GlobalDataFlow.cs:79:30:79:34 | SSA def(sink3) |
| GlobalDataFlow.cs:79:9:79:46 | call to method ReturnRef | ref | GlobalDataFlow.cs:79:30:79:34 | SSA def(sink3) |
| GlobalDataFlow.cs:81:22:81:85 | call to method SelectEven | return | GlobalDataFlow.cs:81:22:81:85 | call to method SelectEven |
-| GlobalDataFlow.cs:81:22:81:85 | call to method SelectEven | yield return | GlobalDataFlow.cs:81:22:81:85 | call to method SelectEven |
| GlobalDataFlow.cs:81:22:81:93 | call to method First | return | GlobalDataFlow.cs:81:22:81:93 | call to method First |
| GlobalDataFlow.cs:83:22:83:87 | call to method Select | return | GlobalDataFlow.cs:83:22:83:87 | call to method Select |
-| GlobalDataFlow.cs:83:22:83:87 | call to method Select | yield return | GlobalDataFlow.cs:83:22:83:87 | call to method Select |
| GlobalDataFlow.cs:83:22:83:95 | call to method First | return | GlobalDataFlow.cs:83:22:83:95 | call to method First |
| GlobalDataFlow.cs:85:22:85:128 | call to method Zip | return | GlobalDataFlow.cs:85:22:85:128 | call to method Zip |
-| GlobalDataFlow.cs:85:22:85:128 | call to method Zip | yield return | GlobalDataFlow.cs:85:22:85:128 | call to method Zip |
| GlobalDataFlow.cs:85:22:85:136 | call to method First | return | GlobalDataFlow.cs:85:22:85:136 | call to method First |
| GlobalDataFlow.cs:87:22:87:128 | call to method Zip | return | GlobalDataFlow.cs:87:22:87:128 | call to method Zip |
-| GlobalDataFlow.cs:87:22:87:128 | call to method Zip | yield return | GlobalDataFlow.cs:87:22:87:128 | call to method Zip |
| GlobalDataFlow.cs:87:22:87:136 | call to method First | return | GlobalDataFlow.cs:87:22:87:136 | call to method First |
| GlobalDataFlow.cs:89:22:89:110 | call to method Aggregate | return | GlobalDataFlow.cs:89:22:89:110 | call to method Aggregate |
| GlobalDataFlow.cs:91:22:91:110 | call to method Aggregate | return | GlobalDataFlow.cs:91:22:91:110 | call to method Aggregate |
@@ -83,16 +76,12 @@
| GlobalDataFlow.cs:111:9:111:49 | call to method ReturnRef | out | GlobalDataFlow.cs:111:30:111:34 | SSA def(sink1) |
| GlobalDataFlow.cs:111:9:111:49 | call to method ReturnRef | ref | GlobalDataFlow.cs:111:30:111:34 | SSA def(sink1) |
| GlobalDataFlow.cs:113:20:113:86 | call to method SelectEven | return | GlobalDataFlow.cs:113:20:113:86 | call to method SelectEven |
-| GlobalDataFlow.cs:113:20:113:86 | call to method SelectEven | yield return | GlobalDataFlow.cs:113:20:113:86 | call to method SelectEven |
| GlobalDataFlow.cs:113:20:113:94 | call to method First | return | GlobalDataFlow.cs:113:20:113:94 | call to method First |
| GlobalDataFlow.cs:115:20:115:82 | call to method Select | return | GlobalDataFlow.cs:115:20:115:82 | call to method Select |
-| GlobalDataFlow.cs:115:20:115:82 | call to method Select | yield return | GlobalDataFlow.cs:115:20:115:82 | call to method Select |
| GlobalDataFlow.cs:115:20:115:90 | call to method First | return | GlobalDataFlow.cs:115:20:115:90 | call to method First |
| GlobalDataFlow.cs:117:20:117:126 | call to method Zip | return | GlobalDataFlow.cs:117:20:117:126 | call to method Zip |
-| GlobalDataFlow.cs:117:20:117:126 | call to method Zip | yield return | GlobalDataFlow.cs:117:20:117:126 | call to method Zip |
| GlobalDataFlow.cs:117:20:117:134 | call to method First | return | GlobalDataFlow.cs:117:20:117:134 | call to method First |
| GlobalDataFlow.cs:119:20:119:126 | call to method Zip | return | GlobalDataFlow.cs:119:20:119:126 | call to method Zip |
-| GlobalDataFlow.cs:119:20:119:126 | call to method Zip | yield return | GlobalDataFlow.cs:119:20:119:126 | call to method Zip |
| GlobalDataFlow.cs:119:20:119:134 | call to method First | return | GlobalDataFlow.cs:119:20:119:134 | call to method First |
| GlobalDataFlow.cs:121:20:121:104 | call to method Aggregate | return | GlobalDataFlow.cs:121:20:121:104 | call to method Aggregate |
| GlobalDataFlow.cs:123:20:123:109 | call to method Aggregate | return | GlobalDataFlow.cs:123:20:123:109 | call to method Aggregate |
@@ -119,7 +108,6 @@
| GlobalDataFlow.cs:160:9:160:25 | call to method OutRef | ref | GlobalDataFlow.cs:160:20:160:24 | SSA def(sink8) |
| GlobalDataFlow.cs:162:22:162:31 | call to method OutYield | qualifier | GlobalDataFlow.cs:162:22:162:31 | [post] this access |
| GlobalDataFlow.cs:162:22:162:31 | call to method OutYield | return | GlobalDataFlow.cs:162:22:162:31 | call to method OutYield |
-| GlobalDataFlow.cs:162:22:162:31 | call to method OutYield | yield return | GlobalDataFlow.cs:162:22:162:31 | call to method OutYield |
| GlobalDataFlow.cs:162:22:162:39 | call to method First | return | GlobalDataFlow.cs:162:22:162:39 | call to method First |
| GlobalDataFlow.cs:164:22:164:43 | call to method TaintedParam | return | GlobalDataFlow.cs:164:22:164:43 | call to method TaintedParam |
| GlobalDataFlow.cs:168:20:168:27 | call to method NonOut | qualifier | GlobalDataFlow.cs:168:20:168:27 | [post] this access |
@@ -132,7 +120,6 @@
| GlobalDataFlow.cs:172:9:172:31 | call to method NonOutRef | ref | GlobalDataFlow.cs:172:23:172:30 | SSA def(nonSink0) |
| GlobalDataFlow.cs:174:20:174:32 | call to method NonOutYield | qualifier | GlobalDataFlow.cs:174:20:174:32 | [post] this access |
| GlobalDataFlow.cs:174:20:174:32 | call to method NonOutYield | return | GlobalDataFlow.cs:174:20:174:32 | call to method NonOutYield |
-| GlobalDataFlow.cs:174:20:174:32 | call to method NonOutYield | yield return | GlobalDataFlow.cs:174:20:174:32 | call to method NonOutYield |
| GlobalDataFlow.cs:174:20:174:40 | call to method First | return | GlobalDataFlow.cs:174:20:174:40 | call to method First |
| GlobalDataFlow.cs:176:20:176:44 | call to method NonTaintedParam | return | GlobalDataFlow.cs:176:20:176:44 | call to method NonTaintedParam |
| GlobalDataFlow.cs:181:21:181:26 | delegate call | return | GlobalDataFlow.cs:181:21:181:26 | delegate call |
@@ -151,26 +138,21 @@
| GlobalDataFlow.cs:209:41:209:77 | call to method AsQueryable | return | GlobalDataFlow.cs:209:41:209:77 | call to method AsQueryable |
| GlobalDataFlow.cs:212:76:212:90 | call to method ReturnCheck2 | return | GlobalDataFlow.cs:212:76:212:90 | call to method ReturnCheck2 |
| GlobalDataFlow.cs:213:22:213:39 | call to method Select | return | GlobalDataFlow.cs:213:22:213:39 | call to method Select |
-| GlobalDataFlow.cs:213:22:213:39 | call to method Select | yield return | GlobalDataFlow.cs:213:22:213:39 | call to method Select |
| GlobalDataFlow.cs:213:22:213:47 | call to method First | return | GlobalDataFlow.cs:213:22:213:47 | call to method First |
| GlobalDataFlow.cs:215:22:215:39 | call to method Select | return | GlobalDataFlow.cs:215:22:215:39 | call to method Select |
| GlobalDataFlow.cs:215:22:215:47 | call to method First | return | GlobalDataFlow.cs:215:22:215:47 | call to method First |
| GlobalDataFlow.cs:217:22:217:49 | call to method Select | return | GlobalDataFlow.cs:217:22:217:49 | call to method Select |
-| GlobalDataFlow.cs:217:22:217:49 | call to method Select | yield return | GlobalDataFlow.cs:217:22:217:49 | call to method Select |
| GlobalDataFlow.cs:217:22:217:57 | call to method First | return | GlobalDataFlow.cs:217:22:217:57 | call to method First |
| GlobalDataFlow.cs:222:76:222:92 | call to method NonReturnCheck | return | GlobalDataFlow.cs:222:76:222:92 | call to method NonReturnCheck |
| GlobalDataFlow.cs:223:23:223:43 | call to method Select | return | GlobalDataFlow.cs:223:23:223:43 | call to method Select |
-| GlobalDataFlow.cs:223:23:223:43 | call to method Select | yield return | GlobalDataFlow.cs:223:23:223:43 | call to method Select |
| GlobalDataFlow.cs:223:23:223:51 | call to method First | return | GlobalDataFlow.cs:223:23:223:51 | call to method First |
| GlobalDataFlow.cs:225:19:225:39 | call to method Select | return | GlobalDataFlow.cs:225:19:225:39 | call to method Select |
| GlobalDataFlow.cs:225:19:225:47 | call to method First | return | GlobalDataFlow.cs:225:19:225:47 | call to method First |
| GlobalDataFlow.cs:227:19:227:39 | call to method Select | return | GlobalDataFlow.cs:227:19:227:39 | call to method Select |
-| GlobalDataFlow.cs:227:19:227:39 | call to method Select | yield return | GlobalDataFlow.cs:227:19:227:39 | call to method Select |
| GlobalDataFlow.cs:227:19:227:47 | call to method First | return | GlobalDataFlow.cs:227:19:227:47 | call to method First |
| GlobalDataFlow.cs:229:19:229:39 | call to method Select | return | GlobalDataFlow.cs:229:19:229:39 | call to method Select |
| GlobalDataFlow.cs:229:19:229:47 | call to method First | return | GlobalDataFlow.cs:229:19:229:47 | call to method First |
| GlobalDataFlow.cs:231:19:231:49 | call to method Select | return | GlobalDataFlow.cs:231:19:231:49 | call to method Select |
-| GlobalDataFlow.cs:231:19:231:49 | call to method Select | yield return | GlobalDataFlow.cs:231:19:231:49 | call to method Select |
| GlobalDataFlow.cs:231:19:231:57 | call to method First | return | GlobalDataFlow.cs:231:19:231:57 | call to method First |
| GlobalDataFlow.cs:238:20:238:49 | call to method Run | return | GlobalDataFlow.cs:238:20:238:49 | call to method Run |
| GlobalDataFlow.cs:239:22:239:32 | access to property Result | qualifier | GlobalDataFlow.cs:239:22:239:25 | [post] access to local variable task |
From dd6b27df24180f7bcd0e40fc1ed996c301b451b5 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Tue, 16 Mar 2021 22:35:47 +0100
Subject: [PATCH 132/725] C++: Fix test annotation.
---
cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp
index 4d28955aaa0..fab96fc878d 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp
@@ -38,7 +38,7 @@ void test_stringstream_string(int amount)
sink(ss2); // $ ast,ir
sink(ss3); // $ ast MISSING: ir
sink(ss4); // $ ast,ir
- sink(ss5); // $ ast MISSIN,ir
+ sink(ss5); // $ ast,ir
sink(ss1.str());
sink(ss2.str()); // $ ast,ir
sink(ss3.str()); // $ ast MISSING: ir
From 43fbcc1c8a19a6a02a0f304b5d22579aef96a996 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Tue, 16 Mar 2021 22:36:17 +0100
Subject: [PATCH 133/725] C++: Convert all the dataflow configurations to taint
configurations.
---
.../cpp/ir/dataflow/DefaultTaintTracking.qll | 26 +++++++++----------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
index 7a4581cf31f..33473ebfb58 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
@@ -3,12 +3,12 @@ import semmle.code.cpp.security.Security
private import semmle.code.cpp.ir.dataflow.DataFlow
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
private import semmle.code.cpp.ir.dataflow.DataFlow2
-private import semmle.code.cpp.ir.dataflow.DataFlow3
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.ir.dataflow.internal.DataFlowDispatch as Dispatch
private import semmle.code.cpp.controlflow.IRGuards
private import semmle.code.cpp.models.interfaces.Taint
private import semmle.code.cpp.models.interfaces.DataFlow
+private import semmle.code.cpp.ir.dataflow.TaintTracking
private import semmle.code.cpp.ir.dataflow.TaintTracking2
private import semmle.code.cpp.ir.dataflow.internal.ModelUtil
@@ -67,23 +67,23 @@ private DataFlow::Node getNodeForExpr(Expr node) {
not argv(node.(VariableAccess).getTarget())
}
-private class DefaultTaintTrackingCfg extends DataFlow::Configuration {
+private class DefaultTaintTrackingCfg extends TaintTracking::Configuration {
DefaultTaintTrackingCfg() { this = "DefaultTaintTrackingCfg" }
override predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
- override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
+ override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
commonTaintStep(n1, n2)
}
- override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
+ override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
- override predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
+ override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
}
-private class ToGlobalVarTaintTrackingCfg extends DataFlow::Configuration {
+private class ToGlobalVarTaintTrackingCfg extends TaintTracking::Configuration {
ToGlobalVarTaintTrackingCfg() { this = "GlobalVarTaintTrackingCfg" }
override predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
@@ -92,7 +92,7 @@ private class ToGlobalVarTaintTrackingCfg extends DataFlow::Configuration {
sink.asVariable() instanceof GlobalOrNamespaceVariable
}
- override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
+ override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
commonTaintStep(n1, n2)
or
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
@@ -100,12 +100,12 @@ private class ToGlobalVarTaintTrackingCfg extends DataFlow::Configuration {
readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable))
}
- override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
+ override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
- override predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
+ override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
}
-private class FromGlobalVarTaintTrackingCfg extends DataFlow3::Configuration {
+private class FromGlobalVarTaintTrackingCfg extends TaintTracking2::Configuration {
FromGlobalVarTaintTrackingCfg() { this = "FromGlobalVarTaintTrackingCfg" }
override predicate isSource(DataFlow::Node source) {
@@ -116,7 +116,7 @@ private class FromGlobalVarTaintTrackingCfg extends DataFlow3::Configuration {
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
- override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
+ override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
commonTaintStep(n1, n2)
or
// Additional step for flow out of variables. There is no flow _into_
@@ -125,9 +125,9 @@ private class FromGlobalVarTaintTrackingCfg extends DataFlow3::Configuration {
readsVariable(n2.asInstruction(), n1.asVariable())
}
- override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
+ override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
- override predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
+ override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
}
private predicate readsVariable(LoadInstruction load, Variable var) {
From 98204a15a6ad8c8dd5dfb42269bfdd153cfc1827 Mon Sep 17 00:00:00 2001
From: haby0
Date: Wed, 17 Mar 2021 15:28:04 +0800
Subject: [PATCH 134/725] Fix the problem
---
.../Security/CWE/CWE-352/JsonStringLib.qll | 2 +-
.../Security/CWE/CWE-352/JsonpInjection.java | 117 ++++++----
.../Security/CWE/CWE-352/JsonpInjection.qhelp | 11 +-
.../Security/CWE/CWE-352/JsonpInjection.ql | 24 ++-
.../CWE/CWE-352/JsonpInjectionLib.qll | 66 +++---
.../Security/CWE/CWE-598/SensitiveGetQuery.ql | 10 -
.../semmle/code/java/frameworks/Servlets.qll | 11 +
.../security/CWE-352/JsonpInjection.qlref | 1 -
.../JsonpController.java | 105 +++++++--
.../JsonpInjection.expected | 87 ++++++++
.../JsonpInjection.qlref | 1 +
.../JsonpInjectionServlet1.java | 0
.../JsonpInjectionServlet2.java | 0
.../RefererFilter.java | 0
.../CWE-352/JsonpInjectionWithFilter/options | 1 +
.../JsonpController.java | 107 +++++++--
.../JsonpInjection.expected | 76 +++++++
.../JsonpInjection.qlref | 1 +
.../options | 1 +
.../JsonpController.java | 203 ++++++++++++++++++
.../JsonpInjection.expected | 92 ++++++++
.../JsonpInjection.qlref | 1 +
.../JsonpInjectionServlet1.java | 0
.../JsonpInjectionServlet2.java | 0
.../options | 1 +
.../CWE-352/JsonpInjection_1.expected | 60 ------
.../CWE-352/JsonpInjection_2.expected | 78 -------
.../CWE-352/JsonpInjection_3.expected | 66 ------
.../query-tests/security/CWE-352/Readme | 3 -
.../security/CWE-352/RefererFilter.java | 43 ----
.../query-tests/security/CWE-352/options | 1 -
.../core/annotation/AliasFor.java | 13 +-
.../core/io/InputStreamSource.java | 8 +
.../org/springframework/core/io/Resource.java | 46 ++++
.../org/springframework/lang/Nullable.java | 13 ++
.../springframework/util/FileCopyUtils.java | 53 +++++
.../org/springframework/util/StringUtils.java | 2 +-
.../web/bind/annotation/GetMapping.java | 36 +++-
.../web/bind/annotation/Mapping.java | 4 +
.../web/bind/annotation/RequestMapping.java | 19 +-
.../web/bind/annotation/RequestParam.java | 23 ++
.../web/bind/annotation/ResponseBody.java | 9 +
.../web/multipart/MultipartFile.java | 38 ++++
.../javax/servlet/annotation/WebServlet.java | 30 +++
44 files changed, 1075 insertions(+), 388 deletions(-)
delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.qlref
rename java/ql/test/experimental/query-tests/security/CWE-352/{ => JsonpInjectionWithFilter}/JsonpController.java (54%)
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithFilter/JsonpInjection.expected
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithFilter/JsonpInjection.qlref
rename java/ql/{src/experimental/Security/CWE/CWE-352 => test/experimental/query-tests/security/CWE-352/JsonpInjectionWithFilter}/JsonpInjectionServlet1.java (100%)
rename java/ql/{src/experimental/Security/CWE/CWE-352 => test/experimental/query-tests/security/CWE-352/JsonpInjectionWithFilter}/JsonpInjectionServlet2.java (100%)
rename java/ql/{src/experimental/Security/CWE/CWE-352 => test/experimental/query-tests/security/CWE-352/JsonpInjectionWithFilter}/RefererFilter.java (100%)
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithFilter/options
rename java/ql/{src/experimental/Security/CWE/CWE-352 => test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringController}/JsonpController.java (54%)
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringController/JsonpInjection.expected
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringController/JsonpInjection.qlref
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringController/options
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringControllerAndServlet/JsonpController.java
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringControllerAndServlet/JsonpInjection.expected
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringControllerAndServlet/JsonpInjection.qlref
rename java/ql/test/experimental/query-tests/security/CWE-352/{ => JsonpInjectionWithSpringControllerAndServlet}/JsonpInjectionServlet1.java (100%)
rename java/ql/test/experimental/query-tests/security/CWE-352/{ => JsonpInjectionWithSpringControllerAndServlet}/JsonpInjectionServlet2.java (100%)
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringControllerAndServlet/options
delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_1.expected
delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_2.expected
delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_3.expected
delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/Readme
delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/RefererFilter.java
delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-352/options
create mode 100644 java/ql/test/stubs/spring-core-5.3.2/org/springframework/core/io/InputStreamSource.java
create mode 100644 java/ql/test/stubs/spring-core-5.3.2/org/springframework/core/io/Resource.java
create mode 100644 java/ql/test/stubs/spring-core-5.3.2/org/springframework/lang/Nullable.java
create mode 100644 java/ql/test/stubs/spring-core-5.3.2/org/springframework/util/FileCopyUtils.java
create mode 100644 java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/Mapping.java
create mode 100644 java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/RequestParam.java
create mode 100644 java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/multipart/MultipartFile.java
create mode 100644 java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/annotation/WebServlet.java
diff --git a/java/ql/src/experimental/Security/CWE/CWE-352/JsonStringLib.qll b/java/ql/src/experimental/Security/CWE/CWE-352/JsonStringLib.qll
index 0da8bc860d1..5cc52e97e33 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-352/JsonStringLib.qll
+++ b/java/ql/src/experimental/Security/CWE/CWE-352/JsonStringLib.qll
@@ -3,7 +3,7 @@ import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.FlowSources
import DataFlow::PathGraph
-/** Json string type data */
+/** Json string type data. */
abstract class JsonpStringSource extends DataFlow::Node { }
/** Convert to String using Gson library. */
diff --git a/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.java b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.java
index 8b4e7cc005e..7f479a8c023 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.java
+++ b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.java
@@ -1,31 +1,39 @@
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.HashMap;
-import java.util.Random;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.multipart.MultipartFile;
@Controller
public class JsonpInjection {
-private static HashMap hashMap = new HashMap();
+
+ private static HashMap hashMap = new HashMap();
static {
hashMap.put("username","admin");
hashMap.put("password","123456");
}
+ private String name = null;
+
@GetMapping(value = "jsonp1")
@ResponseBody
public String bad1(HttpServletRequest request) {
String resultStr = null;
String jsonpCallback = request.getParameter("jsonpCallback");
-
Gson gson = new Gson();
String result = gson.toJson(hashMap);
resultStr = jsonpCallback + "(" + result + ")";
@@ -37,9 +45,7 @@ private static HashMap hashMap = new HashMap();
public String bad2(HttpServletRequest request) {
String resultStr = null;
String jsonpCallback = request.getParameter("jsonpCallback");
-
resultStr = jsonpCallback + "(" + JSONObject.toJSONString(hashMap) + ")";
-
return resultStr;
}
@@ -67,7 +73,6 @@ private static HashMap hashMap = new HashMap();
@ResponseBody
public void bad5(HttpServletRequest request,
HttpServletResponse response) throws Exception {
- response.setContentType("application/json");
String jsonpCallback = request.getParameter("jsonpCallback");
PrintWriter pw = null;
Gson gson = new Gson();
@@ -83,7 +88,6 @@ private static HashMap hashMap = new HashMap();
@ResponseBody
public void bad6(HttpServletRequest request,
HttpServletResponse response) throws Exception {
- response.setContentType("application/json");
String jsonpCallback = request.getParameter("jsonpCallback");
PrintWriter pw = null;
ObjectMapper mapper = new ObjectMapper();
@@ -94,60 +98,96 @@ private static HashMap hashMap = new HashMap();
pw.println(resultStr);
}
- @GetMapping(value = "jsonp7")
+ @RequestMapping(value = "jsonp7", method = RequestMethod.GET)
@ResponseBody
- public String good(HttpServletRequest request) {
+ public String bad7(HttpServletRequest request) {
String resultStr = null;
String jsonpCallback = request.getParameter("jsonpCallback");
-
- String val = "";
- Random random = new Random();
- for (int i = 0; i < 10; i++) {
- val += String.valueOf(random.nextInt(10));
- }
- // good
- jsonpCallback = jsonpCallback + "_" + val;
- String jsonStr = getJsonStr(hashMap);
- resultStr = jsonpCallback + "(" + jsonStr + ")";
+ Gson gson = new Gson();
+ String result = gson.toJson(hashMap);
+ resultStr = jsonpCallback + "(" + result + ")";
return resultStr;
}
+
@GetMapping(value = "jsonp8")
@ResponseBody
public String good1(HttpServletRequest request) {
String resultStr = null;
- String jsonpCallback = request.getParameter("jsonpCallback");
-
String token = request.getParameter("token");
-
- // good
if (verifToken(token)){
- System.out.println(token);
+ String jsonpCallback = request.getParameter("jsonpCallback");
String jsonStr = getJsonStr(hashMap);
resultStr = jsonpCallback + "(" + jsonStr + ")";
return resultStr;
}
-
return "error";
}
+
@GetMapping(value = "jsonp9")
@ResponseBody
public String good2(HttpServletRequest request) {
String resultStr = null;
- String jsonpCallback = request.getParameter("jsonpCallback");
-
- String referer = request.getHeader("Referer");
-
- boolean result = verifReferer(referer);
- // good
+ String token = request.getParameter("token");
+ boolean result = verifToken(token);
if (result){
- String jsonStr = getJsonStr(hashMap);
- resultStr = jsonpCallback + "(" + jsonStr + ")";
- return resultStr;
+ return "";
}
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String jsonStr = getJsonStr(hashMap);
+ resultStr = jsonpCallback + "(" + jsonStr + ")";
+ return resultStr;
+ }
- return "error";
+ @RequestMapping(value = "jsonp10")
+ @ResponseBody
+ public String good3(HttpServletRequest request) {
+ JSONObject parameterObj = readToJSONObect(request);
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String restr = JSONObject.toJSONString(hashMap);
+ resultStr = jsonpCallback + "(" + restr + ");";
+ return resultStr;
+ }
+
+ @RequestMapping(value = "jsonp11")
+ @ResponseBody
+ public String good4(@RequestParam("file") MultipartFile file,HttpServletRequest request) {
+ if(null == file){
+ return "upload file error";
+ }
+ String fileName = file.getOriginalFilename();
+ System.out.println("file operations");
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String restr = JSONObject.toJSONString(hashMap);
+ resultStr = jsonpCallback + "(" + restr + ");";
+ return resultStr;
+ }
+
+ public static JSONObject readToJSONObect(HttpServletRequest request){
+ String jsonText = readPostContent(request);
+ JSONObject jsonObj = JSONObject.parseObject(jsonText, JSONObject.class);
+ return jsonObj;
+ }
+
+ public static String readPostContent(HttpServletRequest request){
+ BufferedReader in= null;
+ String content = null;
+ String line = null;
+ try {
+ in = new BufferedReader(new InputStreamReader(request.getInputStream(),"UTF-8"));
+ StringBuilder buf = new StringBuilder();
+ while ((line = in.readLine()) != null) {
+ buf.append(line);
+ }
+ content = buf.toString();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ String uri = request.getRequestURI();
+ return content;
}
public static String getJsonStr(Object result) {
@@ -160,11 +200,4 @@ private static HashMap hashMap = new HashMap();
}
return true;
}
-
- public static boolean verifReferer(String referer){
- if (!referer.startsWith("http://test.com/")){
- return false;
- }
- return true;
- }
}
\ No newline at end of file
diff --git a/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.qhelp
index bb5d628ac0b..93c167d6c2c 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.qhelp
+++ b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.qhelp
@@ -3,18 +3,21 @@
"qhelp.dtd">
-The software uses external input as the function name to wrap JSON data and return it to the client as a request response. When there is a cross-domain problem,
-there is a problem of sensitive information leakage.
+The software uses external input as the function name to wrap JSON data and returns it to the client as a request response.
+When there is a cross-domain problem, the problem of sensitive information leakage may occur.
-Adding `Referer` or random `token` verification processing can effectively prevent the leakage of sensitive information.
+Adding Referer/Origin or random token verification processing can effectively prevent the leakage of sensitive information.
-The following example shows the case of no verification processing and verification processing for the external input function name.
+The following examples show the bad case and the good case respectively. Bad case, such as bad1 to bad7,
+will cause information leakage problems when there are cross-domain problems. In a good case, for example, in the good1
+method and the good2 method, use the verifToken method to do the random token Verification can
+solve the problem of information leakage caused by cross-domain.
diff --git a/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.ql
index f3ae25daa03..068469328ea 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.ql
@@ -14,25 +14,25 @@ import java
import JsonpInjectionLib
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.deadcode.WebEntryPoints
-import semmle.code.java.security.XSS
import DataFlow::PathGraph
/** Determine whether there is a verification method for the remote streaming source data flow path method. */
predicate existsFilterVerificationMethod() {
- exists(MethodAccess ma,Node existsNode, Method m|
+ exists(MethodAccess ma, Node existsNode, Method m |
ma.getMethod() instanceof VerificationMethodClass and
existsNode.asExpr() = ma and
- m = getAnMethod(existsNode.getEnclosingCallable()) and
+ m = getACallingCallableOrSelf(existsNode.getEnclosingCallable()) and
isDoFilterMethod(m)
)
}
/** Determine whether there is a verification method for the remote streaming source data flow path method. */
predicate existsServletVerificationMethod(Node checkNode) {
- exists(MethodAccess ma,Node existsNode|
+ exists(MethodAccess ma, Node existsNode |
ma.getMethod() instanceof VerificationMethodClass and
existsNode.asExpr() = ma and
- getAnMethod(existsNode.getEnclosingCallable()) = getAnMethod(checkNode.getEnclosingCallable())
+ getACallingCallableOrSelf(existsNode.getEnclosingCallable()) =
+ getACallingCallableOrSelf(checkNode.getEnclosingCallable())
)
}
@@ -40,13 +40,15 @@ predicate existsServletVerificationMethod(Node checkNode) {
class RequestResponseFlowConfig extends TaintTracking::Configuration {
RequestResponseFlowConfig() { this = "RequestResponseFlowConfig" }
- override predicate isSource(DataFlow::Node source) {
- source instanceof RemoteFlowSource and
- getAnMethod(source.getEnclosingCallable()) instanceof RequestGetMethod
- }
+ override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) { sink instanceof XssSink }
+ /** Eliminate the method of calling the node is not the get method. */
+ override predicate isSanitizer(DataFlow::Node node) {
+ not getACallingCallableOrSelf(node.getEnclosingCallable()) instanceof RequestGetMethod
+ }
+
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(MethodAccess ma |
isRequestGetParamMethod(ma) and pred.asExpr() = ma.getQualifier() and succ.asExpr() = ma
@@ -60,5 +62,5 @@ where
not existsFilterVerificationMethod() and
conf.hasFlowPath(source, sink) and
exists(JsonpInjectionFlowConfig jhfc | jhfc.hasFlowTo(sink.getNode()))
-select sink.getNode(), source, sink, "Jsonp Injection query might include code from $@.",
- source.getNode(), "this user input"
\ No newline at end of file
+select sink.getNode(), source, sink, "Jsonp response might include code from $@.", source.getNode(),
+ "this user input"
diff --git a/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionLib.qll b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionLib.qll
index b8964524a9f..d0e00bcb634 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionLib.qll
+++ b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionLib.qll
@@ -6,28 +6,25 @@ import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.frameworks.spring.SpringController
-/** Taint-tracking configuration tracing flow from user-controllable function name jsonp data to output jsonp data. */
+/** Taint-tracking configuration tracing flow from untrusted inputs to verification of remote user input. */
class VerificationMethodFlowConfig extends TaintTracking::Configuration {
VerificationMethodFlowConfig() { this = "VerificationMethodFlowConfig" }
override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma, BarrierGuard bg |
+ exists(MethodAccess ma |
ma.getMethod().getAParameter().getName().regexpMatch("(?i).*(token|auth|referer|origin).*") and
- bg = ma and
- sink.asExpr() = ma.getAnArgument()
+ ma.getAnArgument() = sink.asExpr()
)
}
}
-/** The parameter name of the method is `token`, `auth`, `referer`, `origin`. */
+/** The parameter names of this method are token/auth/referer/origin. */
class VerificationMethodClass extends Method {
VerificationMethodClass() {
- exists(MethodAccess ma, BarrierGuard bg, VerificationMethodFlowConfig vmfc, Node node |
+ exists(MethodAccess ma, VerificationMethodFlowConfig vmfc, Node node |
this = ma.getMethod() and
- this.getAParameter().getName().regexpMatch("(?i).*(token|auth|referer|origin).*") and
- bg = ma and
node.asExpr() = ma.getAnArgument() and
vmfc.hasFlowTo(node)
)
@@ -35,38 +32,43 @@ class VerificationMethodClass extends Method {
}
/** Get Callable by recursive method. */
-Callable getAnMethod(Callable call) {
+Callable getACallingCallableOrSelf(Callable call) {
result = call
or
- result = getAnMethod(call.getAReference().getEnclosingCallable())
+ result = getACallingCallableOrSelf(call.getAReference().getEnclosingCallable())
}
abstract class RequestGetMethod extends Method { }
-/** Holds if `m` is a method of some override of `HttpServlet.doGet`. */
+/** Override method of `doGet` of `Servlet` subclass. */
private class ServletGetMethod extends RequestGetMethod {
- ServletGetMethod() {
- exists(Method m |
- m = this and
- isServletRequestMethod(m) and
- m.getName() = "doGet"
- )
+ ServletGetMethod() { this instanceof DoGetServletMethod }
+}
+
+/** The method of SpringController class processing `get` request. */
+abstract class SpringControllerGetMethod extends RequestGetMethod { }
+
+/** Method using `GetMapping` annotation in SpringController class. */
+class SpringControllerGetMappingGetMethod extends SpringControllerGetMethod {
+ SpringControllerGetMappingGetMethod() {
+ this.getAnAnnotation()
+ .getType()
+ .hasQualifiedName("org.springframework.web.bind.annotation", "GetMapping")
}
}
-/** Holds if `m` is a method of some override of `HttpServlet.doGet`. */
-private class SpringControllerGetMethod extends RequestGetMethod {
- SpringControllerGetMethod() {
- exists(Annotation a |
- a = this.getAnAnnotation() and
- a.getType().hasQualifiedName("org.springframework.web.bind.annotation", "GetMapping")
- )
- or
- exists(Annotation a |
- a = this.getAnAnnotation() and
- a.getType().hasQualifiedName("org.springframework.web.bind.annotation", "RequestMapping") and
- a.getValue("method").toString().regexpMatch("RequestMethod.GET|\\{...\\}")
- )
+/** The method that uses the `RequestMapping` annotation in the SpringController class and only handles the get request. */
+class SpringControllerRequestMappingGetMethod extends SpringControllerGetMethod {
+ SpringControllerRequestMappingGetMethod() {
+ this.getAnAnnotation()
+ .getType()
+ .hasQualifiedName("org.springframework.web.bind.annotation", "RequestMapping") and
+ this.getAnAnnotation().getValue("method").toString().regexpMatch("RequestMethod.GET|\\{...\\}") and
+ not exists(MethodAccess ma |
+ ma.getMethod() instanceof ServletRequestGetBodyMethod and
+ this = getACallingCallableOrSelf(ma.getEnclosingCallable())
+ ) and
+ not this.getAParamType().getName() = "MultipartFile"
}
}
@@ -83,12 +85,12 @@ class JsonpInjectionExpr extends AddExpr {
.regexpMatch("\"\\(\"")
}
- /** Get the jsonp function name of this expression */
+ /** Get the jsonp function name of this expression. */
Expr getFunctionName() {
result = getLeftOperand().(AddExpr).getLeftOperand().(AddExpr).getLeftOperand()
}
- /** Get the json data of this expression */
+ /** Get the json data of this expression. */
Expr getJsonExpr() { result = getLeftOperand().(AddExpr).getRightOperand() }
}
diff --git a/java/ql/src/experimental/Security/CWE/CWE-598/SensitiveGetQuery.ql b/java/ql/src/experimental/Security/CWE/CWE-598/SensitiveGetQuery.ql
index bc9850cfddb..c381595af14 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-598/SensitiveGetQuery.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-598/SensitiveGetQuery.ql
@@ -23,16 +23,6 @@ class SensitiveInfoExpr extends Expr {
}
}
-/** Holds if `m` is a method of some override of `HttpServlet.doGet`. */
-private predicate isGetServletMethod(Method m) {
- isServletRequestMethod(m) and m.getName() = "doGet"
-}
-
-/** The `doGet` method of `HttpServlet`. */
-class DoGetServletMethod extends Method {
- DoGetServletMethod() { isGetServletMethod(this) }
-}
-
/** Holds if `ma` is (perhaps indirectly) called from the `doGet` method of `HttpServlet`. */
predicate isReachableFromServletDoGet(MethodAccess ma) {
ma.getEnclosingCallable() instanceof DoGetServletMethod
diff --git a/java/ql/src/semmle/code/java/frameworks/Servlets.qll b/java/ql/src/semmle/code/java/frameworks/Servlets.qll
index 5cccf62122f..7ac452affa9 100644
--- a/java/ql/src/semmle/code/java/frameworks/Servlets.qll
+++ b/java/ql/src/semmle/code/java/frameworks/Servlets.qll
@@ -354,9 +354,20 @@ class FilterChain extends Interface {
/** Holds if `m` is a filter handler method (for example `doFilter`). */
predicate isDoFilterMethod(Method m) {
+ m.getName().matches("doFilter") and
m.getDeclaringType() instanceof FilterClass and
m.getNumberOfParameters() = 3 and
m.getParameter(0).getType() instanceof ServletRequest and
m.getParameter(1).getType() instanceof ServletResponse and
m.getParameter(2).getType() instanceof FilterChain
}
+
+/** Holds if `m` is a method of some override of `HttpServlet.doGet`. */
+predicate isGetServletMethod(Method m) {
+ isServletRequestMethod(m) and m.getName() = "doGet"
+}
+
+/** The `doGet` method of `HttpServlet`. */
+class DoGetServletMethod extends Method {
+ DoGetServletMethod() { isGetServletMethod(this) }
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.qlref b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.qlref
deleted file mode 100644
index 6ad4b8acda7..00000000000
--- a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.qlref
+++ /dev/null
@@ -1 +0,0 @@
-Security/CWE/CWE-352/JsonpInjection.ql
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpController.java b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithFilter/JsonpController.java
similarity index 54%
rename from java/ql/test/experimental/query-tests/security/CWE-352/JsonpController.java
rename to java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithFilter/JsonpController.java
index cf860c75640..e5b5e70a38d 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpController.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithFilter/JsonpController.java
@@ -1,16 +1,24 @@
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.HashMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.multipart.MultipartFile;
@Controller
public class JsonpController {
+
private static HashMap hashMap = new HashMap();
static {
@@ -18,13 +26,14 @@ public class JsonpController {
hashMap.put("password","123456");
}
+ private String name = null;
- @GetMapping(value = "jsonp1", produces="text/javascript")
+
+ @GetMapping(value = "jsonp1")
@ResponseBody
public String bad1(HttpServletRequest request) {
String resultStr = null;
String jsonpCallback = request.getParameter("jsonpCallback");
-
Gson gson = new Gson();
String result = gson.toJson(hashMap);
resultStr = jsonpCallback + "(" + result + ")";
@@ -36,9 +45,7 @@ public class JsonpController {
public String bad2(HttpServletRequest request) {
String resultStr = null;
String jsonpCallback = request.getParameter("jsonpCallback");
-
resultStr = jsonpCallback + "(" + JSONObject.toJSONString(hashMap) + ")";
-
return resultStr;
}
@@ -91,23 +98,98 @@ public class JsonpController {
pw.println(resultStr);
}
- @GetMapping(value = "jsonp7")
+ @RequestMapping(value = "jsonp7", method = RequestMethod.GET)
+ @ResponseBody
+ public String bad7(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ Gson gson = new Gson();
+ String result = gson.toJson(hashMap);
+ resultStr = jsonpCallback + "(" + result + ")";
+ return resultStr;
+ }
+
+
+ @GetMapping(value = "jsonp8")
@ResponseBody
public String good1(HttpServletRequest request) {
String resultStr = null;
-
String token = request.getParameter("token");
-
if (verifToken(token)){
String jsonpCallback = request.getParameter("jsonpCallback");
String jsonStr = getJsonStr(hashMap);
resultStr = jsonpCallback + "(" + jsonStr + ")";
return resultStr;
}
-
return "error";
}
+
+ @GetMapping(value = "jsonp9")
+ @ResponseBody
+ public String good2(HttpServletRequest request) {
+ String resultStr = null;
+ String token = request.getParameter("token");
+ boolean result = verifToken(token);
+ if (result){
+ return "";
+ }
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String jsonStr = getJsonStr(hashMap);
+ resultStr = jsonpCallback + "(" + jsonStr + ")";
+ return resultStr;
+ }
+
+ @RequestMapping(value = "jsonp10")
+ @ResponseBody
+ public String good3(HttpServletRequest request) {
+ JSONObject parameterObj = readToJSONObect(request);
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String restr = JSONObject.toJSONString(hashMap);
+ resultStr = jsonpCallback + "(" + restr + ");";
+ return resultStr;
+ }
+
+ @RequestMapping(value = "jsonp11")
+ @ResponseBody
+ public String good4(@RequestParam("file") MultipartFile file,HttpServletRequest request) {
+ if(null == file){
+ return "upload file error";
+ }
+ String fileName = file.getOriginalFilename();
+ System.out.println("file operations");
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String restr = JSONObject.toJSONString(hashMap);
+ resultStr = jsonpCallback + "(" + restr + ");";
+ return resultStr;
+ }
+
+ public static JSONObject readToJSONObect(HttpServletRequest request){
+ String jsonText = readPostContent(request);
+ JSONObject jsonObj = JSONObject.parseObject(jsonText, JSONObject.class);
+ return jsonObj;
+ }
+
+ public static String readPostContent(HttpServletRequest request){
+ BufferedReader in= null;
+ String content = null;
+ String line = null;
+ try {
+ in = new BufferedReader(new InputStreamReader(request.getInputStream(),"UTF-8"));
+ StringBuilder buf = new StringBuilder();
+ while ((line = in.readLine()) != null) {
+ buf.append(line);
+ }
+ content = buf.toString();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ String uri = request.getRequestURI();
+ return content;
+ }
+
public static String getJsonStr(Object result) {
return JSONObject.toJSONString(result);
}
@@ -118,11 +200,4 @@ public class JsonpController {
}
return true;
}
-
- public static boolean verifReferer(String referer){
- if (!referer.startsWith("http://test.com/")){
- return false;
- }
- return true;
- }
}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithFilter/JsonpInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithFilter/JsonpInjection.expected
new file mode 100644
index 00000000000..501565f2b4e
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithFilter/JsonpInjection.expected
@@ -0,0 +1,87 @@
+edges
+| JsonpController.java:36:32:36:68 | getParameter(...) : String | JsonpController.java:40:16:40:24 | resultStr |
+| JsonpController.java:39:21:39:54 | ... + ... : String | JsonpController.java:40:16:40:24 | resultStr |
+| JsonpController.java:47:32:47:68 | getParameter(...) : String | JsonpController.java:49:16:49:24 | resultStr |
+| JsonpController.java:48:21:48:80 | ... + ... : String | JsonpController.java:49:16:49:24 | resultStr |
+| JsonpController.java:56:32:56:68 | getParameter(...) : String | JsonpController.java:59:16:59:24 | resultStr |
+| JsonpController.java:58:21:58:55 | ... + ... : String | JsonpController.java:59:16:59:24 | resultStr |
+| JsonpController.java:66:32:66:68 | getParameter(...) : String | JsonpController.java:69:16:69:24 | resultStr |
+| JsonpController.java:68:21:68:54 | ... + ... : String | JsonpController.java:69:16:69:24 | resultStr |
+| JsonpController.java:76:32:76:68 | getParameter(...) : String | JsonpController.java:84:20:84:28 | resultStr |
+| JsonpController.java:83:21:83:54 | ... + ... : String | JsonpController.java:84:20:84:28 | resultStr |
+| JsonpController.java:91:32:91:68 | getParameter(...) : String | JsonpController.java:98:20:98:28 | resultStr |
+| JsonpController.java:97:21:97:54 | ... + ... : String | JsonpController.java:98:20:98:28 | resultStr |
+| JsonpController.java:105:32:105:68 | getParameter(...) : String | JsonpController.java:109:16:109:24 | resultStr |
+| JsonpController.java:108:21:108:54 | ... + ... : String | JsonpController.java:109:16:109:24 | resultStr |
+| JsonpController.java:117:24:117:52 | getParameter(...) : String | JsonpController.java:118:24:118:28 | token |
+| JsonpController.java:119:36:119:72 | getParameter(...) : String | JsonpController.java:122:20:122:28 | resultStr |
+| JsonpController.java:121:25:121:59 | ... + ... : String | JsonpController.java:122:20:122:28 | resultStr |
+| JsonpController.java:132:24:132:52 | getParameter(...) : String | JsonpController.java:133:37:133:41 | token |
+| JsonpController.java:137:32:137:68 | getParameter(...) : String | JsonpController.java:140:16:140:24 | resultStr |
+| JsonpController.java:139:21:139:55 | ... + ... : String | JsonpController.java:140:16:140:24 | resultStr |
+| JsonpController.java:150:21:150:54 | ... + ... : String | JsonpController.java:151:16:151:24 | resultStr |
+| JsonpController.java:165:21:165:54 | ... + ... : String | JsonpController.java:166:16:166:24 | resultStr |
+| JsonpInjectionServlet1.java:31:32:31:64 | getParameter(...) : String | JsonpInjectionServlet1.java:45:24:45:32 | resultStr |
+| JsonpInjectionServlet1.java:36:26:36:49 | getHeader(...) : String | JsonpInjectionServlet1.java:38:39:38:45 | referer |
+| JsonpInjectionServlet1.java:44:25:44:62 | ... + ... : String | JsonpInjectionServlet1.java:45:24:45:32 | resultStr |
+| JsonpInjectionServlet2.java:31:32:31:64 | getParameter(...) : String | JsonpInjectionServlet2.java:39:20:39:28 | resultStr |
+| JsonpInjectionServlet2.java:38:21:38:54 | ... + ... : String | JsonpInjectionServlet2.java:39:20:39:28 | resultStr |
+| RefererFilter.java:22:26:22:53 | getHeader(...) : String | RefererFilter.java:23:39:23:45 | refefer |
+nodes
+| JsonpController.java:36:32:36:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:39:21:39:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:40:16:40:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:40:16:40:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:47:32:47:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:48:21:48:80 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:49:16:49:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:49:16:49:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:56:32:56:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:58:21:58:55 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:59:16:59:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:59:16:59:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:66:32:66:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:68:21:68:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:69:16:69:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:69:16:69:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:76:32:76:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:83:21:83:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:84:20:84:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:84:20:84:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:91:32:91:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:97:21:97:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:98:20:98:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:98:20:98:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:105:32:105:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:108:21:108:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:109:16:109:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:109:16:109:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:117:24:117:52 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:118:24:118:28 | token | semmle.label | token |
+| JsonpController.java:119:36:119:72 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:121:25:121:59 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:122:20:122:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:122:20:122:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:132:24:132:52 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:133:37:133:41 | token | semmle.label | token |
+| JsonpController.java:137:32:137:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:139:21:139:55 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:140:16:140:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:140:16:140:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:150:21:150:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:151:16:151:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:165:21:165:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:166:16:166:24 | resultStr | semmle.label | resultStr |
+| JsonpInjectionServlet1.java:31:32:31:64 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpInjectionServlet1.java:36:26:36:49 | getHeader(...) : String | semmle.label | getHeader(...) : String |
+| JsonpInjectionServlet1.java:38:39:38:45 | referer | semmle.label | referer |
+| JsonpInjectionServlet1.java:44:25:44:62 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpInjectionServlet1.java:45:24:45:32 | resultStr | semmle.label | resultStr |
+| JsonpInjectionServlet1.java:45:24:45:32 | resultStr | semmle.label | resultStr |
+| JsonpInjectionServlet2.java:31:32:31:64 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpInjectionServlet2.java:38:21:38:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpInjectionServlet2.java:39:20:39:28 | resultStr | semmle.label | resultStr |
+| JsonpInjectionServlet2.java:39:20:39:28 | resultStr | semmle.label | resultStr |
+| RefererFilter.java:22:26:22:53 | getHeader(...) : String | semmle.label | getHeader(...) : String |
+| RefererFilter.java:23:39:23:45 | refefer | semmle.label | refefer |
+#select
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithFilter/JsonpInjection.qlref b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithFilter/JsonpInjection.qlref
new file mode 100644
index 00000000000..3f5fc450669
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithFilter/JsonpInjection.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-352/JsonpInjection.ql
diff --git a/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionServlet1.java b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithFilter/JsonpInjectionServlet1.java
similarity index 100%
rename from java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionServlet1.java
rename to java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithFilter/JsonpInjectionServlet1.java
diff --git a/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionServlet2.java b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithFilter/JsonpInjectionServlet2.java
similarity index 100%
rename from java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionServlet2.java
rename to java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithFilter/JsonpInjectionServlet2.java
diff --git a/java/ql/src/experimental/Security/CWE/CWE-352/RefererFilter.java b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithFilter/RefererFilter.java
similarity index 100%
rename from java/ql/src/experimental/Security/CWE/CWE-352/RefererFilter.java
rename to java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithFilter/RefererFilter.java
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithFilter/options b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithFilter/options
new file mode 100644
index 00000000000..c53e31e467f
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithFilter/options
@@ -0,0 +1 @@
+//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/apache-http-4.4.13/:${testdir}/../../../../../stubs/servlet-api-2.4:${testdir}/../../../../../stubs/fastjson-1.2.74/:${testdir}/../../../../../stubs/gson-2.8.6/:${testdir}/../../../../../stubs/jackson-databind-2.10/:${testdir}/../../../../../stubs/spring-context-5.3.2/:${testdir}/../../../../../stubs/spring-web-5.3.2/:${testdir}/../../../../../stubs/spring-core-5.3.2/:${testdir}/../../../../../stubs/tomcat-embed-core-9.0.41/
diff --git a/java/ql/src/experimental/Security/CWE/CWE-352/JsonpController.java b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringController/JsonpController.java
similarity index 54%
rename from java/ql/src/experimental/Security/CWE/CWE-352/JsonpController.java
rename to java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringController/JsonpController.java
index 84a172a7aeb..e5b5e70a38d 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-352/JsonpController.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringController/JsonpController.java
@@ -1,16 +1,24 @@
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.HashMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.multipart.MultipartFile;
@Controller
public class JsonpController {
+
private static HashMap hashMap = new HashMap();
static {
@@ -18,13 +26,14 @@ public class JsonpController {
hashMap.put("password","123456");
}
+ private String name = null;
- @GetMapping(value = "jsonp1", produces="text/javascript")
+
+ @GetMapping(value = "jsonp1")
@ResponseBody
public String bad1(HttpServletRequest request) {
String resultStr = null;
String jsonpCallback = request.getParameter("jsonpCallback");
-
Gson gson = new Gson();
String result = gson.toJson(hashMap);
resultStr = jsonpCallback + "(" + result + ")";
@@ -36,9 +45,7 @@ public class JsonpController {
public String bad2(HttpServletRequest request) {
String resultStr = null;
String jsonpCallback = request.getParameter("jsonpCallback");
-
resultStr = jsonpCallback + "(" + JSONObject.toJSONString(hashMap) + ")";
-
return resultStr;
}
@@ -91,23 +98,98 @@ public class JsonpController {
pw.println(resultStr);
}
- @GetMapping(value = "jsonp7")
+ @RequestMapping(value = "jsonp7", method = RequestMethod.GET)
+ @ResponseBody
+ public String bad7(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ Gson gson = new Gson();
+ String result = gson.toJson(hashMap);
+ resultStr = jsonpCallback + "(" + result + ")";
+ return resultStr;
+ }
+
+
+ @GetMapping(value = "jsonp8")
@ResponseBody
public String good1(HttpServletRequest request) {
String resultStr = null;
-
String token = request.getParameter("token");
-
if (verifToken(token)){
String jsonpCallback = request.getParameter("jsonpCallback");
String jsonStr = getJsonStr(hashMap);
resultStr = jsonpCallback + "(" + jsonStr + ")";
return resultStr;
}
-
return "error";
}
+
+ @GetMapping(value = "jsonp9")
+ @ResponseBody
+ public String good2(HttpServletRequest request) {
+ String resultStr = null;
+ String token = request.getParameter("token");
+ boolean result = verifToken(token);
+ if (result){
+ return "";
+ }
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String jsonStr = getJsonStr(hashMap);
+ resultStr = jsonpCallback + "(" + jsonStr + ")";
+ return resultStr;
+ }
+
+ @RequestMapping(value = "jsonp10")
+ @ResponseBody
+ public String good3(HttpServletRequest request) {
+ JSONObject parameterObj = readToJSONObect(request);
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String restr = JSONObject.toJSONString(hashMap);
+ resultStr = jsonpCallback + "(" + restr + ");";
+ return resultStr;
+ }
+
+ @RequestMapping(value = "jsonp11")
+ @ResponseBody
+ public String good4(@RequestParam("file") MultipartFile file,HttpServletRequest request) {
+ if(null == file){
+ return "upload file error";
+ }
+ String fileName = file.getOriginalFilename();
+ System.out.println("file operations");
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String restr = JSONObject.toJSONString(hashMap);
+ resultStr = jsonpCallback + "(" + restr + ");";
+ return resultStr;
+ }
+
+ public static JSONObject readToJSONObect(HttpServletRequest request){
+ String jsonText = readPostContent(request);
+ JSONObject jsonObj = JSONObject.parseObject(jsonText, JSONObject.class);
+ return jsonObj;
+ }
+
+ public static String readPostContent(HttpServletRequest request){
+ BufferedReader in= null;
+ String content = null;
+ String line = null;
+ try {
+ in = new BufferedReader(new InputStreamReader(request.getInputStream(),"UTF-8"));
+ StringBuilder buf = new StringBuilder();
+ while ((line = in.readLine()) != null) {
+ buf.append(line);
+ }
+ content = buf.toString();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ String uri = request.getRequestURI();
+ return content;
+ }
+
public static String getJsonStr(Object result) {
return JSONObject.toJSONString(result);
}
@@ -118,11 +200,4 @@ public class JsonpController {
}
return true;
}
-
- public static boolean verifReferer(String referer){
- if (!referer.startsWith("http://test.com/")){
- return false;
- }
- return true;
- }
-}
\ No newline at end of file
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringController/JsonpInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringController/JsonpInjection.expected
new file mode 100644
index 00000000000..91d23cebbda
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringController/JsonpInjection.expected
@@ -0,0 +1,76 @@
+edges
+| JsonpController.java:36:32:36:68 | getParameter(...) : String | JsonpController.java:40:16:40:24 | resultStr |
+| JsonpController.java:39:21:39:54 | ... + ... : String | JsonpController.java:40:16:40:24 | resultStr |
+| JsonpController.java:47:32:47:68 | getParameter(...) : String | JsonpController.java:49:16:49:24 | resultStr |
+| JsonpController.java:48:21:48:80 | ... + ... : String | JsonpController.java:49:16:49:24 | resultStr |
+| JsonpController.java:56:32:56:68 | getParameter(...) : String | JsonpController.java:59:16:59:24 | resultStr |
+| JsonpController.java:58:21:58:55 | ... + ... : String | JsonpController.java:59:16:59:24 | resultStr |
+| JsonpController.java:66:32:66:68 | getParameter(...) : String | JsonpController.java:69:16:69:24 | resultStr |
+| JsonpController.java:68:21:68:54 | ... + ... : String | JsonpController.java:69:16:69:24 | resultStr |
+| JsonpController.java:76:32:76:68 | getParameter(...) : String | JsonpController.java:84:20:84:28 | resultStr |
+| JsonpController.java:83:21:83:54 | ... + ... : String | JsonpController.java:84:20:84:28 | resultStr |
+| JsonpController.java:91:32:91:68 | getParameter(...) : String | JsonpController.java:98:20:98:28 | resultStr |
+| JsonpController.java:97:21:97:54 | ... + ... : String | JsonpController.java:98:20:98:28 | resultStr |
+| JsonpController.java:105:32:105:68 | getParameter(...) : String | JsonpController.java:109:16:109:24 | resultStr |
+| JsonpController.java:108:21:108:54 | ... + ... : String | JsonpController.java:109:16:109:24 | resultStr |
+| JsonpController.java:117:24:117:52 | getParameter(...) : String | JsonpController.java:118:24:118:28 | token |
+| JsonpController.java:119:36:119:72 | getParameter(...) : String | JsonpController.java:122:20:122:28 | resultStr |
+| JsonpController.java:121:25:121:59 | ... + ... : String | JsonpController.java:122:20:122:28 | resultStr |
+| JsonpController.java:132:24:132:52 | getParameter(...) : String | JsonpController.java:133:37:133:41 | token |
+| JsonpController.java:137:32:137:68 | getParameter(...) : String | JsonpController.java:140:16:140:24 | resultStr |
+| JsonpController.java:139:21:139:55 | ... + ... : String | JsonpController.java:140:16:140:24 | resultStr |
+| JsonpController.java:150:21:150:54 | ... + ... : String | JsonpController.java:151:16:151:24 | resultStr |
+| JsonpController.java:165:21:165:54 | ... + ... : String | JsonpController.java:166:16:166:24 | resultStr |
+nodes
+| JsonpController.java:36:32:36:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:39:21:39:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:40:16:40:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:40:16:40:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:47:32:47:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:48:21:48:80 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:49:16:49:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:49:16:49:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:56:32:56:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:58:21:58:55 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:59:16:59:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:59:16:59:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:66:32:66:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:68:21:68:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:69:16:69:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:69:16:69:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:76:32:76:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:83:21:83:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:84:20:84:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:84:20:84:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:91:32:91:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:97:21:97:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:98:20:98:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:98:20:98:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:105:32:105:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:108:21:108:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:109:16:109:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:109:16:109:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:117:24:117:52 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:118:24:118:28 | token | semmle.label | token |
+| JsonpController.java:119:36:119:72 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:121:25:121:59 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:122:20:122:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:122:20:122:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:132:24:132:52 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:133:37:133:41 | token | semmle.label | token |
+| JsonpController.java:137:32:137:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:139:21:139:55 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:140:16:140:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:140:16:140:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:150:21:150:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:151:16:151:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:165:21:165:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:166:16:166:24 | resultStr | semmle.label | resultStr |
+#select
+| JsonpController.java:40:16:40:24 | resultStr | JsonpController.java:36:32:36:68 | getParameter(...) : String | JsonpController.java:40:16:40:24 | resultStr | Jsonp response might include code from $@. | JsonpController.java:36:32:36:68 | getParameter(...) | this user input |
+| JsonpController.java:49:16:49:24 | resultStr | JsonpController.java:47:32:47:68 | getParameter(...) : String | JsonpController.java:49:16:49:24 | resultStr | Jsonp response might include code from $@. | JsonpController.java:47:32:47:68 | getParameter(...) | this user input |
+| JsonpController.java:59:16:59:24 | resultStr | JsonpController.java:56:32:56:68 | getParameter(...) : String | JsonpController.java:59:16:59:24 | resultStr | Jsonp response might include code from $@. | JsonpController.java:56:32:56:68 | getParameter(...) | this user input |
+| JsonpController.java:69:16:69:24 | resultStr | JsonpController.java:66:32:66:68 | getParameter(...) : String | JsonpController.java:69:16:69:24 | resultStr | Jsonp response might include code from $@. | JsonpController.java:66:32:66:68 | getParameter(...) | this user input |
+| JsonpController.java:84:20:84:28 | resultStr | JsonpController.java:76:32:76:68 | getParameter(...) : String | JsonpController.java:84:20:84:28 | resultStr | Jsonp response might include code from $@. | JsonpController.java:76:32:76:68 | getParameter(...) | this user input |
+| JsonpController.java:98:20:98:28 | resultStr | JsonpController.java:91:32:91:68 | getParameter(...) : String | JsonpController.java:98:20:98:28 | resultStr | Jsonp response might include code from $@. | JsonpController.java:91:32:91:68 | getParameter(...) | this user input |
+| JsonpController.java:109:16:109:24 | resultStr | JsonpController.java:105:32:105:68 | getParameter(...) : String | JsonpController.java:109:16:109:24 | resultStr | Jsonp response might include code from $@. | JsonpController.java:105:32:105:68 | getParameter(...) | this user input |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringController/JsonpInjection.qlref b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringController/JsonpInjection.qlref
new file mode 100644
index 00000000000..3f5fc450669
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringController/JsonpInjection.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-352/JsonpInjection.ql
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringController/options b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringController/options
new file mode 100644
index 00000000000..c53e31e467f
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringController/options
@@ -0,0 +1 @@
+//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/apache-http-4.4.13/:${testdir}/../../../../../stubs/servlet-api-2.4:${testdir}/../../../../../stubs/fastjson-1.2.74/:${testdir}/../../../../../stubs/gson-2.8.6/:${testdir}/../../../../../stubs/jackson-databind-2.10/:${testdir}/../../../../../stubs/spring-context-5.3.2/:${testdir}/../../../../../stubs/spring-web-5.3.2/:${testdir}/../../../../../stubs/spring-core-5.3.2/:${testdir}/../../../../../stubs/tomcat-embed-core-9.0.41/
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringControllerAndServlet/JsonpController.java b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringControllerAndServlet/JsonpController.java
new file mode 100644
index 00000000000..e5b5e70a38d
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringControllerAndServlet/JsonpController.java
@@ -0,0 +1,203 @@
+import com.alibaba.fastjson.JSONObject;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.gson.Gson;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.multipart.MultipartFile;
+
+@Controller
+public class JsonpController {
+
+ private static HashMap hashMap = new HashMap();
+
+ static {
+ hashMap.put("username","admin");
+ hashMap.put("password","123456");
+ }
+
+ private String name = null;
+
+
+ @GetMapping(value = "jsonp1")
+ @ResponseBody
+ public String bad1(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ Gson gson = new Gson();
+ String result = gson.toJson(hashMap);
+ resultStr = jsonpCallback + "(" + result + ")";
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp2")
+ @ResponseBody
+ public String bad2(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ resultStr = jsonpCallback + "(" + JSONObject.toJSONString(hashMap) + ")";
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp3")
+ @ResponseBody
+ public String bad3(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String jsonStr = getJsonStr(hashMap);
+ resultStr = jsonpCallback + "(" + jsonStr + ")";
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp4")
+ @ResponseBody
+ public String bad4(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String restr = JSONObject.toJSONString(hashMap);
+ resultStr = jsonpCallback + "(" + restr + ");";
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp5")
+ @ResponseBody
+ public void bad5(HttpServletRequest request,
+ HttpServletResponse response) throws Exception {
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ PrintWriter pw = null;
+ Gson gson = new Gson();
+ String result = gson.toJson(hashMap);
+
+ String resultStr = null;
+ pw = response.getWriter();
+ resultStr = jsonpCallback + "(" + result + ")";
+ pw.println(resultStr);
+ }
+
+ @GetMapping(value = "jsonp6")
+ @ResponseBody
+ public void bad6(HttpServletRequest request,
+ HttpServletResponse response) throws Exception {
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ PrintWriter pw = null;
+ ObjectMapper mapper = new ObjectMapper();
+ String result = mapper.writeValueAsString(hashMap);
+ String resultStr = null;
+ pw = response.getWriter();
+ resultStr = jsonpCallback + "(" + result + ")";
+ pw.println(resultStr);
+ }
+
+ @RequestMapping(value = "jsonp7", method = RequestMethod.GET)
+ @ResponseBody
+ public String bad7(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ Gson gson = new Gson();
+ String result = gson.toJson(hashMap);
+ resultStr = jsonpCallback + "(" + result + ")";
+ return resultStr;
+ }
+
+
+ @GetMapping(value = "jsonp8")
+ @ResponseBody
+ public String good1(HttpServletRequest request) {
+ String resultStr = null;
+ String token = request.getParameter("token");
+ if (verifToken(token)){
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String jsonStr = getJsonStr(hashMap);
+ resultStr = jsonpCallback + "(" + jsonStr + ")";
+ return resultStr;
+ }
+ return "error";
+ }
+
+
+ @GetMapping(value = "jsonp9")
+ @ResponseBody
+ public String good2(HttpServletRequest request) {
+ String resultStr = null;
+ String token = request.getParameter("token");
+ boolean result = verifToken(token);
+ if (result){
+ return "";
+ }
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String jsonStr = getJsonStr(hashMap);
+ resultStr = jsonpCallback + "(" + jsonStr + ")";
+ return resultStr;
+ }
+
+ @RequestMapping(value = "jsonp10")
+ @ResponseBody
+ public String good3(HttpServletRequest request) {
+ JSONObject parameterObj = readToJSONObect(request);
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String restr = JSONObject.toJSONString(hashMap);
+ resultStr = jsonpCallback + "(" + restr + ");";
+ return resultStr;
+ }
+
+ @RequestMapping(value = "jsonp11")
+ @ResponseBody
+ public String good4(@RequestParam("file") MultipartFile file,HttpServletRequest request) {
+ if(null == file){
+ return "upload file error";
+ }
+ String fileName = file.getOriginalFilename();
+ System.out.println("file operations");
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String restr = JSONObject.toJSONString(hashMap);
+ resultStr = jsonpCallback + "(" + restr + ");";
+ return resultStr;
+ }
+
+ public static JSONObject readToJSONObect(HttpServletRequest request){
+ String jsonText = readPostContent(request);
+ JSONObject jsonObj = JSONObject.parseObject(jsonText, JSONObject.class);
+ return jsonObj;
+ }
+
+ public static String readPostContent(HttpServletRequest request){
+ BufferedReader in= null;
+ String content = null;
+ String line = null;
+ try {
+ in = new BufferedReader(new InputStreamReader(request.getInputStream(),"UTF-8"));
+ StringBuilder buf = new StringBuilder();
+ while ((line = in.readLine()) != null) {
+ buf.append(line);
+ }
+ content = buf.toString();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ String uri = request.getRequestURI();
+ return content;
+ }
+
+ public static String getJsonStr(Object result) {
+ return JSONObject.toJSONString(result);
+ }
+
+ public static boolean verifToken(String token){
+ if (token != "xxxx"){
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringControllerAndServlet/JsonpInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringControllerAndServlet/JsonpInjection.expected
new file mode 100644
index 00000000000..c2bcab77d4d
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringControllerAndServlet/JsonpInjection.expected
@@ -0,0 +1,92 @@
+edges
+| JsonpController.java:36:32:36:68 | getParameter(...) : String | JsonpController.java:40:16:40:24 | resultStr |
+| JsonpController.java:39:21:39:54 | ... + ... : String | JsonpController.java:40:16:40:24 | resultStr |
+| JsonpController.java:47:32:47:68 | getParameter(...) : String | JsonpController.java:49:16:49:24 | resultStr |
+| JsonpController.java:48:21:48:80 | ... + ... : String | JsonpController.java:49:16:49:24 | resultStr |
+| JsonpController.java:56:32:56:68 | getParameter(...) : String | JsonpController.java:59:16:59:24 | resultStr |
+| JsonpController.java:58:21:58:55 | ... + ... : String | JsonpController.java:59:16:59:24 | resultStr |
+| JsonpController.java:66:32:66:68 | getParameter(...) : String | JsonpController.java:69:16:69:24 | resultStr |
+| JsonpController.java:68:21:68:54 | ... + ... : String | JsonpController.java:69:16:69:24 | resultStr |
+| JsonpController.java:76:32:76:68 | getParameter(...) : String | JsonpController.java:84:20:84:28 | resultStr |
+| JsonpController.java:83:21:83:54 | ... + ... : String | JsonpController.java:84:20:84:28 | resultStr |
+| JsonpController.java:91:32:91:68 | getParameter(...) : String | JsonpController.java:98:20:98:28 | resultStr |
+| JsonpController.java:97:21:97:54 | ... + ... : String | JsonpController.java:98:20:98:28 | resultStr |
+| JsonpController.java:105:32:105:68 | getParameter(...) : String | JsonpController.java:109:16:109:24 | resultStr |
+| JsonpController.java:108:21:108:54 | ... + ... : String | JsonpController.java:109:16:109:24 | resultStr |
+| JsonpController.java:117:24:117:52 | getParameter(...) : String | JsonpController.java:118:24:118:28 | token |
+| JsonpController.java:119:36:119:72 | getParameter(...) : String | JsonpController.java:122:20:122:28 | resultStr |
+| JsonpController.java:121:25:121:59 | ... + ... : String | JsonpController.java:122:20:122:28 | resultStr |
+| JsonpController.java:132:24:132:52 | getParameter(...) : String | JsonpController.java:133:37:133:41 | token |
+| JsonpController.java:137:32:137:68 | getParameter(...) : String | JsonpController.java:140:16:140:24 | resultStr |
+| JsonpController.java:139:21:139:55 | ... + ... : String | JsonpController.java:140:16:140:24 | resultStr |
+| JsonpController.java:150:21:150:54 | ... + ... : String | JsonpController.java:151:16:151:24 | resultStr |
+| JsonpController.java:165:21:165:54 | ... + ... : String | JsonpController.java:166:16:166:24 | resultStr |
+| JsonpInjectionServlet1.java:31:32:31:64 | getParameter(...) : String | JsonpInjectionServlet1.java:45:24:45:32 | resultStr |
+| JsonpInjectionServlet1.java:36:26:36:49 | getHeader(...) : String | JsonpInjectionServlet1.java:38:39:38:45 | referer |
+| JsonpInjectionServlet1.java:44:25:44:62 | ... + ... : String | JsonpInjectionServlet1.java:45:24:45:32 | resultStr |
+| JsonpInjectionServlet2.java:31:32:31:64 | getParameter(...) : String | JsonpInjectionServlet2.java:39:20:39:28 | resultStr |
+| JsonpInjectionServlet2.java:38:21:38:54 | ... + ... : String | JsonpInjectionServlet2.java:39:20:39:28 | resultStr |
+nodes
+| JsonpController.java:36:32:36:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:39:21:39:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:40:16:40:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:40:16:40:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:47:32:47:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:48:21:48:80 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:49:16:49:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:49:16:49:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:56:32:56:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:58:21:58:55 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:59:16:59:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:59:16:59:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:66:32:66:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:68:21:68:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:69:16:69:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:69:16:69:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:76:32:76:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:83:21:83:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:84:20:84:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:84:20:84:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:91:32:91:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:97:21:97:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:98:20:98:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:98:20:98:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:105:32:105:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:108:21:108:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:109:16:109:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:109:16:109:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:117:24:117:52 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:118:24:118:28 | token | semmle.label | token |
+| JsonpController.java:119:36:119:72 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:121:25:121:59 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:122:20:122:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:122:20:122:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:132:24:132:52 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:133:37:133:41 | token | semmle.label | token |
+| JsonpController.java:137:32:137:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:139:21:139:55 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:140:16:140:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:140:16:140:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:150:21:150:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:151:16:151:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:165:21:165:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:166:16:166:24 | resultStr | semmle.label | resultStr |
+| JsonpInjectionServlet1.java:31:32:31:64 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpInjectionServlet1.java:36:26:36:49 | getHeader(...) : String | semmle.label | getHeader(...) : String |
+| JsonpInjectionServlet1.java:38:39:38:45 | referer | semmle.label | referer |
+| JsonpInjectionServlet1.java:44:25:44:62 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpInjectionServlet1.java:45:24:45:32 | resultStr | semmle.label | resultStr |
+| JsonpInjectionServlet1.java:45:24:45:32 | resultStr | semmle.label | resultStr |
+| JsonpInjectionServlet2.java:31:32:31:64 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpInjectionServlet2.java:38:21:38:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpInjectionServlet2.java:39:20:39:28 | resultStr | semmle.label | resultStr |
+| JsonpInjectionServlet2.java:39:20:39:28 | resultStr | semmle.label | resultStr |
+#select
+| JsonpController.java:40:16:40:24 | resultStr | JsonpController.java:36:32:36:68 | getParameter(...) : String | JsonpController.java:40:16:40:24 | resultStr | Jsonp response might include code from $@. | JsonpController.java:36:32:36:68 | getParameter(...) | this user input |
+| JsonpController.java:49:16:49:24 | resultStr | JsonpController.java:47:32:47:68 | getParameter(...) : String | JsonpController.java:49:16:49:24 | resultStr | Jsonp response might include code from $@. | JsonpController.java:47:32:47:68 | getParameter(...) | this user input |
+| JsonpController.java:59:16:59:24 | resultStr | JsonpController.java:56:32:56:68 | getParameter(...) : String | JsonpController.java:59:16:59:24 | resultStr | Jsonp response might include code from $@. | JsonpController.java:56:32:56:68 | getParameter(...) | this user input |
+| JsonpController.java:69:16:69:24 | resultStr | JsonpController.java:66:32:66:68 | getParameter(...) : String | JsonpController.java:69:16:69:24 | resultStr | Jsonp response might include code from $@. | JsonpController.java:66:32:66:68 | getParameter(...) | this user input |
+| JsonpController.java:84:20:84:28 | resultStr | JsonpController.java:76:32:76:68 | getParameter(...) : String | JsonpController.java:84:20:84:28 | resultStr | Jsonp response might include code from $@. | JsonpController.java:76:32:76:68 | getParameter(...) | this user input |
+| JsonpController.java:98:20:98:28 | resultStr | JsonpController.java:91:32:91:68 | getParameter(...) : String | JsonpController.java:98:20:98:28 | resultStr | Jsonp response might include code from $@. | JsonpController.java:91:32:91:68 | getParameter(...) | this user input |
+| JsonpController.java:109:16:109:24 | resultStr | JsonpController.java:105:32:105:68 | getParameter(...) : String | JsonpController.java:109:16:109:24 | resultStr | Jsonp response might include code from $@. | JsonpController.java:105:32:105:68 | getParameter(...) | this user input |
+| JsonpInjectionServlet2.java:39:20:39:28 | resultStr | JsonpInjectionServlet2.java:31:32:31:64 | getParameter(...) : String | JsonpInjectionServlet2.java:39:20:39:28 | resultStr | Jsonp response might include code from $@. | JsonpInjectionServlet2.java:31:32:31:64 | getParameter(...) | this user input |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringControllerAndServlet/JsonpInjection.qlref b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringControllerAndServlet/JsonpInjection.qlref
new file mode 100644
index 00000000000..3f5fc450669
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringControllerAndServlet/JsonpInjection.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-352/JsonpInjection.ql
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionServlet1.java b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringControllerAndServlet/JsonpInjectionServlet1.java
similarity index 100%
rename from java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionServlet1.java
rename to java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringControllerAndServlet/JsonpInjectionServlet1.java
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionServlet2.java b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringControllerAndServlet/JsonpInjectionServlet2.java
similarity index 100%
rename from java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionServlet2.java
rename to java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringControllerAndServlet/JsonpInjectionServlet2.java
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringControllerAndServlet/options b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringControllerAndServlet/options
new file mode 100644
index 00000000000..c53e31e467f
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionWithSpringControllerAndServlet/options
@@ -0,0 +1 @@
+//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/apache-http-4.4.13/:${testdir}/../../../../../stubs/servlet-api-2.4:${testdir}/../../../../../stubs/fastjson-1.2.74/:${testdir}/../../../../../stubs/gson-2.8.6/:${testdir}/../../../../../stubs/jackson-databind-2.10/:${testdir}/../../../../../stubs/spring-context-5.3.2/:${testdir}/../../../../../stubs/spring-web-5.3.2/:${testdir}/../../../../../stubs/spring-core-5.3.2/:${testdir}/../../../../../stubs/tomcat-embed-core-9.0.41/
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_1.expected b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_1.expected
deleted file mode 100644
index a89d03b67a7..00000000000
--- a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_1.expected
+++ /dev/null
@@ -1,60 +0,0 @@
-edges
-| JsonpController.java:26:32:26:68 | getParameter(...) : String | JsonpController.java:31:16:31:24 | resultStr |
-| JsonpController.java:30:21:30:54 | ... + ... : String | JsonpController.java:31:16:31:24 | resultStr |
-| JsonpController.java:38:32:38:68 | getParameter(...) : String | JsonpController.java:42:16:42:24 | resultStr |
-| JsonpController.java:40:21:40:80 | ... + ... : String | JsonpController.java:42:16:42:24 | resultStr |
-| JsonpController.java:49:32:49:68 | getParameter(...) : String | JsonpController.java:52:16:52:24 | resultStr |
-| JsonpController.java:51:21:51:55 | ... + ... : String | JsonpController.java:52:16:52:24 | resultStr |
-| JsonpController.java:59:32:59:68 | getParameter(...) : String | JsonpController.java:62:16:62:24 | resultStr |
-| JsonpController.java:61:21:61:54 | ... + ... : String | JsonpController.java:62:16:62:24 | resultStr |
-| JsonpController.java:69:32:69:68 | getParameter(...) : String | JsonpController.java:77:20:77:28 | resultStr |
-| JsonpController.java:76:21:76:54 | ... + ... : String | JsonpController.java:77:20:77:28 | resultStr |
-| JsonpController.java:84:32:84:68 | getParameter(...) : String | JsonpController.java:91:20:91:28 | resultStr |
-| JsonpController.java:90:21:90:54 | ... + ... : String | JsonpController.java:91:20:91:28 | resultStr |
-| JsonpController.java:99:24:99:52 | getParameter(...) : String | JsonpController.java:101:24:101:28 | token |
-| JsonpController.java:102:36:102:72 | getParameter(...) : String | JsonpController.java:105:20:105:28 | resultStr |
-| JsonpController.java:104:25:104:59 | ... + ... : String | JsonpController.java:105:20:105:28 | resultStr |
-nodes
-| JsonpController.java:26:32:26:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpController.java:30:21:30:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpController.java:31:16:31:24 | resultStr | semmle.label | resultStr |
-| JsonpController.java:31:16:31:24 | resultStr | semmle.label | resultStr |
-| JsonpController.java:38:32:38:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpController.java:40:21:40:80 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpController.java:42:16:42:24 | resultStr | semmle.label | resultStr |
-| JsonpController.java:42:16:42:24 | resultStr | semmle.label | resultStr |
-| JsonpController.java:49:32:49:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpController.java:51:21:51:55 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpController.java:52:16:52:24 | resultStr | semmle.label | resultStr |
-| JsonpController.java:52:16:52:24 | resultStr | semmle.label | resultStr |
-| JsonpController.java:59:32:59:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpController.java:61:21:61:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpController.java:62:16:62:24 | resultStr | semmle.label | resultStr |
-| JsonpController.java:62:16:62:24 | resultStr | semmle.label | resultStr |
-| JsonpController.java:69:32:69:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpController.java:76:21:76:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpController.java:77:20:77:28 | resultStr | semmle.label | resultStr |
-| JsonpController.java:77:20:77:28 | resultStr | semmle.label | resultStr |
-| JsonpController.java:84:32:84:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpController.java:90:21:90:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpController.java:91:20:91:28 | resultStr | semmle.label | resultStr |
-| JsonpController.java:91:20:91:28 | resultStr | semmle.label | resultStr |
-| JsonpController.java:99:24:99:52 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpController.java:101:24:101:28 | token | semmle.label | token |
-| JsonpController.java:102:36:102:72 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpController.java:104:25:104:59 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpController.java:105:20:105:28 | resultStr | semmle.label | resultStr |
-| JsonpController.java:105:20:105:28 | resultStr | semmle.label | resultStr |
-#select
-| JsonpController.java:31:16:31:24 | resultStr | JsonpController.java:26:32:26:68 | getParameter(...) : String | JsonpController.java:31:16:31:24 |
- resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:26:32:26:68 | getParameter(...) | this user input |
-| JsonpController.java:42:16:42:24 | resultStr | JsonpController.java:38:32:38:68 | getParameter(...) : String | JsonpController.java:42:16:42:24 |
- resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:38:32:38:68 | getParameter(...) | this user input |
-| JsonpController.java:52:16:52:24 | resultStr | JsonpController.java:49:32:49:68 | getParameter(...) : String | JsonpController.java:52:16:52:24 |
- resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:49:32:49:68 | getParameter(...) | this user input |
-| JsonpController.java:62:16:62:24 | resultStr | JsonpController.java:59:32:59:68 | getParameter(...) : String | JsonpController.java:62:16:62:24 |
- resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:59:32:59:68 | getParameter(...) | this user input |
-| JsonpController.java:77:20:77:28 | resultStr | JsonpController.java:69:32:69:68 | getParameter(...) : String | JsonpController.java:77:20:77:28 |
- resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:69:32:69:68 | getParameter(...) | this user input |
-| JsonpController.java:91:20:91:28 | resultStr | JsonpController.java:84:32:84:68 | getParameter(...) : String | JsonpController.java:91:20:91:28 |
- resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:84:32:84:68 | getParameter(...) | this user input |
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_2.expected b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_2.expected
deleted file mode 100644
index 4b12308a212..00000000000
--- a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_2.expected
+++ /dev/null
@@ -1,78 +0,0 @@
-edges
-| JsonpController.java:26:32:26:68 | getParameter(...) : String | JsonpController.java:31:16:31:24 | resultStr |
-| JsonpController.java:30:21:30:54 | ... + ... : String | JsonpController.java:31:16:31:24 | resultStr |
-| JsonpController.java:38:32:38:68 | getParameter(...) : String | JsonpController.java:42:16:42:24 | resultStr |
-| JsonpController.java:40:21:40:80 | ... + ... : String | JsonpController.java:42:16:42:24 | resultStr |
-| JsonpController.java:49:32:49:68 | getParameter(...) : String | JsonpController.java:52:16:52:24 | resultStr |
-| JsonpController.java:51:21:51:55 | ... + ... : String | JsonpController.java:52:16:52:24 | resultStr |
-| JsonpController.java:59:32:59:68 | getParameter(...) : String | JsonpController.java:62:16:62:24 | resultStr |
-| JsonpController.java:61:21:61:54 | ... + ... : String | JsonpController.java:62:16:62:24 | resultStr |
-| JsonpController.java:69:32:69:68 | getParameter(...) : String | JsonpController.java:77:20:77:28 | resultStr |
-| JsonpController.java:76:21:76:54 | ... + ... : String | JsonpController.java:77:20:77:28 | resultStr |
-| JsonpController.java:84:32:84:68 | getParameter(...) : String | JsonpController.java:91:20:91:28 | resultStr |
-| JsonpController.java:90:21:90:54 | ... + ... : String | JsonpController.java:91:20:91:28 | resultStr |
-| JsonpController.java:99:24:99:52 | getParameter(...) : String | JsonpController.java:101:24:101:28 | token |
-| JsonpController.java:102:36:102:72 | getParameter(...) : String | JsonpController.java:105:20:105:28 | resultStr |
-| JsonpController.java:104:25:104:59 | ... + ... : String | JsonpController.java:105:20:105:28 | resultStr |
-| JsonpInjectionServlet1.java:31:32:31:64 | getParameter(...) : String | JsonpInjectionServlet1.java:45:24:45:32 | resultStr |
-| JsonpInjectionServlet1.java:36:26:36:49 | getHeader(...) : String | JsonpInjectionServlet1.java:38:39:38:45 | referer |
-| JsonpInjectionServlet1.java:44:25:44:62 | ... + ... : String | JsonpInjectionServlet1.java:45:24:45:32 | resultStr |
-| JsonpInjectionServlet2.java:31:32:31:64 | getParameter(...) : String | JsonpInjectionServlet2.java:39:20:39:28 | resultStr |
-| JsonpInjectionServlet2.java:38:21:38:54 | ... + ... : String | JsonpInjectionServlet2.java:39:20:39:28 | resultStr |
-nodes
-| JsonpController.java:26:32:26:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpController.java:30:21:30:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpController.java:31:16:31:24 | resultStr | semmle.label | resultStr |
-| JsonpController.java:31:16:31:24 | resultStr | semmle.label | resultStr |
-| JsonpController.java:38:32:38:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpController.java:40:21:40:80 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpController.java:42:16:42:24 | resultStr | semmle.label | resultStr |
-| JsonpController.java:42:16:42:24 | resultStr | semmle.label | resultStr |
-| JsonpController.java:49:32:49:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpController.java:51:21:51:55 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpController.java:52:16:52:24 | resultStr | semmle.label | resultStr |
-| JsonpController.java:52:16:52:24 | resultStr | semmle.label | resultStr |
-| JsonpController.java:59:32:59:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpController.java:61:21:61:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpController.java:62:16:62:24 | resultStr | semmle.label | resultStr |
-| JsonpController.java:62:16:62:24 | resultStr | semmle.label | resultStr |
-| JsonpController.java:69:32:69:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpController.java:76:21:76:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpController.java:77:20:77:28 | resultStr | semmle.label | resultStr |
-| JsonpController.java:77:20:77:28 | resultStr | semmle.label | resultStr |
-| JsonpController.java:84:32:84:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpController.java:90:21:90:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpController.java:91:20:91:28 | resultStr | semmle.label | resultStr |
-| JsonpController.java:91:20:91:28 | resultStr | semmle.label | resultStr |
-| JsonpController.java:99:24:99:52 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpController.java:101:24:101:28 | token | semmle.label | token |
-| JsonpController.java:102:36:102:72 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpController.java:104:25:104:59 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpController.java:105:20:105:28 | resultStr | semmle.label | resultStr |
-| JsonpController.java:105:20:105:28 | resultStr | semmle.label | resultStr |
-| JsonpInjectionServlet1.java:31:32:31:64 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpInjectionServlet1.java:36:26:36:49 | getHeader(...) : String | semmle.label | getHeader(...) : String |
-| JsonpInjectionServlet1.java:38:39:38:45 | referer | semmle.label | referer |
-| JsonpInjectionServlet1.java:44:25:44:62 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpInjectionServlet1.java:45:24:45:32 | resultStr | semmle.label | resultStr |
-| JsonpInjectionServlet1.java:45:24:45:32 | resultStr | semmle.label | resultStr |
-| JsonpInjectionServlet2.java:31:32:31:64 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpInjectionServlet2.java:38:21:38:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpInjectionServlet2.java:39:20:39:28 | resultStr | semmle.label | resultStr |
-| JsonpInjectionServlet2.java:39:20:39:28 | resultStr | semmle.label | resultStr |
-#select
-| JsonpController.java:31:16:31:24 | resultStr | JsonpController.java:26:32:26:68 | getParameter(...) : String | JsonpController.java:31:16:31:24 |
- resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:26:32:26:68 | getParameter(...) | this user input |
-| JsonpController.java:42:16:42:24 | resultStr | JsonpController.java:38:32:38:68 | getParameter(...) : String | JsonpController.java:42:16:42:24 |
- resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:38:32:38:68 | getParameter(...) | this user input |
-| JsonpController.java:52:16:52:24 | resultStr | JsonpController.java:49:32:49:68 | getParameter(...) : String | JsonpController.java:52:16:52:24 |
- resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:49:32:49:68 | getParameter(...) | this user input |
-| JsonpController.java:62:16:62:24 | resultStr | JsonpController.java:59:32:59:68 | getParameter(...) : String | JsonpController.java:62:16:62:24 |
- resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:59:32:59:68 | getParameter(...) | this user input |
-| JsonpController.java:77:20:77:28 | resultStr | JsonpController.java:69:32:69:68 | getParameter(...) : String | JsonpController.java:77:20:77:28 |
- resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:69:32:69:68 | getParameter(...) | this user input |
-| JsonpController.java:91:20:91:28 | resultStr | JsonpController.java:84:32:84:68 | getParameter(...) : String | JsonpController.java:91:20:91:28 |
- resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:84:32:84:68 | getParameter(...) | this user input |
-| JsonpInjectionServlet2.java:39:20:39:28 | resultStr | JsonpInjectionServlet2.java:31:32:31:64 | getParameter(...) : String | JsonpInjectionServle
-t2.java:39:20:39:28 | resultStr | Jsonp Injection query might include code from $@. | JsonpInjectionServlet2.java:31:32:31:64 | getParameter(...) |
- this user input |
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_3.expected b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_3.expected
deleted file mode 100644
index 8e33ca6984c..00000000000
--- a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_3.expected
+++ /dev/null
@@ -1,66 +0,0 @@
-edges
-| JsonpController.java:26:32:26:68 | getParameter(...) : String | JsonpController.java:31:16:31:24 | resultStr |
-| JsonpController.java:30:21:30:54 | ... + ... : String | JsonpController.java:31:16:31:24 | resultStr |
-| JsonpController.java:38:32:38:68 | getParameter(...) : String | JsonpController.java:42:16:42:24 | resultStr |
-| JsonpController.java:40:21:40:80 | ... + ... : String | JsonpController.java:42:16:42:24 | resultStr |
-| JsonpController.java:49:32:49:68 | getParameter(...) : String | JsonpController.java:52:16:52:24 | resultStr |
-| JsonpController.java:51:21:51:55 | ... + ... : String | JsonpController.java:52:16:52:24 | resultStr |
-| JsonpController.java:59:32:59:68 | getParameter(...) : String | JsonpController.java:62:16:62:24 | resultStr |
-| JsonpController.java:61:21:61:54 | ... + ... : String | JsonpController.java:62:16:62:24 | resultStr |
-| JsonpController.java:69:32:69:68 | getParameter(...) : String | JsonpController.java:77:20:77:28 | resultStr |
-| JsonpController.java:76:21:76:54 | ... + ... : String | JsonpController.java:77:20:77:28 | resultStr |
-| JsonpController.java:84:32:84:68 | getParameter(...) : String | JsonpController.java:91:20:91:28 | resultStr |
-| JsonpController.java:90:21:90:54 | ... + ... : String | JsonpController.java:91:20:91:28 | resultStr |
-| JsonpController.java:99:24:99:52 | getParameter(...) : String | JsonpController.java:101:24:101:28 | token |
-| JsonpController.java:102:36:102:72 | getParameter(...) : String | JsonpController.java:105:20:105:28 | resultStr |
-| JsonpController.java:104:25:104:59 | ... + ... : String | JsonpController.java:105:20:105:28 | resultStr |
-| JsonpInjectionServlet1.java:31:32:31:64 | getParameter(...) : String | JsonpInjectionServlet1.java:45:24:45:32 | resultStr |
-| JsonpInjectionServlet1.java:36:26:36:49 | getHeader(...) : String | JsonpInjectionServlet1.java:38:39:38:45 | referer |
-| JsonpInjectionServlet1.java:44:25:44:62 | ... + ... : String | JsonpInjectionServlet1.java:45:24:45:32 | resultStr |
-| JsonpInjectionServlet2.java:31:32:31:64 | getParameter(...) : String | JsonpInjectionServlet2.java:39:20:39:28 | resultStr |
-| JsonpInjectionServlet2.java:38:21:38:54 | ... + ... : String | JsonpInjectionServlet2.java:39:20:39:28 | resultStr |
-| RefererFilter.java:22:26:22:53 | getHeader(...) : String | RefererFilter.java:23:39:23:45 | refefer |
-nodes
-| JsonpController.java:26:32:26:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpController.java:30:21:30:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpController.java:31:16:31:24 | resultStr | semmle.label | resultStr |
-| JsonpController.java:31:16:31:24 | resultStr | semmle.label | resultStr |
-| JsonpController.java:38:32:38:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpController.java:40:21:40:80 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpController.java:42:16:42:24 | resultStr | semmle.label | resultStr |
-| JsonpController.java:42:16:42:24 | resultStr | semmle.label | resultStr |
-| JsonpController.java:49:32:49:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpController.java:51:21:51:55 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpController.java:52:16:52:24 | resultStr | semmle.label | resultStr |
-| JsonpController.java:52:16:52:24 | resultStr | semmle.label | resultStr |
-| JsonpController.java:59:32:59:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpController.java:61:21:61:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpController.java:62:16:62:24 | resultStr | semmle.label | resultStr |
-| JsonpController.java:62:16:62:24 | resultStr | semmle.label | resultStr |
-| JsonpController.java:69:32:69:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpController.java:76:21:76:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpController.java:77:20:77:28 | resultStr | semmle.label | resultStr |
-| JsonpController.java:77:20:77:28 | resultStr | semmle.label | resultStr |
-| JsonpController.java:84:32:84:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpController.java:90:21:90:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpController.java:91:20:91:28 | resultStr | semmle.label | resultStr |
-| JsonpController.java:91:20:91:28 | resultStr | semmle.label | resultStr |
-| JsonpController.java:99:24:99:52 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpController.java:101:24:101:28 | token | semmle.label | token |
-| JsonpController.java:102:36:102:72 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpController.java:104:25:104:59 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpController.java:105:20:105:28 | resultStr | semmle.label | resultStr |
-| JsonpController.java:105:20:105:28 | resultStr | semmle.label | resultStr |
-| JsonpInjectionServlet1.java:31:32:31:64 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpInjectionServlet1.java:36:26:36:49 | getHeader(...) : String | semmle.label | getHeader(...) : String |
-| JsonpInjectionServlet1.java:38:39:38:45 | referer | semmle.label | referer |
-| JsonpInjectionServlet1.java:44:25:44:62 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpInjectionServlet1.java:45:24:45:32 | resultStr | semmle.label | resultStr |
-| JsonpInjectionServlet1.java:45:24:45:32 | resultStr | semmle.label | resultStr |
-| JsonpInjectionServlet2.java:31:32:31:64 | getParameter(...) : String | semmle.label | getParameter(...) : String |
-| JsonpInjectionServlet2.java:38:21:38:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpInjectionServlet2.java:39:20:39:28 | resultStr | semmle.label | resultStr |
-| JsonpInjectionServlet2.java:39:20:39:28 | resultStr | semmle.label | resultStr |
-| RefererFilter.java:22:26:22:53 | getHeader(...) : String | semmle.label | getHeader(...) : String |
-| RefererFilter.java:23:39:23:45 | refefer | semmle.label | refefer |
-#select
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/Readme b/java/ql/test/experimental/query-tests/security/CWE-352/Readme
deleted file mode 100644
index 15715d6187c..00000000000
--- a/java/ql/test/experimental/query-tests/security/CWE-352/Readme
+++ /dev/null
@@ -1,3 +0,0 @@
-1. The JsonpInjection_1.expected result is obtained through the test of `JsonpController.java`.
-2. The JsonpInjection_2.expected result is obtained through the test of `JsonpController.java`, `JsonpInjectionServlet1.java`, `JsonpInjectionServlet2.java`.
-3. The JsonpInjection_3.expected result is obtained through the test of `JsonpController.java`, `JsonpInjectionServlet1.java`, `JsonpInjectionServlet2.java`, `RefererFilter.java`.
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/RefererFilter.java b/java/ql/test/experimental/query-tests/security/CWE-352/RefererFilter.java
deleted file mode 100644
index 97444932ae1..00000000000
--- a/java/ql/test/experimental/query-tests/security/CWE-352/RefererFilter.java
+++ /dev/null
@@ -1,43 +0,0 @@
-import java.io.IOException;
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.springframework.util.StringUtils;
-
-public class RefererFilter implements Filter {
-
- @Override
- public void init(FilterConfig filterConfig) throws ServletException {
- }
-
- @Override
- public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
- HttpServletRequest request = (HttpServletRequest) servletRequest;
- HttpServletResponse response = (HttpServletResponse) servletResponse;
- String refefer = request.getHeader("Referer");
- boolean result = verifReferer(refefer);
- if (result){
- filterChain.doFilter(servletRequest, servletResponse);
- }
- response.sendError(444, "Referer xxx.");
- }
-
- @Override
- public void destroy() {
- }
-
- public static boolean verifReferer(String referer){
- if (StringUtils.isEmpty(referer)){
- return false;
- }
- if (referer.startsWith("http://www.baidu.com/")){
- return true;
- }
- return false;
- }
-}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/options b/java/ql/test/experimental/query-tests/security/CWE-352/options
deleted file mode 100644
index 3676b8e38b6..00000000000
--- a/java/ql/test/experimental/query-tests/security/CWE-352/options
+++ /dev/null
@@ -1 +0,0 @@
-//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/apache-http-4.4.13/:${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/fastjson-1.2.74/:${testdir}/../../../../stubs/gson-2.8.6/:${testdir}/../../../../stubs/jackson-databind-2.10/:${testdir}/../../../../stubs/springframework-5.2.3/:${testdir}/../../../../stubs/spring-context-5.3.2/:${testdir}/../../../../stubs/spring-web-5.3.2/:${testdir}/../../../../stubs/spring-core-5.3.2/
diff --git a/java/ql/test/stubs/spring-core-5.3.2/org/springframework/core/annotation/AliasFor.java b/java/ql/test/stubs/spring-core-5.3.2/org/springframework/core/annotation/AliasFor.java
index 3a823fade5b..edfe917400b 100644
--- a/java/ql/test/stubs/spring-core-5.3.2/org/springframework/core/annotation/AliasFor.java
+++ b/java/ql/test/stubs/spring-core-5.3.2/org/springframework/core/annotation/AliasFor.java
@@ -1,10 +1,21 @@
package org.springframework.core.annotation;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+@Documented
public @interface AliasFor {
@AliasFor("attribute")
String value() default "";
@AliasFor("value")
String attribute() default "";
-
+
+ Class extends Annotation> annotation() default Annotation.class;
}
diff --git a/java/ql/test/stubs/spring-core-5.3.2/org/springframework/core/io/InputStreamSource.java b/java/ql/test/stubs/spring-core-5.3.2/org/springframework/core/io/InputStreamSource.java
new file mode 100644
index 00000000000..372d06cc738
--- /dev/null
+++ b/java/ql/test/stubs/spring-core-5.3.2/org/springframework/core/io/InputStreamSource.java
@@ -0,0 +1,8 @@
+package org.springframework.core.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public interface InputStreamSource {
+ InputStream getInputStream() throws IOException;
+}
diff --git a/java/ql/test/stubs/spring-core-5.3.2/org/springframework/core/io/Resource.java b/java/ql/test/stubs/spring-core-5.3.2/org/springframework/core/io/Resource.java
new file mode 100644
index 00000000000..6bd357f2228
--- /dev/null
+++ b/java/ql/test/stubs/spring-core-5.3.2/org/springframework/core/io/Resource.java
@@ -0,0 +1,46 @@
+package org.springframework.core.io;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URL;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import org.springframework.lang.Nullable;
+
+public interface Resource extends InputStreamSource {
+ boolean exists();
+
+ default boolean isReadable() {
+ return this.exists();
+ }
+
+ default boolean isOpen() {
+ return false;
+ }
+
+ default boolean isFile() {
+ return false;
+ }
+
+ URL getURL() throws IOException;
+
+ URI getURI() throws IOException;
+
+ File getFile() throws IOException;
+
+ default ReadableByteChannel readableChannel() throws IOException {
+ return null;
+ }
+
+ long contentLength() throws IOException;
+
+ long lastModified() throws IOException;
+
+ Resource createRelative(String var1) throws IOException;
+
+ @Nullable
+ String getFilename();
+
+ String getDescription();
+}
diff --git a/java/ql/test/stubs/spring-core-5.3.2/org/springframework/lang/Nullable.java b/java/ql/test/stubs/spring-core-5.3.2/org/springframework/lang/Nullable.java
new file mode 100644
index 00000000000..44bdae10fda
--- /dev/null
+++ b/java/ql/test/stubs/spring-core-5.3.2/org/springframework/lang/Nullable.java
@@ -0,0 +1,13 @@
+package org.springframework.lang;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Nullable {
+}
diff --git a/java/ql/test/stubs/spring-core-5.3.2/org/springframework/util/FileCopyUtils.java b/java/ql/test/stubs/spring-core-5.3.2/org/springframework/util/FileCopyUtils.java
new file mode 100644
index 00000000000..78d384d7266
--- /dev/null
+++ b/java/ql/test/stubs/spring-core-5.3.2/org/springframework/util/FileCopyUtils.java
@@ -0,0 +1,53 @@
+package org.springframework.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.nio.file.Files;
+import org.springframework.lang.Nullable;
+
+public abstract class FileCopyUtils {
+ public static final int BUFFER_SIZE = 4096;
+
+ public FileCopyUtils() {
+ }
+
+ public static int copy(File in, File out) throws IOException {
+ return 1;
+ }
+
+ public static void copy(byte[] in, File out) throws IOException {}
+
+ public static byte[] copyToByteArray(File in) throws IOException {
+ return null;
+ }
+
+ public static int copy(InputStream in, OutputStream out) throws IOException {
+ return 1;
+ }
+
+ public static void copy(byte[] in, OutputStream out) throws IOException {}
+
+ public static byte[] copyToByteArray(@Nullable InputStream in) throws IOException {
+ return null;
+ }
+
+ public static int copy(Reader in, Writer out) throws IOException {
+ return 1;
+ }
+
+ public static void copy(String in, Writer out) throws IOException {}
+
+ public static String copyToString(@Nullable Reader in) throws IOException {
+ return null;
+ }
+
+ private static void close(Closeable closeable) {}
+}
diff --git a/java/ql/test/stubs/spring-core-5.3.2/org/springframework/util/StringUtils.java b/java/ql/test/stubs/spring-core-5.3.2/org/springframework/util/StringUtils.java
index 6ee07f84593..6ea27bbffa2 100644
--- a/java/ql/test/stubs/spring-core-5.3.2/org/springframework/util/StringUtils.java
+++ b/java/ql/test/stubs/spring-core-5.3.2/org/springframework/util/StringUtils.java
@@ -5,4 +5,4 @@ public abstract class StringUtils {
public static boolean isEmpty(Object str) {
return str == null || "".equals(str);
}
-}
\ No newline at end of file
+}
diff --git a/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/GetMapping.java b/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/GetMapping.java
index 1190be7b879..541c7cd4e21 100644
--- a/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/GetMapping.java
+++ b/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/GetMapping.java
@@ -1,19 +1,51 @@
package org.springframework.web.bind.annotation;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
-@RequestMapping
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@RequestMapping(
+ method = {RequestMethod.GET}
+)
public @interface GetMapping {
-
+ @AliasFor(
+ annotation = RequestMapping.class
+ )
String name() default "";
+ @AliasFor(
+ annotation = RequestMapping.class
+ )
String[] value() default {};
+ @AliasFor(
+ annotation = RequestMapping.class
+ )
String[] path() default {};
+ @AliasFor(
+ annotation = RequestMapping.class
+ )
String[] params() default {};
+ @AliasFor(
+ annotation = RequestMapping.class
+ )
+ String[] headers() default {};
+
+ @AliasFor(
+ annotation = RequestMapping.class
+ )
String[] consumes() default {};
+ @AliasFor(
+ annotation = RequestMapping.class
+ )
String[] produces() default {};
}
diff --git a/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/Mapping.java b/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/Mapping.java
new file mode 100644
index 00000000000..5f269bbcbb8
--- /dev/null
+++ b/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/Mapping.java
@@ -0,0 +1,4 @@
+package org.springframework.web.bind.annotation;
+
+public @interface Mapping {
+}
diff --git a/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/RequestMapping.java b/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/RequestMapping.java
index ddb36bce4c0..ed692a03063 100644
--- a/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/RequestMapping.java
+++ b/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/RequestMapping.java
@@ -1,15 +1,32 @@
package org.springframework.web.bind.annotation;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Mapping
public @interface RequestMapping {
String name() default "";
@AliasFor("path")
String[] value() default {};
-
+
@AliasFor("value")
String[] path() default {};
RequestMethod[] method() default {};
+
+ String[] params() default {};
+
+ String[] headers() default {};
+
+ String[] consumes() default {};
+
+ String[] produces() default {};
}
diff --git a/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/RequestParam.java b/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/RequestParam.java
new file mode 100644
index 00000000000..56094811c37
--- /dev/null
+++ b/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/RequestParam.java
@@ -0,0 +1,23 @@
+package org.springframework.web.bind.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.springframework.core.annotation.AliasFor;
+
+@Target({ElementType.PARAMETER})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface RequestParam {
+ @AliasFor("name")
+ String value() default "";
+
+ @AliasFor("value")
+ String name() default "";
+
+ boolean required() default true;
+
+ String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
+}
diff --git a/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/ResponseBody.java b/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/ResponseBody.java
index b2134009968..4f21b6daaaf 100644
--- a/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/ResponseBody.java
+++ b/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/bind/annotation/ResponseBody.java
@@ -1,4 +1,13 @@
package org.springframework.web.bind.annotation;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
public @interface ResponseBody {
}
diff --git a/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/multipart/MultipartFile.java b/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/multipart/MultipartFile.java
new file mode 100644
index 00000000000..93ea3439fed
--- /dev/null
+++ b/java/ql/test/stubs/spring-web-5.3.2/org/springframework/web/multipart/MultipartFile.java
@@ -0,0 +1,38 @@
+package org.springframework.web.multipart;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import org.springframework.core.io.InputStreamSource;
+import org.springframework.core.io.Resource;
+import org.springframework.lang.Nullable;
+import org.springframework.util.FileCopyUtils;
+
+public interface MultipartFile extends InputStreamSource {
+ String getName();
+
+ @Nullable
+ String getOriginalFilename();
+
+ @Nullable
+ String getContentType();
+
+ boolean isEmpty();
+
+ long getSize();
+
+ byte[] getBytes() throws IOException;
+
+ InputStream getInputStream() throws IOException;
+
+ default Resource getResource() {
+ return null;
+ }
+
+ void transferTo(File var1) throws IOException, IllegalStateException;
+
+ default void transferTo(Path dest) throws IOException, IllegalStateException {
+ }
+}
diff --git a/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/annotation/WebServlet.java b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/annotation/WebServlet.java
new file mode 100644
index 00000000000..cc255efc00f
--- /dev/null
+++ b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/annotation/WebServlet.java
@@ -0,0 +1,30 @@
+package javax.servlet.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface WebServlet {
+ String name() default "";
+
+ String[] value() default {};
+
+ String[] urlPatterns() default {};
+
+ int loadOnStartup() default -1;
+
+ boolean asyncSupported() default false;
+
+ String smallIcon() default "";
+
+ String largeIcon() default "";
+
+ String description() default "";
+
+ String displayName() default "";
+}
From 15206fd2ce3d53168caa2d0250c2b9036dccfca9 Mon Sep 17 00:00:00 2001
From: haby0
Date: Wed, 17 Mar 2021 15:52:05 +0800
Subject: [PATCH 135/725] JsonpInjection.ql autoformatted
---
java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.ql | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.ql
index 068469328ea..eb4fe4e5b66 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.ql
@@ -46,7 +46,7 @@ class RequestResponseFlowConfig extends TaintTracking::Configuration {
/** Eliminate the method of calling the node is not the get method. */
override predicate isSanitizer(DataFlow::Node node) {
- not getACallingCallableOrSelf(node.getEnclosingCallable()) instanceof RequestGetMethod
+ not getACallingCallableOrSelf(node.getEnclosingCallable()) instanceof RequestGetMethod
}
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
From 0b1705f302d17e933124e3a2b3f22aae5d164f96 Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Wed, 17 Mar 2021 09:25:57 +0100
Subject: [PATCH 136/725] C#: Adjust Callable::canReturn to handle Task-like
async return types
---
csharp/ql/src/semmle/code/csharp/Callable.qll | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/csharp/ql/src/semmle/code/csharp/Callable.qll b/csharp/ql/src/semmle/code/csharp/Callable.qll
index 83e1739be89..111702bac48 100644
--- a/csharp/ql/src/semmle/code/csharp/Callable.qll
+++ b/csharp/ql/src/semmle/code/csharp/Callable.qll
@@ -11,7 +11,6 @@ private import dotnet
private import semmle.code.csharp.ExprOrStmtParent
private import semmle.code.csharp.metrics.Complexity
private import TypeRef
-private import semmle.code.csharp.frameworks.system.threading.Tasks
/**
* An element that can be called.
@@ -210,7 +209,7 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal
not this.getReturnType() instanceof VoidType and
(
not this.(Modifiable).isAsync() or
- not this.getReturnType() instanceof SystemThreadingTasksTaskClass
+ this.getReturnType() instanceof Generic
)
}
From 78843882f90a4f167dfab4bc3aec4a80501e6872 Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Fri, 12 Mar 2021 12:51:00 +0100
Subject: [PATCH 137/725] C#: Upgrade nuget packages
---
csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs | 2 +-
.../Semmle.Autobuild.CSharp/Semmle.Autobuild.CSharp.csproj | 4 ++--
.../Semmle.Autobuild.Shared/Semmle.Autobuild.Shared.csproj | 2 +-
.../Semmle.Extraction.CSharp.Standalone.csproj | 2 +-
4 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs
index 93f0a630d1e..8787406c104 100644
--- a/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs
+++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs
@@ -145,7 +145,7 @@ namespace Semmle.Autobuild.CSharp
try
{
var o = JObject.Parse(File.ReadAllText(path));
- version = (string)o["sdk"]["version"];
+ version = (string)(o?["sdk"]?["version"]!);
}
catch // lgtm[cs/catch-of-all-exceptions]
{
diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/Semmle.Autobuild.CSharp.csproj b/csharp/autobuilder/Semmle.Autobuild.CSharp/Semmle.Autobuild.CSharp.csproj
index b02b1a3fef1..60291bfc308 100644
--- a/csharp/autobuilder/Semmle.Autobuild.CSharp/Semmle.Autobuild.CSharp.csproj
+++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/Semmle.Autobuild.CSharp.csproj
@@ -17,8 +17,8 @@
-
-
+
+
diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/Semmle.Autobuild.Shared.csproj b/csharp/autobuilder/Semmle.Autobuild.Shared/Semmle.Autobuild.Shared.csproj
index 5f59b205527..ae1c61bc992 100644
--- a/csharp/autobuilder/Semmle.Autobuild.Shared/Semmle.Autobuild.Shared.csproj
+++ b/csharp/autobuilder/Semmle.Autobuild.Shared/Semmle.Autobuild.Shared.csproj
@@ -14,7 +14,7 @@
-
+
diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Semmle.Extraction.CSharp.Standalone.csproj b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Semmle.Extraction.CSharp.Standalone.csproj
index adf51941ad7..aed85473e7c 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Semmle.Extraction.CSharp.Standalone.csproj
+++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Semmle.Extraction.CSharp.Standalone.csproj
@@ -23,7 +23,7 @@
-
+
From 2f3869f41bfdfd272e73d5e1314a30950be0d3a5 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Tue, 16 Mar 2021 23:45:36 +0100
Subject: [PATCH 138/725] add model for puppeteer
---
javascript/ql/src/javascript.qll | 1 +
.../javascript/frameworks/Puppeteer.qll | 125 ++++++++++++++++++
.../dataflow/TaintedPathCustomizations.qll | 14 ++
.../ClientRequests/ClientRequests.expected | 6 +
.../frameworks/ClientRequests/puppeteer.ts | 20 +++
.../frameworks/ClientRequests/tsconfig.json | 1 +
.../CWE-022/TaintedPath/TaintedPath.expected | 41 ++++++
.../Security/CWE-022/TaintedPath/pupeteer.js | 18 +++
8 files changed, 226 insertions(+)
create mode 100644 javascript/ql/src/semmle/javascript/frameworks/Puppeteer.qll
create mode 100644 javascript/ql/test/library-tests/frameworks/ClientRequests/puppeteer.ts
create mode 100644 javascript/ql/test/library-tests/frameworks/ClientRequests/tsconfig.json
create mode 100644 javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/pupeteer.js
diff --git a/javascript/ql/src/javascript.qll b/javascript/ql/src/javascript.qll
index b3664fce00e..66dccd8cddd 100644
--- a/javascript/ql/src/javascript.qll
+++ b/javascript/ql/src/javascript.qll
@@ -102,6 +102,7 @@ import semmle.javascript.frameworks.Next
import semmle.javascript.frameworks.NoSQL
import semmle.javascript.frameworks.PkgCloud
import semmle.javascript.frameworks.PropertyProjection
+import semmle.javascript.frameworks.Puppeteer
import semmle.javascript.frameworks.React
import semmle.javascript.frameworks.ReactNative
import semmle.javascript.frameworks.Request
diff --git a/javascript/ql/src/semmle/javascript/frameworks/Puppeteer.qll b/javascript/ql/src/semmle/javascript/frameworks/Puppeteer.qll
new file mode 100644
index 00000000000..94f644ebb94
--- /dev/null
+++ b/javascript/ql/src/semmle/javascript/frameworks/Puppeteer.qll
@@ -0,0 +1,125 @@
+import javascript
+
+/**
+ * Classes and predicates modelling the `puppeteer` library.
+ */
+module Puppeteer {
+ /**
+ * A reference to a module import of puppeteer.
+ */
+ private API::Node puppeteer() { result = API::moduleImport(["puppeteer", "puppeteer-core"]) }
+
+ private class BrowserTypeEntryPoint extends API::EntryPoint {
+ BrowserTypeEntryPoint() { this = "PuppeteerBrowserTypeEntryPoint" }
+
+ override DataFlow::SourceNode getAUse() { result.hasUnderlyingType("puppeteer", "Browser") }
+
+ override DataFlow::Node getARhs() { none() }
+ }
+
+ /**
+ * A reference to a `Browser` from puppeteer.
+ */
+ private API::Node browser() {
+ result = API::root().getASuccessor(any(BrowserTypeEntryPoint b))
+ or
+ result = puppeteer().getMember(["launch", "connect"]).getReturn().getPromised()
+ or
+ result = [page(), context(), target()].getMember("browser").getReturn()
+ }
+
+ private class PageTypeEntryPoint extends API::EntryPoint {
+ PageTypeEntryPoint() { this = "PuppeteerPageTypeEntryPoint" }
+
+ override DataFlow::SourceNode getAUse() { result.hasUnderlyingType("puppeteer", "Page") }
+
+ override DataFlow::Node getARhs() { none() }
+ }
+
+ /**
+ * A reference to a `Page` from puppeteer.
+ */
+ API::Node page() {
+ result = API::root().getASuccessor(any(PageTypeEntryPoint b))
+ or
+ result = [browser(), context()].getMember("newPage").getReturn().getPromised()
+ or
+ result = [browser(), context()].getMember("pages").getReturn().getPromised().getUnknownMember()
+ or
+ result = target().getMember("page").getReturn().getPromised()
+ }
+
+ private class TargetTypeEntryPoint extends API::EntryPoint {
+ TargetTypeEntryPoint() { this = "PuppeteerTargetTypeEntryPoint" }
+
+ override DataFlow::SourceNode getAUse() { result.hasUnderlyingType("puppeteer", "Target") }
+
+ override DataFlow::Node getARhs() { none() }
+ }
+
+ /**
+ * A reference to a `Target` from puppeteer.
+ */
+ private API::Node target() {
+ result = API::root().getASuccessor(any(TargetTypeEntryPoint b))
+ or
+ result = [page(), browser()].getMember("target").getReturn()
+ or
+ result = context().getMember("targets").getReturn().getUnknownMember()
+ or
+ result = target().getMember("opener").getReturn()
+ }
+
+ private class ContextTypeEntryPoint extends API::EntryPoint {
+ ContextTypeEntryPoint() { this = "PuppeteerContextTypeEntryPoint" }
+
+ override DataFlow::SourceNode getAUse() {
+ result.hasUnderlyingType("puppeteer", "BrowserContext")
+ }
+
+ override DataFlow::Node getARhs() { none() }
+ }
+
+ /**
+ * A reference to a `BrowserContext` from puppeteer.
+ */
+ private API::Node context() {
+ result = API::root().getASuccessor(any(ContextTypeEntryPoint b))
+ or
+ result = [page(), target()].getMember("browserContext").getReturn()
+ or
+ result = browser().getMember("browserContexts").getReturn().getUnknownMember()
+ or
+ result = browser().getMember("createIncognitoBrowserContext").getReturn().getPromised()
+ or
+ result = browser().getMember("defaultBrowserContext").getReturn()
+ }
+
+ /**
+ * A call requesting a `Page` to navigate to some url, seen as a `ClientRequest`.
+ */
+ private class PuppeteerGotoCall extends ClientRequest::Range, API::InvokeNode {
+ PuppeteerGotoCall() { this = page().getMember("goto").getACall() }
+
+ override DataFlow::Node getUrl() { result = getArgument(0) }
+
+ override DataFlow::Node getHost() { none() }
+
+ override DataFlow::Node getADataNode() { none() }
+ }
+
+ /**
+ * A call requesting a `Page` to load a stylesheet or script, seen as a `ClientRequest`.
+ */
+ private class PuppeteerLoadResourceCall extends ClientRequest::Range, API::InvokeNode {
+ PuppeteerLoadResourceCall() {
+ this = page().getMember(["addStyleTag", "addScriptTag"]).getACall()
+ }
+
+ override DataFlow::Node getUrl() { result = getParameter(0).getMember("url").getARhs() }
+
+ override DataFlow::Node getHost() { none() }
+
+ override DataFlow::Node getADataNode() { none() }
+ }
+}
diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll
index 095a2e072c1..80c4346bace 100644
--- a/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll
+++ b/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll
@@ -631,6 +631,20 @@ module TaintedPath {
SendPathSink() { this = DataFlow::moduleImport("send").getACall().getArgument(1) }
}
+ /**
+ * A path argument given to a `Page` in puppeteer, specifying where a pdf/screenshot should be saved.
+ */
+ private class PuppeteerPath extends TaintedPath::Sink {
+ PuppeteerPath() {
+ this =
+ Puppeteer::page()
+ .getMember(["pdf", "screenshot"])
+ .getParameter(0)
+ .getMember("path")
+ .getARhs()
+ }
+ }
+
/**
* Holds if there is a step `src -> dst` mapping `srclabel` to `dstlabel` relevant for path traversal vulnerabilities.
*/
diff --git a/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected b/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected
index 2bf2d305cb0..e1cdc2dc000 100644
--- a/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected
+++ b/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected
@@ -5,6 +5,9 @@ test_ClientRequest
| apollo.js:17:1:17:34 | new Pre ... yurl"}) |
| apollo.js:20:1:20:77 | createN ... phql'}) |
| apollo.js:23:1:23:31 | new Web ... wsUri}) |
+| puppeteer.ts:6:11:6:42 | page.go ... e.com') |
+| puppeteer.ts:8:5:8:61 | page.ad ... css" }) |
+| puppeteer.ts:18:30:18:50 | page.go ... estUrl) |
| tst.js:11:5:11:16 | request(url) |
| tst.js:13:5:13:20 | request.get(url) |
| tst.js:15:5:15:23 | request.delete(url) |
@@ -136,6 +139,9 @@ test_getUrl
| apollo.js:17:1:17:34 | new Pre ... yurl"}) | apollo.js:17:26:17:32 | "myurl" |
| apollo.js:20:1:20:77 | createN ... phql'}) | apollo.js:20:30:20:75 | 'https: ... raphql' |
| apollo.js:23:1:23:31 | new Web ... wsUri}) | apollo.js:23:25:23:29 | wsUri |
+| puppeteer.ts:6:11:6:42 | page.go ... e.com') | puppeteer.ts:6:21:6:41 | 'https: ... le.com' |
+| puppeteer.ts:8:5:8:61 | page.ad ... css" }) | puppeteer.ts:8:29:8:58 | "http:/ ... le.css" |
+| puppeteer.ts:18:30:18:50 | page.go ... estUrl) | puppeteer.ts:18:40:18:49 | requestUrl |
| tst.js:11:5:11:16 | request(url) | tst.js:11:13:11:15 | url |
| tst.js:13:5:13:20 | request.get(url) | tst.js:13:17:13:19 | url |
| tst.js:15:5:15:23 | request.delete(url) | tst.js:15:20:15:22 | url |
diff --git a/javascript/ql/test/library-tests/frameworks/ClientRequests/puppeteer.ts b/javascript/ql/test/library-tests/frameworks/ClientRequests/puppeteer.ts
new file mode 100644
index 00000000000..5f630f1a691
--- /dev/null
+++ b/javascript/ql/test/library-tests/frameworks/ClientRequests/puppeteer.ts
@@ -0,0 +1,20 @@
+import * as puppeteer from 'puppeteer';
+
+(async () => {
+ const browser = await puppeteer.launch();
+ const page = await browser.newPage();
+ await page.goto('https://example.com');
+
+ page.addStyleTag({ url: "http://example.org/style.css" })
+})();
+
+class Renderer {
+ private browser: puppeteer.Browser;
+ constructor(browser: puppeteer.Browser) {
+ this.browser = browser;
+ }
+ async foo(requestUrl: string): Promise {
+ const page = await this.browser.newPage();
+ let response = await page.goto(requestUrl);
+ }
+}
\ No newline at end of file
diff --git a/javascript/ql/test/library-tests/frameworks/ClientRequests/tsconfig.json b/javascript/ql/test/library-tests/frameworks/ClientRequests/tsconfig.json
new file mode 100644
index 00000000000..9e26dfeeb6e
--- /dev/null
+++ b/javascript/ql/test/library-tests/frameworks/ClientRequests/tsconfig.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
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 7e4ab1a0427..0b1fe929968 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
@@ -2168,6 +2168,24 @@ nodes
| other-fs-libraries.js:42:53:42:56 | path |
| other-fs-libraries.js:42:53:42:56 | path |
| other-fs-libraries.js:42:53:42:56 | path |
+| pupeteer.js:5:9:5:71 | tainted |
+| pupeteer.js:5:9:5:71 | tainted |
+| pupeteer.js:5:9:5:71 | tainted |
+| pupeteer.js:5:19:5:71 | "dir/" ... t.data" |
+| pupeteer.js:5:19:5:71 | "dir/" ... t.data" |
+| pupeteer.js:5:19:5:71 | "dir/" ... t.data" |
+| pupeteer.js:5:28:5:53 | parseTo ... t).name |
+| pupeteer.js:5:28:5:53 | parseTo ... t).name |
+| pupeteer.js:5:28:5:53 | parseTo ... t).name |
+| pupeteer.js:5:28:5:53 | parseTo ... t).name |
+| pupeteer.js:9:28:9:34 | tainted |
+| pupeteer.js:9:28:9:34 | tainted |
+| pupeteer.js:9:28:9:34 | tainted |
+| pupeteer.js:9:28:9:34 | tainted |
+| pupeteer.js:13:37:13:43 | tainted |
+| pupeteer.js:13:37:13:43 | tainted |
+| pupeteer.js:13:37:13:43 | tainted |
+| pupeteer.js:13:37:13:43 | tainted |
| tainted-access-paths.js:6:7:6:48 | path |
| tainted-access-paths.js:6:7:6:48 | path |
| tainted-access-paths.js:6:7:6:48 | path |
@@ -6403,6 +6421,27 @@ edges
| other-fs-libraries.js:38:24:38:30 | req.url | other-fs-libraries.js:38:14:38:37 | url.par ... , true) |
| other-fs-libraries.js:38:24:38:30 | req.url | other-fs-libraries.js:38:14:38:37 | url.par ... , true) |
| other-fs-libraries.js:38:24:38:30 | req.url | other-fs-libraries.js:38:14:38:37 | url.par ... , true) |
+| 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 |
+| 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 |
+| pupeteer.js:5:9:5:71 | tainted | pupeteer.js:13:37:13:43 | tainted |
+| pupeteer.js:5:9:5:71 | tainted | pupeteer.js:13:37:13:43 | tainted |
+| pupeteer.js:5:9:5:71 | tainted | pupeteer.js:13:37:13:43 | tainted |
+| pupeteer.js:5:9:5:71 | tainted | pupeteer.js:13:37:13:43 | tainted |
+| pupeteer.js:5:9:5:71 | tainted | pupeteer.js:13:37:13:43 | tainted |
+| pupeteer.js:5:9:5:71 | tainted | pupeteer.js:13:37:13:43 | tainted |
+| pupeteer.js:5:19:5:71 | "dir/" ... t.data" | pupeteer.js:5:9:5:71 | tainted |
+| pupeteer.js:5:19:5:71 | "dir/" ... t.data" | pupeteer.js:5:9:5:71 | tainted |
+| pupeteer.js:5:19:5:71 | "dir/" ... t.data" | pupeteer.js:5:9:5:71 | tainted |
+| pupeteer.js:5:28:5:53 | parseTo ... t).name | pupeteer.js:5:19:5:71 | "dir/" ... t.data" |
+| pupeteer.js:5:28:5:53 | parseTo ... t).name | pupeteer.js:5:19:5:71 | "dir/" ... t.data" |
+| pupeteer.js:5:28:5:53 | parseTo ... t).name | pupeteer.js:5:19:5:71 | "dir/" ... t.data" |
+| pupeteer.js:5:28:5:53 | parseTo ... t).name | pupeteer.js:5:19:5:71 | "dir/" ... t.data" |
+| pupeteer.js:5:28:5:53 | parseTo ... t).name | pupeteer.js:5:19:5:71 | "dir/" ... t.data" |
+| pupeteer.js:5:28:5:53 | parseTo ... t).name | pupeteer.js:5:19:5:71 | "dir/" ... t.data" |
| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path |
| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path |
| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path |
@@ -8007,6 +8046,8 @@ edges
| other-fs-libraries.js:40:35:40:38 | path | other-fs-libraries.js:38:24:38:30 | req.url | other-fs-libraries.js:40:35:40:38 | path | This path depends on $@. | other-fs-libraries.js:38:24:38:30 | req.url | a user-provided value |
| other-fs-libraries.js:41:50:41:53 | path | other-fs-libraries.js:38:24:38:30 | req.url | other-fs-libraries.js:41:50:41:53 | path | This path depends on $@. | other-fs-libraries.js:38:24:38:30 | req.url | a user-provided value |
| 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 |
+| 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 |
| tainted-access-paths.js:12:19:12:25 | obj.sub | tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:12:19:12:25 | obj.sub | This path depends on $@. | tainted-access-paths.js:6:24:6:30 | req.url | a user-provided value |
| tainted-access-paths.js:26:19:26:26 | obj.sub3 | tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:26:19:26:26 | obj.sub3 | 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/pupeteer.js b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/pupeteer.js
new file mode 100644
index 00000000000..363b2f014c7
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/pupeteer.js
@@ -0,0 +1,18 @@
+const puppeteer = require('puppeteer');
+const parseTorrent = require('parse-torrent');
+
+(async () => {
+ let tainted = "dir/" + parseTorrent(torrent).name + ".torrent.data";
+
+ const browser = await puppeteer.launch();
+ const page = await browser.newPage();
+ await page.pdf({ path: tainted, format: 'a4' });
+
+ const pages = await browser.pages();
+ for (let i = 0; i < something(); i++) {
+ pages[i].screenshot({ path: tainted });
+ }
+
+ await browser.close();
+})();
+
From 8975c3a7ce678c4e715a8a1222e7b41697595411 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 17 Mar 2021 00:00:19 +0100
Subject: [PATCH 139/725] broaden which types are recognized by API-graphs
---
.../ql/src/semmle/javascript/ApiGraphs.qll | 18 +++++++-
.../javascript/frameworks/Puppeteer.qll | 42 ++-----------------
2 files changed, 21 insertions(+), 39 deletions(-)
diff --git a/javascript/ql/src/semmle/javascript/ApiGraphs.qll b/javascript/ql/src/semmle/javascript/ApiGraphs.qll
index 6c8c9533557..fd955cf00ab 100644
--- a/javascript/ql/src/semmle/javascript/ApiGraphs.qll
+++ b/javascript/ql/src/semmle/javascript/ApiGraphs.qll
@@ -317,6 +317,8 @@ module API {
tn.hasQualifiedName(moduleName, exportedName) and
result = Impl::MkCanonicalNameUse(tn).(Node).getInstance()
)
+ or
+ result = Impl::MkHasUnderlyingType(moduleName, exportedName)
}
}
@@ -413,6 +415,13 @@ module API {
not n.isRoot() and
isUsed(n)
} or
+ /**
+ * An instance of a TypeScript type, identified by name of the type-annotation.
+ * This API node is exclusively used by `API::Node::ofType`.
+ */
+ MkHasUnderlyingType(string moduleName, string exportName) {
+ any(TypeAnnotation n).hasQualifiedName(moduleName, exportName)
+ } or
MkSyntheticCallbackArg(DataFlow::Node src, int bound, DataFlow::InvokeNode nd) {
trackUseNode(src, true, bound).flowsTo(nd.getCalleeNode())
}
@@ -423,7 +432,8 @@ module API {
MkModuleExport or MkClassInstance or MkAsyncFuncResult or MkDef or MkCanonicalNameDef or
MkSyntheticCallbackArg;
- class TUse = MkModuleUse or MkModuleImport or MkUse or MkCanonicalNameUse;
+ class TUse =
+ MkModuleUse or MkModuleImport or MkUse or MkCanonicalNameUse or MkHasUnderlyingType;
private predicate hasSemantics(DataFlow::Node nd) { not nd.getTopLevel().isExterns() }
@@ -678,6 +688,12 @@ module API {
nd = MkUse(ref)
or
exists(CanonicalName n | nd = MkCanonicalNameUse(n) | ref.asExpr() = n.getAnAccess())
+ or
+ exists(string moduleName, string exportsName |
+ nd = MkHasUnderlyingType(moduleName, exportsName)
+ |
+ ref.(DataFlow::SourceNode).hasUnderlyingType(moduleName, exportsName)
+ )
}
/** Holds if module `m` exports `rhs`. */
diff --git a/javascript/ql/src/semmle/javascript/frameworks/Puppeteer.qll b/javascript/ql/src/semmle/javascript/frameworks/Puppeteer.qll
index 94f644ebb94..e60a447cd07 100644
--- a/javascript/ql/src/semmle/javascript/frameworks/Puppeteer.qll
+++ b/javascript/ql/src/semmle/javascript/frameworks/Puppeteer.qll
@@ -9,38 +9,22 @@ module Puppeteer {
*/
private API::Node puppeteer() { result = API::moduleImport(["puppeteer", "puppeteer-core"]) }
- private class BrowserTypeEntryPoint extends API::EntryPoint {
- BrowserTypeEntryPoint() { this = "PuppeteerBrowserTypeEntryPoint" }
-
- override DataFlow::SourceNode getAUse() { result.hasUnderlyingType("puppeteer", "Browser") }
-
- override DataFlow::Node getARhs() { none() }
- }
-
/**
* A reference to a `Browser` from puppeteer.
*/
private API::Node browser() {
- result = API::root().getASuccessor(any(BrowserTypeEntryPoint b))
+ result = API::Node::ofType("puppeteer", "Browser")
or
result = puppeteer().getMember(["launch", "connect"]).getReturn().getPromised()
or
result = [page(), context(), target()].getMember("browser").getReturn()
}
- private class PageTypeEntryPoint extends API::EntryPoint {
- PageTypeEntryPoint() { this = "PuppeteerPageTypeEntryPoint" }
-
- override DataFlow::SourceNode getAUse() { result.hasUnderlyingType("puppeteer", "Page") }
-
- override DataFlow::Node getARhs() { none() }
- }
-
/**
* A reference to a `Page` from puppeteer.
*/
API::Node page() {
- result = API::root().getASuccessor(any(PageTypeEntryPoint b))
+ result = API::Node::ofType("puppeteer", "Page")
or
result = [browser(), context()].getMember("newPage").getReturn().getPromised()
or
@@ -49,19 +33,11 @@ module Puppeteer {
result = target().getMember("page").getReturn().getPromised()
}
- private class TargetTypeEntryPoint extends API::EntryPoint {
- TargetTypeEntryPoint() { this = "PuppeteerTargetTypeEntryPoint" }
-
- override DataFlow::SourceNode getAUse() { result.hasUnderlyingType("puppeteer", "Target") }
-
- override DataFlow::Node getARhs() { none() }
- }
-
/**
* A reference to a `Target` from puppeteer.
*/
private API::Node target() {
- result = API::root().getASuccessor(any(TargetTypeEntryPoint b))
+ result = API::Node::ofType("puppeteer", "Target")
or
result = [page(), browser()].getMember("target").getReturn()
or
@@ -70,21 +46,11 @@ module Puppeteer {
result = target().getMember("opener").getReturn()
}
- private class ContextTypeEntryPoint extends API::EntryPoint {
- ContextTypeEntryPoint() { this = "PuppeteerContextTypeEntryPoint" }
-
- override DataFlow::SourceNode getAUse() {
- result.hasUnderlyingType("puppeteer", "BrowserContext")
- }
-
- override DataFlow::Node getARhs() { none() }
- }
-
/**
* A reference to a `BrowserContext` from puppeteer.
*/
private API::Node context() {
- result = API::root().getASuccessor(any(ContextTypeEntryPoint b))
+ result = API::Node::ofType("puppeteer", "BrowserContext")
or
result = [page(), target()].getMember("browserContext").getReturn()
or
From edb0f7717760c4b2b8cb0208b60de81b7b15d669 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 17 Mar 2021 10:05:36 +0100
Subject: [PATCH 140/725] add missing qldoc
---
.../ql/src/semmle/javascript/frameworks/Puppeteer.qll | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/javascript/ql/src/semmle/javascript/frameworks/Puppeteer.qll b/javascript/ql/src/semmle/javascript/frameworks/Puppeteer.qll
index e60a447cd07..84c2351b600 100644
--- a/javascript/ql/src/semmle/javascript/frameworks/Puppeteer.qll
+++ b/javascript/ql/src/semmle/javascript/frameworks/Puppeteer.qll
@@ -1,7 +1,11 @@
+/**
+ * Provides classes and predicates for reasoning about [puppeteer](https://www.npmjs.com/package/puppeteer).
+ */
+
import javascript
/**
- * Classes and predicates modelling the `puppeteer` library.
+ * Classes and predicates modelling the [puppeteer](https://www.npmjs.com/package/puppeteer) library.
*/
module Puppeteer {
/**
From d1602d538ea437cf59bca5c31cdcf6fe8d9a74b5 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 17 Mar 2021 10:06:41 +0100
Subject: [PATCH 141/725] add change note
---
javascript/change-notes/2021-03-17-puppeteer.md | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 javascript/change-notes/2021-03-17-puppeteer.md
diff --git a/javascript/change-notes/2021-03-17-puppeteer.md b/javascript/change-notes/2021-03-17-puppeteer.md
new file mode 100644
index 00000000000..84e8fadd9b7
--- /dev/null
+++ b/javascript/change-notes/2021-03-17-puppeteer.md
@@ -0,0 +1,4 @@
+lgtm,codescanning
+* URIs used in the puppeteer library are now recognized as sinks for `js/request-forgery`.
+ Affected packages are
+ [puppeteer](https://www.npmjs.com/package/puppeteer)
\ No newline at end of file
From 96124266800de6683872968269245ffbbd5e8b16 Mon Sep 17 00:00:00 2001
From: Cornelius Riemenschneider
Date: Tue, 16 Mar 2021 12:56:40 +0000
Subject: [PATCH 142/725] C++: Initial file-related metric queries.
This adds a library `FailedExtractions.qll` that classifies extractor errors
and provides a unified interface for both recoverable and irrecoverable
extractor errors.
This interface is then used by the new diagnostic queries to list
successfully extracted files, as well as files that encountered an
extraction error.
---
cpp/ql/src/Diagnostics/FailedExtractions.ql | 16 +++
cpp/ql/src/Diagnostics/FailedExtractions.qll | 114 ++++++++++++++++++
.../src/Diagnostics/SuccessfulExtractions.ql | 15 +++
3 files changed, 145 insertions(+)
create mode 100644 cpp/ql/src/Diagnostics/FailedExtractions.ql
create mode 100644 cpp/ql/src/Diagnostics/FailedExtractions.qll
create mode 100644 cpp/ql/src/Diagnostics/SuccessfulExtractions.ql
diff --git a/cpp/ql/src/Diagnostics/FailedExtractions.ql b/cpp/ql/src/Diagnostics/FailedExtractions.ql
new file mode 100644
index 00000000000..6a3d7daae4c
--- /dev/null
+++ b/cpp/ql/src/Diagnostics/FailedExtractions.ql
@@ -0,0 +1,16 @@
+/**
+ * @name Failed extractions
+ * @description Gives the command-line of compilations for which extraction did not run to completion.
+ * @kind diagnostic
+ * @id cpp/diagnostics/failed-extractions
+ */
+
+import cpp
+import FailedExtractions
+
+from ExtractionError error
+where
+ error instanceof ExtractionUnknownError or
+ exists(error.getFile().getRelativePath())
+select error, "Extracting file $@ failed with $@ (at $@)", error.getFile(), error.getErrorMessage(),
+ error.getLocation(), error.getSeverity()
diff --git a/cpp/ql/src/Diagnostics/FailedExtractions.qll b/cpp/ql/src/Diagnostics/FailedExtractions.qll
new file mode 100644
index 00000000000..808c4d7d386
--- /dev/null
+++ b/cpp/ql/src/Diagnostics/FailedExtractions.qll
@@ -0,0 +1,114 @@
+import cpp
+
+/**
+ * The class of errors upon we mark a file as non-successfully extracted.
+ */
+class ReportableError extends Diagnostic {
+ ReportableError() {
+ (
+ this instanceof CompilerDiscretionaryError or
+ this instanceof CompilerError or
+ this instanceof CompilerCatastrophe
+ ) and
+ // If the extractor encounters an error in a compilation, it always emits a
+ // catch-all diagnostic "There was an error during this compilation", to ensure
+ // that the error makes it to the database.
+ // This error doesn't have a file path attached to it, and is thus
+ // useless for us to report. Furthermore, in the common case, we will have a
+ // proper diagnostic for this error we can show.
+ // Instead, we synthesize `TUnknownError` if this is the only error that we can show to the user.
+ not this.getFile().getAbsolutePath() = ""
+ }
+}
+
+newtype TExtractionError =
+ TReportableError(ReportableError err) or
+ TCompilationFailed(Compilation c, File f) {
+ f = c.getAFileCompiled() and not c.normalTermination()
+ } or
+ // Report generic extractor errors only if we haven't seen any other error-level diagnostic
+ TUnknownError(CompilerError err) { not exists(ReportableError e) }
+
+class ExtractionError extends TExtractionError {
+ string toString() { none() }
+
+ string getErrorMessage() { none() }
+
+ File getFile() { none() }
+
+ Location getLocation() { none() }
+
+ int getSeverity() {
+ // Unfortunately, we can't distinguish between errors and fatal errors in SARIF,
+ // so all errors have severity 2.
+ result = 2
+ }
+}
+
+/**
+ * An irrecoverable extraction failure, where extraction was unable to finish.
+ * This can be caused by a multitude of reasons, for example:
+ * - hitting a frontend assertion
+ * - crashing due to dereferencing an invalid pointer
+ * - stack overflow
+ * - out of memory
+ */
+class ExtractionIrrecoverableError extends ExtractionError, TCompilationFailed {
+ Compilation c;
+ File f;
+
+ ExtractionIrrecoverableError() { this = TCompilationFailed(c, f) }
+
+ override string toString() {
+ result = "Irrecoverable extraction error: " + c.toString() + " in " + f.toString()
+ }
+
+ override string getErrorMessage() {
+ result =
+ "Irrecoverable compilation failure, check logs/build-tracer.log in the database directory for more information."
+ }
+
+ override File getFile() { result = f }
+
+ override Location getLocation() { result = f.getLocation() }
+}
+
+/**
+ * A recoverable extraction error.
+ * These are compiler errors from the frontend.
+ * Upon encountering one of these, we still continue extraction, but the
+ * database will be incomplete for that file.
+ */
+class ExtractionRecoverableError extends ExtractionError, TReportableError {
+ ReportableError err;
+
+ ExtractionRecoverableError() { this = TReportableError(err) }
+
+ override string toString() { result = "Recoverable extraction error: " + err }
+
+ override string getErrorMessage() { result = err.getFullMessage() }
+
+ override File getFile() { result = err.getFile() }
+
+ override Location getLocation() { result = err.getLocation() }
+}
+
+/**
+ * An unknown error happened during extraction.
+ * These are only displayed if we know that we encountered an error during extraction,
+ * but, for some reason, failed to emit a proper diagnostic with location information
+ * and error message.
+ */
+class ExtractionUnknownError extends ExtractionError, TUnknownError {
+ CompilerError err;
+
+ ExtractionUnknownError() { this = TUnknownError(err) }
+
+ override string toString() { result = "Unknown extraction error: " + err }
+
+ override string getErrorMessage() { result = err.getFullMessage() }
+
+ override File getFile() { result = err.getFile() }
+
+ override Location getLocation() { result = err.getLocation() }
+}
diff --git a/cpp/ql/src/Diagnostics/SuccessfulExtractions.ql b/cpp/ql/src/Diagnostics/SuccessfulExtractions.ql
new file mode 100644
index 00000000000..cf5ec56a493
--- /dev/null
+++ b/cpp/ql/src/Diagnostics/SuccessfulExtractions.ql
@@ -0,0 +1,15 @@
+/**
+ * @name Successfully extracted files.
+ * @description Lists all files in the database that were extracted without encountering an error.
+ * @kind diagnostic
+ * @id cpp/diagnostics/successfully-extracted-files
+ */
+
+import cpp
+import FailedExtractions
+
+from File f
+where
+ not exists(ExtractionError e | e.getFile() = f) and
+ exists(f.getRelativePath())
+select f
From 4c4fc05553616b354d3f660d546077874d888071 Mon Sep 17 00:00:00 2001
From: Cornelius Riemenschneider
Date: Tue, 16 Mar 2021 16:23:21 +0100
Subject: [PATCH 143/725] C++: Make toString deterministic for tests.
---
cpp/ql/src/Diagnostics/FailedExtractions.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cpp/ql/src/Diagnostics/FailedExtractions.qll b/cpp/ql/src/Diagnostics/FailedExtractions.qll
index 808c4d7d386..951dda23396 100644
--- a/cpp/ql/src/Diagnostics/FailedExtractions.qll
+++ b/cpp/ql/src/Diagnostics/FailedExtractions.qll
@@ -60,7 +60,7 @@ class ExtractionIrrecoverableError extends ExtractionError, TCompilationFailed {
ExtractionIrrecoverableError() { this = TCompilationFailed(c, f) }
override string toString() {
- result = "Irrecoverable extraction error: " + c.toString() + " in " + f.toString()
+ result = "Irrecoverable extraction error while compiling " + f.toString()
}
override string getErrorMessage() {
From 5e4e853ffb3f41c25493d7f891c9d32bdc6b7c22 Mon Sep 17 00:00:00 2001
From: Cornelius Riemenschneider
Date: Tue, 16 Mar 2021 16:27:13 +0100
Subject: [PATCH 144/725] C++: Add missing QLDoc.
---
cpp/ql/src/Diagnostics/FailedExtractions.qll | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/cpp/ql/src/Diagnostics/FailedExtractions.qll b/cpp/ql/src/Diagnostics/FailedExtractions.qll
index 951dda23396..5fd371204ec 100644
--- a/cpp/ql/src/Diagnostics/FailedExtractions.qll
+++ b/cpp/ql/src/Diagnostics/FailedExtractions.qll
@@ -21,7 +21,7 @@ class ReportableError extends Diagnostic {
}
}
-newtype TExtractionError =
+private newtype TExtractionError =
TReportableError(ReportableError err) or
TCompilationFailed(Compilation c, File f) {
f = c.getAFileCompiled() and not c.normalTermination()
@@ -29,15 +29,22 @@ newtype TExtractionError =
// Report generic extractor errors only if we haven't seen any other error-level diagnostic
TUnknownError(CompilerError err) { not exists(ReportableError e) }
+/**
+ * Superclass for the extraction error hierarchy.
+ */
class ExtractionError extends TExtractionError {
string toString() { none() }
+ /** Gets the error message for this error. */
string getErrorMessage() { none() }
+ /** Gets the file this error occured in. */
File getFile() { none() }
+ /** Gets the location this error occured in. */
Location getLocation() { none() }
+ /** Gets the SARIF severity of this error. */
int getSeverity() {
// Unfortunately, we can't distinguish between errors and fatal errors in SARIF,
// so all errors have severity 2.
From 144dcf1b5e51f51865b56864fb6e61316dcdee09 Mon Sep 17 00:00:00 2001
From: Cornelius Riemenschneider
Date: Tue, 16 Mar 2021 16:37:31 +0100
Subject: [PATCH 145/725] C++: Include empty message for
SuccessfulExtractions.ql.
---
cpp/ql/src/Diagnostics/SuccessfulExtractions.ql | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cpp/ql/src/Diagnostics/SuccessfulExtractions.ql b/cpp/ql/src/Diagnostics/SuccessfulExtractions.ql
index cf5ec56a493..4331b768934 100644
--- a/cpp/ql/src/Diagnostics/SuccessfulExtractions.ql
+++ b/cpp/ql/src/Diagnostics/SuccessfulExtractions.ql
@@ -12,4 +12,4 @@ from File f
where
not exists(ExtractionError e | e.getFile() = f) and
exists(f.getRelativePath())
-select f
+select f, ""
From 3914a93504bb92bb514fe74cb609662f683f290b Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Wed, 17 Mar 2021 11:56:59 +0100
Subject: [PATCH 146/725] C++: Remove commonTaintStep from
DefaultTaintTracking.
---
.../cpp/ir/dataflow/DefaultTaintTracking.qll | 209 ------------------
1 file changed, 209 deletions(-)
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
index 33473ebfb58..7b97bc738f4 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
@@ -74,10 +74,6 @@ private class DefaultTaintTrackingCfg extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
- override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
- commonTaintStep(n1, n2)
- }
-
override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
@@ -93,8 +89,6 @@ private class ToGlobalVarTaintTrackingCfg extends TaintTracking::Configuration {
}
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
- commonTaintStep(n1, n2)
- or
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
or
readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable))
@@ -117,8 +111,6 @@ private class FromGlobalVarTaintTrackingCfg extends TaintTracking2::Configuratio
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
- commonTaintStep(n1, n2)
- or
// Additional step for flow out of variables. There is no flow _into_
// variables in this configuration, so this step only serves to take flow
// out of a variable that's a source.
@@ -227,207 +219,6 @@ private predicate nodeIsBarrierIn(DataFlow::Node node) {
)
}
-cached
-private predicate commonTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
- operandToInstructionTaintStep(fromNode.asOperand(), toNode.asInstruction())
- or
- instructionToOperandTaintStep(fromNode.asInstruction(), toNode.asOperand())
-}
-
-private predicate instructionToOperandTaintStep(Instruction fromInstr, Operand toOperand) {
- // Propagate flow from the definition of an operand to the operand, even when the overlap is inexact.
- // We only do this in certain cases:
- // 1. The instruction's result must not be conflated, and
- // 2. The instruction's result type is one the types where we expect element-to-object flow. Currently
- // this is array types and union types. This matches the other two cases of element-to-object flow in
- // `DefaultTaintTracking`.
- toOperand.getAnyDef() = fromInstr and
- not fromInstr.isResultConflated() and
- (
- fromInstr.getResultType() instanceof ArrayType or
- fromInstr.getResultType() instanceof Union
- )
- or
- exists(ReadSideEffectInstruction readInstr |
- fromInstr = readInstr.getArgumentDef() and
- toOperand = readInstr.getSideEffectOperand()
- )
-}
-
-private predicate operandToInstructionTaintStep(Operand fromOperand, Instruction toInstr) {
- // Expressions computed from tainted data are also tainted
- exists(CallInstruction call, int argIndex | call = toInstr |
- isPureFunction(call.getStaticCallTarget().getName()) and
- fromOperand = getACallArgumentOrIndirection(call, argIndex) and
- forall(Operand argOperand | argOperand = call.getAnArgumentOperand() |
- argOperand = getACallArgumentOrIndirection(call, argIndex) or
- predictableInstruction(argOperand.getAnyDef())
- ) and
- // flow through `strlen` tends to cause dubious results, if the length is
- // bounded.
- not call.getStaticCallTarget().getName() = "strlen"
- )
- or
- // Flow from argument to return value
- toInstr =
- any(CallInstruction call |
- exists(int indexIn |
- modelTaintToReturnValue(call.getStaticCallTarget(), indexIn) and
- fromOperand = getACallArgumentOrIndirection(call, indexIn) and
- not predictableOnlyFlow(call.getStaticCallTarget().getName())
- )
- )
- or
- // Flow from input argument to output argument
- // TODO: This won't work in practice as long as all aliased memory is tracked
- // together in a single virtual variable.
- // TODO: Will this work on the test for `TaintedPath.ql`, where the output arg
- // is a pointer addition expression?
- toInstr =
- any(WriteSideEffectInstruction outInstr |
- exists(CallInstruction call, int indexIn, int indexOut |
- modelTaintToParameter(call.getStaticCallTarget(), indexIn, indexOut) and
- fromOperand = getACallArgumentOrIndirection(call, indexIn) and
- outInstr.getIndex() = indexOut and
- outInstr.getPrimaryInstruction() = call
- )
- )
- or
- // Flow through pointer dereference
- toInstr.(LoadInstruction).getSourceAddressOperand() = fromOperand
- or
- // Flow through partial reads of arrays and unions
- toInstr.(LoadInstruction).getSourceValueOperand() = fromOperand and
- exists(Instruction fromInstr | fromInstr = fromOperand.getAnyDef() |
- not fromInstr.isResultConflated() and
- (
- fromInstr.getResultType() instanceof ArrayType or
- fromInstr.getResultType() instanceof Union
- )
- )
- or
- // Unary instructions tend to preserve enough information in practice that we
- // want taint to flow through.
- // The exception is `FieldAddressInstruction`. Together with the rule for
- // `LoadInstruction` above and for `ChiInstruction` below, flow through
- // `FieldAddressInstruction` could cause flow into one field to come out an
- // unrelated field. This would happen across function boundaries, where the IR
- // would not be able to match loads to stores.
- toInstr.(UnaryInstruction).getUnaryOperand() = fromOperand and
- (
- not toInstr instanceof FieldAddressInstruction
- or
- toInstr.(FieldAddressInstruction).getField().getDeclaringType() instanceof Union
- )
- or
- // Flow from an element to an array or union that contains it.
- toInstr.(ChiInstruction).getPartialOperand() = fromOperand and
- not toInstr.isResultConflated() and
- exists(Type t | toInstr.getResultLanguageType().hasType(t, false) |
- t instanceof Union
- or
- t instanceof ArrayType
- )
- or
- exists(BinaryInstruction bin |
- bin = toInstr and
- predictableInstruction(toInstr.getAnOperand().getDef()) and
- fromOperand = toInstr.getAnOperand()
- )
- or
- // This is part of the translation of `a[i]`, where we want taint to flow
- // from `a`.
- toInstr.(PointerAddInstruction).getLeftOperand() = fromOperand
- or
- // Until we have flow through indirections across calls, we'll take flow out
- // of the indirection and into the argument.
- // When we get proper flow through indirections across calls, this code can be
- // moved to `adjusedSink` or possibly into the `DataFlow::ExprNode` class.
- exists(ReadSideEffectInstruction read |
- read.getSideEffectOperand() = fromOperand and
- read.getArgumentDef() = toInstr
- )
- or
- // Until we have from through indirections across calls, we'll take flow out
- // of the parameter and into its indirection.
- // `InitializeIndirectionInstruction` only has a single operand: the address of the
- // value whose indirection we are initializing. When initializing an indirection of a parameter `p`,
- // the IR looks like this:
- // ```
- // m1 = InitializeParameter[p] : &r1
- // r2 = Load[p] : r2, m1
- // m3 = InitializeIndirection[p] : &r2
- // ```
- // So by having flow from `r2` to `m3` we're enabling flow from `m1` to `m3`. This relies on the
- // `LoadOperand`'s overlap being exact.
- toInstr.(InitializeIndirectionInstruction).getAnOperand() = fromOperand
-}
-
-/**
- * Returns the index of the side effect instruction corresponding to the specified function output,
- * if one exists.
- */
-private int getWriteSideEffectIndex(FunctionOutput output) {
- output.isParameterDeref(result)
- or
- output.isQualifierObject() and result = -1
-}
-
-/**
- * Get an operand that goes into argument `argumentIndex` of `call`. This
- * can be either directly or through one pointer indirection.
- */
-private Operand getACallArgumentOrIndirection(CallInstruction call, int argumentIndex) {
- result = call.getPositionalArgumentOperand(argumentIndex)
- or
- exists(ReadSideEffectInstruction readSE |
- // TODO: why are read side effect operands imprecise?
- result = readSE.getSideEffectOperand() and
- readSE.getPrimaryInstruction() = call and
- readSE.getIndex() = argumentIndex
- )
-}
-
-private predicate modelTaintToParameter(Function f, int parameterIn, int parameterOut) {
- exists(FunctionInput modelIn, FunctionOutput modelOut |
- (
- f.(DataFlowFunction).hasDataFlow(modelIn, modelOut)
- or
- f.(TaintFunction).hasTaintFlow(modelIn, modelOut)
- ) and
- (modelIn.isParameter(parameterIn) or modelIn.isParameterDeref(parameterIn)) and
- parameterOut = getWriteSideEffectIndex(modelOut)
- )
-}
-
-private predicate modelTaintToReturnValue(Function f, int parameterIn) {
- // Taint flow from parameter to return value
- exists(FunctionInput modelIn, FunctionOutput modelOut |
- f.(TaintFunction).hasTaintFlow(modelIn, modelOut) and
- (modelIn.isParameter(parameterIn) or modelIn.isParameterDeref(parameterIn)) and
- (modelOut.isReturnValue() or modelOut.isReturnValueDeref())
- )
- or
- // Data flow (not taint flow) to where the return value points. For the time
- // being we will conflate pointers and objects in taint tracking.
- exists(FunctionInput modelIn, FunctionOutput modelOut |
- f.(DataFlowFunction).hasDataFlow(modelIn, modelOut) and
- (modelIn.isParameter(parameterIn) or modelIn.isParameterDeref(parameterIn)) and
- modelOut.isReturnValueDeref()
- )
- or
- // Taint flow from one argument to another and data flow from an argument to a
- // return value. This happens in functions like `strcat` and `memcpy`. We
- // could model this flow in two separate steps, but that would add reverse
- // flow from the write side-effect to the call instruction, which may not be
- // desirable.
- exists(int parameterMid, InParameter modelMid, OutReturnValue returnOut |
- modelTaintToParameter(f, parameterIn, parameterMid) and
- modelMid.isParameter(parameterMid) and
- f.(DataFlowFunction).hasDataFlow(modelMid, returnOut)
- )
-}
-
private Element adjustedSink(DataFlow::Node sink) {
// TODO: is it more appropriate to use asConvertedExpr here and avoid
// `getConversion*`? Or will that cause us to miss some cases where there's
From 7019878775ceef8dba6dfaf64cdd35031c4f1845 Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Wed, 17 Mar 2021 12:18:28 +0100
Subject: [PATCH 147/725] Upgrade nuget package in Semmle.Autobuild.Cpp.csproj
---
.../Semmle.Autobuild.Cpp/Semmle.Autobuild.Cpp.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cpp/autobuilder/Semmle.Autobuild.Cpp/Semmle.Autobuild.Cpp.csproj b/cpp/autobuilder/Semmle.Autobuild.Cpp/Semmle.Autobuild.Cpp.csproj
index 68cfef57b3d..1d6459d77e3 100644
--- a/cpp/autobuilder/Semmle.Autobuild.Cpp/Semmle.Autobuild.Cpp.csproj
+++ b/cpp/autobuilder/Semmle.Autobuild.Cpp/Semmle.Autobuild.Cpp.csproj
@@ -17,7 +17,7 @@
-
+
From 5e0601fe1fe7cd5642881ce3217269467d6700d1 Mon Sep 17 00:00:00 2001
From: Cornelius Riemenschneider
Date: Wed, 17 Mar 2021 12:28:03 +0100
Subject: [PATCH 148/725] C++: Address review comments.
---
...dCompilations.ql => AbortedExtractions.ql} | 8 ++--
cpp/ql/src/Diagnostics/FailedExtractions.ql | 6 +--
cpp/ql/src/Diagnostics/FailedExtractions.qll | 44 ++++++++++++-------
.../src/Diagnostics/SuccessfulExtractions.ql | 4 +-
cpp/ql/src/semmle/code/cpp/Diagnostics.qll | 3 ++
5 files changed, 41 insertions(+), 24 deletions(-)
rename cpp/ql/src/Diagnostics/{FailedCompilations.ql => AbortedExtractions.ql} (71%)
diff --git a/cpp/ql/src/Diagnostics/FailedCompilations.ql b/cpp/ql/src/Diagnostics/AbortedExtractions.ql
similarity index 71%
rename from cpp/ql/src/Diagnostics/FailedCompilations.ql
rename to cpp/ql/src/Diagnostics/AbortedExtractions.ql
index 8af1b0250c0..5bab529b23e 100644
--- a/cpp/ql/src/Diagnostics/FailedCompilations.ql
+++ b/cpp/ql/src/Diagnostics/AbortedExtractions.ql
@@ -1,8 +1,8 @@
/**
- * @name Failed compiler invocations.
- * @description Gives the command-line of compilations for which extraction did not run to completion.
+ * @name Aborted extractor invocations
+ * @description Gives the command line of compilations for which extraction did not run to completion.
* @kind diagnostic
- * @id cpp/diagnostics/failed-compilations
+ * @id cpp/diagnostics/aborted-extractions
*/
import cpp
@@ -19,4 +19,4 @@ string describe(Compilation c) {
from Compilation c
where not c.normalTermination()
-select c, "Extraction failed for " + describe(c), 2
+select c, "Extraction aborted for " + describe(c), 2
diff --git a/cpp/ql/src/Diagnostics/FailedExtractions.ql b/cpp/ql/src/Diagnostics/FailedExtractions.ql
index 6a3d7daae4c..52b07ca2559 100644
--- a/cpp/ql/src/Diagnostics/FailedExtractions.ql
+++ b/cpp/ql/src/Diagnostics/FailedExtractions.ql
@@ -1,6 +1,6 @@
/**
* @name Failed extractions
- * @description Gives the command-line of compilations for which extraction did not run to completion.
+ * @description List all files in the source code directory with extraction errors.
* @kind diagnostic
* @id cpp/diagnostics/failed-extractions
*/
@@ -12,5 +12,5 @@ from ExtractionError error
where
error instanceof ExtractionUnknownError or
exists(error.getFile().getRelativePath())
-select error, "Extracting file $@ failed with $@ (at $@)", error.getFile(), error.getErrorMessage(),
- error.getLocation(), error.getSeverity()
+select error, "Extracting failed in " + error.getFile() + " with error " + error.getErrorMessage(),
+ error.getSeverity()
diff --git a/cpp/ql/src/Diagnostics/FailedExtractions.qll b/cpp/ql/src/Diagnostics/FailedExtractions.qll
index 5fd371204ec..36bf264aa90 100644
--- a/cpp/ql/src/Diagnostics/FailedExtractions.qll
+++ b/cpp/ql/src/Diagnostics/FailedExtractions.qll
@@ -1,7 +1,24 @@
import cpp
+/*
+ * A note about how the C/C++ extractor emits diagnostics:
+ * When the extractor frontend encounters an error, it emits a diagnostic message,
+ * that includes a message, location and severity.
+ * However, that process is best-effort and may fail (e.g. due to lack of memory).
+ * Thus, if the extractor emitted at least one diagnostic of severity discretionary
+ * error (or higher), it *also* emits a simple "There was an error during this compilation"
+ * error diagnostic, without location information.
+ * In the common case, this means that a file with one (or more) errors also gets
+ * the catch-all diagnostic.
+ * This diagnostic has the empty string as file path.
+ * We filter out these useless diagnostics if there is at least one error-level diagnostic
+ * for the affected compilation in the database.
+ * Otherwise, we show it to, to indicate that something went wrong, and we
+ * don't know what exactly happened.
+ */
+
/**
- * The class of errors upon we mark a file as non-successfully extracted.
+ * An error that, if present, leads to a file being marked as non-successfully extracted.
*/
class ReportableError extends Diagnostic {
ReportableError() {
@@ -10,13 +27,7 @@ class ReportableError extends Diagnostic {
this instanceof CompilerError or
this instanceof CompilerCatastrophe
) and
- // If the extractor encounters an error in a compilation, it always emits a
- // catch-all diagnostic "There was an error during this compilation", to ensure
- // that the error makes it to the database.
- // This error doesn't have a file path attached to it, and is thus
- // useless for us to report. Furthermore, in the common case, we will have a
- // proper diagnostic for this error we can show.
- // Instead, we synthesize `TUnknownError` if this is the only error that we can show to the user.
+ // Filter for the catch-all diagnostic, see note above.
not this.getFile().getAbsolutePath() = ""
}
}
@@ -26,8 +37,11 @@ private newtype TExtractionError =
TCompilationFailed(Compilation c, File f) {
f = c.getAFileCompiled() and not c.normalTermination()
} or
- // Report generic extractor errors only if we haven't seen any other error-level diagnostic
- TUnknownError(CompilerError err) { not exists(ReportableError e) }
+ // Show the catch-all diagnostic (see note above) only if we haven't seen any other error-level diagnostic
+ // for that compilation
+ TUnknownError(CompilerError err) {
+ not exists(ReportableError e | e.getCompilation() = err.getCompilation())
+ }
/**
* Superclass for the extraction error hierarchy.
@@ -53,26 +67,26 @@ class ExtractionError extends TExtractionError {
}
/**
- * An irrecoverable extraction failure, where extraction was unable to finish.
+ * An unrecoverable extraction error, where extraction was unable to finish.
* This can be caused by a multitude of reasons, for example:
* - hitting a frontend assertion
* - crashing due to dereferencing an invalid pointer
* - stack overflow
* - out of memory
*/
-class ExtractionIrrecoverableError extends ExtractionError, TCompilationFailed {
+class ExtractionUnrecoverableError extends ExtractionError, TCompilationFailed {
Compilation c;
File f;
- ExtractionIrrecoverableError() { this = TCompilationFailed(c, f) }
+ ExtractionUnrecoverableError() { this = TCompilationFailed(c, f) }
override string toString() {
- result = "Irrecoverable extraction error while compiling " + f.toString()
+ result = "Unrecoverable extraction error while compiling " + f.toString()
}
override string getErrorMessage() {
result =
- "Irrecoverable compilation failure, check logs/build-tracer.log in the database directory for more information."
+ "Unrecoverable compilation failure; check logs/build-tracer.log in the database directory for more information."
}
override File getFile() { result = f }
diff --git a/cpp/ql/src/Diagnostics/SuccessfulExtractions.ql b/cpp/ql/src/Diagnostics/SuccessfulExtractions.ql
index 4331b768934..380f1ce8bbe 100644
--- a/cpp/ql/src/Diagnostics/SuccessfulExtractions.ql
+++ b/cpp/ql/src/Diagnostics/SuccessfulExtractions.ql
@@ -1,6 +1,6 @@
/**
- * @name Successfully extracted files.
- * @description Lists all files in the database that were extracted without encountering an error.
+ * @name Successfully extracted files
+ * @description Lists all files in the source code directory that were extracted without encountering an error.
* @kind diagnostic
* @id cpp/diagnostics/successfully-extracted-files
*/
diff --git a/cpp/ql/src/semmle/code/cpp/Diagnostics.qll b/cpp/ql/src/semmle/code/cpp/Diagnostics.qll
index 79074fa8657..9ad38d4e4be 100644
--- a/cpp/ql/src/semmle/code/cpp/Diagnostics.qll
+++ b/cpp/ql/src/semmle/code/cpp/Diagnostics.qll
@@ -6,6 +6,9 @@ import semmle.code.cpp.Location
/** A compiler-generated error, warning or remark. */
class Diagnostic extends Locatable, @diagnostic {
+ /** Gets the compilation that generated this diagnostic. */
+ Compilation getCompilation() { diagnostic_for(underlyingElement(this), result, _, _) }
+
/**
* Gets the severity of the message, on a range from 1 to 5: 1=remark,
* 2=warning, 3=discretionary error, 4=error, 5=catastrophic error.
From 135a6713e83e9c7d099d0369aea952988c8b5ec1 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Wed, 17 Mar 2021 14:42:50 +0100
Subject: [PATCH 149/725] Python, doc: References to section on API graphs.
---
.../codeql-language-guides/analyzing-data-flow-in-python.rst | 2 ++
docs/codeql/codeql-language-guides/codeql-for-python.rst | 2 ++
.../codeql-language-guides/codeql-library-for-python.rst | 5 +----
3 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/docs/codeql/codeql-language-guides/analyzing-data-flow-in-python.rst b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-python.rst
index 068e925534e..6e35c78a5a4 100644
--- a/docs/codeql/codeql-language-guides/analyzing-data-flow-in-python.rst
+++ b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-python.rst
@@ -99,6 +99,8 @@ Python has builtin functionality for reading and writing files, such as the func
➤ `See this in the query console on LGTM.com `__. Two of the demo projects make use of this low-level API.
+Notice the use of the ``API`` module for referring to library functions. It is further described in ":doc:`Using API graphs in Python `."
+
Unfortunately this will only give the expression in the argument, not the values which could be passed to it. So we use local data flow to find all expressions that flow into the argument:
.. code-block:: ql
diff --git a/docs/codeql/codeql-language-guides/codeql-for-python.rst b/docs/codeql/codeql-language-guides/codeql-for-python.rst
index 3504b1fb2c3..0d4fe4d17b3 100644
--- a/docs/codeql/codeql-language-guides/codeql-for-python.rst
+++ b/docs/codeql/codeql-language-guides/codeql-for-python.rst
@@ -21,6 +21,8 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
- :doc:`Analyzing data flow in Python `: You can use CodeQL to track the flow of data through a Python program to places where the data is used.
+- :doc:`Using API graphs in Python `: API graphs are a uniform interface for referring to functions, classes, and methods defined in external libraries.
+
- :doc:`Functions in Python `: You can use syntactic classes from the standard CodeQL library to find Python functions and identify calls to them.
- :doc:`Expressions and statements in Python `: You can use syntactic classes from the CodeQL library to explore how Python expressions and statements are used in a codebase.
diff --git a/docs/codeql/codeql-language-guides/codeql-library-for-python.rst b/docs/codeql/codeql-language-guides/codeql-library-for-python.rst
index 3f546dad1d0..ed5cf8b1deb 100644
--- a/docs/codeql/codeql-language-guides/codeql-library-for-python.rst
+++ b/docs/codeql/codeql-language-guides/codeql-library-for-python.rst
@@ -23,10 +23,7 @@ The CodeQL library for Python incorporates a large number of classes. Each class
- **Data flow** - classes that represent entities from the data flow graphs.
- **API graphs** - classes that represent entities from the API graphs.
-The first two categories are described below. See ":doc:`Analyzing data flow in Python `" for a description of data flow and associated classes.
-
-..
- and [TO COME IN FUTURE PR] for a description of API graphs and their use.
+The first two categories are described below. See ":doc:`Analyzing data flow in Python `" for a description of data flow and associated classes and ":doc:`Using API graphs in Python `" for a description of API graphs and their use.
Syntactic classes
-----------------
From f04ac87091e2f069e0769cf705b3ec45202b31df Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Wed, 17 Mar 2021 15:04:07 +0100
Subject: [PATCH 150/725] Python, doc: Include new section in toc
---
docs/codeql/codeql-language-guides/codeql-for-python.rst | 1 +
1 file changed, 1 insertion(+)
diff --git a/docs/codeql/codeql-language-guides/codeql-for-python.rst b/docs/codeql/codeql-language-guides/codeql-for-python.rst
index 0d4fe4d17b3..15b0c4a84f8 100644
--- a/docs/codeql/codeql-language-guides/codeql-for-python.rst
+++ b/docs/codeql/codeql-language-guides/codeql-for-python.rst
@@ -11,6 +11,7 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
basic-query-for-python-code
codeql-library-for-python
analyzing-data-flow-in-python
+ using-api-graphs-in-python
functions-in-python
expressions-and-statements-in-python
analyzing-control-flow-in-python
From 4d856d4461179f2e22f0655344d95d915453b443 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Fri, 12 Mar 2021 18:04:13 +0100
Subject: [PATCH 151/725] Python: Add small api enhancements determined useful
during documentation work.
---
.../2021-03-12-small-api-enhancements.md | 5 +
.../dataflow/new/internal/DataFlowPublic.qll | 108 ++-------------
.../dataflow/new/internal/LocalSources.qll | 131 ++++++++++++++++++
3 files changed, 146 insertions(+), 98 deletions(-)
create mode 100644 python/change-notes/2021-03-12-small-api-enhancements.md
create mode 100644 python/ql/src/semmle/python/dataflow/new/internal/LocalSources.qll
diff --git a/python/change-notes/2021-03-12-small-api-enhancements.md b/python/change-notes/2021-03-12-small-api-enhancements.md
new file mode 100644
index 00000000000..f75a91e548a
--- /dev/null
+++ b/python/change-notes/2021-03-12-small-api-enhancements.md
@@ -0,0 +1,5 @@
+lgtm,codescanning
+* Make `ParameterNode` extend `LocalSourceNode`, thus making members like `flowsTo` available.
+* Add member predicate `taintFlowsTo` to `LocalSourceNode` facilitating smoother syntax for local taint tracking.
+* Add member `getALocalTaintSource` to `DataFlow::Node` facilitating smoother syntax for local taint tracking.
+* Add predicate `parameterNode` to map from a `Parameter` to a data-flow node.
diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPublic.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPublic.qll
index 7b75fd70f2a..aa69f829b9f 100644
--- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPublic.qll
+++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPublic.qll
@@ -6,6 +6,7 @@ private import python
private import DataFlowPrivate
import semmle.python.dataflow.new.TypeTracker
import Attributes
+import LocalSources
private import semmle.python.essa.SsaCompute
/**
@@ -138,6 +139,11 @@ class Node extends TNode {
* Gets a local source node from which data may flow to this node in zero or more local steps.
*/
LocalSourceNode getALocalSource() { result.flowsTo(this) }
+
+ /**
+ * Gets a local source node from which data may flow to this node in zero or more local steps.
+ */
+ LocalSourceNode getALocalTaintSource() { result.taintFlowsTo(this) }
}
/** A data-flow node corresponding to an SSA variable. */
@@ -215,7 +221,7 @@ ExprNode exprNode(DataFlowExpr e) { result.getNode().getNode() = e }
* The value of a parameter at function entry, viewed as a node in a data
* flow graph.
*/
-class ParameterNode extends CfgNode {
+class ParameterNode extends CfgNode, LocalSourceNode {
ParameterDefinition def;
ParameterNode() {
@@ -237,6 +243,9 @@ class ParameterNode extends CfgNode {
Parameter getParameter() { result = def.getParameter() }
}
+/** Gets a node corresponding to parameter `p`. */
+ParameterNode parameterNode(Parameter p) { result.getParameter() = p }
+
/** A data flow node that represents a call argument. */
class ArgumentNode extends Node {
ArgumentNode() { this = any(DataFlowCall c).getArg(_) }
@@ -467,103 +476,6 @@ class BarrierGuard extends GuardNode {
}
}
-/**
- * A data flow node that is a source of local flow. This includes things like
- * - Expressions
- * - Function parameters
- */
-class LocalSourceNode extends Node {
- LocalSourceNode() {
- not simpleLocalFlowStep+(any(CfgNode n), this) and
- not this instanceof ModuleVariableNode
- or
- this = any(ModuleVariableNode mvn).getARead()
- }
-
- /** Holds if this `LocalSourceNode` can flow to `nodeTo` in one or more local flow steps. */
- pragma[inline]
- predicate flowsTo(Node nodeTo) { Cached::hasLocalSource(nodeTo, this) }
-
- /**
- * Gets a reference (read or write) of attribute `attrName` on this node.
- */
- AttrRef getAnAttributeReference(string attrName) { Cached::namedAttrRef(this, attrName, result) }
-
- /**
- * Gets a read of attribute `attrName` on this node.
- */
- AttrRead getAnAttributeRead(string attrName) { result = getAnAttributeReference(attrName) }
-
- /**
- * Gets a reference (read or write) of any attribute on this node.
- */
- AttrRef getAnAttributeReference() {
- Cached::namedAttrRef(this, _, result)
- or
- Cached::dynamicAttrRef(this, result)
- }
-
- /**
- * Gets a read of any attribute on this node.
- */
- AttrRead getAnAttributeRead() { result = getAnAttributeReference() }
-
- /**
- * Gets a call to this node.
- */
- CallCfgNode getACall() { Cached::call(this, result) }
-}
-
-cached
-private module Cached {
- /**
- * Holds if `source` is a `LocalSourceNode` that can reach `sink` via local flow steps.
- *
- * The slightly backwards parametering ordering is to force correct indexing.
- */
- cached
- predicate hasLocalSource(Node sink, Node source) {
- // Declaring `source` to be a `SourceNode` currently causes a redundant check in the
- // recursive case, so instead we check it explicitly here.
- source = sink and
- source instanceof LocalSourceNode
- or
- exists(Node mid |
- hasLocalSource(mid, source) and
- simpleLocalFlowStep(mid, sink)
- )
- }
-
- /**
- * Holds if `base` flows to the base of `ref` and `ref` has attribute name `attr`.
- */
- cached
- predicate namedAttrRef(LocalSourceNode base, string attr, AttrRef ref) {
- base.flowsTo(ref.getObject()) and
- ref.getAttributeName() = attr
- }
-
- /**
- * Holds if `base` flows to the base of `ref` and `ref` has no known attribute name.
- */
- cached
- predicate dynamicAttrRef(LocalSourceNode base, AttrRef ref) {
- base.flowsTo(ref.getObject()) and
- not exists(ref.getAttributeName())
- }
-
- /**
- * Holds if `func` flows to the callee of `call`.
- */
- cached
- predicate call(LocalSourceNode func, CallCfgNode call) {
- exists(CfgNode n |
- func.flowsTo(n) and
- n = call.getFunction()
- )
- }
-}
-
/**
* Algebraic datatype for tracking data content associated with values.
* Content can be collection elements or object attributes.
diff --git a/python/ql/src/semmle/python/dataflow/new/internal/LocalSources.qll b/python/ql/src/semmle/python/dataflow/new/internal/LocalSources.qll
new file mode 100644
index 00000000000..a84fed4824a
--- /dev/null
+++ b/python/ql/src/semmle/python/dataflow/new/internal/LocalSources.qll
@@ -0,0 +1,131 @@
+/**
+ * Provides support for intra-procedural tracking of a customizable
+ * set of data flow nodes.
+ *
+ * Note that unlike `TypeTracker.qll`, this library only performs
+ * local tracking within a function.
+ */
+
+import python
+import DataFlowPublic
+private import DataFlowPrivate
+private import TaintTrackingPublic
+
+/**
+ * A data flow node that is a source of local flow. This includes things like
+ * - Expressions
+ * - Function parameters
+ */
+class LocalSourceNode extends Node {
+ LocalSourceNode() {
+ not simpleLocalFlowStep+(any(CfgNode n), this) and
+ not this instanceof ModuleVariableNode
+ or
+ this = any(ModuleVariableNode mvn).getARead()
+ }
+
+ /** Holds if this `LocalSourceNode` can flow to `nodeTo` in one or more local flow steps. */
+ pragma[inline]
+ predicate flowsTo(Node nodeTo) { Cached::hasLocalSource(nodeTo, this) }
+
+ /** Holds if this `LocalSourceNode` can flow to `nodeTo` in one or more local taint steps. */
+ pragma[inline]
+ predicate taintFlowsTo(Node nodeTo) { Cached::hasLocalTaintSource(nodeTo, this) }
+
+ /**
+ * Gets a reference (read or write) of attribute `attrName` on this node.
+ */
+ AttrRef getAnAttributeReference(string attrName) { Cached::namedAttrRef(this, attrName, result) }
+
+ /**
+ * Gets a read of attribute `attrName` on this node.
+ */
+ AttrRead getAnAttributeRead(string attrName) { result = getAnAttributeReference(attrName) }
+
+ /**
+ * Gets a reference (read or write) of any attribute on this node.
+ */
+ AttrRef getAnAttributeReference() {
+ Cached::namedAttrRef(this, _, result)
+ or
+ Cached::dynamicAttrRef(this, result)
+ }
+
+ /**
+ * Gets a read of any attribute on this node.
+ */
+ AttrRead getAnAttributeRead() { result = getAnAttributeReference() }
+
+ /**
+ * Gets a call to this node.
+ */
+ CallCfgNode getACall() { Cached::call(this, result) }
+}
+
+cached
+private module Cached {
+ /**
+ * Holds if `source` is a `LocalSourceNode` that can reach `sink` via local flow steps.
+ *
+ * The slightly backwards parametering ordering is to force correct indexing.
+ */
+ cached
+ predicate hasLocalSource(Node sink, Node source) {
+ // Declaring `source` to be a `SourceNode` currently causes a redundant check in the
+ // recursive case, so instead we check it explicitly here.
+ source = sink and
+ source instanceof LocalSourceNode
+ or
+ exists(Node mid |
+ hasLocalSource(mid, source) and
+ simpleLocalFlowStep(mid, sink)
+ )
+ }
+
+ /**
+ * Holds if `source` is a `LocalSourceNode` that can reach `sink` via local taint steps.
+ *
+ * The slightly backwards parametering ordering is to force correct indexing.
+ */
+ cached
+ predicate hasLocalTaintSource(Node sink, Node source) {
+ // Declaring `source` to be a `SourceNode` currently causes a redundant check in the
+ // recursive case, so instead we check it explicitly here.
+ source = sink and
+ source instanceof LocalSourceNode
+ or
+ exists(Node mid |
+ hasLocalTaintSource(mid, source) and
+ localTaintStep(mid, sink)
+ )
+ }
+
+ /**
+ * Holds if `base` flows to the base of `ref` and `ref` has attribute name `attr`.
+ */
+ cached
+ predicate namedAttrRef(LocalSourceNode base, string attr, AttrRef ref) {
+ base.flowsTo(ref.getObject()) and
+ ref.getAttributeName() = attr
+ }
+
+ /**
+ * Holds if `base` flows to the base of `ref` and `ref` has no known attribute name.
+ */
+ cached
+ predicate dynamicAttrRef(LocalSourceNode base, AttrRef ref) {
+ base.flowsTo(ref.getObject()) and
+ not exists(ref.getAttributeName())
+ }
+
+ /**
+ * Holds if `func` flows to the callee of `call`.
+ */
+ cached
+ predicate call(LocalSourceNode func, CallCfgNode call) {
+ exists(CfgNode n |
+ func.flowsTo(n) and
+ n = call.getFunction()
+ )
+ }
+}
From 63b732ce1f78b2639b796b2ccc12bffae03bef23 Mon Sep 17 00:00:00 2001
From: yoff
Date: Fri, 12 Mar 2021 18:36:20 +0100
Subject: [PATCH 152/725] Apply suggestions from code review
Co-authored-by: Taus
---
.../semmle/python/dataflow/new/internal/DataFlowPublic.qll | 2 +-
.../src/semmle/python/dataflow/new/internal/LocalSources.qll | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPublic.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPublic.qll
index aa69f829b9f..aac5f501263 100644
--- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPublic.qll
+++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPublic.qll
@@ -141,7 +141,7 @@ class Node extends TNode {
LocalSourceNode getALocalSource() { result.flowsTo(this) }
/**
- * Gets a local source node from which data may flow to this node in zero or more local steps.
+ * Gets a local source node from which data may flow to this node in zero or more local taint-flow steps.
*/
LocalSourceNode getALocalTaintSource() { result.taintFlowsTo(this) }
}
diff --git a/python/ql/src/semmle/python/dataflow/new/internal/LocalSources.qll b/python/ql/src/semmle/python/dataflow/new/internal/LocalSources.qll
index a84fed4824a..26d731061b4 100644
--- a/python/ql/src/semmle/python/dataflow/new/internal/LocalSources.qll
+++ b/python/ql/src/semmle/python/dataflow/new/internal/LocalSources.qll
@@ -71,7 +71,7 @@ private module Cached {
*/
cached
predicate hasLocalSource(Node sink, Node source) {
- // Declaring `source` to be a `SourceNode` currently causes a redundant check in the
+ // Declaring `source` to be a `LocalSourceNode` currently causes a redundant check in the
// recursive case, so instead we check it explicitly here.
source = sink and
source instanceof LocalSourceNode
@@ -89,7 +89,7 @@ private module Cached {
*/
cached
predicate hasLocalTaintSource(Node sink, Node source) {
- // Declaring `source` to be a `SourceNode` currently causes a redundant check in the
+ // Declaring `source` to be a `LocalSourceNode` currently causes a redundant check in the
// recursive case, so instead we check it explicitly here.
source = sink and
source instanceof LocalSourceNode
From 8f467003d257dd018c0d3034ff077e90ed7f68dc Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Fri, 12 Mar 2021 18:45:09 +0100
Subject: [PATCH 153/725] Python: More review suggestions
---
python/change-notes/2021-03-12-small-api-enhancements.md | 7 +++----
.../semmle/python/dataflow/new/internal/DataFlowPublic.qll | 2 +-
2 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/python/change-notes/2021-03-12-small-api-enhancements.md b/python/change-notes/2021-03-12-small-api-enhancements.md
index f75a91e548a..ce7421cde7c 100644
--- a/python/change-notes/2021-03-12-small-api-enhancements.md
+++ b/python/change-notes/2021-03-12-small-api-enhancements.md
@@ -1,5 +1,4 @@
lgtm,codescanning
-* Make `ParameterNode` extend `LocalSourceNode`, thus making members like `flowsTo` available.
-* Add member predicate `taintFlowsTo` to `LocalSourceNode` facilitating smoother syntax for local taint tracking.
-* Add member `getALocalTaintSource` to `DataFlow::Node` facilitating smoother syntax for local taint tracking.
-* Add predicate `parameterNode` to map from a `Parameter` to a data-flow node.
+* The class ParameterNode now extends LocalSourceNode, thus making methods like flowsTo available.
+* Local taint tracking can now be performed using the `taintFlowsTo` method on the `LocalSourceNode` class. Conversely, the new member predicate `getALocalTaintSource` can be called on a `DataFlow::Node` to obtain a `LocalSourceNode` from which taint can be tracked locally to that data-flow node.
+* The new predicate `parameterNode` can now be used to map from a `Parameter` to a data-flow node.
diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPublic.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPublic.qll
index aac5f501263..e802373a527 100644
--- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPublic.qll
+++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPublic.qll
@@ -136,7 +136,7 @@ class Node extends TNode {
LocalSourceNode backtrack(TypeBackTracker t2, TypeBackTracker t) { t2 = t.step(result, this) }
/**
- * Gets a local source node from which data may flow to this node in zero or more local steps.
+ * Gets a local source node from which data may flow to this node in zero or more local data-flow steps.
*/
LocalSourceNode getALocalSource() { result.flowsTo(this) }
From b3ff3f7ee7f0838f43c29417033c89322b77dcb2 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Fri, 12 Mar 2021 18:59:49 +0100
Subject: [PATCH 154/725] =?UTF-8?q?Python=C3=86=20adjust=20test=20expectat?=
=?UTF-8?q?ions=20I=20suspect=20it=20has=20to=20do=20with=20ParameterNode?=
=?UTF-8?q?=20being=20a=20LocalSourceNode,=20but=20I=20really=20have=20no?=
=?UTF-8?q?=20idea...?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../ql/test/2/library-tests/locations/general/Locations.expected | 1 +
.../ql/test/3/library-tests/locations/general/Locations.expected | 1 +
2 files changed, 2 insertions(+)
diff --git a/python/ql/test/2/library-tests/locations/general/Locations.expected b/python/ql/test/2/library-tests/locations/general/Locations.expected
index 3c74898d80f..c6473cb2cc2 100644
--- a/python/ql/test/2/library-tests/locations/general/Locations.expected
+++ b/python/ql/test/2/library-tests/locations/general/Locations.expected
@@ -55,6 +55,7 @@
| Dict | 46 | 54 | 46 | 55 |
| Dict | 48 | 9 | 48 | 19 |
| DictUnpacking | 46 | 52 | 46 | 55 |
+| DjangoViewClassHelper | 4 | 1 | 4 | 8 |
| Ellipsis | 7 | 7 | 7 | 9 |
| Ellipsis | 50 | 14 | 50 | 16 |
| ExceptStmt | 32 | 9 | 32 | 31 |
diff --git a/python/ql/test/3/library-tests/locations/general/Locations.expected b/python/ql/test/3/library-tests/locations/general/Locations.expected
index 8da184f747e..70217f5117a 100644
--- a/python/ql/test/3/library-tests/locations/general/Locations.expected
+++ b/python/ql/test/3/library-tests/locations/general/Locations.expected
@@ -44,6 +44,7 @@
| Dict | 46 | 54 | 46 | 55 |
| Dict | 48 | 9 | 48 | 19 |
| DictUnpacking | 46 | 52 | 46 | 55 |
+| DjangoViewClassHelper | 4 | 1 | 4 | 8 |
| Ellipsis | 7 | 7 | 7 | 9 |
| Ellipsis | 50 | 14 | 50 | 16 |
| ExceptStmt | 32 | 9 | 32 | 31 |
From bf5259096e291c3e254a2790a103e4a12b6149ed Mon Sep 17 00:00:00 2001
From: Asger Feldthaus
Date: Wed, 17 Mar 2021 14:34:30 +0000
Subject: [PATCH 155/725] JS: Update cheat sheet
---
.../data-flow-cheat-sheet-for-javascript.rst | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst b/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst
index 92eb35d4d2e..09b821ad9e7 100644
--- a/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst
+++ b/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst
@@ -63,7 +63,7 @@ Classes and member predicates in the ``DataFlow::`` module:
- `getAnInstantiation `__ -- find ``new``-calls with this as the callee
- `getAnInvocation `__ -- find calls or ``new``-calls with this as the callee
- `getAMethodCall `__ -- find method calls with this as the receiver
- - `getAMemberCall `__ -- find calls with a member of this as the receiver
+ - `getAMemberCall `__ -- find calls with a member of this as the callee
- `getAPropertyRead `__ -- find property reads with this as the base
- `getAPropertyWrite `__ -- find property writes with this as the base
- `getAPropertySource `__ -- find nodes flowing into a property of this node
@@ -109,12 +109,14 @@ StringOps module
- StringOps::`StartsWith `__ -- check if a string starts with something
- StringOps::`EndsWith `__ -- check if a string ends with something
- StringOps::`Includes `__ -- check if a string contains something
+- StringOps::`RegExpTest `__ -- check if a string matches a RegExp
Utility
--------
- `ExtendCall `__ -- call that copies properties from one object to another
- `JsonParserCall `__ -- call that deserializes a JSON string
+- `JsonStringifyCall `__ -- call that serializes a JSON string
- `PropertyProjection `__ -- call that extracts nested properties by name
System and Network
From 4f635841796d98b03b1803c486e14420dd612f4d Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 17 Mar 2021 15:55:58 +0100
Subject: [PATCH 156/725] Docs: Highlight that Configuration is not
DataFlow::Configuration
I made that mistake when just reading it over (DOH). I think that calling it
MyConfiguration makes it a bit more clear that this is a configuration class you
wrote yourself :D
---
docs/codeql/writing-codeql-queries/creating-path-queries.rst | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/codeql/writing-codeql-queries/creating-path-queries.rst b/docs/codeql/writing-codeql-queries/creating-path-queries.rst
index 11d72b8baf1..836e7d19585 100644
--- a/docs/codeql/writing-codeql-queries/creating-path-queries.rst
+++ b/docs/codeql/writing-codeql-queries/creating-path-queries.rst
@@ -58,7 +58,7 @@ You should use the following template:
import DataFlow::PathGraph
...
- from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink
+ from MyConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, ""
@@ -66,7 +66,7 @@ Where:
- ``DataFlow::Pathgraph`` is the path graph module you need to import from the standard CodeQL libraries.
- ``source`` and ``sink`` are nodes on the `path graph `__, and ``DataFlow::PathNode`` is their type.
-- ``Configuration`` is a class containing the predicates which define how data may flow between the ``source`` and the ``sink``.
+- ``MyConfiguration`` is a class containing the predicates which define how data may flow between the ``source`` and the ``sink``.
The following sections describe the main requirements for a valid path query.
From d426f1efaf37aa44ab1ea2dfacaa23da9d8005ff Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 17 Mar 2021 16:01:20 +0100
Subject: [PATCH 157/725] Docs: Highlight need for explicit import of DataFlow
lib
at least in some langauges
---
docs/codeql/writing-codeql-queries/creating-path-queries.rst | 2 ++
1 file changed, 2 insertions(+)
diff --git a/docs/codeql/writing-codeql-queries/creating-path-queries.rst b/docs/codeql/writing-codeql-queries/creating-path-queries.rst
index 836e7d19585..60723f488e1 100644
--- a/docs/codeql/writing-codeql-queries/creating-path-queries.rst
+++ b/docs/codeql/writing-codeql-queries/creating-path-queries.rst
@@ -55,6 +55,8 @@ You should use the following template:
*/
import
+ // For some languages (Java/C++/Python) you need to explicitly import the data flow library, such as
+ // import semmle.code.java.dataflow.DataFlow
import DataFlow::PathGraph
...
From 3995ff322d6426aceacc48e15b27338f5990fb9d Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 17 Mar 2021 13:08:42 +0100
Subject: [PATCH 158/725] add models for `koa-route` and `koa-router`
---
.../src/semmle/javascript/frameworks/Koa.qll | 126 ++++++++++++++++--
.../Security/CWE-918/RequestForgery.expected | 27 ++++
.../test/query-tests/Security/CWE-918/tst.js | 20 ++-
3 files changed, 162 insertions(+), 11 deletions(-)
diff --git a/javascript/ql/src/semmle/javascript/frameworks/Koa.qll b/javascript/ql/src/semmle/javascript/frameworks/Koa.qll
index 325afacea10..112203cb26d 100644
--- a/javascript/ql/src/semmle/javascript/frameworks/Koa.qll
+++ b/javascript/ql/src/semmle/javascript/frameworks/Koa.qll
@@ -36,18 +36,11 @@ module Koa {
/**
* A Koa route handler.
*/
- class RouteHandler extends HTTP::Servers::StandardRouteHandler, DataFlow::ValueNode {
- Function function;
-
- RouteHandler() {
- function = astNode and
- any(RouteSetup setup).getARouteHandler() = this
- }
-
+ abstract class RouteHandler extends HTTP::Servers::StandardRouteHandler, DataFlow::SourceNode {
/**
* Gets the parameter of the route handler that contains the context object.
*/
- Parameter getContextParameter() { result = function.getParameter(0) }
+ Parameter getContextParameter() { result = getAFunctionValue().getFunction().getParameter(0) }
/**
* Gets an expression that contains the "context" object of
@@ -70,6 +63,35 @@ module Koa {
* object of a route handler invocation.
*/
Expr getARequestOrContextExpr() { result = getARequestExpr() or result = getAContextExpr() }
+
+ /**
+ * Gets a reference to a request parameter defined by this route handler.
+ */
+ DataFlow::Node getARequestParameterAccess() {
+ none() // overriden in subclasses.
+ }
+
+ /**
+ * Gets the dataflow node that is given to a `RouteSetup` to register the handler.
+ */
+ abstract DataFlow::SourceNode getRouteHandlerRegistration();
+ }
+
+ /**
+ * A koa route handler registered directly with a route-setup.
+ * Like:
+ * ```JavaScript
+ * var route = require('koa-route');
+ * var app = new Koa();
+ * app.use((context, next) => {
+ * ...
+ * });
+ * ```
+ */
+ private class StandardRouteHandler extends RouteHandler {
+ StandardRouteHandler() { any(RouteSetup setup).getARouteHandler() = this }
+
+ override DataFlow::SourceNode getRouteHandlerRegistration() { result = this }
}
/**
@@ -100,6 +122,77 @@ module Koa {
}
}
+ /**
+ * A Koa route handler registered using a routing library.
+ *
+ * Example of what that could look like:
+ * ```JavaScript
+ * const router = require('koa-router')();
+ * const Koa = require('koa');
+ * const app = new Koa();
+ * router.get('/', async (ctx, next) => {
+ * // route handler stuff
+ * });
+ * app.use(router.routes());
+ * ```
+ */
+ private class RoutedRouteHandler extends RouteHandler, DataFlow::SourceNode {
+ DataFlow::InvokeNode router;
+ DataFlow::MethodCallNode call;
+
+ RoutedRouteHandler() {
+ router = DataFlow::moduleImport(["@koa/router", "koa-router"]).getAnInvocation() and
+ call = router.getAMethodCall*() and
+ call.getMethodName() =
+ [
+ "use", "get", "post", "put", "link", "unlink", "delete", "del", "head", "options",
+ "patch", "all"
+ ] and
+ this.flowsTo(call.getArgument(any(int i | i >= 1)))
+ }
+
+ override DataFlow::SourceNode getRouteHandlerRegistration() {
+ result = call
+ or
+ result = router.getAMethodCall("routes")
+ }
+ }
+
+ /**
+ * A route handler registered using the `koa-route` library.
+ *
+ * Example of how `koa-route` can be used:
+ * ```JavaScript
+ * var route = require('koa-route');
+ * var Koa = require('koa');
+ * var app = new Koa();
+ *
+ * app.use(route.get('/pets', (context, param1, param2, param3, ...params) => {
+ * // route handler stuff
+ * }));
+ */
+ class KoaRouteHandler extends RouteHandler, DataFlow::SourceNode {
+ DataFlow::CallNode call;
+
+ KoaRouteHandler() {
+ call =
+ DataFlow::moduleMember("koa-route",
+ [
+ "all", "acl", "bind", "checkout", "connect", "copy", "delete", "del", "get", "head",
+ "link", "lock", "msearch", "merge", "mkactivity", "mkcalendar", "mkcol", "move",
+ "notify", "options", "patch", "post", "propfind", "proppatch", "purge", "put", "rebind",
+ "report", "search", "subscribe", "trace", "unbind", "unlink", "unlock", "unsubscribe"
+ ]).getACall() and
+ this.flowsTo(call.getArgument(1))
+ }
+
+ override DataFlow::Node getARequestParameterAccess() {
+ result = call.getABoundCallbackParameter(1, any(int i | i >= 1))
+ }
+
+ override DataFlow::SourceNode getRouteHandlerRegistration() { result = call }
+ }
+
/**
* A Koa request source, that is, an access to the `request` property
* of a context object.
@@ -189,6 +282,9 @@ module Koa {
kind = "parameter" and
this = getAQueryParameterAccess(rh)
or
+ kind = "parameter" and
+ this = rh.getARequestParameterAccess()
+ or
exists(Expr e | rh.getARequestOrContextExpr() = e |
// `ctx.request.url`, `ctx.request.originalUrl`, or `ctx.request.href`
exists(string propName |
@@ -202,6 +298,10 @@ module Koa {
propName = "href"
)
or
+ // params, when handler is registered by `koa-router` or similar.
+ kind = "parameter" and
+ this.asExpr().(PropAccess).accesses(e, "params")
+ or
// `ctx.request.body`
e instanceof RequestExpr and
kind = "body" and
@@ -285,7 +385,13 @@ module Koa {
getMethodName() = "use"
}
- override DataFlow::SourceNode getARouteHandler() { result.flowsToExpr(getArgument(0)) }
+ override DataFlow::SourceNode getARouteHandler() {
+ // `StandardRouteHandler` uses this predicate in it's charpred, so making this predicate return a `RouteHandler` would give an empty recursion.
+ result.flowsToExpr(getArgument(0))
+ or
+ // For the route-handlers that does not depend on this predicate in their charpred.
+ result.(RouteHandler).getRouteHandlerRegistration().flowsToExpr(getArgument(0))
+ }
override Expr getServer() { result = server }
}
diff --git a/javascript/ql/test/query-tests/Security/CWE-918/RequestForgery.expected b/javascript/ql/test/query-tests/Security/CWE-918/RequestForgery.expected
index 06a55b512a0..7d21bc4e033 100644
--- a/javascript/ql/test/query-tests/Security/CWE-918/RequestForgery.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-918/RequestForgery.expected
@@ -57,6 +57,18 @@ nodes
| tst.js:74:29:74:35 | req.url |
| tst.js:76:19:76:25 | tainted |
| tst.js:76:19:76:25 | tainted |
+| tst.js:83:38:83:43 | param1 |
+| tst.js:83:38:83:43 | param1 |
+| tst.js:84:19:84:24 | param1 |
+| tst.js:84:19:84:24 | param1 |
+| tst.js:91:19:91:28 | ctx.params |
+| tst.js:91:19:91:28 | ctx.params |
+| tst.js:91:19:91:32 | ctx.params.foo |
+| tst.js:91:19:91:32 | ctx.params.foo |
+| tst.js:93:19:93:28 | ctx.params |
+| tst.js:93:19:93:28 | ctx.params |
+| tst.js:93:19:93:32 | ctx.params.foo |
+| tst.js:93:19:93:32 | ctx.params.foo |
edges
| tst.js:14:9:14:52 | tainted | tst.js:18:13:18:19 | tainted |
| tst.js:14:9:14:52 | tainted | tst.js:18:13:18:19 | tainted |
@@ -113,6 +125,18 @@ edges
| tst.js:74:19:74:52 | url.par ... ery.url | tst.js:74:9:74:52 | tainted |
| tst.js:74:29:74:35 | req.url | tst.js:74:19:74:42 | url.par ... , true) |
| tst.js:74:29:74:35 | req.url | tst.js:74:19:74:42 | url.par ... , true) |
+| tst.js:83:38:83:43 | param1 | tst.js:84:19:84:24 | param1 |
+| tst.js:83:38:83:43 | param1 | tst.js:84:19:84:24 | param1 |
+| tst.js:83:38:83:43 | param1 | tst.js:84:19:84:24 | param1 |
+| tst.js:83:38:83:43 | param1 | tst.js:84:19:84:24 | param1 |
+| tst.js:91:19:91:28 | ctx.params | tst.js:91:19:91:32 | ctx.params.foo |
+| tst.js:91:19:91:28 | ctx.params | tst.js:91:19:91:32 | ctx.params.foo |
+| tst.js:91:19:91:28 | ctx.params | tst.js:91:19:91:32 | ctx.params.foo |
+| tst.js:91:19:91:28 | ctx.params | tst.js:91:19:91:32 | ctx.params.foo |
+| tst.js:93:19:93:28 | ctx.params | tst.js:93:19:93:32 | ctx.params.foo |
+| tst.js:93:19:93:28 | ctx.params | tst.js:93:19:93:32 | ctx.params.foo |
+| tst.js:93:19:93:28 | ctx.params | tst.js:93:19:93:32 | ctx.params.foo |
+| tst.js:93:19:93:28 | ctx.params | tst.js:93:19:93:32 | ctx.params.foo |
#select
| tst.js:18:5:18:20 | request(tainted) | tst.js:14:29:14:35 | req.url | tst.js:18:13:18:19 | tainted | The $@ of this request depends on $@. | tst.js:18:13:18:19 | tainted | URL | tst.js:14:29:14:35 | req.url | a user-provided value |
| tst.js:20:5:20:24 | request.get(tainted) | tst.js:14:29:14:35 | req.url | tst.js:20:17:20:23 | tainted | The $@ of this request depends on $@. | tst.js:20:17:20:23 | tainted | URL | tst.js:14:29:14:35 | req.url | a user-provided value |
@@ -130,3 +154,6 @@ edges
| tst.js:64:3:64:38 | client. ... inted}) | tst.js:58:29:58:35 | req.url | tst.js:64:30:64:36 | tainted | The $@ of this request depends on $@. | tst.js:64:30:64:36 | tainted | URL | tst.js:58:29:58:35 | req.url | a user-provided value |
| tst.js:68:3:68:38 | client. ... inted}) | tst.js:58:29:58:35 | req.url | tst.js:68:30:68:36 | tainted | The $@ of this request depends on $@. | tst.js:68:30:68:36 | tainted | URL | tst.js:58:29:58:35 | req.url | a user-provided value |
| tst.js:76:5:76:26 | JSDOM.f ... ainted) | tst.js:74:29:74:35 | req.url | tst.js:76:19:76:25 | tainted | The $@ of this request depends on $@. | tst.js:76:19:76:25 | tainted | URL | tst.js:74:29:74:35 | req.url | a user-provided value |
+| tst.js:84:5:84:25 | JSDOM.f ... param1) | tst.js:83:38:83:43 | param1 | tst.js:84:19:84:24 | param1 | The $@ of this request depends on $@. | tst.js:84:19:84:24 | param1 | URL | tst.js:83:38:83:43 | param1 | a user-provided value |
+| tst.js:91:5:91:33 | JSDOM.f ... ms.foo) | tst.js:91:19:91:28 | ctx.params | tst.js:91:19:91:32 | ctx.params.foo | The $@ of this request depends on $@. | tst.js:91:19:91:32 | ctx.params.foo | URL | tst.js:91:19:91:28 | ctx.params | a user-provided value |
+| tst.js:93:5:93:33 | JSDOM.f ... ms.foo) | tst.js:93:19:93:28 | ctx.params | tst.js:93:19:93:32 | ctx.params.foo | The $@ of this request depends on $@. | tst.js:93:19:93:32 | ctx.params.foo | URL | tst.js:93:19:93:28 | ctx.params | a user-provided value |
diff --git a/javascript/ql/test/query-tests/Security/CWE-918/tst.js b/javascript/ql/test/query-tests/Security/CWE-918/tst.js
index d5951f648b4..0cf66758fc0 100644
--- a/javascript/ql/test/query-tests/Security/CWE-918/tst.js
+++ b/javascript/ql/test/query-tests/Security/CWE-918/tst.js
@@ -74,4 +74,22 @@ var server = http.createServer(async function(req, res) {
var tainted = url.parse(req.url, true).query.url;
JSDOM.fromURL(tainted); // NOT OK
-});
\ No newline at end of file
+});
+
+var route = require('koa-route');
+var Koa = require('koa');
+var app = new Koa();
+
+app.use(route.get('/pets', (context, param1, param2, param3) => {
+ JSDOM.fromURL(param1); // NOT OK
+}));
+
+
+const router = require('koa-router')();
+const app = new Koa();
+router.get('/', async (ctx, next) => {
+ JSDOM.fromURL(ctx.params.foo); // NOT OK
+}).post('/', async (ctx, next) => {
+ JSDOM.fromURL(ctx.params.foo); // NOT OK
+});
+app.use(router.routes());
From b2d6982318c18dcf47f56972448e08202cb27271 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 17 Mar 2021 13:10:03 +0100
Subject: [PATCH 159/725] add change note
---
javascript/change-notes/2021-03-17-koa-route.md | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 javascript/change-notes/2021-03-17-koa-route.md
diff --git a/javascript/change-notes/2021-03-17-koa-route.md b/javascript/change-notes/2021-03-17-koa-route.md
new file mode 100644
index 00000000000..15b1d862c9a
--- /dev/null
+++ b/javascript/change-notes/2021-03-17-koa-route.md
@@ -0,0 +1,5 @@
+lgtm,codescanning
+* Route handlers registered using koa routing libraries are recognized as a source of remote user input.
+ Affected packages are
+ [koa-route](https://www.npmjs.com/package/koa-route), and
+ [koa-router](https://www.npmjs.com/package/koa-router)
From 40ec23cf13767dce637ab1e9ba715b221bb63b62 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 18 Mar 2021 10:47:38 +0100
Subject: [PATCH 160/725] refactor MkHasUnderlyingType to use Label::instance()
---
.../ql/src/semmle/javascript/ApiGraphs.qll | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/javascript/ql/src/semmle/javascript/ApiGraphs.qll b/javascript/ql/src/semmle/javascript/ApiGraphs.qll
index fd955cf00ab..a494a9e335e 100644
--- a/javascript/ql/src/semmle/javascript/ApiGraphs.qll
+++ b/javascript/ql/src/semmle/javascript/ApiGraphs.qll
@@ -318,7 +318,7 @@ module API {
result = Impl::MkCanonicalNameUse(tn).(Node).getInstance()
)
or
- result = Impl::MkHasUnderlyingType(moduleName, exportedName)
+ result = Impl::MkHasUnderlyingType(moduleName, exportedName).(Node).getInstance()
}
}
@@ -416,7 +416,7 @@ module API {
isUsed(n)
} or
/**
- * An instance of a TypeScript type, identified by name of the type-annotation.
+ * A TypeScript type, identified by name of the type-annotation.
* This API node is exclusively used by `API::Node::ofType`.
*/
MkHasUnderlyingType(string moduleName, string exportName) {
@@ -649,6 +649,12 @@ module API {
ref = getANodeWithType(tn)
)
or
+ exists(string moduleName, string exportName |
+ base = MkHasUnderlyingType(moduleName, exportName) and
+ lbl = Label::instance() and
+ ref.(DataFlow::SourceNode).hasUnderlyingType(moduleName, exportName)
+ )
+ or
exists(DataFlow::InvokeNode call |
base = MkSyntheticCallbackArg(_, _, call) and
lbl = Label::parameter(1) and
@@ -688,12 +694,6 @@ module API {
nd = MkUse(ref)
or
exists(CanonicalName n | nd = MkCanonicalNameUse(n) | ref.asExpr() = n.getAnAccess())
- or
- exists(string moduleName, string exportsName |
- nd = MkHasUnderlyingType(moduleName, exportsName)
- |
- ref.(DataFlow::SourceNode).hasUnderlyingType(moduleName, exportsName)
- )
}
/** Holds if module `m` exports `rhs`. */
From 8b931626ceafa7948d9c8d2ba3d18ea1e0d48897 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 18 Mar 2021 11:04:08 +0100
Subject: [PATCH 161/725] add edge from root type MkHasUnderlyingType
---
javascript/ql/src/semmle/javascript/ApiGraphs.qll | 8 ++++++++
javascript/ql/test/ApiGraphs/typed/NodeOfType.expected | 1 +
javascript/ql/test/ApiGraphs/typed/index.ts | 8 ++++++++
3 files changed, 17 insertions(+)
diff --git a/javascript/ql/src/semmle/javascript/ApiGraphs.qll b/javascript/ql/src/semmle/javascript/ApiGraphs.qll
index a494a9e335e..1ab8234cb78 100644
--- a/javascript/ql/src/semmle/javascript/ApiGraphs.qll
+++ b/javascript/ql/src/semmle/javascript/ApiGraphs.qll
@@ -386,6 +386,8 @@ module API {
imports(_, m)
or
m = any(CanonicalName n | isUsed(n)).getExternalModuleName()
+ or
+ any(TypeAnnotation n).hasQualifiedName(m, _)
} or
MkClassInstance(DataFlow::ClassNode cls) { cls = trackDefNode(_) and hasSemantics(cls) } or
MkAsyncFuncResult(DataFlow::FunctionNode f) {
@@ -902,6 +904,12 @@ module API {
succ in [mkCanonicalNameDef(cn2), mkCanonicalNameUse(cn2)]
)
or
+ exists(string moduleName, string exportName |
+ pred = MkModuleImport(moduleName) and
+ lbl = Label::member(exportName) and
+ succ = MkHasUnderlyingType(moduleName, exportName)
+ )
+ or
exists(DataFlow::Node nd, DataFlow::FunctionNode f |
pred = MkDef(nd) and
f = trackDefNode(nd) and
diff --git a/javascript/ql/test/ApiGraphs/typed/NodeOfType.expected b/javascript/ql/test/ApiGraphs/typed/NodeOfType.expected
index 4513f16cfc3..09fd4b89242 100644
--- a/javascript/ql/test/ApiGraphs/typed/NodeOfType.expected
+++ b/javascript/ql/test/ApiGraphs/typed/NodeOfType.expected
@@ -1,3 +1,4 @@
| mongodb | Collection | index.ts:14:3:14:17 | getCollection() |
| mongoose | Model | index.ts:22:3:22:20 | getMongooseModel() |
| mongoose | Query | index.ts:23:3:23:20 | getMongooseQuery() |
+| puppeteer | Browser | index.ts:30:22:30:33 | this.browser |
diff --git a/javascript/ql/test/ApiGraphs/typed/index.ts b/javascript/ql/test/ApiGraphs/typed/index.ts
index 8f1338d95c2..0b35d1582d9 100644
--- a/javascript/ql/test/ApiGraphs/typed/index.ts
+++ b/javascript/ql/test/ApiGraphs/typed/index.ts
@@ -22,3 +22,11 @@ app.post("/find", (req, res) => {
getMongooseModel().find({ id: v }); /* def (parameter 0 (member find (instance (member Model (member exports (module mongoose)))))) */
getMongooseQuery().find({ id: v }); /* def (parameter 0 (member find (instance (member Query (member exports (module mongoose)))))) */
});
+
+import * as puppeteer from 'puppeteer';
+class Renderer {
+ private browser: puppeteer.Browser;
+ foo(): void {
+ const page = this.browser.newPage(); /* use (instance (member Browser (member exports (module puppeteer)))) */
+ }
+}
\ No newline at end of file
From 45a1fc6a969f429859e8080afde1d1122394dc8d Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Thu, 18 Mar 2021 11:20:22 +0100
Subject: [PATCH 162/725] Python: Add link to better PyYAML docs
I found this randomly
---
python/ql/src/semmle/python/frameworks/Yaml.qll | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/python/ql/src/semmle/python/frameworks/Yaml.qll b/python/ql/src/semmle/python/frameworks/Yaml.qll
index 7e9d7f100e9..ec3a21379da 100644
--- a/python/ql/src/semmle/python/frameworks/Yaml.qll
+++ b/python/ql/src/semmle/python/frameworks/Yaml.qll
@@ -1,6 +1,10 @@
/**
- * Provides classes modeling security-relevant aspects of the PyYAML package
- * https://pyyaml.org/wiki/PyYAMLDocumentation (obtained via `import yaml`).
+ * Provides classes modeling security-relevant aspects of the PyYAML package (obtained
+ * via `import yaml`)
+ *
+ * See
+ * - https://pyyaml.org/wiki/PyYAMLDocumentation
+ * - https://pyyaml.docsforge.com/master/documentation/
*/
private import python
@@ -8,6 +12,14 @@ private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.Concepts
+/**
+ * Provides classes modeling security-relevant aspects of the PyYAML package (obtained
+ * via `import yaml`)
+ *
+ * See
+ * - https://pyyaml.org/wiki/PyYAMLDocumentation
+ * - https://pyyaml.docsforge.com/master/documentation/
+ */
private module Yaml {
/** Gets a reference to the `yaml` module. */
private DataFlow::Node yaml(DataFlow::TypeTracker t) {
From 14e9bda5de10ef5a1c6dd96692d083f4e0f16025 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Thu, 18 Mar 2021 11:35:17 +0100
Subject: [PATCH 163/725] Python: Refactor PyYAML tests a bit
---
.../library-tests/frameworks/yaml/Decoding.py | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/python/ql/test/experimental/library-tests/frameworks/yaml/Decoding.py b/python/ql/test/experimental/library-tests/frameworks/yaml/Decoding.py
index 816d91d9068..7d5bcde1503 100644
--- a/python/ql/test/experimental/library-tests/frameworks/yaml/Decoding.py
+++ b/python/ql/test/experimental/library-tests/frameworks/yaml/Decoding.py
@@ -1,15 +1,18 @@
import yaml
-from yaml import SafeLoader
+# Unsafe:
yaml.load(payload) # $decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput
-yaml.load(payload, SafeLoader) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML
-yaml.load(payload, Loader=SafeLoader) # $decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML
-yaml.load(payload, Loader=yaml.BaseLoader) # $decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML
-
-yaml.safe_load(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML
+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
+# 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
+
+# load_all variants
yaml.load_all(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput
yaml.safe_load_all(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML
yaml.unsafe_load_all(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput
From 17d7ba80496ac5c4abb29e44d0a4a8c106407c90 Mon Sep 17 00:00:00 2001
From: Porcuiney Hairs
Date: Tue, 2 Feb 2021 23:58:32 +0530
Subject: [PATCH 164/725] Add Log Injection Vulnerability
---
.../Security/CWE/CWE-117/LogInjection.qhelp | 49 ++++++++++++++
.../Security/CWE/CWE-117/LogInjection.ql | 67 +++++++++++++++++++
.../Security/CWE/CWE-117/LogInjectionBad.java | 24 +++++++
.../CWE/CWE-117/LogInjectionGood.java | 25 +++++++
.../Security/CWE/CWE-532/SensitiveInfoLog.ql | 31 ++-------
.../experimental/semmle/code/java/Logging.qll | 30 +++++++++
6 files changed, 199 insertions(+), 27 deletions(-)
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-117/LogInjection.qhelp
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-117/LogInjection.ql
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-117/LogInjectionBad.java
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-117/LogInjectionGood.java
create mode 100644 java/ql/src/experimental/semmle/code/java/Logging.qll
diff --git a/java/ql/src/experimental/Security/CWE/CWE-117/LogInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-117/LogInjection.qhelp
new file mode 100644
index 00000000000..4ad7b2d5380
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-117/LogInjection.qhelp
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+If unsanitized user input is written to a log entry, a malicious user may be able to forge new log entries.
+
+Forgery can occur if a user provides some input creating the appearance of multiple
+ log entries. This can include unescaped new-line characters, or HTML or other markup.
+
+
+
+
+User input should be suitably sanitized before it is logged.
+
+
+If the log entries are plain text then line breaks should be removed from user input, using for example
+String replace(char oldChar, char newChar) or similar. Care should also be taken that user input is clearly marked
+in log entries, and that a malicious user cannot cause confusion in other ways.
+
+
+For log entries that will be displayed in HTML, user input should be HTML encoded before being logged, to prevent forgery and
+other forms of HTML injection.
+
+
+
+
+
+ In the example, a username, provided by the user, is logged using logger.warn (from org.slf4j.Logger).
+ In the first case (/bad endpoint), the username is logged without any sanitization.
+ If a malicious user provides Guest'%0AUser:'Admin as a username parameter,
+ the log entry will be split into two separate lines, where the first line will be User:'Guest' and the second one will be User:'Admin'.
+
+
+
+ In the second case (/good endpoint), matches() is used to ensure the user input only has alphanumeric characters.
+ If a malicious user provides `Guest'%0AUser:'Admin` as a username parameter,
+ the log entry will not be split into two separate lines, resulting in a single line User:'Guest'User:'Admin'.
+
+
+
+
+
+OWASP: Log Injection.
+
+
diff --git a/java/ql/src/experimental/Security/CWE/CWE-117/LogInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-117/LogInjection.ql
new file mode 100644
index 00000000000..440c39b77e8
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-117/LogInjection.ql
@@ -0,0 +1,67 @@
+/**
+ * @name Log Injection
+ * @description Building log entries from user-controlled data is vulnerable to
+ * insertion of forged log entries by a malicious user.
+ * @kind path-problem
+ * @problem.severity error
+ * @precision high
+ * @id java/log-injection
+ * @tags security
+ * external/cwe/cwe-117
+ */
+
+import java
+import DataFlow::PathGraph
+import experimental.semmle.code.java.Logging
+import semmle.code.java.dataflow.FlowSources
+
+/**
+ * A taint-tracking configuration for tracking untrusted user input used in log entries.
+ */
+private class LogInjectionConfiguration extends TaintTracking::Configuration {
+ LogInjectionConfiguration() { this = "Log Injection" }
+
+ override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
+
+ override predicate isSink(DataFlow::Node sink) {
+ sink.asExpr() = any(LoggingCall c).getALogArgument()
+ }
+
+ override predicate isSanitizer(DataFlow::Node node) {
+ node.getType() instanceof BoxedType or node.getType() instanceof PrimitiveType
+ }
+
+ override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
+ guard instanceof StrCheckSanitizerGuard
+ }
+}
+
+/**
+ * Models any regex or equality check as a sanitizer guard.
+ * Assumes any check on the taint to be a valid sanitizing check.
+ */
+private class StrCheckSanitizerGuard extends DataFlow::BarrierGuard {
+ StrCheckSanitizerGuard() {
+ exists(Method m |
+ m.getDeclaringType().hasQualifiedName("java.util.regex", "Pattern") and
+ m.hasName("matches")
+ or
+ m.getDeclaringType() instanceof TypeString and
+ m.hasName([
+ "startsWith", "regionMatches", "matches", "equals", "equalsIgnoreCase", "endsWith",
+ "contentEquals", "contains"
+ ])
+ |
+ m.getAReference() = this
+ )
+ }
+
+ override predicate checks(Expr e, boolean branch) {
+ e = this.(MethodAccess).getQualifier() and branch = true
+ }
+}
+
+from LogInjectionConfiguration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
+where cfg.hasFlowPath(source, sink)
+select sink.getNode(), source, sink, "$@ flows to log entry.", source.getNode(),
+ "User-provided value"
diff --git a/java/ql/src/experimental/Security/CWE/CWE-117/LogInjectionBad.java b/java/ql/src/experimental/Security/CWE/CWE-117/LogInjectionBad.java
new file mode 100644
index 00000000000..620b9801631
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-117/LogInjectionBad.java
@@ -0,0 +1,24 @@
+package com.example.restservice;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class LogInjection {
+
+ private final Logger log = LoggerFactory.getLogger(LogInjection.class);
+
+ // /bad?username=Guest'%0AUser:'Admin
+ @GetMapping("/bad")
+ public String bad(@RequestParam(value = "username", defaultValue = "name") String username) {
+ log.warn("User:'{}'", username);
+ // The logging call above would result in multiple log entries as shown below:
+ // User:'Guest'
+ // User:'Admin'
+ return username;
+ }
+}
+
diff --git a/java/ql/src/experimental/Security/CWE/CWE-117/LogInjectionGood.java b/java/ql/src/experimental/Security/CWE/CWE-117/LogInjectionGood.java
new file mode 100644
index 00000000000..2ed683a2760
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-117/LogInjectionGood.java
@@ -0,0 +1,25 @@
+package com.example.restservice;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class LogInjection {
+
+ private final Logger log = LoggerFactory.getLogger(LogInjection.class);
+
+ // /good?username=Guest'%0AUser:'Admin
+ @GetMapping("/good")
+ public String good(@RequestParam(value = "username", defaultValue = "name") String username) {
+ // The regex check here, allows only alphanumeric characters to pass.
+ // Hence, does not result in log injection
+ if (username.matches("\w*")) {
+ log.warn("User:'{}'", username);
+
+ return username;
+ }
+ }
+}
diff --git a/java/ql/src/experimental/Security/CWE/CWE-532/SensitiveInfoLog.ql b/java/ql/src/experimental/Security/CWE/CWE-532/SensitiveInfoLog.ql
index 15690b7c32b..853bbb6bace 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-532/SensitiveInfoLog.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-532/SensitiveInfoLog.ql
@@ -10,6 +10,7 @@
import java
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.security.SensitiveActions
+import experimental.semmle.code.java.Logging
import DataFlow
import PathGraph
@@ -27,38 +28,14 @@ class CredentialExpr extends Expr {
}
}
-/** Class of popular logging utilities * */
-class LoggerType extends RefType {
- LoggerType() {
- this.hasQualifiedName("org.apache.log4j", "Category") or //Log4J
- this.hasQualifiedName("org.apache.logging.log4j", "Logger") or //Log4J 2
- this.hasQualifiedName("org.slf4j", "Logger") or //SLF4j and Gradle Logging
- this.hasQualifiedName("org.jboss.logging", "BasicLogger") or //JBoss Logging
- this.hasQualifiedName("org.jboss.logging", "Logger") or //JBoss Logging (`org.jboss.logging.Logger` in some implementations like JBoss Application Server 4.0.4 did not implement `BasicLogger`)
- this.hasQualifiedName("org.apache.commons.logging", "Log") or //Apache Commons Logging
- this.hasQualifiedName("org.scijava.log", "Logger") //SciJava Logging
- }
-}
-
-predicate isSensitiveLoggingSink(DataFlow::Node sink) {
- exists(MethodAccess ma |
- ma.getMethod().getDeclaringType() instanceof LoggerType and
- (
- ma.getMethod().hasName("debug") or
- ma.getMethod().hasName("trace") or
- ma.getMethod().hasName("debugf") or
- ma.getMethod().hasName("debugv")
- ) and //Check low priority log levels which are more likely to be real issues to reduce false positives
- sink.asExpr() = ma.getAnArgument()
- )
-}
-
class LoggerConfiguration extends DataFlow::Configuration {
LoggerConfiguration() { this = "Logger Configuration" }
override predicate isSource(DataFlow::Node source) { source.asExpr() instanceof CredentialExpr }
- override predicate isSink(DataFlow::Node sink) { isSensitiveLoggingSink(sink) }
+ override predicate isSink(DataFlow::Node sink) {
+ exists(LoggingCall c | sink.asExpr() = c.getALogArgument())
+ }
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
TaintTracking::localTaintStep(node1, node2)
diff --git a/java/ql/src/experimental/semmle/code/java/Logging.qll b/java/ql/src/experimental/semmle/code/java/Logging.qll
new file mode 100644
index 00000000000..6e50dc163bc
--- /dev/null
+++ b/java/ql/src/experimental/semmle/code/java/Logging.qll
@@ -0,0 +1,30 @@
+/**
+ * Provides classes and predicates for working with loggers.
+ */
+
+import java
+
+/** Models a call to a logging method. */
+class LoggingCall extends MethodAccess {
+ LoggingCall() {
+ exists(RefType t, Method m |
+ t.hasQualifiedName("org.apache.log4j", "Category") or // Log4j 1
+ t.hasQualifiedName("org.apache.logging.log4j", ["Logger", "LogBuilder"]) or // Log4j 2
+ t.hasQualifiedName("org.apache.commons.logging", "Log") or
+ // JBoss Logging (`org.jboss.logging.Logger` in some implementations like JBoss Application Server 4.0.4 did not implement `BasicLogger`)
+ t.hasQualifiedName("org.jboss.logging", ["BasicLogger", "Logger"]) or
+ t.hasQualifiedName("org.slf4j.spi", "LoggingEventBuilder") or
+ t.hasQualifiedName("org.slf4j", "Logger") or
+ t.hasQualifiedName("org.scijava.log", "Logger") or
+ t.hasQualifiedName("java.lang", "System$Logger") or
+ t.hasQualifiedName("java.util.logging","Logger")
+ |
+ m.getDeclaringType().(RefType).extendsOrImplements*(t) and
+ m.getReturnType() instanceof VoidType and
+ this = m.getAReference()
+ )
+ }
+
+ /** Returns an argument which would be logged by this call. */
+ Argument getALogArgument() { result = this.getArgument(_) }
+}
From d0c82d3756e78678e981b301215d0f7120acb4ce Mon Sep 17 00:00:00 2001
From: Porcuiney Hairs
Date: Wed, 3 Mar 2021 03:42:21 +0530
Subject: [PATCH 165/725] Add flogger and android logging support
---
java/ql/src/experimental/semmle/code/java/Logging.qll | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/java/ql/src/experimental/semmle/code/java/Logging.qll b/java/ql/src/experimental/semmle/code/java/Logging.qll
index 6e50dc163bc..c99334cdedd 100644
--- a/java/ql/src/experimental/semmle/code/java/Logging.qll
+++ b/java/ql/src/experimental/semmle/code/java/Logging.qll
@@ -9,17 +9,22 @@ class LoggingCall extends MethodAccess {
LoggingCall() {
exists(RefType t, Method m |
t.hasQualifiedName("org.apache.log4j", "Category") or // Log4j 1
- t.hasQualifiedName("org.apache.logging.log4j", ["Logger", "LogBuilder"]) or // Log4j 2
+ t.hasQualifiedName("org.apache.logging.log4j", ["Logger", "LogBuilder"]) or // Log4j 2
t.hasQualifiedName("org.apache.commons.logging", "Log") or
// JBoss Logging (`org.jboss.logging.Logger` in some implementations like JBoss Application Server 4.0.4 did not implement `BasicLogger`)
t.hasQualifiedName("org.jboss.logging", ["BasicLogger", "Logger"]) or
t.hasQualifiedName("org.slf4j.spi", "LoggingEventBuilder") or
t.hasQualifiedName("org.slf4j", "Logger") or
t.hasQualifiedName("org.scijava.log", "Logger") or
+ t.hasQualifiedName("com.google.common.flogger", "LoggingApi") or
t.hasQualifiedName("java.lang", "System$Logger") or
- t.hasQualifiedName("java.util.logging","Logger")
+ t.hasQualifiedName("java.util.logging", "Logger") or
+ t.hasQualifiedName("android.util.Log", _)
|
- m.getDeclaringType().(RefType).extendsOrImplements*(t) and
+ (
+ m.getDeclaringType().getASourceSupertype*() = t or
+ m.getDeclaringType().(RefType).extendsOrImplements*(t)
+ ) and
m.getReturnType() instanceof VoidType and
this = m.getAReference()
)
From f27d2bdf6d3ebbb37bb006d249494b96cfafadf9 Mon Sep 17 00:00:00 2001
From: porcupineyhairs <61983466+porcupineyhairs@users.noreply.github.com>
Date: Wed, 3 Mar 2021 12:36:01 +0530
Subject: [PATCH 166/725] Update
java/ql/src/experimental/semmle/code/java/Logging.qll
Co-authored-by: Marcono1234
---
java/ql/src/experimental/semmle/code/java/Logging.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/ql/src/experimental/semmle/code/java/Logging.qll b/java/ql/src/experimental/semmle/code/java/Logging.qll
index c99334cdedd..7a70cd8149b 100644
--- a/java/ql/src/experimental/semmle/code/java/Logging.qll
+++ b/java/ql/src/experimental/semmle/code/java/Logging.qll
@@ -19,7 +19,7 @@ class LoggingCall extends MethodAccess {
t.hasQualifiedName("com.google.common.flogger", "LoggingApi") or
t.hasQualifiedName("java.lang", "System$Logger") or
t.hasQualifiedName("java.util.logging", "Logger") or
- t.hasQualifiedName("android.util.Log", _)
+ t.hasQualifiedName("android.util", "Log")
|
(
m.getDeclaringType().getASourceSupertype*() = t or
From 84c9137152a4533393bea86292debb3dad1b4fff Mon Sep 17 00:00:00 2001
From: Porcuiney Hairs
Date: Wed, 3 Mar 2021 17:27:56 +0530
Subject: [PATCH 167/725] Include suggestions from review
---
java/ql/src/experimental/semmle/code/java/Logging.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/ql/src/experimental/semmle/code/java/Logging.qll b/java/ql/src/experimental/semmle/code/java/Logging.qll
index 7a70cd8149b..b3444972710 100644
--- a/java/ql/src/experimental/semmle/code/java/Logging.qll
+++ b/java/ql/src/experimental/semmle/code/java/Logging.qll
@@ -23,7 +23,7 @@ class LoggingCall extends MethodAccess {
|
(
m.getDeclaringType().getASourceSupertype*() = t or
- m.getDeclaringType().(RefType).extendsOrImplements*(t)
+ m.getDeclaringType().extendsOrImplements*(t)
) and
m.getReturnType() instanceof VoidType and
this = m.getAReference()
From a88c3682ff429dddb09dfd826c9c5efb4bc2c593 Mon Sep 17 00:00:00 2001
From: Porcuiney Hairs
Date: Thu, 18 Mar 2021 16:11:27 +0530
Subject: [PATCH 168/725] remove sanitiserGuards
---
.../Security/CWE/CWE-117/LogInjection.ql | 29 -------------------
1 file changed, 29 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-117/LogInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-117/LogInjection.ql
index 440c39b77e8..7183c74b5bf 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-117/LogInjection.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-117/LogInjection.ql
@@ -30,35 +30,6 @@ private class LogInjectionConfiguration extends TaintTracking::Configuration {
override predicate isSanitizer(DataFlow::Node node) {
node.getType() instanceof BoxedType or node.getType() instanceof PrimitiveType
}
-
- override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
- guard instanceof StrCheckSanitizerGuard
- }
-}
-
-/**
- * Models any regex or equality check as a sanitizer guard.
- * Assumes any check on the taint to be a valid sanitizing check.
- */
-private class StrCheckSanitizerGuard extends DataFlow::BarrierGuard {
- StrCheckSanitizerGuard() {
- exists(Method m |
- m.getDeclaringType().hasQualifiedName("java.util.regex", "Pattern") and
- m.hasName("matches")
- or
- m.getDeclaringType() instanceof TypeString and
- m.hasName([
- "startsWith", "regionMatches", "matches", "equals", "equalsIgnoreCase", "endsWith",
- "contentEquals", "contains"
- ])
- |
- m.getAReference() = this
- )
- }
-
- override predicate checks(Expr e, boolean branch) {
- e = this.(MethodAccess).getQualifier() and branch = true
- }
}
from LogInjectionConfiguration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
From 5ec8511d50c96fac2909dfba39424fac79c9df89 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Thu, 18 Mar 2021 11:47:46 +0100
Subject: [PATCH 169/725] Python: Port PyYAML model to API graphs
---
.../ql/src/semmle/python/frameworks/Yaml.qll | 76 ++-----------------
1 file changed, 7 insertions(+), 69 deletions(-)
diff --git a/python/ql/src/semmle/python/frameworks/Yaml.qll b/python/ql/src/semmle/python/frameworks/Yaml.qll
index ec3a21379da..8240dd21378 100644
--- a/python/ql/src/semmle/python/frameworks/Yaml.qll
+++ b/python/ql/src/semmle/python/frameworks/Yaml.qll
@@ -11,6 +11,7 @@ private import python
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.Concepts
+private import semmle.python.ApiGraphs
/**
* Provides classes modeling security-relevant aspects of the PyYAML package (obtained
@@ -20,70 +21,7 @@ private import semmle.python.Concepts
* - https://pyyaml.org/wiki/PyYAMLDocumentation
* - https://pyyaml.docsforge.com/master/documentation/
*/
-private module Yaml {
- /** Gets a reference to the `yaml` module. */
- private DataFlow::Node yaml(DataFlow::TypeTracker t) {
- t.start() and
- result = DataFlow::importNode("yaml")
- or
- exists(DataFlow::TypeTracker t2 | result = yaml(t2).track(t2, t))
- }
-
- /** Gets a reference to the `yaml` module. */
- DataFlow::Node yaml() { result = yaml(DataFlow::TypeTracker::end()) }
-
- /** Provides models for the `yaml` module. */
- module yaml {
- /**
- * Gets a reference to the attribute `attr_name` of the `yaml` module.
- * WARNING: Only holds for a few predefined attributes.
- *
- * For example, using `attr_name = "load"` will get all uses of `yaml.load`.
- */
- private DataFlow::Node yaml_attr(DataFlow::TypeTracker t, string attr_name) {
- attr_name in [
- // functions
- "load", "load_all", "full_load", "full_load_all", "unsafe_load", "unsafe_load_all",
- "safe_load", "safe_load_all",
- // Classes
- "SafeLoader", "BaseLoader"
- ] and
- (
- t.start() and
- result = DataFlow::importNode("yaml." + attr_name)
- or
- t.startInAttr(attr_name) and
- result = yaml()
- )
- or
- // Due to bad performance when using normal setup with `yaml_attr(t2, attr_name).track(t2, t)`
- // we have inlined that code and forced a join
- exists(DataFlow::TypeTracker t2 |
- exists(DataFlow::StepSummary summary |
- yaml_attr_first_join(t2, attr_name, result, summary) and
- t = t2.append(summary)
- )
- )
- }
-
- pragma[nomagic]
- private predicate yaml_attr_first_join(
- DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res, DataFlow::StepSummary summary
- ) {
- DataFlow::StepSummary::step(yaml_attr(t2, attr_name), res, summary)
- }
-
- /**
- * Gets a reference to the attribute `attr_name` of the `yaml` module.
- * WARNING: Only holds for a few predefined attributes.
- *
- * For example, using `attr_name = "load"` will get all uses of `yaml.load`.
- */
- DataFlow::Node yaml_attr(string attr_name) {
- result = yaml_attr(DataFlow::TypeTracker::end(), attr_name)
- }
- }
-}
+private module Yaml { }
/**
* A call to any of the loading functions in `yaml` (`load`, `load_all`, `full_load`,
@@ -91,7 +29,7 @@ private module Yaml {
*
* See https://pyyaml.org/wiki/PyYAMLDocumentation (you will have to scroll down).
*/
-private class YamlLoadCall extends Decoding::Range, DataFlow::CfgNode {
+private class YamlLoadCall extends Decoding::Range, DataFlow::CallCfgNode {
override CallNode node;
string func_name;
@@ -100,7 +38,7 @@ private class YamlLoadCall extends Decoding::Range, DataFlow::CfgNode {
"load", "load_all", "full_load", "full_load_all", "unsafe_load", "unsafe_load_all",
"safe_load", "safe_load_all"
] and
- node.getFunction() = Yaml::yaml::yaml_attr(func_name).asCfgNode()
+ this = API::moduleImport("yaml").getMember(func_name).getACall()
}
/**
@@ -117,13 +55,13 @@ private class YamlLoadCall extends Decoding::Range, DataFlow::CfgNode {
// If the `Loader` is not set to either `SafeLoader` or `BaseLoader` or not set at all,
// then the default loader will be used, which is not safe.
not exists(DataFlow::Node loader_arg |
- loader_arg.asCfgNode() in [node.getArg(1), node.getArgByName("Loader")]
+ loader_arg in [this.getArg(1), this.getArgByName("Loader")]
|
- loader_arg = Yaml::yaml::yaml_attr(["SafeLoader", "BaseLoader"])
+ loader_arg = API::moduleImport("yaml").getMember(["SafeLoader", "BaseLoader"]).getAUse()
)
}
- override DataFlow::Node getAnInput() { result.asCfgNode() = node.getArg(0) }
+ override DataFlow::Node getAnInput() { result = this.getArg(0) }
override DataFlow::Node getOutput() { result = this }
From 25b15d74709e912f97ca7d461552eb5681c4ca50 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Thu, 18 Mar 2021 11:48:30 +0100
Subject: [PATCH 170/725] Python: Move PyYAML modeling classes within module
For now, this is how we're trying to structure things -- all in all it doesn't
matter too much, since everything is still marked as private.
---
.../ql/src/semmle/python/frameworks/Yaml.qll | 84 +++++++++----------
1 file changed, 42 insertions(+), 42 deletions(-)
diff --git a/python/ql/src/semmle/python/frameworks/Yaml.qll b/python/ql/src/semmle/python/frameworks/Yaml.qll
index 8240dd21378..1bf5c517ec8 100644
--- a/python/ql/src/semmle/python/frameworks/Yaml.qll
+++ b/python/ql/src/semmle/python/frameworks/Yaml.qll
@@ -21,49 +21,49 @@ private import semmle.python.ApiGraphs
* - https://pyyaml.org/wiki/PyYAMLDocumentation
* - https://pyyaml.docsforge.com/master/documentation/
*/
-private module Yaml { }
-
-/**
- * A call to any of the loading functions in `yaml` (`load`, `load_all`, `full_load`,
- * `full_load_all`, `unsafe_load`, `unsafe_load_all`, `safe_load`, `safe_load_all`)
- *
- * See https://pyyaml.org/wiki/PyYAMLDocumentation (you will have to scroll down).
- */
-private class YamlLoadCall extends Decoding::Range, DataFlow::CallCfgNode {
- override CallNode node;
- string func_name;
-
- YamlLoadCall() {
- func_name in [
- "load", "load_all", "full_load", "full_load_all", "unsafe_load", "unsafe_load_all",
- "safe_load", "safe_load_all"
- ] and
- this = API::moduleImport("yaml").getMember(func_name).getACall()
- }
-
+private module Yaml {
/**
- * This function was thought safe from the 5.1 release in 2017, when the default loader was changed to `FullLoader`.
- * In 2020 new exploits were found, meaning it's not safe. The Current plan is to change the default to `SafeLoader` in release 6.0
- * (as explained in https://github.com/yaml/pyyaml/issues/420#issuecomment-696752389).
- * Until 6.0 is released, we will mark `yaml.load` as possibly leading to arbitrary code execution.
- * See https://github.com/yaml/pyyaml/wiki/PyYAML-yaml.load(input)-Deprecation for more details.
+ * A call to any of the loading functions in `yaml` (`load`, `load_all`, `full_load`,
+ * `full_load_all`, `unsafe_load`, `unsafe_load_all`, `safe_load`, `safe_load_all`)
+ *
+ * See https://pyyaml.org/wiki/PyYAMLDocumentation (you will have to scroll down).
*/
- override predicate mayExecuteInput() {
- func_name in ["full_load", "full_load_all", "unsafe_load", "unsafe_load_all"]
- or
- func_name in ["load", "load_all"] and
- // If the `Loader` is not set to either `SafeLoader` or `BaseLoader` or not set at all,
- // then the default loader will be used, which is not safe.
- not exists(DataFlow::Node loader_arg |
- loader_arg in [this.getArg(1), this.getArgByName("Loader")]
- |
- loader_arg = API::moduleImport("yaml").getMember(["SafeLoader", "BaseLoader"]).getAUse()
- )
+ private class YamlLoadCall extends Decoding::Range, DataFlow::CallCfgNode {
+ override CallNode node;
+ string func_name;
+
+ YamlLoadCall() {
+ func_name in [
+ "load", "load_all", "full_load", "full_load_all", "unsafe_load", "unsafe_load_all",
+ "safe_load", "safe_load_all"
+ ] and
+ this = API::moduleImport("yaml").getMember(func_name).getACall()
+ }
+
+ /**
+ * This function was thought safe from the 5.1 release in 2017, when the default loader was changed to `FullLoader`.
+ * In 2020 new exploits were found, meaning it's not safe. The Current plan is to change the default to `SafeLoader` in release 6.0
+ * (as explained in https://github.com/yaml/pyyaml/issues/420#issuecomment-696752389).
+ * Until 6.0 is released, we will mark `yaml.load` as possibly leading to arbitrary code execution.
+ * See https://github.com/yaml/pyyaml/wiki/PyYAML-yaml.load(input)-Deprecation for more details.
+ */
+ override predicate mayExecuteInput() {
+ func_name in ["full_load", "full_load_all", "unsafe_load", "unsafe_load_all"]
+ or
+ func_name in ["load", "load_all"] and
+ // If the `Loader` is not set to either `SafeLoader` or `BaseLoader` or not set at all,
+ // then the default loader will be used, which is not safe.
+ not exists(DataFlow::Node loader_arg |
+ loader_arg in [this.getArg(1), this.getArgByName("Loader")]
+ |
+ loader_arg = API::moduleImport("yaml").getMember(["SafeLoader", "BaseLoader"]).getAUse()
+ )
+ }
+
+ override DataFlow::Node getAnInput() { result = this.getArg(0) }
+
+ override DataFlow::Node getOutput() { result = this }
+
+ override string getFormat() { result = "YAML" }
}
-
- override DataFlow::Node getAnInput() { result = this.getArg(0) }
-
- override DataFlow::Node getOutput() { result = this }
-
- override string getFormat() { result = "YAML" }
}
From 54e6f51512e99279ea249c6553168a65d604c618 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Thu, 18 Mar 2021 11:49:33 +0100
Subject: [PATCH 171/725] Python: Add example of C-based PyYAML loaders
```
In [6]: yaml.load("!!python/object/new:os.system [echo EXPLOIT!]", yaml.CLoader)
EXPLOIT!
Out[6]: 0
```
---
.../experimental/library-tests/frameworks/yaml/Decoding.py | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/python/ql/test/experimental/library-tests/frameworks/yaml/Decoding.py b/python/ql/test/experimental/library-tests/frameworks/yaml/Decoding.py
index 7d5bcde1503..80360648391 100644
--- a/python/ql/test/experimental/library-tests/frameworks/yaml/Decoding.py
+++ b/python/ql/test/experimental/library-tests/frameworks/yaml/Decoding.py
@@ -17,3 +17,9 @@ yaml.load_all(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFo
yaml.safe_load_all(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML
yaml.unsafe_load_all(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput
yaml.full_load_all(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput
+
+# C-based loaders with `libyaml`
+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.CSafeLoader) # $decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML SPURIOUS: decodeMayExecuteInput
+yaml.load(payload, yaml.CBaseLoader) # $decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML SPURIOUS: decodeMayExecuteInput
From 42b2c3ed52e529c72f815844d8aaf01f937320d7 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Thu, 18 Mar 2021 11:55:01 +0100
Subject: [PATCH 172/725] Python: Model C-based loaders for PyYAML
Not really that important. But easy to do while I was working on this library.
---
.../change-notes/2021-03-18-yaml-handle-C-based-loaders.md | 2 ++
python/ql/src/semmle/python/frameworks/Yaml.qll | 5 ++++-
.../experimental/library-tests/frameworks/yaml/Decoding.py | 4 ++--
3 files changed, 8 insertions(+), 3 deletions(-)
create mode 100644 python/change-notes/2021-03-18-yaml-handle-C-based-loaders.md
diff --git a/python/change-notes/2021-03-18-yaml-handle-C-based-loaders.md b/python/change-notes/2021-03-18-yaml-handle-C-based-loaders.md
new file mode 100644
index 00000000000..054d10dbb0b
--- /dev/null
+++ b/python/change-notes/2021-03-18-yaml-handle-C-based-loaders.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* Improved modeling of the `PyYAML` PyPI package, so we now correctly treat `CSafeLoader` and `CBaseLoader` as being safe loaders that can not lead to code execution.
diff --git a/python/ql/src/semmle/python/frameworks/Yaml.qll b/python/ql/src/semmle/python/frameworks/Yaml.qll
index 1bf5c517ec8..f7ac0a9fda7 100644
--- a/python/ql/src/semmle/python/frameworks/Yaml.qll
+++ b/python/ql/src/semmle/python/frameworks/Yaml.qll
@@ -56,7 +56,10 @@ private module Yaml {
not exists(DataFlow::Node loader_arg |
loader_arg in [this.getArg(1), this.getArgByName("Loader")]
|
- loader_arg = API::moduleImport("yaml").getMember(["SafeLoader", "BaseLoader"]).getAUse()
+ loader_arg =
+ API::moduleImport("yaml")
+ .getMember(["SafeLoader", "BaseLoader", "CSafeLoader", "CBaseLoader"])
+ .getAUse()
)
}
diff --git a/python/ql/test/experimental/library-tests/frameworks/yaml/Decoding.py b/python/ql/test/experimental/library-tests/frameworks/yaml/Decoding.py
index 80360648391..a9369d6ec3d 100644
--- a/python/ql/test/experimental/library-tests/frameworks/yaml/Decoding.py
+++ b/python/ql/test/experimental/library-tests/frameworks/yaml/Decoding.py
@@ -21,5 +21,5 @@ yaml.full_load_all(payload) # $ decodeInput=payload decodeOutput=Attribute() dec
# C-based loaders with `libyaml`
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.CSafeLoader) # $decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML SPURIOUS: decodeMayExecuteInput
-yaml.load(payload, yaml.CBaseLoader) # $decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML SPURIOUS: decodeMayExecuteInput
+yaml.load(payload, yaml.CSafeLoader) # $decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML
+yaml.load(payload, yaml.CBaseLoader) # $decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML
From d998d06b942911cae3cdae9d5a21e65abe33dbf6 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 18 Mar 2021 13:37:18 +0100
Subject: [PATCH 173/725] add link to source in alert-message for
js/shell-command-constructed-from-input
---
.../src/Security/CWE-078/UnsafeShellCommandConstruction.ql | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/javascript/ql/src/Security/CWE-078/UnsafeShellCommandConstruction.ql b/javascript/ql/src/Security/CWE-078/UnsafeShellCommandConstruction.ql
index f5d5113634d..1b4d5523ba4 100644
--- a/javascript/ql/src/Security/CWE-078/UnsafeShellCommandConstruction.ql
+++ b/javascript/ql/src/Security/CWE-078/UnsafeShellCommandConstruction.ql
@@ -18,6 +18,6 @@ import DataFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, Sink sinkNode
where cfg.hasFlowPath(source, sink) and sinkNode = sink.getNode()
-select sinkNode.getAlertLocation(), source, sink, "$@ based on library input is later used in $@.",
- sinkNode.getAlertLocation(), sinkNode.getSinkType(), sinkNode.getCommandExecution(),
- "shell command"
+select sinkNode.getAlertLocation(), source, sink, "$@ based on $@ is later used in $@.",
+ sinkNode.getAlertLocation(), sinkNode.getSinkType(), source.getNode(), "library input",
+ sinkNode.getCommandExecution(), "shell command"
From add0c88530970a200c60aa91fb24259df5d4fe26 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 18 Mar 2021 13:39:12 +0100
Subject: [PATCH 174/725] loosen the requirement that the `package.json` file
must be the top-most `package.json`
---
.../src/semmle/javascript/PackageExports.qll | 27 +++----------------
1 file changed, 3 insertions(+), 24 deletions(-)
diff --git a/javascript/ql/src/semmle/javascript/PackageExports.qll b/javascript/ql/src/semmle/javascript/PackageExports.qll
index bdb0c297411..9de6a7780b1 100644
--- a/javascript/ql/src/semmle/javascript/PackageExports.qll
+++ b/javascript/ql/src/semmle/javascript/PackageExports.qll
@@ -17,33 +17,12 @@ DataFlow::ParameterNode getALibraryInputParameter() {
}
/**
- * Gets the number of occurrences of "/" in `path`.
- */
-bindingset[path]
-private int countSlashes(string path) { result = count(path.splitAt("/")) - 1 }
-
-/**
- * Gets the topmost named package.json that appears in the project.
- *
- * There can be multiple results if the there exists multiple package.json that are equally deeply nested in the folder structure.
- * Results are limited to package.json files that are at most nested 2 directories deep.
- */
-PackageJSON getTopmostPackageJSON() {
- result =
- min(PackageJSON j |
- countSlashes(j.getFile().getRelativePath()) <= 3 and
- exists(j.getPackageName())
- |
- j order by countSlashes(j.getFile().getRelativePath())
- )
-}
-
-/**
- * Gets a value exported by the main module from one of the topmost `package.json` files (see `getTopmostPackageJSON`).
+ * Gets a value exported by the main module from a named `package.json` file.
* The value is either directly the `module.exports` value, a nested property of `module.exports`, or a method on an exported class.
*/
private DataFlow::Node getAValueExportedByPackage() {
- result = getAnExportFromModule(getTopmostPackageJSON().getMainModule())
+ result =
+ getAnExportFromModule(any(PackageJSON pack | exists(pack.getPackageName())).getMainModule())
or
result = getAValueExportedByPackage().(DataFlow::PropWrite).getRhs()
or
From c0bb169342f7561ce7d50ee1ed07b030aad252fa Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 18 Mar 2021 13:41:36 +0100
Subject: [PATCH 175/725] recognize a `src/index.js` file as a main module for
a package
---
.../ql/src/semmle/javascript/NodeModuleResolutionImpl.qll | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/javascript/ql/src/semmle/javascript/NodeModuleResolutionImpl.qll b/javascript/ql/src/semmle/javascript/NodeModuleResolutionImpl.qll
index 01b01f1ac04..60ccf7216c9 100644
--- a/javascript/ql/src/semmle/javascript/NodeModuleResolutionImpl.qll
+++ b/javascript/ql/src/semmle/javascript/NodeModuleResolutionImpl.qll
@@ -96,8 +96,11 @@ File resolveMainModule(PackageJSON pkg, int priority) {
)
)
or
- result =
- tryExtensions(pkg.getFile().getParentContainer(), "index", priority - prioritiesPerCandidate())
+ exists(Folder folder | folder = pkg.getFile().getParentContainer() |
+ result =
+ tryExtensions([folder, folder.getChildContainer("src")], "index",
+ priority - prioritiesPerCandidate())
+ )
}
/**
From 67a5831ac09f26971965d7eb65fd3520dd653e54 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 18 Mar 2021 13:59:44 +0100
Subject: [PATCH 176/725] update expected output
---
.../PackageExports/tests.expected | 3 -
.../library-tests/PackageExports/tests.ql | 2 -
.../UnsafeShellCommandConstruction.expected | 132 ++++++++++--------
.../Security/CWE-078/lib/subLib/index.js | 2 +-
4 files changed, 76 insertions(+), 63 deletions(-)
diff --git a/javascript/ql/test/library-tests/PackageExports/tests.expected b/javascript/ql/test/library-tests/PackageExports/tests.expected
index 648226a0d13..1d46e149521 100644
--- a/javascript/ql/test/library-tests/PackageExports/tests.expected
+++ b/javascript/ql/test/library-tests/PackageExports/tests.expected
@@ -1,6 +1,3 @@
-getTopmostPackageJSON
-| absent_main/package.json:1:1:4:1 | {\\n " ... t.js"\\n} |
-| lib1/package.json:1:1:4:1 | {\\n " ... n.js"\\n} |
getALibraryInputParameter
| lib1/foo.js:3:44:3:44 | d |
| lib1/main.js:6:17:6:17 | a |
diff --git a/javascript/ql/test/library-tests/PackageExports/tests.ql b/javascript/ql/test/library-tests/PackageExports/tests.ql
index 1a23e8d24d9..08ce8bcd068 100644
--- a/javascript/ql/test/library-tests/PackageExports/tests.ql
+++ b/javascript/ql/test/library-tests/PackageExports/tests.ql
@@ -1,8 +1,6 @@
import javascript
import semmle.javascript.PackageExports as Exports
-query PackageJSON getTopmostPackageJSON() { result = Exports::getTopmostPackageJSON() }
-
query DataFlow::Node getALibraryInputParameter() { result = Exports::getALibraryInputParameter() }
query DataFlow::Node getAnExportedValue(Module mod, string name) {
diff --git a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected
index 2d707e1a693..f4ed7e6d10b 100644
--- a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected
@@ -205,6 +205,14 @@ nodes
| lib/lib.js:405:39:405:42 | name |
| lib/lib.js:406:22:406:25 | name |
| lib/lib.js:406:22:406:25 | name |
+| lib/subLib/index.js:3:28:3:31 | name |
+| lib/subLib/index.js:3:28:3:31 | name |
+| lib/subLib/index.js:4:22:4:25 | name |
+| lib/subLib/index.js:4:22:4:25 | name |
+| lib/subLib/index.js:7:32:7:35 | name |
+| lib/subLib/index.js:7:32:7:35 | name |
+| lib/subLib/index.js:8:22:8:25 | name |
+| lib/subLib/index.js:8:22:8:25 | name |
edges
| lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name |
| lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name |
@@ -444,61 +452,71 @@ edges
| lib/lib.js:405:39:405:42 | name | lib/lib.js:406:22:406:25 | name |
| lib/lib.js:405:39:405:42 | name | lib/lib.js:406:22:406:25 | name |
| lib/lib.js:405:39:405:42 | name | lib/lib.js:406:22:406:25 | name |
+| lib/subLib/index.js:3:28:3:31 | name | lib/subLib/index.js:4:22:4:25 | name |
+| lib/subLib/index.js:3:28:3:31 | name | lib/subLib/index.js:4:22:4:25 | name |
+| lib/subLib/index.js:3:28:3:31 | name | lib/subLib/index.js:4:22:4:25 | name |
+| lib/subLib/index.js:3:28:3:31 | name | lib/subLib/index.js:4:22:4:25 | name |
+| lib/subLib/index.js:7:32:7:35 | name | lib/subLib/index.js:8:22:8:25 | name |
+| lib/subLib/index.js:7:32:7:35 | name | lib/subLib/index.js:8:22:8:25 | name |
+| lib/subLib/index.js:7:32:7:35 | name | lib/subLib/index.js:8:22:8:25 | name |
+| lib/subLib/index.js:7:32:7:35 | name | lib/subLib/index.js:8:22:8:25 | name |
#select
-| lib/lib2.js:4:10:4:25 | "rm -rf " + name | lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name | $@ based on library input is later used in $@. | lib/lib2.js:4:10:4:25 | "rm -rf " + name | String concatenation | lib/lib2.js:4:2:4:26 | cp.exec ... + name) | shell command |
-| lib/lib2.js:8:10:8:25 | "rm -rf " + name | lib/lib2.js:7:32:7:35 | name | lib/lib2.js:8:22:8:25 | name | $@ based on library input is later used in $@. | lib/lib2.js:8:10:8:25 | "rm -rf " + name | String concatenation | lib/lib2.js:8:2:8:26 | cp.exec ... + name) | shell command |
-| lib/lib.js:4:10:4:25 | "rm -rf " + name | lib/lib.js:3:28:3:31 | name | lib/lib.js:4:22:4:25 | name | $@ based on library input is later used in $@. | lib/lib.js:4:10:4:25 | "rm -rf " + name | String concatenation | lib/lib.js:4:2:4:26 | cp.exec ... + name) | shell command |
-| lib/lib.js:11:10:11:25 | "rm -rf " + name | lib/lib.js:10:32:10:35 | name | lib/lib.js:11:22:11:25 | name | $@ based on library input is later used in $@. | lib/lib.js:11:10:11:25 | "rm -rf " + name | String concatenation | lib/lib.js:11:2:11:26 | cp.exec ... + name) | shell command |
-| lib/lib.js:15:10:15:25 | "rm -rf " + name | lib/lib.js:14:36:14:39 | name | lib/lib.js:15:22:15:25 | name | $@ based on library input is later used in $@. | lib/lib.js:15:10:15:25 | "rm -rf " + name | String concatenation | lib/lib.js:15:2:15:26 | cp.exec ... + name) | shell command |
-| lib/lib.js:20:10:20:25 | "rm -rf " + name | lib/lib.js:19:34:19:37 | name | lib/lib.js:20:22:20:25 | name | $@ based on library input is later used in $@. | lib/lib.js:20:10:20:25 | "rm -rf " + name | String concatenation | lib/lib.js:20:2:20:26 | cp.exec ... + name) | shell command |
-| lib/lib.js:27:10:27:25 | "rm -rf " + name | lib/lib.js:26:35:26:38 | name | lib/lib.js:27:22:27:25 | name | $@ based on library input is later used in $@. | lib/lib.js:27:10:27:25 | "rm -rf " + name | String concatenation | lib/lib.js:27:2:27:26 | cp.exec ... + name) | shell command |
-| lib/lib.js:35:11:35:26 | "rm -rf " + name | lib/lib.js:34:14:34:17 | name | lib/lib.js:35:23:35:26 | name | $@ based on library input is later used in $@. | lib/lib.js:35:11:35:26 | "rm -rf " + name | String concatenation | lib/lib.js:35:3:35:27 | cp.exec ... + name) | shell command |
-| lib/lib.js:38:11:38:26 | "rm -rf " + name | lib/lib.js:37:13:37:16 | name | lib/lib.js:38:23:38:26 | name | $@ based on library input is later used in $@. | lib/lib.js:38:11:38:26 | "rm -rf " + name | String concatenation | lib/lib.js:38:3:38:27 | cp.exec ... + name) | shell command |
-| lib/lib.js:41:11:41:26 | "rm -rf " + name | lib/lib.js:40:6:40:9 | name | lib/lib.js:41:23:41:26 | name | $@ based on library input is later used in $@. | lib/lib.js:41:11:41:26 | "rm -rf " + name | String concatenation | lib/lib.js:41:3:41:27 | cp.exec ... + name) | shell command |
-| lib/lib.js:50:35:50:50 | "rm -rf " + name | lib/lib.js:49:31:49:34 | name | lib/lib.js:50:47:50:50 | name | $@ based on library input is later used in $@. | lib/lib.js:50:35:50:50 | "rm -rf " + name | String concatenation | lib/lib.js:50:2:50:51 | require ... + name) | shell command |
-| lib/lib.js:54:13:54:28 | "rm -rf " + name | lib/lib.js:53:33:53:36 | name | lib/lib.js:54:25:54:28 | name | $@ based on library input is later used in $@. | lib/lib.js:54:13:54:28 | "rm -rf " + name | String concatenation | lib/lib.js:55:2:55:14 | cp.exec(cmd1) | shell command |
-| lib/lib.js:57:13:57:28 | "rm -rf " + name | lib/lib.js:53:33:53:36 | name | lib/lib.js:57:25:57:28 | name | $@ based on library input is later used in $@. | lib/lib.js:57:13:57:28 | "rm -rf " + name | String concatenation | lib/lib.js:59:3:59:14 | cp.exec(cmd) | shell command |
-| lib/lib.js:65:10:65:25 | "rm -rf " + name | lib/lib.js:64:41:64:44 | name | lib/lib.js:65:22:65:25 | name | $@ based on library input is later used in $@. | lib/lib.js:65:10:65:25 | "rm -rf " + name | String concatenation | lib/lib.js:65:2:65:26 | cp.exec ... + name) | shell command |
-| lib/lib.js:71:10:71:31 | "cat /f ... + name | lib/lib.js:64:41:64:44 | name | lib/lib.js:71:28:71:31 | name | $@ based on library input is later used in $@. | lib/lib.js:71:10:71:31 | "cat /f ... + name | String concatenation | lib/lib.js:71:2:71:32 | cp.exec ... + name) | shell command |
-| lib/lib.js:73:10:73:31 | "cat \\" ... + "\\"" | lib/lib.js:64:41:64:44 | name | lib/lib.js:73:21:73:24 | name | $@ based on library input is later used in $@. | lib/lib.js:73:10:73:31 | "cat \\" ... + "\\"" | String concatenation | lib/lib.js:73:2:73:32 | cp.exec ... + "\\"") | shell command |
-| lib/lib.js:75:10:75:29 | "cat '" + name + "'" | lib/lib.js:64:41:64:44 | name | lib/lib.js:75:20:75:23 | name | $@ based on library input is later used in $@. | lib/lib.js:75:10:75:29 | "cat '" + name + "'" | String concatenation | lib/lib.js:75:2:75:30 | cp.exec ... + "'") | shell command |
-| lib/lib.js:77:10:77:37 | "cat '/ ... e + "'" | lib/lib.js:64:41:64:44 | name | lib/lib.js:77:28:77:31 | name | $@ based on library input is later used in $@. | lib/lib.js:77:10:77:37 | "cat '/ ... e + "'" | String concatenation | lib/lib.js:77:2:77:38 | cp.exec ... + "'") | shell command |
-| lib/lib.js:83:10:83:25 | "rm -rf " + name | lib/lib.js:82:35:82:38 | name | lib/lib.js:83:22:83:25 | name | $@ based on library input is later used in $@. | lib/lib.js:83:10:83:25 | "rm -rf " + name | String concatenation | lib/lib.js:83:2:83:26 | cp.exec ... + name) | shell command |
-| lib/lib.js:86:13:86:16 | name | lib/lib.js:82:35:82:38 | name | lib/lib.js:86:13:86:16 | name | $@ based on library input is later used in $@. | lib/lib.js:86:13:86:16 | name | Array element | lib/lib.js:87:2:87:25 | cp.exec ... n(" ")) | shell command |
-| lib/lib.js:89:21:89:24 | name | lib/lib.js:82:35:82:38 | name | lib/lib.js:89:21:89:24 | name | $@ based on library input is later used in $@. | lib/lib.js:89:21:89:24 | name | Array element | lib/lib.js:89:2:89:36 | cp.exec ... n(" ")) | shell command |
-| lib/lib.js:91:21:91:38 | "\\"" + name + "\\"" | lib/lib.js:82:35:82:38 | name | lib/lib.js:91:21:91:38 | "\\"" + name + "\\"" | $@ based on library input is later used in $@. | lib/lib.js:91:21:91:38 | "\\"" + name + "\\"" | Array element | lib/lib.js:91:2:91:50 | cp.exec ... n(" ")) | shell command |
-| lib/lib.js:98:35:98:38 | name | lib/lib.js:97:35:97:38 | name | lib/lib.js:98:35:98:38 | name | $@ based on library input is later used in $@. | lib/lib.js:98:35:98:38 | name | Formatted string | lib/lib.js:98:2:98:40 | cp.exec ... name)) | shell command |
-| lib/lib.js:100:37:100:40 | name | lib/lib.js:97:35:97:38 | name | lib/lib.js:100:37:100:40 | name | $@ based on library input is later used in $@. | lib/lib.js:100:37:100:40 | name | Formatted string | lib/lib.js:100:2:100:42 | cp.exec ... name)) | shell command |
-| lib/lib.js:102:46:102:49 | name | lib/lib.js:97:35:97:38 | name | lib/lib.js:102:46:102:49 | name | $@ based on library input is later used in $@. | lib/lib.js:102:46:102:49 | name | Formatted string | lib/lib.js:102:2:102:51 | cp.exec ... name)) | shell command |
-| lib/lib.js:108:41:108:44 | name | lib/lib.js:97:35:97:38 | name | lib/lib.js:108:41:108:44 | name | $@ based on library input is later used in $@. | lib/lib.js:108:41:108:44 | name | Formatted string | lib/lib.js:108:2:108:46 | cp.exec ... name)) | shell command |
-| lib/lib.js:112:10:112:25 | "rm -rf " + name | lib/lib.js:111:34:111:37 | name | lib/lib.js:112:22:112:25 | name | $@ based on library input is later used in $@. | lib/lib.js:112:10:112:25 | "rm -rf " + name | String concatenation | lib/lib.js:112:2:112:26 | cp.exec ... + name) | shell command |
-| lib/lib.js:121:10:121:25 | "rm -rf " + name | lib/lib.js:120:33:120:36 | name | lib/lib.js:121:22:121:25 | name | $@ based on library input is later used in $@. | lib/lib.js:121:10:121:25 | "rm -rf " + name | String concatenation | lib/lib.js:121:2:121:26 | cp.exec ... + name) | shell command |
-| lib/lib.js:131:11:131:26 | "rm -rf " + name | lib/lib.js:130:6:130:9 | name | lib/lib.js:131:23:131:26 | name | $@ based on library input is later used in $@. | lib/lib.js:131:11:131:26 | "rm -rf " + name | String concatenation | lib/lib.js:131:3:131:27 | cp.exec ... + name) | shell command |
-| lib/lib.js:149:12:149:27 | "rm -rf " + name | lib/lib.js:148:37:148:40 | name | lib/lib.js:149:24:149:27 | name | $@ based on library input is later used in $@. | lib/lib.js:149:12:149:27 | "rm -rf " + name | String concatenation | lib/lib.js:152:2:152:23 | cp.spaw ... gs, cb) | shell command |
-| lib/lib.js:161:13:161:28 | "rm -rf " + name | lib/lib.js:155:38:155:41 | name | lib/lib.js:161:25:161:28 | name | $@ based on library input is later used in $@. | lib/lib.js:161:13:161:28 | "rm -rf " + name | String concatenation | lib/lib.js:163:2:167:2 | cp.spaw ... t' }\\n\\t) | shell command |
-| lib/lib.js:173:10:173:23 | "fo \| " + name | lib/lib.js:170:41:170:44 | name | lib/lib.js:173:20:173:23 | name | $@ based on library input is later used in $@. | lib/lib.js:173:10:173:23 | "fo \| " + name | String concatenation | lib/lib.js:173:2:173:24 | cp.exec ... + name) | shell command |
-| lib/lib.js:182:10:182:27 | "rm -rf " + broken | lib/lib.js:177:38:177:41 | name | lib/lib.js:182:22:182:27 | broken | $@ based on library input is later used in $@. | lib/lib.js:182:10:182:27 | "rm -rf " + broken | String concatenation | lib/lib.js:182:2:182:28 | cp.exec ... broken) | shell command |
-| lib/lib.js:187:10:187:25 | "rm -rf " + name | lib/lib.js:186:34:186:37 | name | lib/lib.js:187:22:187:25 | name | $@ based on library input is later used in $@. | lib/lib.js:187:10:187:25 | "rm -rf " + name | String concatenation | lib/lib.js:187:2:187:26 | cp.exec ... + name) | shell command |
-| lib/lib.js:190:11:190:26 | "rm -rf " + name | lib/lib.js:186:34:186:37 | name | lib/lib.js:190:23:190:26 | name | $@ based on library input is later used in $@. | lib/lib.js:190:11:190:26 | "rm -rf " + name | String concatenation | lib/lib.js:190:3:190:27 | cp.exec ... + name) | shell command |
-| lib/lib.js:197:10:197:25 | "rm -rf " + name | lib/lib.js:196:45:196:48 | name | lib/lib.js:197:22:197:25 | name | $@ based on library input is later used in $@. | lib/lib.js:197:10:197:25 | "rm -rf " + name | String concatenation | lib/lib.js:197:2:197:26 | cp.exec ... + name) | shell command |
-| lib/lib.js:200:11:200:26 | "rm -rf " + name | lib/lib.js:196:45:196:48 | name | lib/lib.js:200:23:200:26 | name | $@ based on library input is later used in $@. | lib/lib.js:200:11:200:26 | "rm -rf " + name | String concatenation | lib/lib.js:200:3:200:27 | cp.exec ... + name) | shell command |
-| lib/lib.js:207:10:207:25 | "rm -rf " + name | lib/lib.js:206:45:206:48 | name | lib/lib.js:207:22:207:25 | name | $@ based on library input is later used in $@. | lib/lib.js:207:10:207:25 | "rm -rf " + name | String concatenation | lib/lib.js:207:2:207:26 | cp.exec ... + name) | shell command |
-| lib/lib.js:212:11:212:26 | "rm -rf " + name | lib/lib.js:206:45:206:48 | name | lib/lib.js:212:23:212:26 | name | $@ based on library input is later used in $@. | lib/lib.js:212:11:212:26 | "rm -rf " + name | String concatenation | lib/lib.js:212:3:212:27 | cp.exec ... + name) | shell command |
-| lib/lib.js:217:10:217:25 | "rm -rf " + name | lib/lib.js:216:39:216:42 | name | lib/lib.js:217:22:217:25 | name | $@ based on library input is later used in $@. | lib/lib.js:217:10:217:25 | "rm -rf " + name | String concatenation | lib/lib.js:217:2:217:26 | cp.exec ... + name) | shell command |
-| lib/lib.js:220:11:220:26 | "rm -rf " + name | lib/lib.js:216:39:216:42 | name | lib/lib.js:220:23:220:26 | name | $@ based on library input is later used in $@. | lib/lib.js:220:11:220:26 | "rm -rf " + name | String concatenation | lib/lib.js:220:3:220:27 | cp.exec ... + name) | shell command |
-| lib/lib.js:224:10:224:25 | "rm -rf " + name | lib/lib.js:216:39:216:42 | name | lib/lib.js:224:22:224:25 | name | $@ based on library input is later used in $@. | lib/lib.js:224:10:224:25 | "rm -rf " + name | String concatenation | lib/lib.js:224:2:224:26 | cp.exec ... + name) | shell command |
-| lib/lib.js:228:10:228:25 | "rm -rf " + name | lib/lib.js:227:39:227:42 | name | lib/lib.js:228:22:228:25 | name | $@ based on library input is later used in $@. | lib/lib.js:228:10:228:25 | "rm -rf " + name | String concatenation | lib/lib.js:228:2:228:26 | cp.exec ... + name) | shell command |
-| lib/lib.js:236:10:236:25 | "rm -rf " + name | lib/lib.js:227:39:227:42 | name | lib/lib.js:236:22:236:25 | name | $@ based on library input is later used in $@. | lib/lib.js:236:10:236:25 | "rm -rf " + name | String concatenation | lib/lib.js:236:2:236:26 | cp.exec ... + name) | shell command |
-| lib/lib.js:249:10:249:25 | "rm -rf " + name | lib/lib.js:248:42:248:45 | name | lib/lib.js:249:22:249:25 | name | $@ based on library input is later used in $@. | lib/lib.js:249:10:249:25 | "rm -rf " + name | String concatenation | lib/lib.js:249:2:249:26 | cp.exec ... + name) | shell command |
-| lib/lib.js:258:10:258:25 | "rm -rf " + name | lib/lib.js:257:35:257:38 | name | lib/lib.js:258:22:258:25 | name | $@ based on library input is later used in $@. | lib/lib.js:258:10:258:25 | "rm -rf " + name | String concatenation | lib/lib.js:258:2:258:26 | cp.exec ... + name) | shell command |
-| lib/lib.js:261:11:261:33 | "rm -rf ... + name | lib/lib.js:257:35:257:38 | name | lib/lib.js:261:30:261:33 | name | $@ based on library input is later used in $@. | lib/lib.js:261:11:261:33 | "rm -rf ... + name | String concatenation | lib/lib.js:261:3:261:34 | cp.exec ... + name) | shell command |
-| lib/lib.js:268:10:268:32 | "rm -rf ... version | lib/lib.js:267:46:267:48 | obj | lib/lib.js:268:22:268:32 | obj.version | $@ based on library input is later used in $@. | lib/lib.js:268:10:268:32 | "rm -rf ... version | String concatenation | lib/lib.js:268:2:268:33 | cp.exec ... ersion) | shell command |
-| lib/lib.js:277:11:277:30 | "rm -rf " + opts.bla | lib/lib.js:276:8:276:11 | opts | lib/lib.js:277:23:277:30 | opts.bla | $@ based on library input is later used in $@. | lib/lib.js:277:11:277:30 | "rm -rf " + opts.bla | String concatenation | lib/lib.js:277:3:277:31 | cp.exec ... ts.bla) | shell command |
-| lib/lib.js:308:11:308:26 | "rm -rf " + name | lib/lib.js:307:39:307:42 | name | lib/lib.js:308:23:308:26 | name | $@ based on library input is later used in $@. | lib/lib.js:308:11:308:26 | "rm -rf " + name | String concatenation | lib/lib.js:308:3:308:27 | cp.exec ... + name) | shell command |
-| lib/lib.js:315:10:315:25 | "rm -rf " + name | lib/lib.js:314:40:314:43 | name | lib/lib.js:315:22:315:25 | name | $@ based on library input is later used in $@. | lib/lib.js:315:10:315:25 | "rm -rf " + name | String concatenation | lib/lib.js:315:2:315:26 | cp.exec ... + name) | shell command |
-| lib/lib.js:320:11:320:26 | "rm -rf " + name | lib/lib.js:314:40:314:43 | name | lib/lib.js:320:23:320:26 | name | $@ based on library input is later used in $@. | lib/lib.js:320:11:320:26 | "rm -rf " + name | String concatenation | lib/lib.js:320:3:320:27 | cp.exec ... + name) | shell command |
-| lib/lib.js:325:12:325:51 | "MyWind ... " + arg | lib/lib.js:324:40:324:42 | arg | lib/lib.js:325:49:325:51 | arg | $@ based on library input is later used in $@. | lib/lib.js:325:12:325:51 | "MyWind ... " + arg | String concatenation | lib/lib.js:326:2:326:13 | cp.exec(cmd) | shell command |
-| lib/lib.js:340:10:340:26 | "rm -rf " + id(n) | lib/lib.js:339:39:339:39 | n | lib/lib.js:340:22:340:26 | id(n) | $@ based on library input is later used in $@. | lib/lib.js:340:10:340:26 | "rm -rf " + id(n) | String concatenation | lib/lib.js:340:2:340:27 | cp.exec ... id(n)) | shell command |
-| lib/lib.js:351:10:351:27 | "rm -rf " + unsafe | lib/lib.js:349:29:349:34 | unsafe | lib/lib.js:351:22:351:27 | unsafe | $@ based on library input is later used in $@. | lib/lib.js:351:10:351:27 | "rm -rf " + unsafe | String concatenation | lib/lib.js:351:2:351:28 | cp.exec ... unsafe) | shell command |
-| lib/lib.js:366:17:366:56 | "learn ... + model | lib/lib.js:360:20:360:23 | opts | lib/lib.js:366:28:366:42 | this.learn_args | $@ based on library input is later used in $@. | lib/lib.js:366:17:366:56 | "learn ... + model | String concatenation | lib/lib.js:367:3:367:18 | cp.exec(command) | shell command |
-| lib/lib.js:406:10:406:25 | "rm -rf " + name | lib/lib.js:405:39:405:42 | name | lib/lib.js:406:22:406:25 | name | $@ based on library input is later used in $@. | lib/lib.js:406:10:406:25 | "rm -rf " + name | String concatenation | lib/lib.js:406:2:406:26 | cp.exec ... + name) | shell command |
+| lib/lib2.js:4:10:4:25 | "rm -rf " + name | lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name | $@ based on $@ is later used in $@. | lib/lib2.js:4:10:4:25 | "rm -rf " + name | String concatenation | lib/lib2.js:3:28:3:31 | name | library input | lib/lib2.js:4:2:4:26 | cp.exec ... + name) | shell command |
+| lib/lib2.js:8:10:8:25 | "rm -rf " + name | lib/lib2.js:7:32:7:35 | name | lib/lib2.js:8:22:8:25 | name | $@ based on $@ is later used in $@. | lib/lib2.js:8:10:8:25 | "rm -rf " + name | String concatenation | lib/lib2.js:7:32:7:35 | name | library input | lib/lib2.js:8:2:8:26 | cp.exec ... + name) | shell command |
+| lib/lib.js:4:10:4:25 | "rm -rf " + name | lib/lib.js:3:28:3:31 | name | lib/lib.js:4:22:4:25 | name | $@ based on $@ is later used in $@. | lib/lib.js:4:10:4:25 | "rm -rf " + name | String concatenation | lib/lib.js:3:28:3:31 | name | library input | lib/lib.js:4:2:4:26 | cp.exec ... + name) | shell command |
+| lib/lib.js:11:10:11:25 | "rm -rf " + name | lib/lib.js:10:32:10:35 | name | lib/lib.js:11:22:11:25 | name | $@ based on $@ is later used in $@. | lib/lib.js:11:10:11:25 | "rm -rf " + name | String concatenation | lib/lib.js:10:32:10:35 | name | library input | lib/lib.js:11:2:11:26 | cp.exec ... + name) | shell command |
+| lib/lib.js:15:10:15:25 | "rm -rf " + name | lib/lib.js:14:36:14:39 | name | lib/lib.js:15:22:15:25 | name | $@ based on $@ is later used in $@. | lib/lib.js:15:10:15:25 | "rm -rf " + name | String concatenation | lib/lib.js:14:36:14:39 | name | library input | lib/lib.js:15:2:15:26 | cp.exec ... + name) | shell command |
+| lib/lib.js:20:10:20:25 | "rm -rf " + name | lib/lib.js:19:34:19:37 | name | lib/lib.js:20:22:20:25 | name | $@ based on $@ is later used in $@. | lib/lib.js:20:10:20:25 | "rm -rf " + name | String concatenation | lib/lib.js:19:34:19:37 | name | library input | lib/lib.js:20:2:20:26 | cp.exec ... + name) | shell command |
+| lib/lib.js:27:10:27:25 | "rm -rf " + name | lib/lib.js:26:35:26:38 | name | lib/lib.js:27:22:27:25 | name | $@ based on $@ is later used in $@. | lib/lib.js:27:10:27:25 | "rm -rf " + name | String concatenation | lib/lib.js:26:35:26:38 | name | library input | lib/lib.js:27:2:27:26 | cp.exec ... + name) | shell command |
+| lib/lib.js:35:11:35:26 | "rm -rf " + name | lib/lib.js:34:14:34:17 | name | lib/lib.js:35:23:35:26 | name | $@ based on $@ is later used in $@. | lib/lib.js:35:11:35:26 | "rm -rf " + name | String concatenation | lib/lib.js:34:14:34:17 | name | library input | lib/lib.js:35:3:35:27 | cp.exec ... + name) | shell command |
+| lib/lib.js:38:11:38:26 | "rm -rf " + name | lib/lib.js:37:13:37:16 | name | lib/lib.js:38:23:38:26 | name | $@ based on $@ is later used in $@. | lib/lib.js:38:11:38:26 | "rm -rf " + name | String concatenation | lib/lib.js:37:13:37:16 | name | library input | lib/lib.js:38:3:38:27 | cp.exec ... + name) | shell command |
+| lib/lib.js:41:11:41:26 | "rm -rf " + name | lib/lib.js:40:6:40:9 | name | lib/lib.js:41:23:41:26 | name | $@ based on $@ is later used in $@. | lib/lib.js:41:11:41:26 | "rm -rf " + name | String concatenation | lib/lib.js:40:6:40:9 | name | library input | lib/lib.js:41:3:41:27 | cp.exec ... + name) | shell command |
+| lib/lib.js:50:35:50:50 | "rm -rf " + name | lib/lib.js:49:31:49:34 | name | lib/lib.js:50:47:50:50 | name | $@ based on $@ is later used in $@. | lib/lib.js:50:35:50:50 | "rm -rf " + name | String concatenation | lib/lib.js:49:31:49:34 | name | library input | lib/lib.js:50:2:50:51 | require ... + name) | shell command |
+| lib/lib.js:54:13:54:28 | "rm -rf " + name | lib/lib.js:53:33:53:36 | name | lib/lib.js:54:25:54:28 | name | $@ based on $@ is later used in $@. | lib/lib.js:54:13:54:28 | "rm -rf " + name | String concatenation | lib/lib.js:53:33:53:36 | name | library input | lib/lib.js:55:2:55:14 | cp.exec(cmd1) | shell command |
+| lib/lib.js:57:13:57:28 | "rm -rf " + name | lib/lib.js:53:33:53:36 | name | lib/lib.js:57:25:57:28 | name | $@ based on $@ is later used in $@. | lib/lib.js:57:13:57:28 | "rm -rf " + name | String concatenation | lib/lib.js:53:33:53:36 | name | library input | lib/lib.js:59:3:59:14 | cp.exec(cmd) | shell command |
+| lib/lib.js:65:10:65:25 | "rm -rf " + name | lib/lib.js:64:41:64:44 | name | lib/lib.js:65:22:65:25 | name | $@ based on $@ is later used in $@. | lib/lib.js:65:10:65:25 | "rm -rf " + name | String concatenation | lib/lib.js:64:41:64:44 | name | library input | lib/lib.js:65:2:65:26 | cp.exec ... + name) | shell command |
+| lib/lib.js:71:10:71:31 | "cat /f ... + name | lib/lib.js:64:41:64:44 | name | lib/lib.js:71:28:71:31 | name | $@ based on $@ is later used in $@. | lib/lib.js:71:10:71:31 | "cat /f ... + name | String concatenation | lib/lib.js:64:41:64:44 | name | library input | lib/lib.js:71:2:71:32 | cp.exec ... + name) | shell command |
+| lib/lib.js:73:10:73:31 | "cat \\" ... + "\\"" | lib/lib.js:64:41:64:44 | name | lib/lib.js:73:21:73:24 | name | $@ based on $@ is later used in $@. | lib/lib.js:73:10:73:31 | "cat \\" ... + "\\"" | String concatenation | lib/lib.js:64:41:64:44 | name | library input | lib/lib.js:73:2:73:32 | cp.exec ... + "\\"") | shell command |
+| lib/lib.js:75:10:75:29 | "cat '" + name + "'" | lib/lib.js:64:41:64:44 | name | lib/lib.js:75:20:75:23 | name | $@ based on $@ is later used in $@. | lib/lib.js:75:10:75:29 | "cat '" + name + "'" | String concatenation | lib/lib.js:64:41:64:44 | name | library input | lib/lib.js:75:2:75:30 | cp.exec ... + "'") | shell command |
+| lib/lib.js:77:10:77:37 | "cat '/ ... e + "'" | lib/lib.js:64:41:64:44 | name | lib/lib.js:77:28:77:31 | name | $@ based on $@ is later used in $@. | lib/lib.js:77:10:77:37 | "cat '/ ... e + "'" | String concatenation | lib/lib.js:64:41:64:44 | name | library input | lib/lib.js:77:2:77:38 | cp.exec ... + "'") | shell command |
+| lib/lib.js:83:10:83:25 | "rm -rf " + name | lib/lib.js:82:35:82:38 | name | lib/lib.js:83:22:83:25 | name | $@ based on $@ is later used in $@. | lib/lib.js:83:10:83:25 | "rm -rf " + name | String concatenation | lib/lib.js:82:35:82:38 | name | library input | lib/lib.js:83:2:83:26 | cp.exec ... + name) | shell command |
+| lib/lib.js:86:13:86:16 | name | lib/lib.js:82:35:82:38 | name | lib/lib.js:86:13:86:16 | name | $@ based on $@ is later used in $@. | lib/lib.js:86:13:86:16 | name | Array element | lib/lib.js:82:35:82:38 | name | library input | lib/lib.js:87:2:87:25 | cp.exec ... n(" ")) | shell command |
+| lib/lib.js:89:21:89:24 | name | lib/lib.js:82:35:82:38 | name | lib/lib.js:89:21:89:24 | name | $@ based on $@ is later used in $@. | lib/lib.js:89:21:89:24 | name | Array element | lib/lib.js:82:35:82:38 | name | library input | lib/lib.js:89:2:89:36 | cp.exec ... n(" ")) | shell command |
+| lib/lib.js:91:21:91:38 | "\\"" + name + "\\"" | lib/lib.js:82:35:82:38 | name | lib/lib.js:91:21:91:38 | "\\"" + name + "\\"" | $@ based on $@ is later used in $@. | lib/lib.js:91:21:91:38 | "\\"" + name + "\\"" | Array element | lib/lib.js:82:35:82:38 | name | library input | lib/lib.js:91:2:91:50 | cp.exec ... n(" ")) | shell command |
+| lib/lib.js:98:35:98:38 | name | lib/lib.js:97:35:97:38 | name | lib/lib.js:98:35:98:38 | name | $@ based on $@ is later used in $@. | lib/lib.js:98:35:98:38 | name | Formatted string | lib/lib.js:97:35:97:38 | name | library input | lib/lib.js:98:2:98:40 | cp.exec ... name)) | shell command |
+| lib/lib.js:100:37:100:40 | name | lib/lib.js:97:35:97:38 | name | lib/lib.js:100:37:100:40 | name | $@ based on $@ is later used in $@. | lib/lib.js:100:37:100:40 | name | Formatted string | lib/lib.js:97:35:97:38 | name | library input | lib/lib.js:100:2:100:42 | cp.exec ... name)) | shell command |
+| lib/lib.js:102:46:102:49 | name | lib/lib.js:97:35:97:38 | name | lib/lib.js:102:46:102:49 | name | $@ based on $@ is later used in $@. | lib/lib.js:102:46:102:49 | name | Formatted string | lib/lib.js:97:35:97:38 | name | library input | lib/lib.js:102:2:102:51 | cp.exec ... name)) | shell command |
+| lib/lib.js:108:41:108:44 | name | lib/lib.js:97:35:97:38 | name | lib/lib.js:108:41:108:44 | name | $@ based on $@ is later used in $@. | lib/lib.js:108:41:108:44 | name | Formatted string | lib/lib.js:97:35:97:38 | name | library input | lib/lib.js:108:2:108:46 | cp.exec ... name)) | shell command |
+| lib/lib.js:112:10:112:25 | "rm -rf " + name | lib/lib.js:111:34:111:37 | name | lib/lib.js:112:22:112:25 | name | $@ based on $@ is later used in $@. | lib/lib.js:112:10:112:25 | "rm -rf " + name | String concatenation | lib/lib.js:111:34:111:37 | name | library input | lib/lib.js:112:2:112:26 | cp.exec ... + name) | shell command |
+| lib/lib.js:121:10:121:25 | "rm -rf " + name | lib/lib.js:120:33:120:36 | name | lib/lib.js:121:22:121:25 | name | $@ based on $@ is later used in $@. | lib/lib.js:121:10:121:25 | "rm -rf " + name | String concatenation | lib/lib.js:120:33:120:36 | name | library input | lib/lib.js:121:2:121:26 | cp.exec ... + name) | shell command |
+| lib/lib.js:131:11:131:26 | "rm -rf " + name | lib/lib.js:130:6:130:9 | name | lib/lib.js:131:23:131:26 | name | $@ based on $@ is later used in $@. | lib/lib.js:131:11:131:26 | "rm -rf " + name | String concatenation | lib/lib.js:130:6:130:9 | name | library input | lib/lib.js:131:3:131:27 | cp.exec ... + name) | shell command |
+| lib/lib.js:149:12:149:27 | "rm -rf " + name | lib/lib.js:148:37:148:40 | name | lib/lib.js:149:24:149:27 | name | $@ based on $@ is later used in $@. | lib/lib.js:149:12:149:27 | "rm -rf " + name | String concatenation | lib/lib.js:148:37:148:40 | name | library input | lib/lib.js:152:2:152:23 | cp.spaw ... gs, cb) | shell command |
+| lib/lib.js:161:13:161:28 | "rm -rf " + name | lib/lib.js:155:38:155:41 | name | lib/lib.js:161:25:161:28 | name | $@ based on $@ is later used in $@. | lib/lib.js:161:13:161:28 | "rm -rf " + name | String concatenation | lib/lib.js:155:38:155:41 | name | library input | lib/lib.js:163:2:167:2 | cp.spaw ... t' }\\n\\t) | shell command |
+| lib/lib.js:173:10:173:23 | "fo \| " + name | lib/lib.js:170:41:170:44 | name | lib/lib.js:173:20:173:23 | name | $@ based on $@ is later used in $@. | lib/lib.js:173:10:173:23 | "fo \| " + name | String concatenation | lib/lib.js:170:41:170:44 | name | library input | lib/lib.js:173:2:173:24 | cp.exec ... + name) | shell command |
+| lib/lib.js:182:10:182:27 | "rm -rf " + broken | lib/lib.js:177:38:177:41 | name | lib/lib.js:182:22:182:27 | broken | $@ based on $@ is later used in $@. | lib/lib.js:182:10:182:27 | "rm -rf " + broken | String concatenation | lib/lib.js:177:38:177:41 | name | library input | lib/lib.js:182:2:182:28 | cp.exec ... broken) | shell command |
+| lib/lib.js:187:10:187:25 | "rm -rf " + name | lib/lib.js:186:34:186:37 | name | lib/lib.js:187:22:187:25 | name | $@ based on $@ is later used in $@. | lib/lib.js:187:10:187:25 | "rm -rf " + name | String concatenation | lib/lib.js:186:34:186:37 | name | library input | lib/lib.js:187:2:187:26 | cp.exec ... + name) | shell command |
+| lib/lib.js:190:11:190:26 | "rm -rf " + name | lib/lib.js:186:34:186:37 | name | lib/lib.js:190:23:190:26 | name | $@ based on $@ is later used in $@. | lib/lib.js:190:11:190:26 | "rm -rf " + name | String concatenation | lib/lib.js:186:34:186:37 | name | library input | lib/lib.js:190:3:190:27 | cp.exec ... + name) | shell command |
+| lib/lib.js:197:10:197:25 | "rm -rf " + name | lib/lib.js:196:45:196:48 | name | lib/lib.js:197:22:197:25 | name | $@ based on $@ is later used in $@. | lib/lib.js:197:10:197:25 | "rm -rf " + name | String concatenation | lib/lib.js:196:45:196:48 | name | library input | lib/lib.js:197:2:197:26 | cp.exec ... + name) | shell command |
+| lib/lib.js:200:11:200:26 | "rm -rf " + name | lib/lib.js:196:45:196:48 | name | lib/lib.js:200:23:200:26 | name | $@ based on $@ is later used in $@. | lib/lib.js:200:11:200:26 | "rm -rf " + name | String concatenation | lib/lib.js:196:45:196:48 | name | library input | lib/lib.js:200:3:200:27 | cp.exec ... + name) | shell command |
+| lib/lib.js:207:10:207:25 | "rm -rf " + name | lib/lib.js:206:45:206:48 | name | lib/lib.js:207:22:207:25 | name | $@ based on $@ is later used in $@. | lib/lib.js:207:10:207:25 | "rm -rf " + name | String concatenation | lib/lib.js:206:45:206:48 | name | library input | lib/lib.js:207:2:207:26 | cp.exec ... + name) | shell command |
+| lib/lib.js:212:11:212:26 | "rm -rf " + name | lib/lib.js:206:45:206:48 | name | lib/lib.js:212:23:212:26 | name | $@ based on $@ is later used in $@. | lib/lib.js:212:11:212:26 | "rm -rf " + name | String concatenation | lib/lib.js:206:45:206:48 | name | library input | lib/lib.js:212:3:212:27 | cp.exec ... + name) | shell command |
+| lib/lib.js:217:10:217:25 | "rm -rf " + name | lib/lib.js:216:39:216:42 | name | lib/lib.js:217:22:217:25 | name | $@ based on $@ is later used in $@. | lib/lib.js:217:10:217:25 | "rm -rf " + name | String concatenation | lib/lib.js:216:39:216:42 | name | library input | lib/lib.js:217:2:217:26 | cp.exec ... + name) | shell command |
+| lib/lib.js:220:11:220:26 | "rm -rf " + name | lib/lib.js:216:39:216:42 | name | lib/lib.js:220:23:220:26 | name | $@ based on $@ is later used in $@. | lib/lib.js:220:11:220:26 | "rm -rf " + name | String concatenation | lib/lib.js:216:39:216:42 | name | library input | lib/lib.js:220:3:220:27 | cp.exec ... + name) | shell command |
+| lib/lib.js:224:10:224:25 | "rm -rf " + name | lib/lib.js:216:39:216:42 | name | lib/lib.js:224:22:224:25 | name | $@ based on $@ is later used in $@. | lib/lib.js:224:10:224:25 | "rm -rf " + name | String concatenation | lib/lib.js:216:39:216:42 | name | library input | lib/lib.js:224:2:224:26 | cp.exec ... + name) | shell command |
+| lib/lib.js:228:10:228:25 | "rm -rf " + name | lib/lib.js:227:39:227:42 | name | lib/lib.js:228:22:228:25 | name | $@ based on $@ is later used in $@. | lib/lib.js:228:10:228:25 | "rm -rf " + name | String concatenation | lib/lib.js:227:39:227:42 | name | library input | lib/lib.js:228:2:228:26 | cp.exec ... + name) | shell command |
+| lib/lib.js:236:10:236:25 | "rm -rf " + name | lib/lib.js:227:39:227:42 | name | lib/lib.js:236:22:236:25 | name | $@ based on $@ is later used in $@. | lib/lib.js:236:10:236:25 | "rm -rf " + name | String concatenation | lib/lib.js:227:39:227:42 | name | library input | lib/lib.js:236:2:236:26 | cp.exec ... + name) | shell command |
+| lib/lib.js:249:10:249:25 | "rm -rf " + name | lib/lib.js:248:42:248:45 | name | lib/lib.js:249:22:249:25 | name | $@ based on $@ is later used in $@. | lib/lib.js:249:10:249:25 | "rm -rf " + name | String concatenation | lib/lib.js:248:42:248:45 | name | library input | lib/lib.js:249:2:249:26 | cp.exec ... + name) | shell command |
+| lib/lib.js:258:10:258:25 | "rm -rf " + name | lib/lib.js:257:35:257:38 | name | lib/lib.js:258:22:258:25 | name | $@ based on $@ is later used in $@. | lib/lib.js:258:10:258:25 | "rm -rf " + name | String concatenation | lib/lib.js:257:35:257:38 | name | library input | lib/lib.js:258:2:258:26 | cp.exec ... + name) | shell command |
+| lib/lib.js:261:11:261:33 | "rm -rf ... + name | lib/lib.js:257:35:257:38 | name | lib/lib.js:261:30:261:33 | name | $@ based on $@ is later used in $@. | lib/lib.js:261:11:261:33 | "rm -rf ... + name | String concatenation | lib/lib.js:257:35:257:38 | name | library input | lib/lib.js:261:3:261:34 | cp.exec ... + name) | shell command |
+| lib/lib.js:268:10:268:32 | "rm -rf ... version | lib/lib.js:267:46:267:48 | obj | lib/lib.js:268:22:268:32 | obj.version | $@ based on $@ is later used in $@. | lib/lib.js:268:10:268:32 | "rm -rf ... version | String concatenation | lib/lib.js:267:46:267:48 | obj | library input | lib/lib.js:268:2:268:33 | cp.exec ... ersion) | shell command |
+| lib/lib.js:277:11:277:30 | "rm -rf " + opts.bla | lib/lib.js:276:8:276:11 | opts | lib/lib.js:277:23:277:30 | opts.bla | $@ based on $@ is later used in $@. | lib/lib.js:277:11:277:30 | "rm -rf " + opts.bla | String concatenation | lib/lib.js:276:8:276:11 | opts | library input | lib/lib.js:277:3:277:31 | cp.exec ... ts.bla) | shell command |
+| lib/lib.js:308:11:308:26 | "rm -rf " + name | lib/lib.js:307:39:307:42 | name | lib/lib.js:308:23:308:26 | name | $@ based on $@ is later used in $@. | lib/lib.js:308:11:308:26 | "rm -rf " + name | String concatenation | lib/lib.js:307:39:307:42 | name | library input | lib/lib.js:308:3:308:27 | cp.exec ... + name) | shell command |
+| lib/lib.js:315:10:315:25 | "rm -rf " + name | lib/lib.js:314:40:314:43 | name | lib/lib.js:315:22:315:25 | name | $@ based on $@ is later used in $@. | lib/lib.js:315:10:315:25 | "rm -rf " + name | String concatenation | lib/lib.js:314:40:314:43 | name | library input | lib/lib.js:315:2:315:26 | cp.exec ... + name) | shell command |
+| lib/lib.js:320:11:320:26 | "rm -rf " + name | lib/lib.js:314:40:314:43 | name | lib/lib.js:320:23:320:26 | name | $@ based on $@ is later used in $@. | lib/lib.js:320:11:320:26 | "rm -rf " + name | String concatenation | lib/lib.js:314:40:314:43 | name | library input | lib/lib.js:320:3:320:27 | cp.exec ... + name) | shell command |
+| lib/lib.js:325:12:325:51 | "MyWind ... " + arg | lib/lib.js:324:40:324:42 | arg | lib/lib.js:325:49:325:51 | arg | $@ based on $@ is later used in $@. | lib/lib.js:325:12:325:51 | "MyWind ... " + arg | String concatenation | lib/lib.js:324:40:324:42 | arg | library input | lib/lib.js:326:2:326:13 | cp.exec(cmd) | shell command |
+| lib/lib.js:340:10:340:26 | "rm -rf " + id(n) | lib/lib.js:339:39:339:39 | n | lib/lib.js:340:22:340:26 | id(n) | $@ based on $@ is later used in $@. | lib/lib.js:340:10:340:26 | "rm -rf " + id(n) | String concatenation | lib/lib.js:339:39:339:39 | n | library input | lib/lib.js:340:2:340:27 | cp.exec ... id(n)) | shell command |
+| lib/lib.js:351:10:351:27 | "rm -rf " + unsafe | lib/lib.js:349:29:349:34 | unsafe | lib/lib.js:351:22:351:27 | unsafe | $@ based on $@ is later used in $@. | lib/lib.js:351:10:351:27 | "rm -rf " + unsafe | String concatenation | lib/lib.js:349:29:349:34 | unsafe | library input | lib/lib.js:351:2:351:28 | cp.exec ... unsafe) | shell command |
+| lib/lib.js:366:17:366:56 | "learn ... + model | lib/lib.js:360:20:360:23 | opts | lib/lib.js:366:28:366:42 | this.learn_args | $@ based on $@ is later used in $@. | lib/lib.js:366:17:366:56 | "learn ... + model | String concatenation | lib/lib.js:360:20:360:23 | opts | library input | lib/lib.js:367:3:367:18 | cp.exec(command) | shell command |
+| lib/lib.js:406:10:406:25 | "rm -rf " + name | lib/lib.js:405:39:405:42 | name | lib/lib.js:406:22:406:25 | name | $@ based on $@ is later used in $@. | lib/lib.js:406:10:406:25 | "rm -rf " + name | String concatenation | lib/lib.js:405:39:405:42 | name | library input | lib/lib.js:406:2:406:26 | cp.exec ... + name) | shell command |
+| lib/subLib/index.js:4:10:4:25 | "rm -rf " + name | lib/subLib/index.js:3:28:3:31 | name | lib/subLib/index.js:4:22:4:25 | name | $@ based on $@ is later used in $@. | lib/subLib/index.js:4:10:4:25 | "rm -rf " + name | String concatenation | lib/subLib/index.js:3:28:3:31 | name | library input | lib/subLib/index.js:4:2:4:26 | cp.exec ... + name) | shell command |
+| lib/subLib/index.js:8:10:8:25 | "rm -rf " + name | lib/subLib/index.js:7:32:7:35 | name | lib/subLib/index.js:8:22:8:25 | name | $@ based on $@ is later used in $@. | lib/subLib/index.js:8:10:8:25 | "rm -rf " + name | String concatenation | lib/subLib/index.js:7:32:7:35 | name | library input | lib/subLib/index.js:8:2:8:26 | cp.exec ... + name) | shell command |
diff --git a/javascript/ql/test/query-tests/Security/CWE-078/lib/subLib/index.js b/javascript/ql/test/query-tests/Security/CWE-078/lib/subLib/index.js
index c101df07d02..325a652c9b5 100644
--- a/javascript/ql/test/query-tests/Security/CWE-078/lib/subLib/index.js
+++ b/javascript/ql/test/query-tests/Security/CWE-078/lib/subLib/index.js
@@ -1,7 +1,7 @@
var cp = require("child_process")
module.exports = function (name) {
- cp.exec("rm -rf " + name); // OK - this file belongs in a sub-"module", and is not the primary exported module.
+ cp.exec("rm -rf " + name); // NOT OK - functions exported as part of a submodule are also flagged.
};
module.exports.foo = function (name) {
From 0e98ea0c1034c9338dbd5d4434b5b31744c20fb1 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 18 Mar 2021 14:09:08 +0100
Subject: [PATCH 177/725] remove spurious import of PackageExports
---
javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll | 2 --
1 file changed, 2 deletions(-)
diff --git a/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll b/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll
index d05c2194c94..9894dd119df 100644
--- a/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll
+++ b/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll
@@ -646,8 +646,6 @@ module NodeJSLib {
}
}
- private import semmle.javascript.PackageExports as Exports
-
/**
* A direct step from an named export to a property-read reading the exported value.
*/
From 38a9c71380e952e81ccde62e339e8f54ef412cda Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 18 Mar 2021 14:30:10 +0100
Subject: [PATCH 178/725] Apply suggestions from code review
Co-authored-by: Asger F
---
.../ql/src/semmle/javascript/frameworks/Koa.qll | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/javascript/ql/src/semmle/javascript/frameworks/Koa.qll b/javascript/ql/src/semmle/javascript/frameworks/Koa.qll
index 112203cb26d..4372a7480ba 100644
--- a/javascript/ql/src/semmle/javascript/frameworks/Koa.qll
+++ b/javascript/ql/src/semmle/javascript/frameworks/Koa.qll
@@ -72,9 +72,9 @@ module Koa {
}
/**
- * Gets the dataflow node that is given to a `RouteSetup` to register the handler.
+ * Gets a dataflow node that can be given to a `RouteSetup` to register the handler.
*/
- abstract DataFlow::SourceNode getRouteHandlerRegistration();
+ abstract DataFlow::SourceNode getARouteHandlerRegistrationObject();
}
/**
@@ -91,7 +91,7 @@ module Koa {
private class StandardRouteHandler extends RouteHandler {
StandardRouteHandler() { any(RouteSetup setup).getARouteHandler() = this }
- override DataFlow::SourceNode getRouteHandlerRegistration() { result = this }
+ override DataFlow::SourceNode getARouteHandlerRegistrationObject() { result = this }
}
/**
@@ -136,7 +136,7 @@ module Koa {
* app.use(router.routes());
* ```
*/
- private class RoutedRouteHandler extends RouteHandler, DataFlow::SourceNode {
+ private class RoutedRouteHandler extends RouteHandler {
DataFlow::InvokeNode router;
DataFlow::MethodCallNode call;
@@ -151,7 +151,7 @@ module Koa {
this.flowsTo(call.getArgument(any(int i | i >= 1)))
}
- override DataFlow::SourceNode getRouteHandlerRegistration() {
+ override DataFlow::SourceNode getARouteHandlerRegistrationObject() {
result = call
or
result = router.getAMethodCall("routes")
@@ -171,7 +171,7 @@ module Koa {
* // route handler stuff
* }));
*/
- class KoaRouteHandler extends RouteHandler, DataFlow::SourceNode {
+ class KoaRouteHandler extends RouteHandler {
DataFlow::CallNode call;
KoaRouteHandler() {
@@ -190,7 +190,7 @@ module Koa {
result = call.getABoundCallbackParameter(1, any(int i | i >= 1))
}
- override DataFlow::SourceNode getRouteHandlerRegistration() { result = call }
+ override DataFlow::SourceNode getARouteHandlerRegistrationObject() { result = call }
}
/**
@@ -390,7 +390,7 @@ module Koa {
result.flowsToExpr(getArgument(0))
or
// For the route-handlers that does not depend on this predicate in their charpred.
- result.(RouteHandler).getRouteHandlerRegistration().flowsToExpr(getArgument(0))
+ result.(RouteHandler).getARouteHandlerRegistrationObject().flowsToExpr(getArgument(0))
}
override Expr getServer() { result = server }
From f94f82a0dc442a96165a5b38b5c975e84f502c7a Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 18 Mar 2021 14:35:10 +0100
Subject: [PATCH 179/725] use getAChainedMethodCall
---
.../ql/src/semmle/javascript/frameworks/Koa.qll | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/javascript/ql/src/semmle/javascript/frameworks/Koa.qll b/javascript/ql/src/semmle/javascript/frameworks/Koa.qll
index 4372a7480ba..59d256cdba2 100644
--- a/javascript/ql/src/semmle/javascript/frameworks/Koa.qll
+++ b/javascript/ql/src/semmle/javascript/frameworks/Koa.qll
@@ -142,12 +142,12 @@ module Koa {
RoutedRouteHandler() {
router = DataFlow::moduleImport(["@koa/router", "koa-router"]).getAnInvocation() and
- call = router.getAMethodCall*() and
- call.getMethodName() =
- [
- "use", "get", "post", "put", "link", "unlink", "delete", "del", "head", "options",
- "patch", "all"
- ] and
+ call =
+ router
+ .getAChainedMethodCall([
+ "use", "get", "post", "put", "link", "unlink", "delete", "del", "head", "options",
+ "patch", "all"
+ ]) and
this.flowsTo(call.getArgument(any(int i | i >= 1)))
}
From af5a61782cbaf151c3328faffded5cc36e183483 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 18 Mar 2021 14:51:11 +0100
Subject: [PATCH 180/725] also look for main modules in a `lib` folder
---
.../ql/src/semmle/javascript/NodeModuleResolutionImpl.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/src/semmle/javascript/NodeModuleResolutionImpl.qll b/javascript/ql/src/semmle/javascript/NodeModuleResolutionImpl.qll
index 60ccf7216c9..84538e9925a 100644
--- a/javascript/ql/src/semmle/javascript/NodeModuleResolutionImpl.qll
+++ b/javascript/ql/src/semmle/javascript/NodeModuleResolutionImpl.qll
@@ -98,7 +98,7 @@ File resolveMainModule(PackageJSON pkg, int priority) {
or
exists(Folder folder | folder = pkg.getFile().getParentContainer() |
result =
- tryExtensions([folder, folder.getChildContainer("src")], "index",
+ tryExtensions([folder, folder.getChildContainer(["src", "lib"])], "index",
priority - prioritiesPerCandidate())
)
}
From 7180a1ed5253a8a7368a1abc03af261764500576 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 18 Mar 2021 15:16:31 +0100
Subject: [PATCH 181/725] add `Type` to `MkHasUnderlyingType`
---
javascript/ql/src/semmle/javascript/ApiGraphs.qll | 2 ++
1 file changed, 2 insertions(+)
diff --git a/javascript/ql/src/semmle/javascript/ApiGraphs.qll b/javascript/ql/src/semmle/javascript/ApiGraphs.qll
index 1ab8234cb78..d9d4d0e80fa 100644
--- a/javascript/ql/src/semmle/javascript/ApiGraphs.qll
+++ b/javascript/ql/src/semmle/javascript/ApiGraphs.qll
@@ -423,6 +423,8 @@ module API {
*/
MkHasUnderlyingType(string moduleName, string exportName) {
any(TypeAnnotation n).hasQualifiedName(moduleName, exportName)
+ or
+ any(Type t).hasUnderlyingType(moduleName, exportName)
} or
MkSyntheticCallbackArg(DataFlow::Node src, int bound, DataFlow::InvokeNode nd) {
trackUseNode(src, true, bound).flowsTo(nd.getCalleeNode())
From ed8e0fb593143677c6ab2a0440fe1c5a5eb35d11 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 18 Mar 2021 15:34:17 +0100
Subject: [PATCH 182/725] remove CannonicalName API nodes
---
.../ql/src/semmle/javascript/ApiGraphs.qll | 74 +------------------
1 file changed, 2 insertions(+), 72 deletions(-)
diff --git a/javascript/ql/src/semmle/javascript/ApiGraphs.qll b/javascript/ql/src/semmle/javascript/ApiGraphs.qll
index d9d4d0e80fa..5b3e2ed5f09 100644
--- a/javascript/ql/src/semmle/javascript/ApiGraphs.qll
+++ b/javascript/ql/src/semmle/javascript/ApiGraphs.qll
@@ -313,11 +313,6 @@ module API {
module Node {
/** Gets a node whose type has the given qualified name. */
Node ofType(string moduleName, string exportedName) {
- exists(TypeName tn |
- tn.hasQualifiedName(moduleName, exportedName) and
- result = Impl::MkCanonicalNameUse(tn).(Node).getInstance()
- )
- or
result = Impl::MkHasUnderlyingType(moduleName, exportedName).(Node).getInstance()
}
}
@@ -395,28 +390,6 @@ module API {
} or
MkDef(DataFlow::Node nd) { rhs(_, _, nd) } or
MkUse(DataFlow::Node nd) { use(_, _, nd) } or
- /**
- * A TypeScript canonical name that is defined somewhere, and that isn't a module root.
- * (Module roots are represented by `MkModuleExport` nodes instead.)
- *
- * For most purposes, you probably want to use the `mkCanonicalNameDef` predicate instead of
- * this constructor.
- */
- MkCanonicalNameDef(CanonicalName n) {
- not n.isRoot() and
- isDefined(n)
- } or
- /**
- * A TypeScript canonical name that is used somewhere, and that isn't a module root.
- * (Module roots are represented by `MkModuleImport` nodes instead.)
- *
- * For most purposes, you probably want to use the `mkCanonicalNameUse` predicate instead of
- * this constructor.
- */
- MkCanonicalNameUse(CanonicalName n) {
- not n.isRoot() and
- isUsed(n)
- } or
/**
* A TypeScript type, identified by name of the type-annotation.
* This API node is exclusively used by `API::Node::ofType`.
@@ -433,11 +406,9 @@ module API {
class TDef = MkModuleDef or TNonModuleDef;
class TNonModuleDef =
- MkModuleExport or MkClassInstance or MkAsyncFuncResult or MkDef or MkCanonicalNameDef or
- MkSyntheticCallbackArg;
+ MkModuleExport or MkClassInstance or MkAsyncFuncResult or MkDef or MkSyntheticCallbackArg;
- class TUse =
- MkModuleUse or MkModuleImport or MkUse or MkCanonicalNameUse or MkHasUnderlyingType;
+ class TUse = MkModuleUse or MkModuleImport or MkUse or MkHasUnderlyingType;
private predicate hasSemantics(DataFlow::Node nd) { not nd.getTopLevel().isExterns() }
@@ -474,20 +445,6 @@ module API {
)
}
- /** An API-graph node representing definitions of the canonical name `cn`. */
- private TApiNode mkCanonicalNameDef(CanonicalName cn) {
- if cn.isModuleRoot()
- then result = MkModuleExport(cn.getExternalModuleName())
- else result = MkCanonicalNameDef(cn)
- }
-
- /** An API-graph node representing uses of the canonical name `cn`. */
- private TApiNode mkCanonicalNameUse(CanonicalName cn) {
- if cn.isModuleRoot()
- then result = MkModuleImport(cn.getExternalModuleName())
- else result = MkCanonicalNameUse(cn)
- }
-
/**
* Holds if `rhs` is the right-hand side of a definition of a node that should have an
* incoming edge from `base` labeled `lbl` in the API graph.
@@ -591,11 +548,6 @@ module API {
exists(string m | nd = MkModuleExport(m) | exports(m, rhs))
or
nd = MkDef(rhs)
- or
- exists(CanonicalName n | nd = MkCanonicalNameDef(n) |
- rhs = n.(Namespace).getADefinition().flow() or
- rhs = n.(CanonicalFunctionName).getADefinition().flow()
- )
}
/**
@@ -647,12 +599,6 @@ module API {
ref = cls.getConstructor().getParameter(i)
)
or
- exists(TypeName tn |
- base = MkCanonicalNameUse(tn) and
- lbl = Label::instance() and
- ref = getANodeWithType(tn)
- )
- or
exists(string moduleName, string exportName |
base = MkHasUnderlyingType(moduleName, exportName) and
lbl = Label::instance() and
@@ -696,8 +642,6 @@ module API {
)
or
nd = MkUse(ref)
- or
- exists(CanonicalName n | nd = MkCanonicalNameUse(n) | ref.asExpr() = n.getAnAccess())
}
/** Holds if module `m` exports `rhs`. */
@@ -852,13 +796,6 @@ module API {
result = awaited(call, DataFlow::TypeTracker::end())
}
- private DataFlow::SourceNode getANodeWithType(TypeName tn) {
- exists(string moduleName, string typeName |
- tn.hasQualifiedName(moduleName, typeName) and
- result.hasUnderlyingType(moduleName, typeName)
- )
- }
-
/**
* Holds if there is an edge from `pred` to `succ` in the API graph that is labeled with `lbl`.
*/
@@ -899,13 +836,6 @@ module API {
succ = MkClassInstance(trackDefNode(def))
)
or
- exists(CanonicalName cn1, string n, CanonicalName cn2 |
- pred in [mkCanonicalNameDef(cn1), mkCanonicalNameUse(cn1)] and
- cn2 = cn1.getChild(n) and
- lbl = Label::member(n) and
- succ in [mkCanonicalNameDef(cn2), mkCanonicalNameUse(cn2)]
- )
- or
exists(string moduleName, string exportName |
pred = MkModuleImport(moduleName) and
lbl = Label::member(exportName) and
From 58617c5c59f495cd6031204d2c4d0baf872494d8 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 17 Mar 2021 19:44:24 +0100
Subject: [PATCH 183/725] recognize client websockets as ClientRequests
---
.../javascript/frameworks/WebSocket.qll | 22 ++++++++++++++++++-
.../ClientRequests/ClientRequests.expected | 4 ++++
.../frameworks/ClientRequests/tst.js | 12 +++++++++-
.../Security/CWE-918/RequestForgery.expected | 16 ++++++++++++++
.../test/query-tests/Security/CWE-918/tst.js | 7 ++++++
5 files changed, 59 insertions(+), 2 deletions(-)
diff --git a/javascript/ql/src/semmle/javascript/frameworks/WebSocket.qll b/javascript/ql/src/semmle/javascript/frameworks/WebSocket.qll
index 8d4cb9b7126..fc3f1135d06 100644
--- a/javascript/ql/src/semmle/javascript/frameworks/WebSocket.qll
+++ b/javascript/ql/src/semmle/javascript/frameworks/WebSocket.qll
@@ -81,7 +81,7 @@ module ClientWebSocket {
/**
* A client WebSocket instance.
*/
- class ClientSocket extends EventEmitter::Range, DataFlow::SourceNode {
+ class ClientSocket extends EventEmitter::Range, DataFlow::NewNode, ClientRequest::Range {
SocketClass socketClass;
ClientSocket() { this = socketClass.getAnInstantiation() }
@@ -90,6 +90,26 @@ module ClientWebSocket {
* Gets the WebSocket library name.
*/
LibraryName getLibrary() { result = socketClass.getLibrary() }
+
+ override DataFlow::Node getUrl() { result = getArgument(0) }
+
+ override DataFlow::Node getHost() { none() }
+
+ override DataFlow::Node getADataNode() {
+ exists(SendNode send |
+ send.getEmitter() = this and
+ result = send.getSentItem(_)
+ )
+ }
+
+ override DataFlow::Node getAResponseDataNode(string responseType, boolean promise) {
+ responseType = "json" and
+ promise = false and
+ exists(WebSocketReceiveNode receiver |
+ receiver.getEmitter() = this and
+ result = receiver.getReceivedItem(_)
+ )
+ }
}
/**
diff --git a/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected b/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected
index 2bf2d305cb0..2ee9976ce3b 100644
--- a/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected
+++ b/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected
@@ -83,6 +83,7 @@ test_ClientRequest
| tst.js:269:13:269:48 | httpPro ... ptions) |
| tst.js:271:3:271:61 | proxy.w ... 080' }) |
| tst.js:274:1:283:2 | httpPro ... true\\n}) |
+| tst.js:286:20:286:55 | new Web ... :8080') |
test_getADataNode
| tst.js:53:5:53:23 | axios({data: data}) | tst.js:53:18:53:21 | data |
| tst.js:57:5:57:39 | axios.p ... data2}) | tst.js:57:19:57:23 | data1 |
@@ -121,6 +122,7 @@ test_getADataNode
| tst.js:249:1:251:2 | form.su ... e();\\n}) | tst.js:246:26:246:43 | Buffer.from("foo") |
| tst.js:249:1:251:2 | form.su ... e();\\n}) | tst.js:247:24:247:68 | request ... o.png') |
| tst.js:257:1:262:2 | form.su ... rs()\\n}) | tst.js:255:25:255:35 | 'new_value' |
+| tst.js:286:20:286:55 | new Web ... :8080') | tst.js:288:21:288:35 | 'Hello Server!' |
test_getHost
| tst.js:87:5:87:39 | http.ge ... host}) | tst.js:87:34:87:37 | host |
| tst.js:89:5:89:23 | axios({host: host}) | tst.js:89:18:89:21 | host |
@@ -218,6 +220,7 @@ test_getUrl
| tst.js:267:1:267:61 | httpPro ... 9000'}) | tst.js:267:37:267:59 | 'http:/ ... t:9000' |
| tst.js:271:3:271:61 | proxy.w ... 080' }) | tst.js:271:33:271:58 | 'http:/ ... m:8080' |
| tst.js:274:1:283:2 | httpPro ... true\\n}) | tst.js:275:13:281:5 | {\\n ... ,\\n } |
+| tst.js:286:20:286:55 | new Web ... :8080') | tst.js:286:34:286:54 | 'ws://l ... t:8080' |
test_getAResponseDataNode
| tst.js:19:5:19:23 | requestPromise(url) | tst.js:19:5:19:23 | requestPromise(url) | text | true |
| tst.js:21:5:21:23 | superagent.get(url) | tst.js:21:5:21:23 | superagent.get(url) | stream | true |
@@ -284,3 +287,4 @@ test_getAResponseDataNode
| tst.js:231:5:233:6 | needle. ... \\n }) | tst.js:231:50:231:53 | body | json | false |
| tst.js:235:5:237:6 | needle. ... \\n }) | tst.js:235:67:235:70 | resp | fetch.response | false |
| tst.js:235:5:237:6 | needle. ... \\n }) | tst.js:235:73:235:76 | body | json | false |
+| tst.js:286:20:286:55 | new Web ... :8080') | tst.js:291:44:291:53 | event.data | json | false |
diff --git a/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js b/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js
index 4003fd1b0a3..bc15565c072 100644
--- a/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js
+++ b/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js
@@ -280,4 +280,14 @@ httpProxy.createProxyServer({
passphrase: 'password',
},
changeOrigin: true
-}).listen(8000);
\ No newline at end of file
+}).listen(8000);
+
+function webSocket() {
+ const socket = new WebSocket('ws://localhost:8080');
+ socket.addEventListener('open', function (event) {
+ socket.send('Hello Server!');
+ });
+ socket.addEventListener('message', function (event) {
+ console.log("Data from server: " + event.data);
+ });
+}
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Security/CWE-918/RequestForgery.expected b/javascript/ql/test/query-tests/Security/CWE-918/RequestForgery.expected
index 06a55b512a0..4e24efd2a5d 100644
--- a/javascript/ql/test/query-tests/Security/CWE-918/RequestForgery.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-918/RequestForgery.expected
@@ -57,6 +57,14 @@ nodes
| tst.js:74:29:74:35 | req.url |
| tst.js:76:19:76:25 | tainted |
| tst.js:76:19:76:25 | tainted |
+| tst.js:81:9:81:52 | tainted |
+| tst.js:81:19:81:42 | url.par ... , true) |
+| tst.js:81:19:81:48 | url.par ... ).query |
+| tst.js:81:19:81:52 | url.par ... ery.url |
+| tst.js:81:29:81:35 | req.url |
+| tst.js:81:29:81:35 | req.url |
+| tst.js:83:19:83:25 | tainted |
+| tst.js:83:19:83:25 | tainted |
edges
| tst.js:14:9:14:52 | tainted | tst.js:18:13:18:19 | tainted |
| tst.js:14:9:14:52 | tainted | tst.js:18:13:18:19 | tainted |
@@ -113,6 +121,13 @@ edges
| tst.js:74:19:74:52 | url.par ... ery.url | tst.js:74:9:74:52 | tainted |
| tst.js:74:29:74:35 | req.url | tst.js:74:19:74:42 | url.par ... , true) |
| tst.js:74:29:74:35 | req.url | tst.js:74:19:74:42 | url.par ... , true) |
+| tst.js:81:9:81:52 | tainted | tst.js:83:19:83:25 | tainted |
+| tst.js:81:9:81:52 | tainted | tst.js:83:19:83:25 | tainted |
+| tst.js:81:19:81:42 | url.par ... , true) | tst.js:81:19:81:48 | url.par ... ).query |
+| tst.js:81:19:81:48 | url.par ... ).query | tst.js:81:19:81:52 | url.par ... ery.url |
+| tst.js:81:19:81:52 | url.par ... ery.url | tst.js:81:9:81:52 | tainted |
+| tst.js:81:29:81:35 | req.url | tst.js:81:19:81:42 | url.par ... , true) |
+| tst.js:81:29:81:35 | req.url | tst.js:81:19:81:42 | url.par ... , true) |
#select
| tst.js:18:5:18:20 | request(tainted) | tst.js:14:29:14:35 | req.url | tst.js:18:13:18:19 | tainted | The $@ of this request depends on $@. | tst.js:18:13:18:19 | tainted | URL | tst.js:14:29:14:35 | req.url | a user-provided value |
| tst.js:20:5:20:24 | request.get(tainted) | tst.js:14:29:14:35 | req.url | tst.js:20:17:20:23 | tainted | The $@ of this request depends on $@. | tst.js:20:17:20:23 | tainted | URL | tst.js:14:29:14:35 | req.url | a user-provided value |
@@ -130,3 +145,4 @@ edges
| tst.js:64:3:64:38 | client. ... inted}) | tst.js:58:29:58:35 | req.url | tst.js:64:30:64:36 | tainted | The $@ of this request depends on $@. | tst.js:64:30:64:36 | tainted | URL | tst.js:58:29:58:35 | req.url | a user-provided value |
| tst.js:68:3:68:38 | client. ... inted}) | tst.js:58:29:58:35 | req.url | tst.js:68:30:68:36 | tainted | The $@ of this request depends on $@. | tst.js:68:30:68:36 | tainted | URL | tst.js:58:29:58:35 | req.url | a user-provided value |
| tst.js:76:5:76:26 | JSDOM.f ... ainted) | tst.js:74:29:74:35 | req.url | tst.js:76:19:76:25 | tainted | The $@ of this request depends on $@. | tst.js:76:19:76:25 | tainted | URL | tst.js:74:29:74:35 | req.url | a user-provided value |
+| tst.js:83:5:83:26 | new Web ... ainted) | tst.js:81:29:81:35 | req.url | tst.js:83:19:83:25 | tainted | The $@ of this request depends on $@. | tst.js:83:19:83:25 | tainted | URL | tst.js:81:29:81:35 | req.url | a user-provided value |
diff --git a/javascript/ql/test/query-tests/Security/CWE-918/tst.js b/javascript/ql/test/query-tests/Security/CWE-918/tst.js
index d5951f648b4..5464375cb62 100644
--- a/javascript/ql/test/query-tests/Security/CWE-918/tst.js
+++ b/javascript/ql/test/query-tests/Security/CWE-918/tst.js
@@ -74,4 +74,11 @@ var server = http.createServer(async function(req, res) {
var tainted = url.parse(req.url, true).query.url;
JSDOM.fromURL(tainted); // NOT OK
+});
+
+import {JSDOM} from "jsdom";
+var server = http.createServer(async function(req, res) {
+ var tainted = url.parse(req.url, true).query.url;
+
+ new WebSocket(tainted); // NOT OK
});
\ No newline at end of file
From 28ad6675788bd73a6a25bfc198cc780f8fb821ac Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 18 Mar 2021 19:40:46 +0100
Subject: [PATCH 184/725] add model for async-execute
---
.../javascript/frameworks/SystemCommandExecutors.qll | 12 +++++++++---
.../CWE-078/UnsafeShellCommandConstruction.expected | 9 +++++++++
.../ql/test/query-tests/Security/CWE-078/lib/lib.js | 5 +++++
3 files changed, 23 insertions(+), 3 deletions(-)
diff --git a/javascript/ql/src/semmle/javascript/frameworks/SystemCommandExecutors.qll b/javascript/ql/src/semmle/javascript/frameworks/SystemCommandExecutors.qll
index 913f7fd7205..ce445cfd174 100644
--- a/javascript/ql/src/semmle/javascript/frameworks/SystemCommandExecutors.qll
+++ b/javascript/ql/src/semmle/javascript/frameworks/SystemCommandExecutors.qll
@@ -43,9 +43,15 @@ private predicate execApi(string mod, int cmdArg, int optionsArg, boolean shell)
)
or
shell = true and
- mod = "exec" and
- optionsArg = -2 and
- cmdArg = 0
+ (
+ mod = "exec" and
+ optionsArg = -2 and
+ cmdArg = 0
+ or
+ mod = "async-execute" and
+ optionsArg = 1 and
+ cmdArg = 0
+ )
}
private class SystemCommandExecutors extends SystemCommandExecution, DataFlow::InvokeNode {
diff --git a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected
index 2d707e1a693..ee6ebd939da 100644
--- a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected
@@ -205,6 +205,10 @@ nodes
| lib/lib.js:405:39:405:42 | name |
| lib/lib.js:406:22:406:25 | name |
| lib/lib.js:406:22:406:25 | name |
+| lib/lib.js:413:39:413:42 | name |
+| lib/lib.js:413:39:413:42 | name |
+| lib/lib.js:414:24:414:27 | name |
+| lib/lib.js:414:24:414:27 | name |
edges
| lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name |
| lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name |
@@ -444,6 +448,10 @@ edges
| lib/lib.js:405:39:405:42 | name | lib/lib.js:406:22:406:25 | name |
| lib/lib.js:405:39:405:42 | name | lib/lib.js:406:22:406:25 | name |
| lib/lib.js:405:39:405:42 | name | lib/lib.js:406:22:406:25 | name |
+| lib/lib.js:413:39:413:42 | name | lib/lib.js:414:24:414:27 | name |
+| lib/lib.js:413:39:413:42 | name | lib/lib.js:414:24:414:27 | name |
+| lib/lib.js:413:39:413:42 | name | lib/lib.js:414:24:414:27 | name |
+| lib/lib.js:413:39:413:42 | name | lib/lib.js:414:24:414:27 | name |
#select
| lib/lib2.js:4:10:4:25 | "rm -rf " + name | lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name | $@ based on library input is later used in $@. | lib/lib2.js:4:10:4:25 | "rm -rf " + name | String concatenation | lib/lib2.js:4:2:4:26 | cp.exec ... + name) | shell command |
| lib/lib2.js:8:10:8:25 | "rm -rf " + name | lib/lib2.js:7:32:7:35 | name | lib/lib2.js:8:22:8:25 | name | $@ based on library input is later used in $@. | lib/lib2.js:8:10:8:25 | "rm -rf " + name | String concatenation | lib/lib2.js:8:2:8:26 | cp.exec ... + name) | shell command |
@@ -502,3 +510,4 @@ edges
| lib/lib.js:351:10:351:27 | "rm -rf " + unsafe | lib/lib.js:349:29:349:34 | unsafe | lib/lib.js:351:22:351:27 | unsafe | $@ based on library input is later used in $@. | lib/lib.js:351:10:351:27 | "rm -rf " + unsafe | String concatenation | lib/lib.js:351:2:351:28 | cp.exec ... unsafe) | shell command |
| lib/lib.js:366:17:366:56 | "learn ... + model | lib/lib.js:360:20:360:23 | opts | lib/lib.js:366:28:366:42 | this.learn_args | $@ based on library input is later used in $@. | lib/lib.js:366:17:366:56 | "learn ... + model | String concatenation | lib/lib.js:367:3:367:18 | cp.exec(command) | shell command |
| lib/lib.js:406:10:406:25 | "rm -rf " + name | lib/lib.js:405:39:405:42 | name | lib/lib.js:406:22:406:25 | name | $@ based on library input is later used in $@. | lib/lib.js:406:10:406:25 | "rm -rf " + name | String concatenation | lib/lib.js:406:2:406:26 | cp.exec ... + name) | shell command |
+| lib/lib.js:414:12:414:27 | "rm -rf " + name | lib/lib.js:413:39:413:42 | name | lib/lib.js:414:24:414:27 | name | $@ based on library input is later used in $@. | lib/lib.js:414:12:414:27 | "rm -rf " + name | String concatenation | lib/lib.js:414:2:414:28 | asyncEx ... + name) | shell command |
diff --git a/javascript/ql/test/query-tests/Security/CWE-078/lib/lib.js b/javascript/ql/test/query-tests/Security/CWE-078/lib/lib.js
index d1a885438ff..e492c1b04ce 100644
--- a/javascript/ql/test/query-tests/Security/CWE-078/lib/lib.js
+++ b/javascript/ql/test/query-tests/Security/CWE-078/lib/lib.js
@@ -408,3 +408,8 @@ module.exports.sanitizer3 = function (name) {
var sanitized = yetAnohterSanitizer(name);
cp.exec("rm -rf " + sanitized); // OK
}
+
+var asyncExec = require("async-execute");
+module.exports.asyncStuff = function (name) {
+ asyncExec("rm -rf " + name); // NOT OK
+}
\ No newline at end of file
From d489d63b8e78af348b9e3cfae7354efcbd826c5d Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 18 Mar 2021 20:54:33 +0100
Subject: [PATCH 185/725] recognize object transformations in module.exports
when looking for library inputs
---
.../src/semmle/javascript/PackageExports.qll | 57 ++++++++++++++++++-
.../javascript/dataflow/TypeTracking.qll | 6 ++
.../UnsafeShellCommandConstruction.expected | 9 +++
.../query-tests/Security/CWE-078/lib/lib.js | 29 +++++++++-
4 files changed, 99 insertions(+), 2 deletions(-)
diff --git a/javascript/ql/src/semmle/javascript/PackageExports.qll b/javascript/ql/src/semmle/javascript/PackageExports.qll
index bdb0c297411..5a0a7ddcc0f 100644
--- a/javascript/ql/src/semmle/javascript/PackageExports.qll
+++ b/javascript/ql/src/semmle/javascript/PackageExports.qll
@@ -42,7 +42,7 @@ PackageJSON getTopmostPackageJSON() {
* Gets a value exported by the main module from one of the topmost `package.json` files (see `getTopmostPackageJSON`).
* The value is either directly the `module.exports` value, a nested property of `module.exports`, or a method on an exported class.
*/
-private DataFlow::Node getAValueExportedByPackage() {
+DataFlow::Node getAValueExportedByPackage() {
result = getAnExportFromModule(getTopmostPackageJSON().getMainModule())
or
result = getAValueExportedByPackage().(DataFlow::PropWrite).getRhs()
@@ -70,6 +70,61 @@ private DataFlow::Node getAValueExportedByPackage() {
result = cla.getAStaticMethod() or
result = cla.getConstructor()
)
+ or
+ // *****
+ // Various standard library methods for transforming exported objects.
+ // *****
+ //
+ // Object.defineProperties
+ exists(DataFlow::MethodCallNode call |
+ call = DataFlow::globalVarRef("Object").getAMethodCall("defineProperties") and
+ [call, call.getArgument(0)] = getAValueExportedByPackage() and
+ result = call.getArgument(any(int i | i > 0))
+ )
+ or
+ // Object.defineProperty
+ exists(DataFlow::MethodCallNode call |
+ call = DataFlow::globalVarRef("Object").getAMethodCall("defineProperty") and
+ [call, call.getArgument(0)] = getAValueExportedByPackage()
+ |
+ result = call.getArgument(2).getALocalSource().getAPropertyReference("value")
+ or
+ result =
+ call.getArgument(2)
+ .getALocalSource()
+ .getAPropertyReference("get")
+ .(DataFlow::FunctionNode)
+ .getAReturn()
+ )
+ or
+ // Object.assign
+ exists(DataFlow::MethodCallNode assign |
+ assign = DataFlow::globalVarRef("Object").getAMethodCall("assign")
+ |
+ getAValueExportedByPackage() = [assign, assign.getArgument(0)] and
+ result = assign.getAnArgument()
+ )
+ or
+ // Array.prototype.{map, reduce, entries, values}
+ exists(DataFlow::MethodCallNode map |
+ map.getMethodName() = ["map", "reduce", "entries", "values"] and
+ map = getAValueExportedByPackage()
+ |
+ result = map.getArgument(0).getABoundFunctionValue(_).getAReturn()
+ or
+ // assuming that the receiver of the call is somehow exported
+ result = map.getReceiver()
+ )
+ or
+ // Object.{fromEntries, freeze, entries, values}
+ exists(DataFlow::MethodCallNode freeze |
+ freeze =
+ DataFlow::globalVarRef("Object")
+ .getAMethodCall(["fromEntries", "freeze", "entries", "values"])
+ |
+ freeze = getAValueExportedByPackage() and
+ result = freeze.getArgument(0)
+ )
}
/**
diff --git a/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll b/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll
index 0996fc5c0ac..7facab07ff1 100644
--- a/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll
+++ b/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll
@@ -252,6 +252,12 @@ class TypeBackTracker extends TTypeBackTracker {
*/
predicate start() { hasReturn = false and prop = "" }
+ /**
+ * Holds if this is the starting point of type backtracking, and the value is in the property named `propName`.
+ * The type tracking only ends after the property has been stored.
+ */
+ predicate isInProp(PropertyName propName) { hasReturn = false and prop = propName }
+
/**
* Holds if this is the end point of type tracking.
*/
diff --git a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected
index ee6ebd939da..29a83580ad1 100644
--- a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected
@@ -209,6 +209,10 @@ nodes
| lib/lib.js:413:39:413:42 | name |
| lib/lib.js:414:24:414:27 | name |
| lib/lib.js:414:24:414:27 | name |
+| lib/lib.js:418:20:418:23 | name |
+| lib/lib.js:418:20:418:23 | name |
+| lib/lib.js:419:25:419:28 | name |
+| lib/lib.js:419:25:419:28 | name |
edges
| lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name |
| lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name |
@@ -452,6 +456,10 @@ edges
| lib/lib.js:413:39:413:42 | name | lib/lib.js:414:24:414:27 | name |
| lib/lib.js:413:39:413:42 | name | lib/lib.js:414:24:414:27 | name |
| lib/lib.js:413:39:413:42 | name | lib/lib.js:414:24:414:27 | name |
+| lib/lib.js:418:20:418:23 | name | lib/lib.js:419:25:419:28 | name |
+| lib/lib.js:418:20:418:23 | name | lib/lib.js:419:25:419:28 | name |
+| lib/lib.js:418:20:418:23 | name | lib/lib.js:419:25:419:28 | name |
+| lib/lib.js:418:20:418:23 | name | lib/lib.js:419:25:419:28 | name |
#select
| lib/lib2.js:4:10:4:25 | "rm -rf " + name | lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name | $@ based on library input is later used in $@. | lib/lib2.js:4:10:4:25 | "rm -rf " + name | String concatenation | lib/lib2.js:4:2:4:26 | cp.exec ... + name) | shell command |
| lib/lib2.js:8:10:8:25 | "rm -rf " + name | lib/lib2.js:7:32:7:35 | name | lib/lib2.js:8:22:8:25 | name | $@ based on library input is later used in $@. | lib/lib2.js:8:10:8:25 | "rm -rf " + name | String concatenation | lib/lib2.js:8:2:8:26 | cp.exec ... + name) | shell command |
@@ -511,3 +519,4 @@ edges
| lib/lib.js:366:17:366:56 | "learn ... + model | lib/lib.js:360:20:360:23 | opts | lib/lib.js:366:28:366:42 | this.learn_args | $@ based on library input is later used in $@. | lib/lib.js:366:17:366:56 | "learn ... + model | String concatenation | lib/lib.js:367:3:367:18 | cp.exec(command) | shell command |
| lib/lib.js:406:10:406:25 | "rm -rf " + name | lib/lib.js:405:39:405:42 | name | lib/lib.js:406:22:406:25 | name | $@ based on library input is later used in $@. | lib/lib.js:406:10:406:25 | "rm -rf " + name | String concatenation | lib/lib.js:406:2:406:26 | cp.exec ... + name) | shell command |
| lib/lib.js:414:12:414:27 | "rm -rf " + name | lib/lib.js:413:39:413:42 | name | lib/lib.js:414:24:414:27 | name | $@ based on library input is later used in $@. | lib/lib.js:414:12:414:27 | "rm -rf " + name | String concatenation | lib/lib.js:414:2:414:28 | asyncEx ... + name) | shell command |
+| lib/lib.js:419:13:419:28 | "rm -rf " + name | lib/lib.js:418:20:418:23 | name | lib/lib.js:419:25:419:28 | name | $@ based on library input is later used in $@. | lib/lib.js:419:13:419:28 | "rm -rf " + name | String concatenation | lib/lib.js:419:3:419:29 | asyncEx ... + name) | shell command |
diff --git a/javascript/ql/test/query-tests/Security/CWE-078/lib/lib.js b/javascript/ql/test/query-tests/Security/CWE-078/lib/lib.js
index e492c1b04ce..a645788aa72 100644
--- a/javascript/ql/test/query-tests/Security/CWE-078/lib/lib.js
+++ b/javascript/ql/test/query-tests/Security/CWE-078/lib/lib.js
@@ -412,4 +412,31 @@ module.exports.sanitizer3 = function (name) {
var asyncExec = require("async-execute");
module.exports.asyncStuff = function (name) {
asyncExec("rm -rf " + name); // NOT OK
-}
\ No newline at end of file
+}
+
+const myFuncs = {
+ myFunc: function (name) {
+ asyncExec("rm -rf " + name); // NOT OK
+ }
+};
+
+module.exports.blabity = {};
+
+Object.defineProperties(
+ module.exports.blabity,
+ Object.assign(
+ {},
+ Object.entries(myFuncs).reduce(
+ (props, [ key, value ]) => Object.assign(
+ props,
+ {
+ [key]: {
+ value,
+ configurable: true,
+ },
+ },
+ ),
+ {}
+ )
+ )
+);
From 20f0b3329aa1f59d8b6e9217a8785a04388b722b Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Wed, 17 Mar 2021 09:55:52 +0100
Subject: [PATCH 186/725] C#: Fix code quality issues reported by code scanning
---
.../Semmle.Extraction.CSharp/Entities/CachedSymbol.cs | 9 +++------
.../Entities/Expressions/ImplicitCast.cs | 2 +-
.../Entities/Expressions/MemberAccess.cs | 2 +-
3 files changed, 5 insertions(+), 8 deletions(-)
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CachedSymbol.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CachedSymbol.cs
index f7f18ff4c25..d6fb0d65e5d 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CachedSymbol.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CachedSymbol.cs
@@ -151,13 +151,10 @@ namespace Semmle.Extraction.CSharp.Entities
if (handleProp is null)
{
var underlyingSymbolProp = GetPropertyInfo(Symbol, "UnderlyingSymbol");
- if (underlyingSymbolProp is not null)
+ if (underlyingSymbolProp?.GetValue(Symbol) is object underlying)
{
- if (underlyingSymbolProp.GetValue(Symbol) is object underlying)
- {
- handleProp = GetPropertyInfo(underlying, "Handle");
- handleObj = underlying;
- }
+ handleProp = GetPropertyInfo(underlying, "Handle");
+ handleObj = underlying;
}
}
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitCast.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitCast.cs
index a6995adc30f..2d617cdb1b9 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitCast.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitCast.cs
@@ -3,7 +3,7 @@ using Semmle.Extraction.Kinds;
namespace Semmle.Extraction.CSharp.Entities.Expressions
{
- internal class ImplicitCast : Expression
+ internal sealed class ImplicitCast : Expression
{
public Expression Expr
{
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/MemberAccess.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/MemberAccess.cs
index 0de154509c8..426145e2d27 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/MemberAccess.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/MemberAccess.cs
@@ -4,7 +4,7 @@ using Semmle.Extraction.Kinds;
namespace Semmle.Extraction.CSharp.Entities.Expressions
{
- internal class MemberAccess : Expression
+ internal sealed class MemberAccess : Expression
{
private MemberAccess(ExpressionNodeInfo info, ExpressionSyntax qualifier, ISymbol? target) : base(info)
{
From 7543f10593dfe5bd3aaea1572d1c9135aff49b31 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Fri, 19 Mar 2021 09:53:25 +0100
Subject: [PATCH 187/725] Python: Reorganize PyYAML tests a bit
---
.../library-tests/frameworks/yaml/Decoding.py | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/python/ql/test/experimental/library-tests/frameworks/yaml/Decoding.py b/python/ql/test/experimental/library-tests/frameworks/yaml/Decoding.py
index a9369d6ec3d..987ea76552a 100644
--- a/python/ql/test/experimental/library-tests/frameworks/yaml/Decoding.py
+++ b/python/ql/test/experimental/library-tests/frameworks/yaml/Decoding.py
@@ -6,20 +6,32 @@ yaml.load(payload, yaml.Loader) # $decodeInput=payload decodeOutput=Attribute()
yaml.unsafe_load(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput
yaml.full_load(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput
-# Safe
+# 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
+################################################################################
# load_all variants
+################################################################################
+
+# Unsafe:
yaml.load_all(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput
-yaml.safe_load_all(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML
yaml.unsafe_load_all(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput
yaml.full_load_all(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput
+# Safe:
+yaml.safe_load_all(payload) # $ decodeInput=payload decodeOutput=Attribute() 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
+
+# Safe:
yaml.load(payload, yaml.CSafeLoader) # $decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML
yaml.load(payload, yaml.CBaseLoader) # $decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML
From e90fb1a225661dce835ac3d4683e824439b37b41 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Fri, 19 Mar 2021 10:09:33 +0100
Subject: [PATCH 188/725] reuse classes modelling standard library functions
---
.../src/semmle/javascript/PackageExports.qll | 19 ++++++++-----------
1 file changed, 8 insertions(+), 11 deletions(-)
diff --git a/javascript/ql/src/semmle/javascript/PackageExports.qll b/javascript/ql/src/semmle/javascript/PackageExports.qll
index 5a0a7ddcc0f..c8807043487 100644
--- a/javascript/ql/src/semmle/javascript/PackageExports.qll
+++ b/javascript/ql/src/semmle/javascript/PackageExports.qll
@@ -83,14 +83,13 @@ DataFlow::Node getAValueExportedByPackage() {
)
or
// Object.defineProperty
- exists(DataFlow::MethodCallNode call |
- call = DataFlow::globalVarRef("Object").getAMethodCall("defineProperty") and
- [call, call.getArgument(0)] = getAValueExportedByPackage()
+ exists(CallToObjectDefineProperty call |
+ [call, call.getBaseObject()] = getAValueExportedByPackage()
|
- result = call.getArgument(2).getALocalSource().getAPropertyReference("value")
+ result = call.getPropertyDescriptor().getALocalSource().getAPropertyReference("value")
or
result =
- call.getArgument(2)
+ call.getPropertyDescriptor()
.getALocalSource()
.getAPropertyReference("get")
.(DataFlow::FunctionNode)
@@ -98,11 +97,9 @@ DataFlow::Node getAValueExportedByPackage() {
)
or
// Object.assign
- exists(DataFlow::MethodCallNode assign |
- assign = DataFlow::globalVarRef("Object").getAMethodCall("assign")
- |
- getAValueExportedByPackage() = [assign, assign.getArgument(0)] and
- result = assign.getAnArgument()
+ exists(ExtendCall assign |
+ getAValueExportedByPackage() = [assign, assign.getDestinationOperand()] and
+ result = assign.getASourceOperand()
)
or
// Array.prototype.{map, reduce, entries, values}
@@ -120,7 +117,7 @@ DataFlow::Node getAValueExportedByPackage() {
exists(DataFlow::MethodCallNode freeze |
freeze =
DataFlow::globalVarRef("Object")
- .getAMethodCall(["fromEntries", "freeze", "entries", "values"])
+ .getAMethodCall(["fromEntries", "freeze", "seal", "entries", "values"])
|
freeze = getAValueExportedByPackage() and
result = freeze.getArgument(0)
From a28a36ab298f5c4847d12b9da82e7646edfd0d4b Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Fri, 19 Mar 2021 10:10:56 +0100
Subject: [PATCH 189/725] add change-note
---
javascript/change-notes/2021-03-19-async-execute.md | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 javascript/change-notes/2021-03-19-async-execute.md
diff --git a/javascript/change-notes/2021-03-19-async-execute.md b/javascript/change-notes/2021-03-19-async-execute.md
new file mode 100644
index 00000000000..4f7e9240bc3
--- /dev/null
+++ b/javascript/change-notes/2021-03-19-async-execute.md
@@ -0,0 +1,4 @@
+lgtm,codescanning
+* The command injection security queries now recognize additional sinks.
+ Affected packages are
+ [async-execute](https://npmjs.com/package/async-execute)
From 36b0ab1de55b2862194df0c63672ff880e72cba6 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Fri, 19 Mar 2021 10:29:38 +0100
Subject: [PATCH 190/725] Apply suggestions from code review
Co-authored-by: Esben Sparre Andreasen
---
javascript/ql/src/semmle/javascript/PackageExports.qll | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/javascript/ql/src/semmle/javascript/PackageExports.qll b/javascript/ql/src/semmle/javascript/PackageExports.qll
index c8807043487..ffa96b051e1 100644
--- a/javascript/ql/src/semmle/javascript/PackageExports.qll
+++ b/javascript/ql/src/semmle/javascript/PackageExports.qll
@@ -42,7 +42,7 @@ PackageJSON getTopmostPackageJSON() {
* Gets a value exported by the main module from one of the topmost `package.json` files (see `getTopmostPackageJSON`).
* The value is either directly the `module.exports` value, a nested property of `module.exports`, or a method on an exported class.
*/
-DataFlow::Node getAValueExportedByPackage() {
+private DataFlow::Node getAValueExportedByPackage() {
result = getAnExportFromModule(getTopmostPackageJSON().getMainModule())
or
result = getAValueExportedByPackage().(DataFlow::PropWrite).getRhs()
@@ -72,7 +72,7 @@ DataFlow::Node getAValueExportedByPackage() {
)
or
// *****
- // Various standard library methods for transforming exported objects.
+ // Common styles of transforming exported objects.
// *****
//
// Object.defineProperties
@@ -96,7 +96,7 @@ DataFlow::Node getAValueExportedByPackage() {
.getAReturn()
)
or
- // Object.assign
+ // Object.assign and friends
exists(ExtendCall assign |
getAValueExportedByPackage() = [assign, assign.getDestinationOperand()] and
result = assign.getASourceOperand()
@@ -113,7 +113,7 @@ DataFlow::Node getAValueExportedByPackage() {
result = map.getReceiver()
)
or
- // Object.{fromEntries, freeze, entries, values}
+ // Object.{fromEntries, freeze, seal, entries, values}
exists(DataFlow::MethodCallNode freeze |
freeze =
DataFlow::globalVarRef("Object")
From 783a63a8a8a20a72394b56676512638138a3ee56 Mon Sep 17 00:00:00 2001
From: Cornelius Riemenschneider
Date: Fri, 19 Mar 2021 10:54:41 +0100
Subject: [PATCH 191/725] Update cpp/ql/src/Summary/LinesOfCode.ql
Co-authored-by: Shati Patel <42641846+shati-patel@users.noreply.github.com>
---
cpp/ql/src/Summary/LinesOfCode.ql | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cpp/ql/src/Summary/LinesOfCode.ql b/cpp/ql/src/Summary/LinesOfCode.ql
index 79e4e7b16f2..3b2aa2ac4c9 100644
--- a/cpp/ql/src/Summary/LinesOfCode.ql
+++ b/cpp/ql/src/Summary/LinesOfCode.ql
@@ -1,7 +1,7 @@
/**
* @id cpp/summary/lines-of-code
* @name Total lines of C/C++ code in the database
- * @description The total number of lines of C/C++ code across all files, including system headers, libraries, and auto-generated files. This is a useful metric of the size of a database. Lines of code are all lines in a file that was seen during the build that contain code, i.e. are not whitespace or comments.
+ * @description The total number of lines of C/C++ code across all files, including system headers, libraries, and auto-generated files. This is a useful metric of the size of a database. For all files that were seen during the build, this query counts the lines of code, excluding whitespace or comments.
* @kind metric
* @tags summary
*/
From 79d6731ed8e1725c1064214b7a201c4ad0794009 Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Fri, 19 Mar 2021 11:01:28 +0100
Subject: [PATCH 192/725] C#: Adjust make_stubs.py to use codeql instead of
odasa
---
csharp/ql/src/Stubs/make_stubs.py | 50 ++++++++++++++-----------------
1 file changed, 23 insertions(+), 27 deletions(-)
diff --git a/csharp/ql/src/Stubs/make_stubs.py b/csharp/ql/src/Stubs/make_stubs.py
index e94cf0c7261..19a917cd8ab 100644
--- a/csharp/ql/src/Stubs/make_stubs.py
+++ b/csharp/ql/src/Stubs/make_stubs.py
@@ -43,12 +43,6 @@ if not foundCS:
print("Test directory does not contain .cs files. Please specify a working qltest directory.")
exit(1)
-cmd = ['odasa', 'selfTest']
-print('Running ' + ' '.join(cmd))
-if subprocess.check_call(cmd):
- print("odasa selfTest failed. Ensure odasa is on your current path.")
- exit(1)
-
csharpQueries = os.path.abspath(os.path.dirname(sys.argv[0]))
outputFile = os.path.join(testDir, 'stubs.cs')
@@ -58,35 +52,36 @@ if os.path.isfile(outputFile):
os.remove(outputFile) # It would interfere with the test.
print("Removed previous", outputFile)
-cmd = ['odasa', 'qltest', '--optimize', '--leave-temp-files', testDir]
+cmd = ['codeql', 'test', 'run', '--keep-databases', testDir]
print('Running ' + ' '.join(cmd))
if subprocess.check_call(cmd):
- print("qltest failed. Please fix up the test before proceeding.")
+ print("codeql test failed. Please fix up the test before proceeding.")
exit(1)
-dbDir = os.path.join(testDir, os.path.basename(testDir) + ".testproj", "db-csharp")
+dbDir = os.path.join(testDir, os.path.basename(testDir) + ".testproj")
if not os.path.isdir(dbDir):
- print("Expected database directory " + dbDir + " not found. Please contact Semmle.")
+ print("Expected database directory " + dbDir + " not found.")
exit(1)
-cmd = ['odasa', 'runQuery', '--query', os.path.join(csharpQueries, 'MinimalStubsFromSource.ql'), '--db', dbDir, '--output-file', outputFile]
+cmd = ['codeql', 'query', 'run', os.path.join(
+ csharpQueries, 'MinimalStubsFromSource.ql'), '--database', dbDir, '--output', outputFile]
print('Running ' + ' '.join(cmd))
if subprocess.check_call(cmd):
- print('Failed to run the query to generate output file. Please contact Semmle.')
+ print('Failed to run the query to generate output file.')
exit(1)
# Remove the leading " and trailing " bytes from the file
len = os.stat(outputFile).st_size
f = open(outputFile, "rb")
try:
- quote = f.read(1)
- if quote != b'"':
- print("Unexpected character in file. Please contact Semmle.")
- contents = f.read(len-3)
- quote = f.read(1)
- if quote != b'"':
- print("Unexpected end character. Please contact Semmle.", quote)
+ quote = f.read(6)
+ if quote != b"\x02\x01\x86'\x85'":
+ print("Unexpected start characters in file.", quote)
+ contents = f.read(len-21)
+ quote = f.read(15)
+ if quote != b'\x0e\x01\x08#select\x01\x01\x00s\x00':
+ print("Unexpected end character in file.", quote)
finally:
f.close()
@@ -94,16 +89,17 @@ f = open(outputFile, "wb")
f.write(contents)
f.close()
-cmd = ['odasa', 'qltest', '--optimize', testDir]
+cmd = ['codeql', 'test', 'run', testDir]
print('Running ' + ' '.join(cmd))
if subprocess.check_call(cmd):
- print('\nTest failed. You may need to fix up', outputFile)
- print('It may help to view', outputFile, ' in Visual Studio')
- print("Next steps:")
- print('1. Look at the compilation errors, and fix up', outputFile, 'so that the test compiles')
- print('2. Re-run odasa qltest --optimize "' + testDir + '"')
- print('3. git add "' + outputFile + '"')
- exit(1)
+ print('\nTest failed. You may need to fix up', outputFile)
+ print('It may help to view', outputFile, ' in Visual Studio')
+ print("Next steps:")
+ print('1. Look at the compilation errors, and fix up',
+ outputFile, 'so that the test compiles')
+ print('2. Re-run codeql test run "' + testDir + '"')
+ print('3. git add "' + outputFile + '"')
+ exit(1)
print("\nStub generation successful! Next steps:")
print('1. Edit "semmle-extractor-options" in the .cs files to remove unused references')
From 39a7d3decc4ba2fd0552d50835d676ecd8d7de7b Mon Sep 17 00:00:00 2001
From: Cornelius Riemenschneider
Date: Wed, 17 Mar 2021 12:28:03 +0100
Subject: [PATCH 193/725] C++: Address review comments.
---
cpp/ql/src/Diagnostics/FailedExtractions.qll | 5 +----
.../{AbortedExtractions.ql => FailedInvocations.ql} | 4 ++--
2 files changed, 3 insertions(+), 6 deletions(-)
rename cpp/ql/src/Diagnostics/{AbortedExtractions.ql => FailedInvocations.ql} (87%)
diff --git a/cpp/ql/src/Diagnostics/FailedExtractions.qll b/cpp/ql/src/Diagnostics/FailedExtractions.qll
index 36bf264aa90..ac3088adf19 100644
--- a/cpp/ql/src/Diagnostics/FailedExtractions.qll
+++ b/cpp/ql/src/Diagnostics/FailedExtractions.qll
@@ -84,10 +84,7 @@ class ExtractionUnrecoverableError extends ExtractionError, TCompilationFailed {
result = "Unrecoverable extraction error while compiling " + f.toString()
}
- override string getErrorMessage() {
- result =
- "Unrecoverable compilation failure; check logs/build-tracer.log in the database directory for more information."
- }
+ override string getErrorMessage() { result = "unrecoverable compilation failure." }
override File getFile() { result = f }
diff --git a/cpp/ql/src/Diagnostics/AbortedExtractions.ql b/cpp/ql/src/Diagnostics/FailedInvocations.ql
similarity index 87%
rename from cpp/ql/src/Diagnostics/AbortedExtractions.ql
rename to cpp/ql/src/Diagnostics/FailedInvocations.ql
index 5bab529b23e..ec1a243f245 100644
--- a/cpp/ql/src/Diagnostics/AbortedExtractions.ql
+++ b/cpp/ql/src/Diagnostics/FailedInvocations.ql
@@ -1,8 +1,8 @@
/**
- * @name Aborted extractor invocations
+ * @name Failed extractor invocations
* @description Gives the command line of compilations for which extraction did not run to completion.
* @kind diagnostic
- * @id cpp/diagnostics/aborted-extractions
+ * @id cpp/diagnostics/failed-extractor-invocations
*/
import cpp
From 63e560e3b4fdba76e434a27ed86b8a36e7242590 Mon Sep 17 00:00:00 2001
From: Cornelius Riemenschneider
Date: Fri, 19 Mar 2021 11:27:51 +0100
Subject: [PATCH 194/725] Fix QL doc.
---
cpp/ql/src/Diagnostics/FailedExtractions.qll | 1 +
1 file changed, 1 insertion(+)
diff --git a/cpp/ql/src/Diagnostics/FailedExtractions.qll b/cpp/ql/src/Diagnostics/FailedExtractions.qll
index ac3088adf19..d692be10dac 100644
--- a/cpp/ql/src/Diagnostics/FailedExtractions.qll
+++ b/cpp/ql/src/Diagnostics/FailedExtractions.qll
@@ -47,6 +47,7 @@ private newtype TExtractionError =
* Superclass for the extraction error hierarchy.
*/
class ExtractionError extends TExtractionError {
+ /** Gets the string representation of the error. */
string toString() { none() }
/** Gets the error message for this error. */
From e482d21949a917fe2585c425acc958710d30bc08 Mon Sep 17 00:00:00 2001
From: Cornelius Riemenschneider
Date: Fri, 19 Mar 2021 11:40:20 +0100
Subject: [PATCH 195/725] C++: Make QLdoc check happy.
---
cpp/ql/src/Diagnostics/FailedExtractions.qll | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/cpp/ql/src/Diagnostics/FailedExtractions.qll b/cpp/ql/src/Diagnostics/FailedExtractions.qll
index d692be10dac..62a89f7cdc1 100644
--- a/cpp/ql/src/Diagnostics/FailedExtractions.qll
+++ b/cpp/ql/src/Diagnostics/FailedExtractions.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides a common hierarchy of all types of errors that can occur during extraction.
+ */
+
import cpp
/*
From 4f46908224bd6840ad90ec463a6688d942d003a1 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Wed, 20 Nov 2019 16:30:00 +0000
Subject: [PATCH 196/725] JS: Add test with ES getters/setters
---
.../TaintTracking/getters-and-setters.js | 55 +++++++++++++++++++
1 file changed, 55 insertions(+)
create mode 100644 javascript/ql/test/library-tests/TaintTracking/getters-and-setters.js
diff --git a/javascript/ql/test/library-tests/TaintTracking/getters-and-setters.js b/javascript/ql/test/library-tests/TaintTracking/getters-and-setters.js
new file mode 100644
index 00000000000..1918fd23e95
--- /dev/null
+++ b/javascript/ql/test/library-tests/TaintTracking/getters-and-setters.js
@@ -0,0 +1,55 @@
+import * as dummy from 'dummy';
+
+function testGetterSource() {
+ class C {
+ get x() {
+ return source();
+ }
+ };
+ sink(new C().x); // NOT OK
+
+ function indirection(c) {
+ if (c) {
+ sink(c.x); // NOT OK
+ }
+ }
+ indirection(new C());
+ indirection(null);
+}
+
+function testSetterSink() {
+ class C {
+ set x(v) {
+ sink(v); // NOT OK
+ }
+ };
+ function indirection(c) {
+ c.x = source();
+ }
+ indirection(new C());
+ indirection(null);
+}
+
+function testFlowThroughGetter() {
+ class C {
+ constructor(x) {
+ this._x = x;
+ }
+
+ get x() {
+ return this._x;
+ }
+ };
+
+ function indirection(c) {
+ sink(c.x); // NOT OK
+ }
+ indirection(new C(source()));
+ indirection(null);
+
+ function getX(c) {
+ return c.x;
+ }
+ sink(getX(new C(source()))); // NOT OK
+ sink(getX(new C(source()))); // NOT OK
+}
From 2f3d51641335525759dd538ef8ef7e068a84aae6 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Wed, 20 Nov 2019 17:19:44 +0000
Subject: [PATCH 197/725] JS: Track flow into ES accessors
---
.../semmle/javascript/dataflow/DataFlow.qll | 7 +++++++
.../dataflow/internal/CallGraphs.qll | 14 +++++++++++++
.../dataflow/internal/FlowSteps.qll | 21 ++++++++++++++-----
.../TaintTracking/BasicTaintTracking.expected | 4 ++++
.../TaintTracking/DataFlowTracking.expected | 4 ++++
.../TaintTracking/getters-and-setters.js | 4 ++--
6 files changed, 47 insertions(+), 7 deletions(-)
diff --git a/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll b/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll
index 6f861f1556c..3db26d0af33 100644
--- a/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll
+++ b/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll
@@ -531,6 +531,13 @@ module DataFlow {
predicate isPrivateField() {
getPropertyName().charAt(0) = "#" and getPropertyNameExpr() instanceof Label
}
+
+ /**
+ * Gets an accessor (`get` or `set` method) that may be invoked by this property reference.
+ */
+ final DataFlow::FunctionNode getAnAccessorCallee() {
+ result = CallGraph::getAnAccessorCallee(this)
+ }
}
/**
diff --git a/javascript/ql/src/semmle/javascript/dataflow/internal/CallGraphs.qll b/javascript/ql/src/semmle/javascript/dataflow/internal/CallGraphs.qll
index 433cbc2004b..651c97feb6c 100644
--- a/javascript/ql/src/semmle/javascript/dataflow/internal/CallGraphs.qll
+++ b/javascript/ql/src/semmle/javascript/dataflow/internal/CallGraphs.qll
@@ -162,4 +162,18 @@ module CallGraph {
)
)
}
+
+ /**
+ * Gets a getter or setter invoked as a result of the given property access.
+ */
+ cached
+ DataFlow::FunctionNode getAnAccessorCallee(DataFlow::PropRef ref) {
+ exists(DataFlow::ClassNode cls, string name |
+ ref = cls.getAnInstanceReference().getAPropertyRead(name) and
+ result = cls.getInstanceMember(name, DataFlow::MemberKind::getter())
+ or
+ ref = cls.getAnInstanceReference().getAPropertyWrite(name) and
+ result = cls.getInstanceMember(name, DataFlow::MemberKind::setter())
+ )
+ }
}
diff --git a/javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll b/javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll
index 4b9158d3575..0325d41bcc3 100644
--- a/javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll
+++ b/javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll
@@ -25,7 +25,8 @@ predicate shouldTrackProperties(AbstractValue obj) {
*/
pragma[noinline]
predicate returnExpr(Function f, DataFlow::Node source, DataFlow::Node sink) {
- sink.asExpr() = f.getAReturnedExpr() and source = sink
+ sink.asExpr() = f.getAReturnedExpr() and source = sink and
+ not f = any(SetterMethodDeclaration decl).getBody()
}
/**
@@ -120,7 +121,11 @@ private module CachedSteps {
* Holds if `invk` may invoke `f`.
*/
cached
- predicate calls(DataFlow::InvokeNode invk, Function f) { f = invk.getACallee(0) }
+ predicate calls(DataFlow::SourceNode invk, Function f) {
+ f = invk.(DataFlow::InvokeNode).getACallee(0)
+ or
+ f = invk.(DataFlow::PropRef).getAnAccessorCallee().getFunction()
+ }
private predicate callsBoundInternal(
DataFlow::InvokeNode invk, Function f, int boundArgs, boolean contextDependent
@@ -177,11 +182,11 @@ private module CachedSteps {
*/
cached
predicate argumentPassing(
- DataFlow::InvokeNode invk, DataFlow::ValueNode arg, Function f, DataFlow::SourceNode parm
+ DataFlow::SourceNode invk, DataFlow::ValueNode arg, Function f, DataFlow::SourceNode parm
) {
calls(invk, f) and
(
- exists(int i | arg = invk.getArgument(i) |
+ exists(int i | arg = invk.(DataFlow::InvokeNode).getArgument(i) |
exists(Parameter p |
f.getParameter(i) = p and
not p.isRestParameter() and
@@ -193,6 +198,12 @@ private module CachedSteps {
or
arg = invk.(DataFlow::CallNode).getReceiver() and
parm = DataFlow::thisNode(f)
+ or
+ arg = invk.(DataFlow::PropRef).getBase() and
+ parm = DataFlow::thisNode(f)
+ or
+ arg = invk.(DataFlow::PropWrite).getRhs() and
+ parm = DataFlow::parameterNode(f.getParameter(0))
)
or
exists(DataFlow::Node callback, int i, Parameter p |
@@ -213,7 +224,7 @@ private module CachedSteps {
callsBound(invk, f, boundArgs) and
f.getParameter(boundArgs + i) = p and
not p.isRestParameter() and
- arg = invk.getArgument(i) and
+ arg = invk.(DataFlow::InvokeNode).getArgument(i) and
parm = DataFlow::parameterNode(p)
)
}
diff --git a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected
index c98e9ad7e79..b35dff0c16f 100644
--- a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected
+++ b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected
@@ -66,6 +66,10 @@ typeInferenceMismatch
| exceptions.js:144:9:144:16 | source() | exceptions.js:132:8:132:27 | returnThrownSource() |
| exceptions.js:150:13:150:20 | source() | exceptions.js:153:10:153:10 | e |
| exceptions.js:158:13:158:20 | source() | exceptions.js:161:10:161:10 | e |
+| getters-and-setters.js:6:20:6:27 | source() | getters-and-setters.js:9:10:9:18 | new C().x |
+| getters-and-setters.js:6:20:6:27 | source() | getters-and-setters.js:13:18:13:20 | c.x |
+| getters-and-setters.js:27:15:27:22 | source() | getters-and-setters.js:23:18:23:18 | v |
+| getters-and-setters.js:47:23:47:30 | source() | getters-and-setters.js:45:14:45:16 | c.x |
| importedReactComponent.jsx:4:40:4:47 | source() | exportedReactComponent.jsx:2:10:2:19 | props.text |
| indexOf.js:4:11:4:18 | source() | indexOf.js:9:10:9:10 | x |
| json-stringify.js:2:16:2:23 | source() | json-stringify.js:5:8:5:29 | JSON.st ... source) |
diff --git a/javascript/ql/test/library-tests/TaintTracking/DataFlowTracking.expected b/javascript/ql/test/library-tests/TaintTracking/DataFlowTracking.expected
index 34a6b30bdce..fd9e0fe2b46 100644
--- a/javascript/ql/test/library-tests/TaintTracking/DataFlowTracking.expected
+++ b/javascript/ql/test/library-tests/TaintTracking/DataFlowTracking.expected
@@ -41,6 +41,10 @@
| exceptions.js:144:9:144:16 | source() | exceptions.js:132:8:132:27 | returnThrownSource() |
| exceptions.js:150:13:150:20 | source() | exceptions.js:153:10:153:10 | e |
| exceptions.js:158:13:158:20 | source() | exceptions.js:161:10:161:10 | e |
+| getters-and-setters.js:6:20:6:27 | source() | getters-and-setters.js:9:10:9:18 | new C().x |
+| getters-and-setters.js:6:20:6:27 | source() | getters-and-setters.js:13:18:13:20 | c.x |
+| getters-and-setters.js:27:15:27:22 | source() | getters-and-setters.js:23:18:23:18 | v |
+| getters-and-setters.js:47:23:47:30 | source() | getters-and-setters.js:45:14:45:16 | c.x |
| indexOf.js:4:11:4:18 | source() | indexOf.js:9:10:9:10 | x |
| indexOf.js:4:11:4:18 | source() | indexOf.js:13:10:13:10 | x |
| nested-props.js:4:13:4:20 | source() | nested-props.js:5:10:5:14 | obj.x |
diff --git a/javascript/ql/test/library-tests/TaintTracking/getters-and-setters.js b/javascript/ql/test/library-tests/TaintTracking/getters-and-setters.js
index 1918fd23e95..00691e4987d 100644
--- a/javascript/ql/test/library-tests/TaintTracking/getters-and-setters.js
+++ b/javascript/ql/test/library-tests/TaintTracking/getters-and-setters.js
@@ -50,6 +50,6 @@ function testFlowThroughGetter() {
function getX(c) {
return c.x;
}
- sink(getX(new C(source()))); // NOT OK
- sink(getX(new C(source()))); // NOT OK
+ sink(getX(new C(source()))); // NOT OK - but not flagged
+ getX(null);
}
From 01fd00de564b0e61f6cedf7dadea2563d628b57d Mon Sep 17 00:00:00 2001
From: Asger Feldthaus
Date: Fri, 19 Mar 2021 11:49:06 +0000
Subject: [PATCH 198/725] JS: Fix join order in argumentPassing
---
.../ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll b/javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll
index 0325d41bcc3..79ffecad221 100644
--- a/javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll
+++ b/javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll
@@ -206,10 +206,11 @@ private module CachedSteps {
parm = DataFlow::parameterNode(f.getParameter(0))
)
or
- exists(DataFlow::Node callback, int i, Parameter p |
+ exists(DataFlow::Node callback, int i, Parameter p, Function target |
invk.(DataFlow::PartialInvokeNode).isPartialArgument(callback, arg, i) and
partiallyCalls(invk, callback, f) and
- f.getParameter(i) = p and
+ f = pragma[only_bind_into](target) and
+ target.getParameter(i) = p and
not p.isRestParameter() and
parm = DataFlow::parameterNode(p)
)
From 42c4b22ea130a3ed50b13563f249f8465256904c Mon Sep 17 00:00:00 2001
From: Asger Feldthaus
Date: Fri, 19 Mar 2021 12:41:34 +0000
Subject: [PATCH 199/725] JS: Fix query ID for UntrustedCheckout
---
.../ql/src/experimental/Security/CWE-094/UntrustedCheckout.ql | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-094/UntrustedCheckout.ql b/javascript/ql/src/experimental/Security/CWE-094/UntrustedCheckout.ql
index 62a0d743152..5227d433088 100644
--- a/javascript/ql/src/experimental/Security/CWE-094/UntrustedCheckout.ql
+++ b/javascript/ql/src/experimental/Security/CWE-094/UntrustedCheckout.ql
@@ -6,7 +6,7 @@
* @kind problem
* @problem.severity warning
* @precision low
- * @id js/actions/pull_request_target
+ * @id js/actions/pull-request-target
* @tags actions
* security
* external/cwe/cwe-094
From 18ac2596d0214490a7c88063dbd716389e1a5127 Mon Sep 17 00:00:00 2001
From: Tom Hvitved
Date: Fri, 19 Mar 2021 13:54:03 +0100
Subject: [PATCH 200/725] Data flow: Add section on lambda flow to
`dataflow.md`
---
docs/ql-libraries/dataflow/dataflow.md | 39 ++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/docs/ql-libraries/dataflow/dataflow.md b/docs/ql-libraries/dataflow/dataflow.md
index 46ba27acccf..77e75a36241 100644
--- a/docs/ql-libraries/dataflow/dataflow.md
+++ b/docs/ql-libraries/dataflow/dataflow.md
@@ -186,6 +186,45 @@ values, for example through `out` parameters in C#, the `ReturnKind` class can
be defined and used to match up different kinds of `ReturnNode`s with the
corresponding `OutNode`s.
+#### Lambda flow
+
+For lambda-like calls, the library supports built-in call resolution based on data flow between a lambda creation expression and a lambda call. The interface that needs to be implemented is
+
+```ql
+class LambdaCallKind
+
+/** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */
+predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c)
+
+/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */
+predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver)
+
+/** Extra data-flow steps needed for lamba flow analysis. */
+predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue)
+```
+
+with the semantics that `call` will resolve to `c` if there is a data-flow path from `creation` to `receiver`, with matching `kind`s.
+
+The implementation keeps track of a one-level call context, which means that we are able to handle situations like this:
+```csharp
+Apply(f, x) { f(x); }
+
+Apply(x => NonSink(x), "tainted"); // GOOD
+
+Apply(x => Sink(x), "not tainted"); // GOOD
+```
+
+However, since we only track one level the following example will have false-positive flow:
+```csharp
+Apply(f, x) { f(x); }
+
+ApplyWrapper(f, x) { Apply(f, x) }
+
+ApplyWrapper(x => NonSink(x), "tainted"); // GOOD (FALSE POSITIVE)
+
+ApplyWrapper(x => Sink(x), "not tainted"); // GOOD (FALSE POSITIVE)
+```
+
## Flow through global variables
Flow through global variables are called jump-steps, since such flow steps
From d9079e34e3ae78c51cec8a28b33ae2cac8a3e69c Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Fri, 19 Mar 2021 15:51:54 +0100
Subject: [PATCH 201/725] Python: Move framework tests out of experimental
Since they are not experimental anymore :smile:
---
.../library-tests/frameworks/crypto/ConceptsTest.expected | 0
.../library-tests/frameworks/crypto/ConceptsTest.ql | 0
.../library-tests/frameworks/crypto/test_dsa.py | 0
.../{experimental => }/library-tests/frameworks/crypto/test_ec.py | 0
.../library-tests/frameworks/crypto/test_rsa.py | 0
.../library-tests/frameworks/cryptodome/ConceptsTest.expected | 0
.../library-tests/frameworks/cryptodome/ConceptsTest.ql | 0
.../library-tests/frameworks/cryptodome/test_dsa.py | 0
.../library-tests/frameworks/cryptodome/test_ec.py | 0
.../library-tests/frameworks/cryptodome/test_rsa.py | 0
.../library-tests/frameworks/cryptography/ConceptsTest.expected | 0
.../library-tests/frameworks/cryptography/ConceptsTest.ql | 0
.../library-tests/frameworks/cryptography/test_dsa.py | 0
.../library-tests/frameworks/cryptography/test_ec.py | 0
.../library-tests/frameworks/cryptography/test_rsa.py | 0
.../library-tests/frameworks/dill/ConceptsTest.expected | 0
.../library-tests/frameworks/dill/ConceptsTest.ql | 0
.../{experimental => }/library-tests/frameworks/dill/Decoding.py | 0
.../library-tests/frameworks/django-v1/ConceptsTest.expected | 0
.../library-tests/frameworks/django-v1/ConceptsTest.ql | 0
.../library-tests/frameworks/django-v1/routing_test.py | 0
.../library-tests/frameworks/django-v2-v3/ConceptsTest.expected | 0
.../library-tests/frameworks/django-v2-v3/ConceptsTest.ql | 0
.../library-tests/frameworks/django-v2-v3/README.md | 0
.../library-tests/frameworks/django-v2-v3/TestTaint.expected | 0
.../library-tests/frameworks/django-v2-v3/TestTaint.ql | 0
.../library-tests/frameworks/django-v2-v3/dummy_import.py | 0
.../library-tests/frameworks/django-v2-v3/manage.py | 0
.../library-tests/frameworks/django-v2-v3/options | 0
.../library-tests/frameworks/django-v2-v3/response_test.py | 0
.../library-tests/frameworks/django-v2-v3/routing_test.py | 0
.../library-tests/frameworks/django-v2-v3/taint_test.py | 0
.../library-tests/frameworks/django-v2-v3/testapp/__init__.py | 0
.../library-tests/frameworks/django-v2-v3/testapp/admin.py | 0
.../library-tests/frameworks/django-v2-v3/testapp/apps.py | 0
.../frameworks/django-v2-v3/testapp/migrations/__init__.py | 0
.../library-tests/frameworks/django-v2-v3/testapp/models.py | 0
.../library-tests/frameworks/django-v2-v3/testapp/tests.py | 0
.../library-tests/frameworks/django-v2-v3/testapp/urls.py | 0
.../library-tests/frameworks/django-v2-v3/testapp/views.py | 0
.../library-tests/frameworks/django-v2-v3/testproj/__init__.py | 0
.../library-tests/frameworks/django-v2-v3/testproj/asgi.py | 0
.../library-tests/frameworks/django-v2-v3/testproj/settings.py | 0
.../library-tests/frameworks/django-v2-v3/testproj/urls.py | 0
.../library-tests/frameworks/django-v2-v3/testproj/wsgi.py | 0
.../library-tests/frameworks/django/ConceptsTest.expected | 0
.../library-tests/frameworks/django/ConceptsTest.ql | 0
.../library-tests/frameworks/django/SqlExecution.py | 0
.../library-tests/frameworks/fabric/ConceptsTest.expected | 0
.../library-tests/frameworks/fabric/ConceptsTest.ql | 0
.../library-tests/frameworks/fabric/TestTaint.expected | 0
.../library-tests/frameworks/fabric/TestTaint.ql | 0
.../library-tests/frameworks/fabric/fabric_v1_execute.py | 0
.../library-tests/frameworks/fabric/fabric_v1_test.py | 0
.../library-tests/frameworks/fabric/fabric_v2_test.py | 0
.../library-tests/frameworks/flask/ConceptsTest.expected | 0
.../library-tests/frameworks/flask/ConceptsTest.ql | 0
.../library-tests/frameworks/flask/TestTaint.expected | 0
.../library-tests/frameworks/flask/TestTaint.ql | 0
.../library-tests/frameworks/flask/external_blueprint.py | 0
.../{experimental => }/library-tests/frameworks/flask/old_test.py | 0
.../library-tests/frameworks/flask/response_test.py | 0
.../library-tests/frameworks/flask/routing_test.py | 0
.../library-tests/frameworks/flask/taint_test.py | 0
.../library-tests/frameworks/invoke/ConceptsTest.expected | 0
.../library-tests/frameworks/invoke/ConceptsTest.ql | 0
.../library-tests/frameworks/invoke/invoke_test.py | 0
.../library-tests/frameworks/modeling-example/NaiveModel.expected | 0
.../library-tests/frameworks/modeling-example/NaiveModel.ql | 0
.../frameworks/modeling-example/ProperModel.expected | 0
.../library-tests/frameworks/modeling-example/ProperModel.ql | 0
.../library-tests/frameworks/modeling-example/README.md | 0
.../library-tests/frameworks/modeling-example/SharedCode.qll | 0
.../library-tests/frameworks/modeling-example/test.py | 0
.../frameworks/mysql-connector-python/ConceptsTest.expected | 0
.../frameworks/mysql-connector-python/ConceptsTest.ql | 0
.../library-tests/frameworks/mysql-connector-python/pep249.py | 0
.../library-tests/frameworks/mysqldb/ConceptsTest.expected | 0
.../library-tests/frameworks/mysqldb/ConceptsTest.ql | 0
.../{experimental => }/library-tests/frameworks/mysqldb/pep249.py | 0
.../library-tests/frameworks/pymysql/ConceptsTest.expected | 0
.../library-tests/frameworks/pymysql/ConceptsTest.ql | 0
.../{experimental => }/library-tests/frameworks/pymysql/pep249.py | 0
.../library-tests/frameworks/stdlib-py2/CodeExecution.py | 0
.../library-tests/frameworks/stdlib-py2/ConceptsTest.expected | 0
.../library-tests/frameworks/stdlib-py2/ConceptsTest.ql | 0
.../library-tests/frameworks/stdlib-py2/SystemCommandExecution.py | 0
.../library-tests/frameworks/stdlib-py2/options | 0
.../library-tests/frameworks/stdlib-py3/CodeExecution.py | 0
.../library-tests/frameworks/stdlib-py3/ConceptsTest.expected | 0
.../library-tests/frameworks/stdlib-py3/ConceptsTest.ql | 0
.../library-tests/frameworks/stdlib-py3/Decoding.py | 0
.../library-tests/frameworks/stdlib-py3/Encoding.py | 0
.../library-tests/frameworks/stdlib-py3/options | 0
.../library-tests/frameworks/stdlib/CodeExecution.py | 0
.../library-tests/frameworks/stdlib/CodeExecutionPossibleFP1.py | 0
.../library-tests/frameworks/stdlib/CodeExecutionPossibleFP2.py | 0
.../library-tests/frameworks/stdlib/CodeExecutionPossibleFP3.py | 0
.../library-tests/frameworks/stdlib/ConceptsTest.expected | 0
.../library-tests/frameworks/stdlib/ConceptsTest.ql | 0
.../library-tests/frameworks/stdlib/Decoding.py | 0
.../library-tests/frameworks/stdlib/Encoding.py | 0
.../library-tests/frameworks/stdlib/FileSystemAccess.py | 0
.../library-tests/frameworks/stdlib/PathNormalization.py | 0
.../library-tests/frameworks/stdlib/SafeAccessCheck.py | 0
.../library-tests/frameworks/stdlib/SystemCommandExecution.py | 0
.../library-tests/frameworks/stdlib/TestTaint.expected | 0
.../library-tests/frameworks/stdlib/TestTaint.ql | 0
.../library-tests/frameworks/stdlib/http_server.py | 0
.../{experimental => }/library-tests/frameworks/stdlib/pep249.py | 0
.../library-tests/frameworks/tornado/ConceptsTest.expected | 0
.../library-tests/frameworks/tornado/ConceptsTest.ql | 0
.../{experimental => }/library-tests/frameworks/tornado/README.md | 0
.../library-tests/frameworks/tornado/TestTaint.expected | 0
.../library-tests/frameworks/tornado/TestTaint.ql | 0
.../{experimental => }/library-tests/frameworks/tornado/basic.py | 0
.../{experimental => }/library-tests/frameworks/tornado/options | 0
.../library-tests/frameworks/tornado/response_test.py | 0
.../library-tests/frameworks/tornado/routing_test.py | 0
.../library-tests/frameworks/tornado/taint_test.py | 0
.../library-tests/frameworks/yaml/ConceptsTest.expected | 0
.../library-tests/frameworks/yaml/ConceptsTest.ql | 0
.../{experimental => }/library-tests/frameworks/yaml/Decoding.py | 0
123 files changed, 0 insertions(+), 0 deletions(-)
rename python/ql/test/{experimental => }/library-tests/frameworks/crypto/ConceptsTest.expected (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/crypto/ConceptsTest.ql (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/crypto/test_dsa.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/crypto/test_ec.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/crypto/test_rsa.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/cryptodome/ConceptsTest.expected (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/cryptodome/ConceptsTest.ql (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/cryptodome/test_dsa.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/cryptodome/test_ec.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/cryptodome/test_rsa.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/cryptography/ConceptsTest.expected (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/cryptography/ConceptsTest.ql (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/cryptography/test_dsa.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/cryptography/test_ec.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/cryptography/test_rsa.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/dill/ConceptsTest.expected (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/dill/ConceptsTest.ql (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/dill/Decoding.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django-v1/ConceptsTest.expected (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django-v1/ConceptsTest.ql (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django-v1/routing_test.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django-v2-v3/ConceptsTest.expected (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django-v2-v3/ConceptsTest.ql (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django-v2-v3/README.md (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django-v2-v3/TestTaint.expected (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django-v2-v3/TestTaint.ql (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django-v2-v3/dummy_import.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django-v2-v3/manage.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django-v2-v3/options (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django-v2-v3/response_test.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django-v2-v3/routing_test.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django-v2-v3/taint_test.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django-v2-v3/testapp/__init__.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django-v2-v3/testapp/admin.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django-v2-v3/testapp/apps.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django-v2-v3/testapp/migrations/__init__.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django-v2-v3/testapp/models.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django-v2-v3/testapp/tests.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django-v2-v3/testapp/urls.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django-v2-v3/testapp/views.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django-v2-v3/testproj/__init__.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django-v2-v3/testproj/asgi.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django-v2-v3/testproj/settings.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django-v2-v3/testproj/urls.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django-v2-v3/testproj/wsgi.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django/ConceptsTest.expected (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django/ConceptsTest.ql (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/django/SqlExecution.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/fabric/ConceptsTest.expected (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/fabric/ConceptsTest.ql (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/fabric/TestTaint.expected (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/fabric/TestTaint.ql (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/fabric/fabric_v1_execute.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/fabric/fabric_v1_test.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/fabric/fabric_v2_test.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/flask/ConceptsTest.expected (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/flask/ConceptsTest.ql (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/flask/TestTaint.expected (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/flask/TestTaint.ql (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/flask/external_blueprint.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/flask/old_test.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/flask/response_test.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/flask/routing_test.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/flask/taint_test.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/invoke/ConceptsTest.expected (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/invoke/ConceptsTest.ql (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/invoke/invoke_test.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/modeling-example/NaiveModel.expected (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/modeling-example/NaiveModel.ql (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/modeling-example/ProperModel.expected (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/modeling-example/ProperModel.ql (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/modeling-example/README.md (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/modeling-example/SharedCode.qll (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/modeling-example/test.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/mysql-connector-python/ConceptsTest.expected (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/mysql-connector-python/ConceptsTest.ql (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/mysql-connector-python/pep249.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/mysqldb/ConceptsTest.expected (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/mysqldb/ConceptsTest.ql (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/mysqldb/pep249.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/pymysql/ConceptsTest.expected (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/pymysql/ConceptsTest.ql (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/pymysql/pep249.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib-py2/CodeExecution.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib-py2/ConceptsTest.expected (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib-py2/ConceptsTest.ql (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib-py2/SystemCommandExecution.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib-py2/options (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib-py3/CodeExecution.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib-py3/ConceptsTest.expected (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib-py3/ConceptsTest.ql (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib-py3/Decoding.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib-py3/Encoding.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib-py3/options (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib/CodeExecution.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib/CodeExecutionPossibleFP1.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib/CodeExecutionPossibleFP2.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib/CodeExecutionPossibleFP3.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib/ConceptsTest.expected (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib/ConceptsTest.ql (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib/Decoding.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib/Encoding.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib/FileSystemAccess.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib/PathNormalization.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib/SafeAccessCheck.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib/SystemCommandExecution.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib/TestTaint.expected (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib/TestTaint.ql (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib/http_server.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib/pep249.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/tornado/ConceptsTest.expected (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/tornado/ConceptsTest.ql (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/tornado/README.md (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/tornado/TestTaint.expected (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/tornado/TestTaint.ql (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/tornado/basic.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/tornado/options (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/tornado/response_test.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/tornado/routing_test.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/tornado/taint_test.py (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/yaml/ConceptsTest.expected (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/yaml/ConceptsTest.ql (100%)
rename python/ql/test/{experimental => }/library-tests/frameworks/yaml/Decoding.py (100%)
diff --git a/python/ql/test/experimental/library-tests/frameworks/crypto/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/crypto/ConceptsTest.expected
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/crypto/ConceptsTest.expected
rename to python/ql/test/library-tests/frameworks/crypto/ConceptsTest.expected
diff --git a/python/ql/test/experimental/library-tests/frameworks/crypto/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/crypto/ConceptsTest.ql
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/crypto/ConceptsTest.ql
rename to python/ql/test/library-tests/frameworks/crypto/ConceptsTest.ql
diff --git a/python/ql/test/experimental/library-tests/frameworks/crypto/test_dsa.py b/python/ql/test/library-tests/frameworks/crypto/test_dsa.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/crypto/test_dsa.py
rename to python/ql/test/library-tests/frameworks/crypto/test_dsa.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/crypto/test_ec.py b/python/ql/test/library-tests/frameworks/crypto/test_ec.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/crypto/test_ec.py
rename to python/ql/test/library-tests/frameworks/crypto/test_ec.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/crypto/test_rsa.py b/python/ql/test/library-tests/frameworks/crypto/test_rsa.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/crypto/test_rsa.py
rename to python/ql/test/library-tests/frameworks/crypto/test_rsa.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptodome/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/cryptodome/ConceptsTest.expected
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/cryptodome/ConceptsTest.expected
rename to python/ql/test/library-tests/frameworks/cryptodome/ConceptsTest.expected
diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptodome/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/cryptodome/ConceptsTest.ql
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/cryptodome/ConceptsTest.ql
rename to python/ql/test/library-tests/frameworks/cryptodome/ConceptsTest.ql
diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptodome/test_dsa.py b/python/ql/test/library-tests/frameworks/cryptodome/test_dsa.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/cryptodome/test_dsa.py
rename to python/ql/test/library-tests/frameworks/cryptodome/test_dsa.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptodome/test_ec.py b/python/ql/test/library-tests/frameworks/cryptodome/test_ec.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/cryptodome/test_ec.py
rename to python/ql/test/library-tests/frameworks/cryptodome/test_ec.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptodome/test_rsa.py b/python/ql/test/library-tests/frameworks/cryptodome/test_rsa.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/cryptodome/test_rsa.py
rename to python/ql/test/library-tests/frameworks/cryptodome/test_rsa.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptography/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/cryptography/ConceptsTest.expected
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/cryptography/ConceptsTest.expected
rename to python/ql/test/library-tests/frameworks/cryptography/ConceptsTest.expected
diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptography/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/cryptography/ConceptsTest.ql
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/cryptography/ConceptsTest.ql
rename to python/ql/test/library-tests/frameworks/cryptography/ConceptsTest.ql
diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptography/test_dsa.py b/python/ql/test/library-tests/frameworks/cryptography/test_dsa.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/cryptography/test_dsa.py
rename to python/ql/test/library-tests/frameworks/cryptography/test_dsa.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptography/test_ec.py b/python/ql/test/library-tests/frameworks/cryptography/test_ec.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/cryptography/test_ec.py
rename to python/ql/test/library-tests/frameworks/cryptography/test_ec.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptography/test_rsa.py b/python/ql/test/library-tests/frameworks/cryptography/test_rsa.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/cryptography/test_rsa.py
rename to python/ql/test/library-tests/frameworks/cryptography/test_rsa.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/dill/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/dill/ConceptsTest.expected
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/dill/ConceptsTest.expected
rename to python/ql/test/library-tests/frameworks/dill/ConceptsTest.expected
diff --git a/python/ql/test/experimental/library-tests/frameworks/dill/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/dill/ConceptsTest.ql
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/dill/ConceptsTest.ql
rename to python/ql/test/library-tests/frameworks/dill/ConceptsTest.ql
diff --git a/python/ql/test/experimental/library-tests/frameworks/dill/Decoding.py b/python/ql/test/library-tests/frameworks/dill/Decoding.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/dill/Decoding.py
rename to python/ql/test/library-tests/frameworks/dill/Decoding.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/django-v1/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/django-v1/ConceptsTest.expected
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django-v1/ConceptsTest.expected
rename to python/ql/test/library-tests/frameworks/django-v1/ConceptsTest.expected
diff --git a/python/ql/test/experimental/library-tests/frameworks/django-v1/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/django-v1/ConceptsTest.ql
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django-v1/ConceptsTest.ql
rename to python/ql/test/library-tests/frameworks/django-v1/ConceptsTest.ql
diff --git a/python/ql/test/experimental/library-tests/frameworks/django-v1/routing_test.py b/python/ql/test/library-tests/frameworks/django-v1/routing_test.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django-v1/routing_test.py
rename to python/ql/test/library-tests/frameworks/django-v1/routing_test.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/django-v2-v3/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/django-v2-v3/ConceptsTest.expected
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django-v2-v3/ConceptsTest.expected
rename to python/ql/test/library-tests/frameworks/django-v2-v3/ConceptsTest.expected
diff --git a/python/ql/test/experimental/library-tests/frameworks/django-v2-v3/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/django-v2-v3/ConceptsTest.ql
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django-v2-v3/ConceptsTest.ql
rename to python/ql/test/library-tests/frameworks/django-v2-v3/ConceptsTest.ql
diff --git a/python/ql/test/experimental/library-tests/frameworks/django-v2-v3/README.md b/python/ql/test/library-tests/frameworks/django-v2-v3/README.md
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django-v2-v3/README.md
rename to python/ql/test/library-tests/frameworks/django-v2-v3/README.md
diff --git a/python/ql/test/experimental/library-tests/frameworks/django-v2-v3/TestTaint.expected b/python/ql/test/library-tests/frameworks/django-v2-v3/TestTaint.expected
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django-v2-v3/TestTaint.expected
rename to python/ql/test/library-tests/frameworks/django-v2-v3/TestTaint.expected
diff --git a/python/ql/test/experimental/library-tests/frameworks/django-v2-v3/TestTaint.ql b/python/ql/test/library-tests/frameworks/django-v2-v3/TestTaint.ql
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django-v2-v3/TestTaint.ql
rename to python/ql/test/library-tests/frameworks/django-v2-v3/TestTaint.ql
diff --git a/python/ql/test/experimental/library-tests/frameworks/django-v2-v3/dummy_import.py b/python/ql/test/library-tests/frameworks/django-v2-v3/dummy_import.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django-v2-v3/dummy_import.py
rename to python/ql/test/library-tests/frameworks/django-v2-v3/dummy_import.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/django-v2-v3/manage.py b/python/ql/test/library-tests/frameworks/django-v2-v3/manage.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django-v2-v3/manage.py
rename to python/ql/test/library-tests/frameworks/django-v2-v3/manage.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/django-v2-v3/options b/python/ql/test/library-tests/frameworks/django-v2-v3/options
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django-v2-v3/options
rename to python/ql/test/library-tests/frameworks/django-v2-v3/options
diff --git a/python/ql/test/experimental/library-tests/frameworks/django-v2-v3/response_test.py b/python/ql/test/library-tests/frameworks/django-v2-v3/response_test.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django-v2-v3/response_test.py
rename to python/ql/test/library-tests/frameworks/django-v2-v3/response_test.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/django-v2-v3/routing_test.py b/python/ql/test/library-tests/frameworks/django-v2-v3/routing_test.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django-v2-v3/routing_test.py
rename to python/ql/test/library-tests/frameworks/django-v2-v3/routing_test.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/django-v2-v3/taint_test.py b/python/ql/test/library-tests/frameworks/django-v2-v3/taint_test.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django-v2-v3/taint_test.py
rename to python/ql/test/library-tests/frameworks/django-v2-v3/taint_test.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/django-v2-v3/testapp/__init__.py b/python/ql/test/library-tests/frameworks/django-v2-v3/testapp/__init__.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django-v2-v3/testapp/__init__.py
rename to python/ql/test/library-tests/frameworks/django-v2-v3/testapp/__init__.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/django-v2-v3/testapp/admin.py b/python/ql/test/library-tests/frameworks/django-v2-v3/testapp/admin.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django-v2-v3/testapp/admin.py
rename to python/ql/test/library-tests/frameworks/django-v2-v3/testapp/admin.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/django-v2-v3/testapp/apps.py b/python/ql/test/library-tests/frameworks/django-v2-v3/testapp/apps.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django-v2-v3/testapp/apps.py
rename to python/ql/test/library-tests/frameworks/django-v2-v3/testapp/apps.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/django-v2-v3/testapp/migrations/__init__.py b/python/ql/test/library-tests/frameworks/django-v2-v3/testapp/migrations/__init__.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django-v2-v3/testapp/migrations/__init__.py
rename to python/ql/test/library-tests/frameworks/django-v2-v3/testapp/migrations/__init__.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/django-v2-v3/testapp/models.py b/python/ql/test/library-tests/frameworks/django-v2-v3/testapp/models.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django-v2-v3/testapp/models.py
rename to python/ql/test/library-tests/frameworks/django-v2-v3/testapp/models.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/django-v2-v3/testapp/tests.py b/python/ql/test/library-tests/frameworks/django-v2-v3/testapp/tests.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django-v2-v3/testapp/tests.py
rename to python/ql/test/library-tests/frameworks/django-v2-v3/testapp/tests.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/django-v2-v3/testapp/urls.py b/python/ql/test/library-tests/frameworks/django-v2-v3/testapp/urls.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django-v2-v3/testapp/urls.py
rename to python/ql/test/library-tests/frameworks/django-v2-v3/testapp/urls.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/django-v2-v3/testapp/views.py b/python/ql/test/library-tests/frameworks/django-v2-v3/testapp/views.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django-v2-v3/testapp/views.py
rename to python/ql/test/library-tests/frameworks/django-v2-v3/testapp/views.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/django-v2-v3/testproj/__init__.py b/python/ql/test/library-tests/frameworks/django-v2-v3/testproj/__init__.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django-v2-v3/testproj/__init__.py
rename to python/ql/test/library-tests/frameworks/django-v2-v3/testproj/__init__.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/django-v2-v3/testproj/asgi.py b/python/ql/test/library-tests/frameworks/django-v2-v3/testproj/asgi.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django-v2-v3/testproj/asgi.py
rename to python/ql/test/library-tests/frameworks/django-v2-v3/testproj/asgi.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/django-v2-v3/testproj/settings.py b/python/ql/test/library-tests/frameworks/django-v2-v3/testproj/settings.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django-v2-v3/testproj/settings.py
rename to python/ql/test/library-tests/frameworks/django-v2-v3/testproj/settings.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/django-v2-v3/testproj/urls.py b/python/ql/test/library-tests/frameworks/django-v2-v3/testproj/urls.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django-v2-v3/testproj/urls.py
rename to python/ql/test/library-tests/frameworks/django-v2-v3/testproj/urls.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/django-v2-v3/testproj/wsgi.py b/python/ql/test/library-tests/frameworks/django-v2-v3/testproj/wsgi.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django-v2-v3/testproj/wsgi.py
rename to python/ql/test/library-tests/frameworks/django-v2-v3/testproj/wsgi.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/django/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/django/ConceptsTest.expected
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django/ConceptsTest.expected
rename to python/ql/test/library-tests/frameworks/django/ConceptsTest.expected
diff --git a/python/ql/test/experimental/library-tests/frameworks/django/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/django/ConceptsTest.ql
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django/ConceptsTest.ql
rename to python/ql/test/library-tests/frameworks/django/ConceptsTest.ql
diff --git a/python/ql/test/experimental/library-tests/frameworks/django/SqlExecution.py b/python/ql/test/library-tests/frameworks/django/SqlExecution.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/django/SqlExecution.py
rename to python/ql/test/library-tests/frameworks/django/SqlExecution.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/fabric/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/fabric/ConceptsTest.expected
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/fabric/ConceptsTest.expected
rename to python/ql/test/library-tests/frameworks/fabric/ConceptsTest.expected
diff --git a/python/ql/test/experimental/library-tests/frameworks/fabric/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/fabric/ConceptsTest.ql
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/fabric/ConceptsTest.ql
rename to python/ql/test/library-tests/frameworks/fabric/ConceptsTest.ql
diff --git a/python/ql/test/experimental/library-tests/frameworks/fabric/TestTaint.expected b/python/ql/test/library-tests/frameworks/fabric/TestTaint.expected
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/fabric/TestTaint.expected
rename to python/ql/test/library-tests/frameworks/fabric/TestTaint.expected
diff --git a/python/ql/test/experimental/library-tests/frameworks/fabric/TestTaint.ql b/python/ql/test/library-tests/frameworks/fabric/TestTaint.ql
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/fabric/TestTaint.ql
rename to python/ql/test/library-tests/frameworks/fabric/TestTaint.ql
diff --git a/python/ql/test/experimental/library-tests/frameworks/fabric/fabric_v1_execute.py b/python/ql/test/library-tests/frameworks/fabric/fabric_v1_execute.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/fabric/fabric_v1_execute.py
rename to python/ql/test/library-tests/frameworks/fabric/fabric_v1_execute.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/fabric/fabric_v1_test.py b/python/ql/test/library-tests/frameworks/fabric/fabric_v1_test.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/fabric/fabric_v1_test.py
rename to python/ql/test/library-tests/frameworks/fabric/fabric_v1_test.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/fabric/fabric_v2_test.py b/python/ql/test/library-tests/frameworks/fabric/fabric_v2_test.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/fabric/fabric_v2_test.py
rename to python/ql/test/library-tests/frameworks/fabric/fabric_v2_test.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/flask/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/flask/ConceptsTest.expected
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/flask/ConceptsTest.expected
rename to python/ql/test/library-tests/frameworks/flask/ConceptsTest.expected
diff --git a/python/ql/test/experimental/library-tests/frameworks/flask/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/flask/ConceptsTest.ql
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/flask/ConceptsTest.ql
rename to python/ql/test/library-tests/frameworks/flask/ConceptsTest.ql
diff --git a/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.expected b/python/ql/test/library-tests/frameworks/flask/TestTaint.expected
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.expected
rename to python/ql/test/library-tests/frameworks/flask/TestTaint.expected
diff --git a/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.ql b/python/ql/test/library-tests/frameworks/flask/TestTaint.ql
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.ql
rename to python/ql/test/library-tests/frameworks/flask/TestTaint.ql
diff --git a/python/ql/test/experimental/library-tests/frameworks/flask/external_blueprint.py b/python/ql/test/library-tests/frameworks/flask/external_blueprint.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/flask/external_blueprint.py
rename to python/ql/test/library-tests/frameworks/flask/external_blueprint.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/flask/old_test.py b/python/ql/test/library-tests/frameworks/flask/old_test.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/flask/old_test.py
rename to python/ql/test/library-tests/frameworks/flask/old_test.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/flask/response_test.py b/python/ql/test/library-tests/frameworks/flask/response_test.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/flask/response_test.py
rename to python/ql/test/library-tests/frameworks/flask/response_test.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/flask/routing_test.py b/python/ql/test/library-tests/frameworks/flask/routing_test.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/flask/routing_test.py
rename to python/ql/test/library-tests/frameworks/flask/routing_test.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/flask/taint_test.py b/python/ql/test/library-tests/frameworks/flask/taint_test.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/flask/taint_test.py
rename to python/ql/test/library-tests/frameworks/flask/taint_test.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/invoke/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/invoke/ConceptsTest.expected
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/invoke/ConceptsTest.expected
rename to python/ql/test/library-tests/frameworks/invoke/ConceptsTest.expected
diff --git a/python/ql/test/experimental/library-tests/frameworks/invoke/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/invoke/ConceptsTest.ql
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/invoke/ConceptsTest.ql
rename to python/ql/test/library-tests/frameworks/invoke/ConceptsTest.ql
diff --git a/python/ql/test/experimental/library-tests/frameworks/invoke/invoke_test.py b/python/ql/test/library-tests/frameworks/invoke/invoke_test.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/invoke/invoke_test.py
rename to python/ql/test/library-tests/frameworks/invoke/invoke_test.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/modeling-example/NaiveModel.expected b/python/ql/test/library-tests/frameworks/modeling-example/NaiveModel.expected
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/modeling-example/NaiveModel.expected
rename to python/ql/test/library-tests/frameworks/modeling-example/NaiveModel.expected
diff --git a/python/ql/test/experimental/library-tests/frameworks/modeling-example/NaiveModel.ql b/python/ql/test/library-tests/frameworks/modeling-example/NaiveModel.ql
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/modeling-example/NaiveModel.ql
rename to python/ql/test/library-tests/frameworks/modeling-example/NaiveModel.ql
diff --git a/python/ql/test/experimental/library-tests/frameworks/modeling-example/ProperModel.expected b/python/ql/test/library-tests/frameworks/modeling-example/ProperModel.expected
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/modeling-example/ProperModel.expected
rename to python/ql/test/library-tests/frameworks/modeling-example/ProperModel.expected
diff --git a/python/ql/test/experimental/library-tests/frameworks/modeling-example/ProperModel.ql b/python/ql/test/library-tests/frameworks/modeling-example/ProperModel.ql
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/modeling-example/ProperModel.ql
rename to python/ql/test/library-tests/frameworks/modeling-example/ProperModel.ql
diff --git a/python/ql/test/experimental/library-tests/frameworks/modeling-example/README.md b/python/ql/test/library-tests/frameworks/modeling-example/README.md
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/modeling-example/README.md
rename to python/ql/test/library-tests/frameworks/modeling-example/README.md
diff --git a/python/ql/test/experimental/library-tests/frameworks/modeling-example/SharedCode.qll b/python/ql/test/library-tests/frameworks/modeling-example/SharedCode.qll
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/modeling-example/SharedCode.qll
rename to python/ql/test/library-tests/frameworks/modeling-example/SharedCode.qll
diff --git a/python/ql/test/experimental/library-tests/frameworks/modeling-example/test.py b/python/ql/test/library-tests/frameworks/modeling-example/test.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/modeling-example/test.py
rename to python/ql/test/library-tests/frameworks/modeling-example/test.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/mysql-connector-python/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/mysql-connector-python/ConceptsTest.expected
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/mysql-connector-python/ConceptsTest.expected
rename to python/ql/test/library-tests/frameworks/mysql-connector-python/ConceptsTest.expected
diff --git a/python/ql/test/experimental/library-tests/frameworks/mysql-connector-python/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/mysql-connector-python/ConceptsTest.ql
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/mysql-connector-python/ConceptsTest.ql
rename to python/ql/test/library-tests/frameworks/mysql-connector-python/ConceptsTest.ql
diff --git a/python/ql/test/experimental/library-tests/frameworks/mysql-connector-python/pep249.py b/python/ql/test/library-tests/frameworks/mysql-connector-python/pep249.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/mysql-connector-python/pep249.py
rename to python/ql/test/library-tests/frameworks/mysql-connector-python/pep249.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/mysqldb/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/mysqldb/ConceptsTest.expected
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/mysqldb/ConceptsTest.expected
rename to python/ql/test/library-tests/frameworks/mysqldb/ConceptsTest.expected
diff --git a/python/ql/test/experimental/library-tests/frameworks/mysqldb/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/mysqldb/ConceptsTest.ql
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/mysqldb/ConceptsTest.ql
rename to python/ql/test/library-tests/frameworks/mysqldb/ConceptsTest.ql
diff --git a/python/ql/test/experimental/library-tests/frameworks/mysqldb/pep249.py b/python/ql/test/library-tests/frameworks/mysqldb/pep249.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/mysqldb/pep249.py
rename to python/ql/test/library-tests/frameworks/mysqldb/pep249.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/pymysql/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/pymysql/ConceptsTest.expected
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/pymysql/ConceptsTest.expected
rename to python/ql/test/library-tests/frameworks/pymysql/ConceptsTest.expected
diff --git a/python/ql/test/experimental/library-tests/frameworks/pymysql/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/pymysql/ConceptsTest.ql
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/pymysql/ConceptsTest.ql
rename to python/ql/test/library-tests/frameworks/pymysql/ConceptsTest.ql
diff --git a/python/ql/test/experimental/library-tests/frameworks/pymysql/pep249.py b/python/ql/test/library-tests/frameworks/pymysql/pep249.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/pymysql/pep249.py
rename to python/ql/test/library-tests/frameworks/pymysql/pep249.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/CodeExecution.py b/python/ql/test/library-tests/frameworks/stdlib-py2/CodeExecution.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/stdlib-py2/CodeExecution.py
rename to python/ql/test/library-tests/frameworks/stdlib-py2/CodeExecution.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/stdlib-py2/ConceptsTest.expected
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/stdlib-py2/ConceptsTest.expected
rename to python/ql/test/library-tests/frameworks/stdlib-py2/ConceptsTest.expected
diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/stdlib-py2/ConceptsTest.ql
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/stdlib-py2/ConceptsTest.ql
rename to python/ql/test/library-tests/frameworks/stdlib-py2/ConceptsTest.ql
diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/SystemCommandExecution.py b/python/ql/test/library-tests/frameworks/stdlib-py2/SystemCommandExecution.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/stdlib-py2/SystemCommandExecution.py
rename to python/ql/test/library-tests/frameworks/stdlib-py2/SystemCommandExecution.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/options b/python/ql/test/library-tests/frameworks/stdlib-py2/options
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/stdlib-py2/options
rename to python/ql/test/library-tests/frameworks/stdlib-py2/options
diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib-py3/CodeExecution.py b/python/ql/test/library-tests/frameworks/stdlib-py3/CodeExecution.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/stdlib-py3/CodeExecution.py
rename to python/ql/test/library-tests/frameworks/stdlib-py3/CodeExecution.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib-py3/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/stdlib-py3/ConceptsTest.expected
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/stdlib-py3/ConceptsTest.expected
rename to python/ql/test/library-tests/frameworks/stdlib-py3/ConceptsTest.expected
diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib-py3/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/stdlib-py3/ConceptsTest.ql
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/stdlib-py3/ConceptsTest.ql
rename to python/ql/test/library-tests/frameworks/stdlib-py3/ConceptsTest.ql
diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib-py3/Decoding.py b/python/ql/test/library-tests/frameworks/stdlib-py3/Decoding.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/stdlib-py3/Decoding.py
rename to python/ql/test/library-tests/frameworks/stdlib-py3/Decoding.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib-py3/Encoding.py b/python/ql/test/library-tests/frameworks/stdlib-py3/Encoding.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/stdlib-py3/Encoding.py
rename to python/ql/test/library-tests/frameworks/stdlib-py3/Encoding.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib-py3/options b/python/ql/test/library-tests/frameworks/stdlib-py3/options
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/stdlib-py3/options
rename to python/ql/test/library-tests/frameworks/stdlib-py3/options
diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/CodeExecution.py b/python/ql/test/library-tests/frameworks/stdlib/CodeExecution.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/stdlib/CodeExecution.py
rename to python/ql/test/library-tests/frameworks/stdlib/CodeExecution.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/CodeExecutionPossibleFP1.py b/python/ql/test/library-tests/frameworks/stdlib/CodeExecutionPossibleFP1.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/stdlib/CodeExecutionPossibleFP1.py
rename to python/ql/test/library-tests/frameworks/stdlib/CodeExecutionPossibleFP1.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/CodeExecutionPossibleFP2.py b/python/ql/test/library-tests/frameworks/stdlib/CodeExecutionPossibleFP2.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/stdlib/CodeExecutionPossibleFP2.py
rename to python/ql/test/library-tests/frameworks/stdlib/CodeExecutionPossibleFP2.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/CodeExecutionPossibleFP3.py b/python/ql/test/library-tests/frameworks/stdlib/CodeExecutionPossibleFP3.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/stdlib/CodeExecutionPossibleFP3.py
rename to python/ql/test/library-tests/frameworks/stdlib/CodeExecutionPossibleFP3.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/stdlib/ConceptsTest.expected
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/stdlib/ConceptsTest.expected
rename to python/ql/test/library-tests/frameworks/stdlib/ConceptsTest.expected
diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/stdlib/ConceptsTest.ql
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/stdlib/ConceptsTest.ql
rename to python/ql/test/library-tests/frameworks/stdlib/ConceptsTest.ql
diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/Decoding.py b/python/ql/test/library-tests/frameworks/stdlib/Decoding.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/stdlib/Decoding.py
rename to python/ql/test/library-tests/frameworks/stdlib/Decoding.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/Encoding.py b/python/ql/test/library-tests/frameworks/stdlib/Encoding.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/stdlib/Encoding.py
rename to python/ql/test/library-tests/frameworks/stdlib/Encoding.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/FileSystemAccess.py b/python/ql/test/library-tests/frameworks/stdlib/FileSystemAccess.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/stdlib/FileSystemAccess.py
rename to python/ql/test/library-tests/frameworks/stdlib/FileSystemAccess.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/PathNormalization.py b/python/ql/test/library-tests/frameworks/stdlib/PathNormalization.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/stdlib/PathNormalization.py
rename to python/ql/test/library-tests/frameworks/stdlib/PathNormalization.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/SafeAccessCheck.py b/python/ql/test/library-tests/frameworks/stdlib/SafeAccessCheck.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/stdlib/SafeAccessCheck.py
rename to python/ql/test/library-tests/frameworks/stdlib/SafeAccessCheck.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py b/python/ql/test/library-tests/frameworks/stdlib/SystemCommandExecution.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py
rename to python/ql/test/library-tests/frameworks/stdlib/SystemCommandExecution.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/TestTaint.expected b/python/ql/test/library-tests/frameworks/stdlib/TestTaint.expected
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/stdlib/TestTaint.expected
rename to python/ql/test/library-tests/frameworks/stdlib/TestTaint.expected
diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/TestTaint.ql b/python/ql/test/library-tests/frameworks/stdlib/TestTaint.ql
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/stdlib/TestTaint.ql
rename to python/ql/test/library-tests/frameworks/stdlib/TestTaint.ql
diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/http_server.py b/python/ql/test/library-tests/frameworks/stdlib/http_server.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/stdlib/http_server.py
rename to python/ql/test/library-tests/frameworks/stdlib/http_server.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/pep249.py b/python/ql/test/library-tests/frameworks/stdlib/pep249.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/stdlib/pep249.py
rename to python/ql/test/library-tests/frameworks/stdlib/pep249.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/tornado/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/tornado/ConceptsTest.expected
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/tornado/ConceptsTest.expected
rename to python/ql/test/library-tests/frameworks/tornado/ConceptsTest.expected
diff --git a/python/ql/test/experimental/library-tests/frameworks/tornado/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/tornado/ConceptsTest.ql
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/tornado/ConceptsTest.ql
rename to python/ql/test/library-tests/frameworks/tornado/ConceptsTest.ql
diff --git a/python/ql/test/experimental/library-tests/frameworks/tornado/README.md b/python/ql/test/library-tests/frameworks/tornado/README.md
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/tornado/README.md
rename to python/ql/test/library-tests/frameworks/tornado/README.md
diff --git a/python/ql/test/experimental/library-tests/frameworks/tornado/TestTaint.expected b/python/ql/test/library-tests/frameworks/tornado/TestTaint.expected
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/tornado/TestTaint.expected
rename to python/ql/test/library-tests/frameworks/tornado/TestTaint.expected
diff --git a/python/ql/test/experimental/library-tests/frameworks/tornado/TestTaint.ql b/python/ql/test/library-tests/frameworks/tornado/TestTaint.ql
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/tornado/TestTaint.ql
rename to python/ql/test/library-tests/frameworks/tornado/TestTaint.ql
diff --git a/python/ql/test/experimental/library-tests/frameworks/tornado/basic.py b/python/ql/test/library-tests/frameworks/tornado/basic.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/tornado/basic.py
rename to python/ql/test/library-tests/frameworks/tornado/basic.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/tornado/options b/python/ql/test/library-tests/frameworks/tornado/options
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/tornado/options
rename to python/ql/test/library-tests/frameworks/tornado/options
diff --git a/python/ql/test/experimental/library-tests/frameworks/tornado/response_test.py b/python/ql/test/library-tests/frameworks/tornado/response_test.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/tornado/response_test.py
rename to python/ql/test/library-tests/frameworks/tornado/response_test.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/tornado/routing_test.py b/python/ql/test/library-tests/frameworks/tornado/routing_test.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/tornado/routing_test.py
rename to python/ql/test/library-tests/frameworks/tornado/routing_test.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/tornado/taint_test.py b/python/ql/test/library-tests/frameworks/tornado/taint_test.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/tornado/taint_test.py
rename to python/ql/test/library-tests/frameworks/tornado/taint_test.py
diff --git a/python/ql/test/experimental/library-tests/frameworks/yaml/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/yaml/ConceptsTest.expected
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/yaml/ConceptsTest.expected
rename to python/ql/test/library-tests/frameworks/yaml/ConceptsTest.expected
diff --git a/python/ql/test/experimental/library-tests/frameworks/yaml/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/yaml/ConceptsTest.ql
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/yaml/ConceptsTest.ql
rename to python/ql/test/library-tests/frameworks/yaml/ConceptsTest.ql
diff --git a/python/ql/test/experimental/library-tests/frameworks/yaml/Decoding.py b/python/ql/test/library-tests/frameworks/yaml/Decoding.py
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/yaml/Decoding.py
rename to python/ql/test/library-tests/frameworks/yaml/Decoding.py
From 8949b9eb0a659765640c13ada6755bc86c21388d Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Fri, 19 Mar 2021 15:59:06 +0100
Subject: [PATCH 202/725] add shell interpreted arrays as sinks for
js/shell-command-constructed-from-input
---
...ShellCommandConstructionCustomizations.qll | 47 +++++++++++++
.../UnsafeShellCommandConstruction.expected | 69 +++++++++++++++++++
.../Security/CWE-078/UselessUseOfCat.expected | 9 +++
.../query-tests/Security/CWE-078/lib/lib.js | 28 ++++++++
4 files changed, 153 insertions(+)
diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeShellCommandConstructionCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeShellCommandConstructionCustomizations.qll
index 32eda1db8de..ac5f8e2c56b 100644
--- a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeShellCommandConstructionCustomizations.qll
+++ b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeShellCommandConstructionCustomizations.qll
@@ -143,6 +143,53 @@ module UnsafeShellCommandConstruction {
override DataFlow::Node getAlertLocation() { result = this }
}
+ /**
+ * Gets a node that ends up in an array that is ultimately executed as a shell script by `sys`.
+ */
+ private DataFlow::SourceNode endsInShellExecutedArray(
+ DataFlow::TypeBackTracker t, SystemCommandExecution sys
+ ) {
+ t.start() and
+ result = sys.getArgumentList().getALocalSource() and
+ // the array gets joined to a string when `shell` is set to true.
+ sys.getOptionsArg()
+ .getALocalSource()
+ .getAPropertyWrite("shell")
+ .getRhs()
+ .asExpr()
+ .(BooleanLiteral)
+ .getValue() = "true"
+ or
+ exists(DataFlow::TypeBackTracker t2 |
+ result = endsInShellExecutedArray(t2, sys).backtrack(t2, t)
+ )
+ }
+
+ /**
+ * An argument to a command invocation where the `shell` option is set to true.
+ */
+ class ShellTrueCommandExecutionSink extends Sink {
+ SystemCommandExecution sys;
+
+ ShellTrueCommandExecutionSink() {
+ // `shell` is set to true. That means string-concatenation happens behind the scenes.
+ // We just assume that a `shell` option in any library means the same thing as it does in NodeJS.
+ exists(DataFlow::SourceNode arr |
+ arr = endsInShellExecutedArray(DataFlow::TypeBackTracker::end(), sys)
+ |
+ this = arr.(DataFlow::ArrayCreationNode).getAnElement()
+ or
+ this = arr.getAMethodCall(["push", "unshift"]).getAnArgument()
+ )
+ }
+
+ override string getSinkType() { result = "Shell argument" }
+
+ override SystemCommandExecution getCommandExecution() { result = sys }
+
+ override DataFlow::Node getAlertLocation() { result = this }
+ }
+
/**
* A sanitizer like: "'"+name.replace(/'/g,"'\\''")+"'"
* Which sanitizes on Unix.
diff --git a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected
index 2d707e1a693..de59b2e9cde 100644
--- a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected
@@ -205,6 +205,30 @@ nodes
| lib/lib.js:405:39:405:42 | name |
| lib/lib.js:406:22:406:25 | name |
| lib/lib.js:406:22:406:25 | name |
+| lib/lib.js:414:40:414:43 | name |
+| lib/lib.js:414:40:414:43 | name |
+| lib/lib.js:415:22:415:25 | name |
+| lib/lib.js:415:22:415:25 | name |
+| lib/lib.js:417:28:417:31 | name |
+| lib/lib.js:417:28:417:31 | name |
+| lib/lib.js:418:25:418:28 | name |
+| lib/lib.js:418:25:418:28 | name |
+| lib/lib.js:419:32:419:35 | name |
+| lib/lib.js:419:32:419:35 | name |
+| lib/lib.js:420:29:420:32 | name |
+| lib/lib.js:420:29:420:32 | name |
+| lib/lib.js:424:24:424:27 | name |
+| lib/lib.js:424:24:424:27 | name |
+| lib/lib.js:426:11:426:14 | name |
+| lib/lib.js:426:11:426:14 | name |
+| lib/lib.js:428:28:428:51 | (name ? ... ' : '') |
+| lib/lib.js:428:28:428:57 | (name ? ... ) + '-' |
+| lib/lib.js:428:29:428:50 | name ? ... :' : '' |
+| lib/lib.js:428:36:428:39 | name |
+| lib/lib.js:428:36:428:45 | name + ':' |
+| lib/lib.js:431:23:431:26 | last |
+| lib/lib.js:436:19:436:22 | last |
+| lib/lib.js:436:19:436:22 | last |
edges
| lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name |
| lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name |
@@ -444,6 +468,43 @@ edges
| lib/lib.js:405:39:405:42 | name | lib/lib.js:406:22:406:25 | name |
| lib/lib.js:405:39:405:42 | name | lib/lib.js:406:22:406:25 | name |
| lib/lib.js:405:39:405:42 | name | lib/lib.js:406:22:406:25 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:415:22:415:25 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:415:22:415:25 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:415:22:415:25 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:415:22:415:25 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:417:28:417:31 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:417:28:417:31 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:417:28:417:31 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:417:28:417:31 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:418:25:418:28 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:418:25:418:28 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:418:25:418:28 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:418:25:418:28 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:419:32:419:35 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:419:32:419:35 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:419:32:419:35 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:419:32:419:35 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:420:29:420:32 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:420:29:420:32 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:420:29:420:32 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:420:29:420:32 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:424:24:424:27 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:424:24:424:27 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:424:24:424:27 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:424:24:424:27 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:426:11:426:14 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:426:11:426:14 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:426:11:426:14 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:426:11:426:14 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:428:36:428:39 | name |
+| lib/lib.js:414:40:414:43 | name | lib/lib.js:428:36:428:39 | name |
+| lib/lib.js:428:28:428:51 | (name ? ... ' : '') | lib/lib.js:428:28:428:57 | (name ? ... ) + '-' |
+| lib/lib.js:428:28:428:57 | (name ? ... ) + '-' | lib/lib.js:431:23:431:26 | last |
+| lib/lib.js:428:29:428:50 | name ? ... :' : '' | lib/lib.js:428:28:428:51 | (name ? ... ' : '') |
+| lib/lib.js:428:36:428:39 | name | lib/lib.js:428:36:428:45 | name + ':' |
+| lib/lib.js:428:36:428:45 | name + ':' | lib/lib.js:428:29:428:50 | name ? ... :' : '' |
+| lib/lib.js:431:23:431:26 | last | lib/lib.js:436:19:436:22 | last |
+| lib/lib.js:431:23:431:26 | last | lib/lib.js:436:19:436:22 | last |
#select
| lib/lib2.js:4:10:4:25 | "rm -rf " + name | lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name | $@ based on library input is later used in $@. | lib/lib2.js:4:10:4:25 | "rm -rf " + name | String concatenation | lib/lib2.js:4:2:4:26 | cp.exec ... + name) | shell command |
| lib/lib2.js:8:10:8:25 | "rm -rf " + name | lib/lib2.js:7:32:7:35 | name | lib/lib2.js:8:22:8:25 | name | $@ based on library input is later used in $@. | lib/lib2.js:8:10:8:25 | "rm -rf " + name | String concatenation | lib/lib2.js:8:2:8:26 | cp.exec ... + name) | shell command |
@@ -502,3 +563,11 @@ edges
| lib/lib.js:351:10:351:27 | "rm -rf " + unsafe | lib/lib.js:349:29:349:34 | unsafe | lib/lib.js:351:22:351:27 | unsafe | $@ based on library input is later used in $@. | lib/lib.js:351:10:351:27 | "rm -rf " + unsafe | String concatenation | lib/lib.js:351:2:351:28 | cp.exec ... unsafe) | shell command |
| lib/lib.js:366:17:366:56 | "learn ... + model | lib/lib.js:360:20:360:23 | opts | lib/lib.js:366:28:366:42 | this.learn_args | $@ based on library input is later used in $@. | lib/lib.js:366:17:366:56 | "learn ... + model | String concatenation | lib/lib.js:367:3:367:18 | cp.exec(command) | shell command |
| lib/lib.js:406:10:406:25 | "rm -rf " + name | lib/lib.js:405:39:405:42 | name | lib/lib.js:406:22:406:25 | name | $@ based on library input is later used in $@. | lib/lib.js:406:10:406:25 | "rm -rf " + name | String concatenation | lib/lib.js:406:2:406:26 | cp.exec ... + name) | shell command |
+| lib/lib.js:415:10:415:25 | "rm -rf " + name | lib/lib.js:414:40:414:43 | name | lib/lib.js:415:22:415:25 | name | $@ based on library input is later used in $@. | lib/lib.js:415:10:415:25 | "rm -rf " + name | String concatenation | lib/lib.js:415:2:415:26 | cp.exec ... + name) | shell command |
+| lib/lib.js:417:28:417:31 | name | lib/lib.js:414:40:414:43 | name | lib/lib.js:417:28:417:31 | name | $@ based on library input is later used in $@. | lib/lib.js:417:28:417:31 | name | Shell argument | lib/lib.js:417:2:417:66 | cp.exec ... => {}) | shell command |
+| lib/lib.js:418:25:418:28 | name | lib/lib.js:414:40:414:43 | name | lib/lib.js:418:25:418:28 | name | $@ based on library input is later used in $@. | lib/lib.js:418:25:418:28 | name | Shell argument | lib/lib.js:418:2:418:45 | cp.spaw ... true}) | shell command |
+| lib/lib.js:419:32:419:35 | name | lib/lib.js:414:40:414:43 | name | lib/lib.js:419:32:419:35 | name | $@ based on library input is later used in $@. | lib/lib.js:419:32:419:35 | name | Shell argument | lib/lib.js:419:2:419:52 | cp.exec ... true}) | shell command |
+| lib/lib.js:420:29:420:32 | name | lib/lib.js:414:40:414:43 | name | lib/lib.js:420:29:420:32 | name | $@ based on library input is later used in $@. | lib/lib.js:420:29:420:32 | name | Shell argument | lib/lib.js:420:2:420:49 | cp.spaw ... true}) | shell command |
+| lib/lib.js:424:24:424:27 | name | lib/lib.js:414:40:414:43 | name | lib/lib.js:424:24:424:27 | name | $@ based on library input is later used in $@. | lib/lib.js:424:24:424:27 | name | Shell argument | lib/lib.js:424:2:424:40 | spawn(" ... WN_OPT) | shell command |
+| lib/lib.js:426:11:426:14 | name | lib/lib.js:414:40:414:43 | name | lib/lib.js:426:11:426:14 | name | $@ based on library input is later used in $@. | lib/lib.js:426:11:426:14 | name | Shell argument | lib/lib.js:427:2:427:28 | spawn(" ... WN_OPT) | shell command |
+| lib/lib.js:436:19:436:22 | last | lib/lib.js:414:40:414:43 | name | lib/lib.js:436:19:436:22 | last | $@ based on library input is later used in $@. | lib/lib.js:436:19:436:22 | last | Shell argument | lib/lib.js:428:2:428:70 | spawn(" ... WN_OPT) | shell command |
diff --git a/javascript/ql/test/query-tests/Security/CWE-078/UselessUseOfCat.expected b/javascript/ql/test/query-tests/Security/CWE-078/UselessUseOfCat.expected
index 7a7e22fb332..4c7e10b658e 100644
--- a/javascript/ql/test/query-tests/Security/CWE-078/UselessUseOfCat.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-078/UselessUseOfCat.expected
@@ -53,6 +53,8 @@ syncCommand
| command-line-parameter-command-injection.js:20:2:20:30 | cp.exec ... + arg0) |
| command-line-parameter-command-injection.js:26:2:26:51 | cp.exec ... tion"`) |
| command-line-parameter-command-injection.js:27:2:27:58 | cp.exec ... tion"`) |
+| lib/lib.js:419:2:419:52 | cp.exec ... true}) |
+| lib/lib.js:420:2:420:49 | cp.spaw ... true}) |
| other.js:7:5:7:36 | require ... nc(cmd) |
| other.js:9:5:9:35 | require ... nc(cmd) |
| other.js:12:5:12:30 | require ... nc(cmd) |
@@ -100,6 +102,13 @@ options
| lib/lib.js:152:2:152:23 | cp.spaw ... gs, cb) | lib/lib.js:152:21:152:22 | cb |
| lib/lib.js:159:2:159:23 | cp.spaw ... gs, cb) | lib/lib.js:159:21:159:22 | cb |
| lib/lib.js:163:2:167:2 | cp.spaw ... t' }\\n\\t) | lib/lib.js:166:3:166:22 | { stdio: 'inherit' } |
+| lib/lib.js:417:2:417:66 | cp.exec ... => {}) | lib/lib.js:417:35:417:47 | {shell: true} |
+| lib/lib.js:418:2:418:45 | cp.spaw ... true}) | lib/lib.js:418:32:418:44 | {shell: true} |
+| lib/lib.js:419:2:419:52 | cp.exec ... true}) | lib/lib.js:419:39:419:51 | {shell: true} |
+| lib/lib.js:420:2:420:49 | cp.spaw ... true}) | lib/lib.js:420:36:420:48 | {shell: true} |
+| lib/lib.js:424:2:424:40 | spawn(" ... WN_OPT) | lib/lib.js:424:31:424:39 | SPAWN_OPT |
+| lib/lib.js:427:2:427:28 | spawn(" ... WN_OPT) | lib/lib.js:427:19:427:27 | SPAWN_OPT |
+| lib/lib.js:428:2:428:70 | spawn(" ... WN_OPT) | lib/lib.js:428:61:428:69 | SPAWN_OPT |
| uselesscat.js:28:1:28:39 | execSyn ... 1000}) | uselesscat.js:28:28:28:38 | {uid: 1000} |
| uselesscat.js:30:1:30:64 | exec('c ... t) { }) | uselesscat.js:30:26:30:38 | { cwd: './' } |
| uselesscat.js:34:1:34:54 | execSyn ... utf8'}) | uselesscat.js:34:36:34:53 | {encoding: 'utf8'} |
diff --git a/javascript/ql/test/query-tests/Security/CWE-078/lib/lib.js b/javascript/ql/test/query-tests/Security/CWE-078/lib/lib.js
index d1a885438ff..ea10bf14fa0 100644
--- a/javascript/ql/test/query-tests/Security/CWE-078/lib/lib.js
+++ b/javascript/ql/test/query-tests/Security/CWE-078/lib/lib.js
@@ -408,3 +408,31 @@ module.exports.sanitizer3 = function (name) {
var sanitized = yetAnohterSanitizer(name);
cp.exec("rm -rf " + sanitized); // OK
}
+
+const cp = require("child_process");
+const spawn = cp.spawn;
+module.exports.shellOption = function (name) {
+ cp.exec("rm -rf " + name); // NOT OK
+
+ cp.execFile("rm", ["-rf", name], {shell: true}, (err, out) => {}); // NOT OK
+ cp.spawn("rm", ["-rf", name], {shell: true}); // NOT OK
+ cp.execFileSync("rm", ["-rf", name], {shell: true}); // NOT OK
+ cp.spawnSync("rm", ["-rf", name], {shell: true}); // NOT OK
+
+ const SPAWN_OPT = {shell: true};
+
+ spawn("rm", ["first", name], SPAWN_OPT); // NOT OK
+ var arr = [];
+ arr.push(name); // NOT OK
+ spawn("rm", arr, SPAWN_OPT);
+ spawn("rm", build("node", (name ? name + ':' : '') + '-'), SPAWN_OPT); // This is bad, but the alert location is down in `build`.
+}
+
+function build(first, last) {
+ var arr = [];
+ if (something() === 'gm')
+ arr.push('convert');
+ first && arr.push(first);
+ last && arr.push(last); // NOT OK
+ return arr;
+};
\ No newline at end of file
From 6c1ec6d96b1bfd7ab98b2d47c6fb059339956c16 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Fri, 19 Mar 2021 16:09:05 +0100
Subject: [PATCH 203/725] C++: Accept test changes.
---
.../UncontrolledFormatString.expected | 115 ++++++++++++++++
...olledFormatStringThroughGlobalVar.expected | 125 ++++++++++++++++++
2 files changed, 240 insertions(+)
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatString.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatString.expected
index 58e3dda0964..01ef5030e5f 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatString.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatString.expected
@@ -1,3 +1,118 @@
edges
+| globalVars.c:8:7:8:10 | copy | globalVars.c:27:9:27:12 | copy |
+| globalVars.c:8:7:8:10 | copy | globalVars.c:27:9:27:12 | copy |
+| globalVars.c:8:7:8:10 | copy | globalVars.c:30:15:30:18 | copy |
+| globalVars.c:8:7:8:10 | copy | globalVars.c:30:15:30:18 | copy |
+| globalVars.c:8:7:8:10 | copy | globalVars.c:33:15:33:18 | copy |
+| globalVars.c:8:7:8:10 | copy | globalVars.c:33:15:33:18 | copy |
+| globalVars.c:8:7:8:10 | copy | globalVars.c:35:11:35:14 | copy |
+| globalVars.c:8:7:8:10 | copy | globalVars.c:35:11:35:14 | copy |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:38:9:38:13 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:38:9:38:13 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:41:15:41:19 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:41:15:41:19 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:44:15:44:19 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:44:15:44:19 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:15:21:15:23 | val | globalVars.c:16:10:16:12 | val |
+| globalVars.c:19:25:19:27 | *str | globalVars.c:20:9:20:11 | (const char *)... |
+| globalVars.c:19:25:19:27 | *str | globalVars.c:20:9:20:11 | str indirection |
+| globalVars.c:19:25:19:27 | str | globalVars.c:20:9:20:11 | (const char *)... |
+| globalVars.c:19:25:19:27 | str | globalVars.c:20:9:20:11 | str |
+| globalVars.c:19:25:19:27 | str | globalVars.c:20:9:20:11 | str |
+| globalVars.c:19:25:19:27 | str | globalVars.c:20:9:20:11 | str indirection |
+| globalVars.c:20:9:20:11 | str | globalVars.c:20:9:20:11 | (const char *)... |
+| globalVars.c:20:9:20:11 | str | globalVars.c:20:9:20:11 | str indirection |
+| globalVars.c:27:9:27:12 | copy | globalVars.c:27:9:27:12 | (const char *)... |
+| globalVars.c:27:9:27:12 | copy | globalVars.c:27:9:27:12 | copy indirection |
+| globalVars.c:30:2:30:13 | copy | globalVars.c:19:25:19:27 | str |
+| globalVars.c:30:2:30:13 | copy | globalVars.c:19:25:19:27 | str |
+| globalVars.c:30:15:30:18 | copy | globalVars.c:30:2:30:13 | copy |
+| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy |
+| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy |
+| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy indirection |
+| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy indirection |
+| globalVars.c:30:15:30:18 | copy indirection | globalVars.c:19:25:19:27 | *str |
+| globalVars.c:33:15:33:18 | copy | globalVars.c:33:15:33:18 | copy |
+| globalVars.c:33:15:33:18 | copy | globalVars.c:33:15:33:18 | copy |
+| globalVars.c:33:15:33:18 | copy | globalVars.c:33:15:33:18 | copy indirection |
+| globalVars.c:35:2:35:9 | copy | globalVars.c:15:21:15:23 | val |
+| globalVars.c:35:2:35:9 | copy | globalVars.c:15:21:15:23 | val |
+| globalVars.c:35:11:35:14 | copy | globalVars.c:35:2:35:9 | copy |
+| globalVars.c:35:11:35:14 | copy | globalVars.c:35:11:35:14 | copy |
+| globalVars.c:35:11:35:14 | copy | globalVars.c:35:11:35:14 | copy |
+| globalVars.c:35:11:35:14 | copy | globalVars.c:35:11:35:14 | copy indirection |
+| globalVars.c:38:9:38:13 | copy2 | globalVars.c:38:9:38:13 | (const char *)... |
+| globalVars.c:38:9:38:13 | copy2 | globalVars.c:38:9:38:13 | copy2 indirection |
+| globalVars.c:41:2:41:13 | copy2 | globalVars.c:19:25:19:27 | str |
+| globalVars.c:41:2:41:13 | copy2 | globalVars.c:19:25:19:27 | str |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:2:41:13 | copy2 |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 indirection |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 indirection |
+| globalVars.c:41:15:41:19 | copy2 indirection | globalVars.c:19:25:19:27 | *str |
+| globalVars.c:44:15:44:19 | copy2 | globalVars.c:44:15:44:19 | copy2 |
+| globalVars.c:44:15:44:19 | copy2 | globalVars.c:44:15:44:19 | copy2 |
+| globalVars.c:44:15:44:19 | copy2 | globalVars.c:44:15:44:19 | copy2 indirection |
+| globalVars.c:50:9:50:13 | copy2 | globalVars.c:50:9:50:13 | (const char *)... |
+| globalVars.c:50:9:50:13 | copy2 | globalVars.c:50:9:50:13 | copy2 indirection |
nodes
+| globalVars.c:8:7:8:10 | copy | semmle.label | copy |
+| globalVars.c:9:7:9:11 | copy2 | semmle.label | copy2 |
+| globalVars.c:15:21:15:23 | val | semmle.label | val |
+| globalVars.c:15:21:15:23 | val | semmle.label | val |
+| globalVars.c:16:10:16:12 | val | semmle.label | val |
+| globalVars.c:16:10:16:12 | val | semmle.label | val |
+| globalVars.c:19:25:19:27 | *str | semmle.label | *str |
+| globalVars.c:19:25:19:27 | str | semmle.label | str |
+| globalVars.c:19:25:19:27 | str | semmle.label | str |
+| globalVars.c:20:9:20:11 | (const char *)... | semmle.label | (const char *)... |
+| globalVars.c:20:9:20:11 | (const char *)... | semmle.label | (const char *)... |
+| globalVars.c:20:9:20:11 | str | semmle.label | str |
+| globalVars.c:20:9:20:11 | str | semmle.label | str |
+| globalVars.c:20:9:20:11 | str indirection | semmle.label | str indirection |
+| globalVars.c:20:9:20:11 | str indirection | semmle.label | str indirection |
+| globalVars.c:27:9:27:12 | (const char *)... | semmle.label | (const char *)... |
+| globalVars.c:27:9:27:12 | (const char *)... | semmle.label | (const char *)... |
+| globalVars.c:27:9:27:12 | copy | semmle.label | copy |
+| globalVars.c:27:9:27:12 | copy | semmle.label | copy |
+| globalVars.c:27:9:27:12 | copy indirection | semmle.label | copy indirection |
+| globalVars.c:27:9:27:12 | copy indirection | semmle.label | copy indirection |
+| globalVars.c:30:2:30:13 | copy | semmle.label | copy |
+| globalVars.c:30:15:30:18 | copy | semmle.label | copy |
+| globalVars.c:30:15:30:18 | copy | semmle.label | copy |
+| globalVars.c:30:15:30:18 | copy indirection | semmle.label | copy indirection |
+| globalVars.c:30:15:30:18 | copy indirection | semmle.label | copy indirection |
+| globalVars.c:33:15:33:18 | copy | semmle.label | copy |
+| globalVars.c:33:15:33:18 | copy | semmle.label | copy |
+| globalVars.c:33:15:33:18 | copy indirection | semmle.label | copy indirection |
+| globalVars.c:33:15:33:18 | copy indirection | semmle.label | copy indirection |
+| globalVars.c:35:2:35:9 | copy | semmle.label | copy |
+| globalVars.c:35:11:35:14 | copy | semmle.label | copy |
+| globalVars.c:35:11:35:14 | copy | semmle.label | copy |
+| globalVars.c:35:11:35:14 | copy indirection | semmle.label | copy indirection |
+| globalVars.c:35:11:35:14 | copy indirection | semmle.label | copy indirection |
+| globalVars.c:38:9:38:13 | (const char *)... | semmle.label | (const char *)... |
+| globalVars.c:38:9:38:13 | (const char *)... | semmle.label | (const char *)... |
+| globalVars.c:38:9:38:13 | copy2 | semmle.label | copy2 |
+| globalVars.c:38:9:38:13 | copy2 | semmle.label | copy2 |
+| globalVars.c:38:9:38:13 | copy2 indirection | semmle.label | copy2 indirection |
+| globalVars.c:38:9:38:13 | copy2 indirection | semmle.label | copy2 indirection |
+| globalVars.c:41:2:41:13 | copy2 | semmle.label | copy2 |
+| globalVars.c:41:15:41:19 | copy2 | semmle.label | copy2 |
+| globalVars.c:41:15:41:19 | copy2 | semmle.label | copy2 |
+| globalVars.c:41:15:41:19 | copy2 indirection | semmle.label | copy2 indirection |
+| globalVars.c:41:15:41:19 | copy2 indirection | semmle.label | copy2 indirection |
+| globalVars.c:44:15:44:19 | copy2 | semmle.label | copy2 |
+| globalVars.c:44:15:44:19 | copy2 | semmle.label | copy2 |
+| globalVars.c:44:15:44:19 | copy2 indirection | semmle.label | copy2 indirection |
+| globalVars.c:44:15:44:19 | copy2 indirection | semmle.label | copy2 indirection |
+| globalVars.c:50:9:50:13 | (const char *)... | semmle.label | (const char *)... |
+| globalVars.c:50:9:50:13 | (const char *)... | semmle.label | (const char *)... |
+| globalVars.c:50:9:50:13 | copy2 | semmle.label | copy2 |
+| globalVars.c:50:9:50:13 | copy2 | semmle.label | copy2 |
+| globalVars.c:50:9:50:13 | copy2 indirection | semmle.label | copy2 indirection |
+| globalVars.c:50:9:50:13 | copy2 indirection | semmle.label | copy2 indirection |
#select
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected
index f095153f39c..5ac81e70545 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected
@@ -2,16 +2,37 @@ edges
| globalVars.c:8:7:8:10 | copy | globalVars.c:27:9:27:12 | copy |
| globalVars.c:8:7:8:10 | copy | globalVars.c:27:9:27:12 | copy |
| globalVars.c:8:7:8:10 | copy | globalVars.c:27:9:27:12 | copy |
+| globalVars.c:8:7:8:10 | copy | globalVars.c:27:9:27:12 | copy |
+| globalVars.c:8:7:8:10 | copy | globalVars.c:27:9:27:12 | copy |
+| globalVars.c:8:7:8:10 | copy | globalVars.c:27:9:27:12 | copy |
| globalVars.c:8:7:8:10 | copy | globalVars.c:30:15:30:18 | copy |
| globalVars.c:8:7:8:10 | copy | globalVars.c:30:15:30:18 | copy |
| globalVars.c:8:7:8:10 | copy | globalVars.c:30:15:30:18 | copy |
+| globalVars.c:8:7:8:10 | copy | globalVars.c:30:15:30:18 | copy |
+| globalVars.c:8:7:8:10 | copy | globalVars.c:30:15:30:18 | copy |
+| globalVars.c:8:7:8:10 | copy | globalVars.c:30:15:30:18 | copy |
+| globalVars.c:8:7:8:10 | copy | globalVars.c:33:15:33:18 | copy |
+| globalVars.c:8:7:8:10 | copy | globalVars.c:33:15:33:18 | copy |
+| globalVars.c:8:7:8:10 | copy | globalVars.c:35:11:35:14 | copy |
+| globalVars.c:8:7:8:10 | copy | globalVars.c:35:11:35:14 | copy |
| globalVars.c:8:7:8:10 | copy | globalVars.c:35:11:35:14 | copy |
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:38:9:38:13 | copy2 |
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:38:9:38:13 | copy2 |
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:38:9:38:13 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:38:9:38:13 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:38:9:38:13 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:38:9:38:13 | copy2 |
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:41:15:41:19 | copy2 |
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:41:15:41:19 | copy2 |
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:41:15:41:19 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:41:15:41:19 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:41:15:41:19 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:41:15:41:19 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:44:15:44:19 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:44:15:44:19 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 |
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 |
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 |
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 |
@@ -20,7 +41,16 @@ edges
| globalVars.c:11:22:11:25 | argv | globalVars.c:12:2:12:15 | Store |
| globalVars.c:12:2:12:15 | Store | globalVars.c:8:7:8:10 | copy |
| globalVars.c:15:21:15:23 | val | globalVars.c:16:2:16:12 | Store |
+| globalVars.c:15:21:15:23 | val | globalVars.c:16:10:16:12 | val |
| globalVars.c:16:2:16:12 | Store | globalVars.c:9:7:9:11 | copy2 |
+| globalVars.c:19:25:19:27 | *str | globalVars.c:20:9:20:11 | (const char *)... |
+| globalVars.c:19:25:19:27 | *str | globalVars.c:20:9:20:11 | str indirection |
+| globalVars.c:19:25:19:27 | str | globalVars.c:20:9:20:11 | (const char *)... |
+| globalVars.c:19:25:19:27 | str | globalVars.c:20:9:20:11 | str |
+| globalVars.c:19:25:19:27 | str | globalVars.c:20:9:20:11 | str |
+| globalVars.c:19:25:19:27 | str | globalVars.c:20:9:20:11 | str indirection |
+| globalVars.c:20:9:20:11 | str | globalVars.c:20:9:20:11 | (const char *)... |
+| globalVars.c:20:9:20:11 | str | globalVars.c:20:9:20:11 | str indirection |
| globalVars.c:24:2:24:9 | argv | globalVars.c:11:22:11:25 | argv |
| globalVars.c:24:11:24:14 | argv | globalVars.c:24:2:24:9 | argv |
| globalVars.c:24:11:24:14 | argv | globalVars.c:24:2:24:9 | argv |
@@ -28,67 +58,162 @@ edges
| globalVars.c:24:11:24:14 | argv | globalVars.c:24:11:24:14 | argv indirection |
| globalVars.c:24:11:24:14 | argv indirection | globalVars.c:11:22:11:25 | *argv |
| globalVars.c:27:9:27:12 | copy | globalVars.c:27:9:27:12 | (const char *)... |
+| globalVars.c:27:9:27:12 | copy | globalVars.c:27:9:27:12 | (const char *)... |
+| globalVars.c:27:9:27:12 | copy | globalVars.c:27:9:27:12 | copy |
| globalVars.c:27:9:27:12 | copy | globalVars.c:27:9:27:12 | copy |
| globalVars.c:27:9:27:12 | copy | globalVars.c:27:9:27:12 | copy indirection |
+| globalVars.c:27:9:27:12 | copy | globalVars.c:27:9:27:12 | copy indirection |
+| globalVars.c:30:2:30:13 | copy | globalVars.c:19:25:19:27 | str |
+| globalVars.c:30:2:30:13 | copy | globalVars.c:19:25:19:27 | str |
+| globalVars.c:30:15:30:18 | copy | globalVars.c:30:2:30:13 | copy |
+| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy |
+| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy |
+| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy |
| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy |
| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy |
| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy |
| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy indirection |
+| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy indirection |
+| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy indirection |
+| globalVars.c:30:15:30:18 | copy indirection | globalVars.c:19:25:19:27 | *str |
+| globalVars.c:33:15:33:18 | copy | globalVars.c:33:15:33:18 | copy |
+| globalVars.c:33:15:33:18 | copy | globalVars.c:33:15:33:18 | copy |
+| globalVars.c:33:15:33:18 | copy | globalVars.c:33:15:33:18 | copy indirection |
+| globalVars.c:35:2:35:9 | copy | globalVars.c:15:21:15:23 | val |
+| globalVars.c:35:2:35:9 | copy | globalVars.c:15:21:15:23 | val |
| globalVars.c:35:2:35:9 | copy | globalVars.c:15:21:15:23 | val |
| globalVars.c:35:11:35:14 | copy | globalVars.c:35:2:35:9 | copy |
+| globalVars.c:35:11:35:14 | copy | globalVars.c:35:2:35:9 | copy |
+| globalVars.c:35:11:35:14 | copy | globalVars.c:35:11:35:14 | copy |
+| globalVars.c:35:11:35:14 | copy | globalVars.c:35:11:35:14 | copy |
+| globalVars.c:35:11:35:14 | copy | globalVars.c:35:11:35:14 | copy indirection |
+| globalVars.c:38:9:38:13 | copy2 | globalVars.c:38:9:38:13 | (const char *)... |
| globalVars.c:38:9:38:13 | copy2 | globalVars.c:38:9:38:13 | (const char *)... |
| globalVars.c:38:9:38:13 | copy2 | globalVars.c:38:9:38:13 | copy2 |
+| globalVars.c:38:9:38:13 | copy2 | globalVars.c:38:9:38:13 | copy2 |
| globalVars.c:38:9:38:13 | copy2 | globalVars.c:38:9:38:13 | copy2 indirection |
+| globalVars.c:38:9:38:13 | copy2 | globalVars.c:38:9:38:13 | copy2 indirection |
+| globalVars.c:41:2:41:13 | copy2 | globalVars.c:19:25:19:27 | str |
+| globalVars.c:41:2:41:13 | copy2 | globalVars.c:19:25:19:27 | str |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:2:41:13 | copy2 |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 |
| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 |
| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 |
| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 |
| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 indirection |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 indirection |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 indirection |
+| globalVars.c:41:15:41:19 | copy2 indirection | globalVars.c:19:25:19:27 | *str |
+| globalVars.c:44:15:44:19 | copy2 | globalVars.c:44:15:44:19 | copy2 |
+| globalVars.c:44:15:44:19 | copy2 | globalVars.c:44:15:44:19 | copy2 |
+| globalVars.c:44:15:44:19 | copy2 | globalVars.c:44:15:44:19 | copy2 indirection |
+| globalVars.c:50:9:50:13 | copy2 | globalVars.c:50:9:50:13 | (const char *)... |
| globalVars.c:50:9:50:13 | copy2 | globalVars.c:50:9:50:13 | (const char *)... |
| globalVars.c:50:9:50:13 | copy2 | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:50:9:50:13 | copy2 | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:50:9:50:13 | copy2 | globalVars.c:50:9:50:13 | copy2 indirection |
| globalVars.c:50:9:50:13 | copy2 | globalVars.c:50:9:50:13 | copy2 indirection |
nodes
| globalVars.c:8:7:8:10 | copy | semmle.label | copy |
+| globalVars.c:8:7:8:10 | copy | semmle.label | copy |
+| globalVars.c:9:7:9:11 | copy2 | semmle.label | copy2 |
| globalVars.c:9:7:9:11 | copy2 | semmle.label | copy2 |
| globalVars.c:11:22:11:25 | *argv | semmle.label | *argv |
| globalVars.c:11:22:11:25 | argv | semmle.label | argv |
| globalVars.c:12:2:12:15 | Store | semmle.label | Store |
| globalVars.c:15:21:15:23 | val | semmle.label | val |
+| globalVars.c:15:21:15:23 | val | semmle.label | val |
+| globalVars.c:15:21:15:23 | val | semmle.label | val |
| globalVars.c:16:2:16:12 | Store | semmle.label | Store |
+| globalVars.c:16:10:16:12 | val | semmle.label | val |
+| globalVars.c:16:10:16:12 | val | semmle.label | val |
+| globalVars.c:19:25:19:27 | *str | semmle.label | *str |
+| globalVars.c:19:25:19:27 | str | semmle.label | str |
+| globalVars.c:19:25:19:27 | str | semmle.label | str |
+| globalVars.c:20:9:20:11 | (const char *)... | semmle.label | (const char *)... |
+| globalVars.c:20:9:20:11 | (const char *)... | semmle.label | (const char *)... |
+| globalVars.c:20:9:20:11 | str | semmle.label | str |
+| globalVars.c:20:9:20:11 | str | semmle.label | str |
+| globalVars.c:20:9:20:11 | str indirection | semmle.label | str indirection |
+| globalVars.c:20:9:20:11 | str indirection | semmle.label | str indirection |
| globalVars.c:24:2:24:9 | argv | semmle.label | argv |
| globalVars.c:24:11:24:14 | argv | semmle.label | argv |
| globalVars.c:24:11:24:14 | argv | semmle.label | argv |
| globalVars.c:24:11:24:14 | argv indirection | semmle.label | argv indirection |
| globalVars.c:27:9:27:12 | (const char *)... | semmle.label | (const char *)... |
| globalVars.c:27:9:27:12 | (const char *)... | semmle.label | (const char *)... |
+| globalVars.c:27:9:27:12 | (const char *)... | semmle.label | (const char *)... |
+| globalVars.c:27:9:27:12 | (const char *)... | semmle.label | (const char *)... |
+| globalVars.c:27:9:27:12 | copy | semmle.label | copy |
+| globalVars.c:27:9:27:12 | copy | semmle.label | copy |
| globalVars.c:27:9:27:12 | copy | semmle.label | copy |
| globalVars.c:27:9:27:12 | copy | semmle.label | copy |
| globalVars.c:27:9:27:12 | copy | semmle.label | copy |
| globalVars.c:27:9:27:12 | copy indirection | semmle.label | copy indirection |
| globalVars.c:27:9:27:12 | copy indirection | semmle.label | copy indirection |
+| globalVars.c:27:9:27:12 | copy indirection | semmle.label | copy indirection |
+| globalVars.c:27:9:27:12 | copy indirection | semmle.label | copy indirection |
+| globalVars.c:30:2:30:13 | copy | semmle.label | copy |
+| globalVars.c:30:15:30:18 | copy | semmle.label | copy |
+| globalVars.c:30:15:30:18 | copy | semmle.label | copy |
| globalVars.c:30:15:30:18 | copy | semmle.label | copy |
| globalVars.c:30:15:30:18 | copy | semmle.label | copy |
| globalVars.c:30:15:30:18 | copy | semmle.label | copy |
| globalVars.c:30:15:30:18 | copy indirection | semmle.label | copy indirection |
| globalVars.c:30:15:30:18 | copy indirection | semmle.label | copy indirection |
+| globalVars.c:30:15:30:18 | copy indirection | semmle.label | copy indirection |
+| globalVars.c:30:15:30:18 | copy indirection | semmle.label | copy indirection |
+| globalVars.c:33:15:33:18 | copy | semmle.label | copy |
+| globalVars.c:33:15:33:18 | copy | semmle.label | copy |
+| globalVars.c:33:15:33:18 | copy indirection | semmle.label | copy indirection |
+| globalVars.c:33:15:33:18 | copy indirection | semmle.label | copy indirection |
+| globalVars.c:35:2:35:9 | copy | semmle.label | copy |
| globalVars.c:35:2:35:9 | copy | semmle.label | copy |
| globalVars.c:35:11:35:14 | copy | semmle.label | copy |
+| globalVars.c:35:11:35:14 | copy | semmle.label | copy |
+| globalVars.c:35:11:35:14 | copy | semmle.label | copy |
+| globalVars.c:35:11:35:14 | copy indirection | semmle.label | copy indirection |
+| globalVars.c:35:11:35:14 | copy indirection | semmle.label | copy indirection |
+| globalVars.c:38:9:38:13 | (const char *)... | semmle.label | (const char *)... |
+| globalVars.c:38:9:38:13 | (const char *)... | semmle.label | (const char *)... |
| globalVars.c:38:9:38:13 | (const char *)... | semmle.label | (const char *)... |
| globalVars.c:38:9:38:13 | (const char *)... | semmle.label | (const char *)... |
| globalVars.c:38:9:38:13 | copy2 | semmle.label | copy2 |
| globalVars.c:38:9:38:13 | copy2 | semmle.label | copy2 |
| globalVars.c:38:9:38:13 | copy2 | semmle.label | copy2 |
+| globalVars.c:38:9:38:13 | copy2 | semmle.label | copy2 |
+| globalVars.c:38:9:38:13 | copy2 | semmle.label | copy2 |
| globalVars.c:38:9:38:13 | copy2 indirection | semmle.label | copy2 indirection |
| globalVars.c:38:9:38:13 | copy2 indirection | semmle.label | copy2 indirection |
+| globalVars.c:38:9:38:13 | copy2 indirection | semmle.label | copy2 indirection |
+| globalVars.c:38:9:38:13 | copy2 indirection | semmle.label | copy2 indirection |
+| globalVars.c:41:2:41:13 | copy2 | semmle.label | copy2 |
+| globalVars.c:41:15:41:19 | copy2 | semmle.label | copy2 |
+| globalVars.c:41:15:41:19 | copy2 | semmle.label | copy2 |
| globalVars.c:41:15:41:19 | copy2 | semmle.label | copy2 |
| globalVars.c:41:15:41:19 | copy2 | semmle.label | copy2 |
| globalVars.c:41:15:41:19 | copy2 | semmle.label | copy2 |
| globalVars.c:41:15:41:19 | copy2 indirection | semmle.label | copy2 indirection |
| globalVars.c:41:15:41:19 | copy2 indirection | semmle.label | copy2 indirection |
+| globalVars.c:41:15:41:19 | copy2 indirection | semmle.label | copy2 indirection |
+| globalVars.c:41:15:41:19 | copy2 indirection | semmle.label | copy2 indirection |
+| globalVars.c:44:15:44:19 | copy2 | semmle.label | copy2 |
+| globalVars.c:44:15:44:19 | copy2 | semmle.label | copy2 |
+| globalVars.c:44:15:44:19 | copy2 indirection | semmle.label | copy2 indirection |
+| globalVars.c:44:15:44:19 | copy2 indirection | semmle.label | copy2 indirection |
+| globalVars.c:50:9:50:13 | (const char *)... | semmle.label | (const char *)... |
+| globalVars.c:50:9:50:13 | (const char *)... | semmle.label | (const char *)... |
| globalVars.c:50:9:50:13 | (const char *)... | semmle.label | (const char *)... |
| globalVars.c:50:9:50:13 | (const char *)... | semmle.label | (const char *)... |
| globalVars.c:50:9:50:13 | copy2 | semmle.label | copy2 |
| globalVars.c:50:9:50:13 | copy2 | semmle.label | copy2 |
| globalVars.c:50:9:50:13 | copy2 | semmle.label | copy2 |
+| globalVars.c:50:9:50:13 | copy2 | semmle.label | copy2 |
+| globalVars.c:50:9:50:13 | copy2 | semmle.label | copy2 |
+| globalVars.c:50:9:50:13 | copy2 indirection | semmle.label | copy2 indirection |
+| globalVars.c:50:9:50:13 | copy2 indirection | semmle.label | copy2 indirection |
| globalVars.c:50:9:50:13 | copy2 indirection | semmle.label | copy2 indirection |
| globalVars.c:50:9:50:13 | copy2 indirection | semmle.label | copy2 indirection |
#select
From 6e1ee07e908b1beb91169b4abd891c53018a5c23 Mon Sep 17 00:00:00 2001
From: Tom Hvitved
Date: Fri, 19 Mar 2021 16:25:48 +0100
Subject: [PATCH 204/725] Address review comment
---
docs/ql-libraries/dataflow/dataflow.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/ql-libraries/dataflow/dataflow.md b/docs/ql-libraries/dataflow/dataflow.md
index 77e75a36241..c4c09a4c949 100644
--- a/docs/ql-libraries/dataflow/dataflow.md
+++ b/docs/ql-libraries/dataflow/dataflow.md
@@ -186,9 +186,9 @@ values, for example through `out` parameters in C#, the `ReturnKind` class can
be defined and used to match up different kinds of `ReturnNode`s with the
corresponding `OutNode`s.
-#### Lambda flow
+#### First-class functions
-For lambda-like calls, the library supports built-in call resolution based on data flow between a lambda creation expression and a lambda call. The interface that needs to be implemented is
+For calls to first-class functions, the library supports built-in call resolution based on data flow between a function creation expression and a call. The interface that needs to be implemented is
```ql
class LambdaCallKind
From ea8c8df65338cd08347652388343335972d8d83b Mon Sep 17 00:00:00 2001
From: Asger Feldthaus
Date: Fri, 19 Mar 2021 15:30:49 +0000
Subject: [PATCH 205/725] JS: Fix bad join orders in summarizedHigherOrderCall
---
.../javascript/dataflow/Configuration.qll | 26 +++++++++++--------
1 file changed, 15 insertions(+), 11 deletions(-)
diff --git a/javascript/ql/src/semmle/javascript/dataflow/Configuration.qll b/javascript/ql/src/semmle/javascript/dataflow/Configuration.qll
index 9c4587d1414..f45ed826b8c 100644
--- a/javascript/ql/src/semmle/javascript/dataflow/Configuration.qll
+++ b/javascript/ql/src/semmle/javascript/dataflow/Configuration.qll
@@ -1356,19 +1356,20 @@ private predicate summarizedHigherOrderCall(
DataFlow::Node arg, DataFlow::Node cb, int i, DataFlow::Configuration cfg, PathSummary summary
) {
exists(
- Function f, DataFlow::InvokeNode outer, DataFlow::InvokeNode inner, int j,
- DataFlow::Node innerArg, DataFlow::SourceNode cbParm, PathSummary oldSummary
+ Function f, DataFlow::InvokeNode inner, int j, DataFlow::Node innerArg,
+ DataFlow::SourceNode cbParm, PathSummary oldSummary
|
// Captured flow does not need to be summarized - it is handled by the local case in `higherOrderCall`.
- not arg = DataFlow::capturedVariableNode(_) and
- summarizedHigherOrderCallAux(f, outer, arg, innerArg, cfg, oldSummary, cbParm, inner, j, cb)
+ not arg = DataFlow::capturedVariableNode(_)
|
// direct higher-order call
+ summarizedHigherOrderCallAux(f, arg, innerArg, cfg, oldSummary, cbParm, inner, j, cb) and
cbParm.flowsTo(inner.getCalleeNode()) and
i = j and
summary = oldSummary
or
// indirect higher-order call
+ summarizedHigherOrderCallAux(f, arg, innerArg, cfg, oldSummary, cbParm, inner, j, cb) and
exists(DataFlow::Node cbArg, PathSummary newSummary |
cbParm.flowsTo(cbArg) and
summarizedHigherOrderCall(innerArg, cbArg, i, cfg, newSummary) and
@@ -1382,14 +1383,17 @@ private predicate summarizedHigherOrderCall(
*/
pragma[noinline]
private predicate summarizedHigherOrderCallAux(
- Function f, DataFlow::InvokeNode outer, DataFlow::Node arg, DataFlow::Node innerArg,
- DataFlow::Configuration cfg, PathSummary oldSummary, DataFlow::SourceNode cbParm,
- DataFlow::InvokeNode inner, int j, DataFlow::Node cb
+ Function f, DataFlow::Node arg, DataFlow::Node innerArg, DataFlow::Configuration cfg,
+ PathSummary oldSummary, DataFlow::SourceNode cbParm, DataFlow::InvokeNode inner, int j,
+ DataFlow::Node cb
) {
- reachableFromInput(f, outer, arg, innerArg, cfg, oldSummary) and
- // Only track actual parameter flow.
- argumentPassing(outer, cb, f, cbParm) and
- innerArg = inner.getArgument(j)
+ exists(DataFlow::Node outer1, DataFlow::Node outer2 |
+ reachableFromInput(f, outer1, arg, innerArg, cfg, oldSummary) and
+ outer1 = pragma[only_bind_into](outer2) and
+ // Only track actual parameter flow.
+ argumentPassing(outer2, cb, f, cbParm) and
+ innerArg = inner.getArgument(j)
+ )
}
/**
From eae7bccaad7999b0c1cc3d825718e36de486d617 Mon Sep 17 00:00:00 2001
From: yoff
Date: Fri, 19 Mar 2021 16:50:48 +0100
Subject: [PATCH 206/725] Apply suggestions from code review
Co-authored-by: Shati Patel <42641846+shati-patel@users.noreply.github.com>
---
.../analyzing-data-flow-in-python.rst | 2 +-
.../codeql-library-for-python.rst | 4 +-
.../using-api-graphs-in-python.rst | 42 +++++++++----------
3 files changed, 25 insertions(+), 23 deletions(-)
diff --git a/docs/codeql/codeql-language-guides/analyzing-data-flow-in-python.rst b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-python.rst
index 6e35c78a5a4..f5128e5eafd 100644
--- a/docs/codeql/codeql-language-guides/analyzing-data-flow-in-python.rst
+++ b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-python.rst
@@ -99,7 +99,7 @@ Python has builtin functionality for reading and writing files, such as the func
➤ `See this in the query console on LGTM.com `__. Two of the demo projects make use of this low-level API.
-Notice the use of the ``API`` module for referring to library functions. It is further described in ":doc:`Using API graphs in Python `."
+Notice the use of the ``API`` module for referring to library functions. For more information, see ":doc:`Using API graphs in Python `."
Unfortunately this will only give the expression in the argument, not the values which could be passed to it. So we use local data flow to find all expressions that flow into the argument:
diff --git a/docs/codeql/codeql-language-guides/codeql-library-for-python.rst b/docs/codeql/codeql-language-guides/codeql-library-for-python.rst
index ed5cf8b1deb..204a6d22c3b 100644
--- a/docs/codeql/codeql-language-guides/codeql-library-for-python.rst
+++ b/docs/codeql/codeql-language-guides/codeql-library-for-python.rst
@@ -23,7 +23,9 @@ The CodeQL library for Python incorporates a large number of classes. Each class
- **Data flow** - classes that represent entities from the data flow graphs.
- **API graphs** - classes that represent entities from the API graphs.
-The first two categories are described below. See ":doc:`Analyzing data flow in Python `" for a description of data flow and associated classes and ":doc:`Using API graphs in Python `" for a description of API graphs and their use.
+The first two categories are described below.
+For a description of data flow and associated classes, see ":doc:`Analyzing data flow in Python `".
+For a description of API graphs and their use, see ":doc:`Using API graphs in Python `."
Syntactic classes
-----------------
diff --git a/docs/codeql/codeql-language-guides/using-api-graphs-in-python.rst b/docs/codeql/codeql-language-guides/using-api-graphs-in-python.rst
index ebfd70fe1fc..219519d6d92 100644
--- a/docs/codeql/codeql-language-guides/using-api-graphs-in-python.rst
+++ b/docs/codeql/codeql-language-guides/using-api-graphs-in-python.rst
@@ -10,7 +10,7 @@ About this article
------------------
This article describes how to use API graphs to reference classes and functions defined in library
-code. This can be used to conveniently refer to external library functions when defining things like
+code. You can use API graphs to conveniently refer to external library functions when defining things like
remote flow sources.
@@ -18,8 +18,8 @@ Module imports
--------------
The most common entry point into the API graph will be the point where an external module or package is
-imported. The API graph node corresponding to the ``re`` library, for instance, can be accessed
-using the ``API::moduleImport`` method defined in the ``semmle.python.ApiGraphs`` module, as the
+imported. For example, you can access the API graph node corresponding to the ``re`` library
+by using the ``API::moduleImport`` method defined in the ``semmle.python.ApiGraphs`` module, as the
following snippet demonstrates.
.. code-block:: ql
@@ -31,8 +31,8 @@ following snippet demonstrates.
➤ `See this in the query console on LGTM.com `__.
-On its own, this only selects the API graph node corresponding to the ``re`` module. To find
-where this module is referenced, we use the ``getAUse`` method. Thus, the following query selects
+This query only selects the API graph node corresponding to the ``re`` module. To find
+where this module is referenced, you can use the ``getAUse`` method. The following query selects
all references to the ``re`` module in the current database.
.. code-block:: ql
@@ -56,8 +56,8 @@ correctly recognized as a reference to the ``re.compile`` function.
r = my_re_compile(".*")
-If only immediate uses are required, without taking local flow into account, then the method
-``getAnImmediateUse`` may be used instead.
+If you only require immediate uses, without taking local flow into account, then you can use
+the ``getAnImmediateUse`` method instead.
Note that the given module name *must not* contain any dots. Thus, something like
``API::moduleImport("flask.views")`` will not do what you expect. Instead, this should be decomposed
@@ -67,8 +67,8 @@ section.
Accessing attributes
--------------------
-Given a node in the API graph, we may access its attributes by using the ``getMember`` method. Using
-the above ``re.compile`` example, we may now find references to ``re.compile`` by doing
+Given a node in the API graph, you can access its attributes by using the ``getMember`` method. Using
+the above ``re.compile`` example, you can now find references to ``re.compile``.
.. code-block:: ql
@@ -79,15 +79,15 @@ the above ``re.compile`` example, we may now find references to ``re.compile`` b
➤ `See this in the query console on LGTM.com `__.
-In addition to ``getMember``, the method ``getUnknownMember`` can be used to find references to API
-components where the name is not known statically, and the ``getAMember`` method can be used to
+In addition to ``getMember``, you can use the ``getUnknownMember`` method to find references to API
+components where the name is not known statically. You can use the ``getAMember`` method to
access all members, both known and unknown.
Calls and class instantiations
------------------------------
To track instances of classes defined in external libraries, or the results of calling externally
-defined functions, we may use the ``getReturn`` method. Thus, the following snippet finds all places
+defined functions, you can use the ``getReturn`` method. The following snippet finds all places
where the return value of ``re.compile`` is used:
.. code-block:: ql
@@ -100,8 +100,8 @@ where the return value of ``re.compile`` is used:
➤ `See this in the query console on LGTM.com `__.
Note that this includes all uses of the result of ``re.compile``, including those reachable via
-local flow. To get just the *calls* to ``re.compile``, we can use ``getAnImmediateUse`` instead of
-``getAUse``. As this is a common occurrence, the method ``getACall`` can be used instead of
+local flow. To get just the *calls* to ``re.compile``, you can use ``getAnImmediateUse`` instead of
+``getAUse``. As this is a common occurrence, you can use ``getACall`` instead of
``getReturn`` followed by ``getAnImmediateUse``.
➤ `See this in the query console on LGTM.com `__.
@@ -113,14 +113,14 @@ Subclasses
----------
For many libraries, the main mode of usage is to extend one or more library classes. To track this
-in the API graph, we can use the ``getASubclass`` method to get the API graph node corresponding to
+in the API graph, you can use the ``getASubclass`` method to get the API graph node corresponding to
all the immediate subclasses of this node. To find *all* subclasses, use ``*`` or ``+`` to apply the
-method repeatedly, as in `getASubclass*`.
+method repeatedly, as in ``getASubclass*``.
Note that ``getASubclass`` does not account for any subclassing that takes place in library code
that has not been extracted. Thus, it may be necessary to account for this in the models you write.
-For example, the ``flask.views.View`` class has a predefined subclass ``MethodView``, and so to find
-all subclasses of ``View``, we must explicitly include the subclasses of ``MethodView`` as well.
+For example, the ``flask.views.View`` class has a predefined subclass ``MethodView``. To find
+all subclasses of ``View``, you must explicitly include the subclasses of ``MethodView`` as well.
.. code-block:: ql
@@ -132,7 +132,7 @@ all subclasses of ``View``, we must explicitly include the subclasses of ``Metho
API::moduleImport("flask").getMember("views").getMember(["View", "MethodView"]).getASubclass*()
}
- select viewClass()
+ select viewClass().getAUse()
➤ `See this in the query console on LGTM.com `__.
@@ -141,10 +141,10 @@ Note the use of the set literal ``["View", "MethodView"]`` to match both classes
Built-in functions and classes
------------------------------
-Built-in functions and classes can be accessed using the ``API::builtin`` method, giving the name of
+You can access built-in functions and classes using the ``API::builtin`` method, giving the name of
the built-in as an argument.
-To find all calls to the built-in ``open`` function, for instance, can be done using the following snippet
+For example, to find all calls to the built-in ``open`` function, you can use the following snippet.
.. code-block:: ql
From 6ca425f03384bdce531fcd2f972080839593b11a Mon Sep 17 00:00:00 2001
From: Asger Feldthaus
Date: Mon, 1 Mar 2021 15:19:08 +0000
Subject: [PATCH 207/725] JS: Implement new metric queries for line counting
---
javascript/ql/src/Diagnostics/Files.ql | 10 ++++++++++
javascript/ql/src/Diagnostics/Lines.ql | 10 ++++++++++
javascript/ql/src/Diagnostics/LinesOfCode.ql | 10 ++++++++++
javascript/ql/src/Diagnostics/LinesOfCodePerFile.ql | 12 ++++++++++++
javascript/ql/src/Diagnostics/LinesPerFile.ql | 12 ++++++++++++
.../ql/test/query-tests/Diagnostics/Lines.expected | 1 +
.../ql/test/query-tests/Diagnostics/Lines.qlref | 1 +
.../query-tests/Diagnostics/LinesOfCode.expected | 1 +
.../test/query-tests/Diagnostics/LinesOfCode.qlref | 1 +
.../Diagnostics/LinesOfCodePerFile.expected | 4 ++++
.../query-tests/Diagnostics/LinesOfCodePerFile.qlref | 1 +
.../query-tests/Diagnostics/LinesPerFile.expected | 4 ++++
.../test/query-tests/Diagnostics/LinesPerFile.qlref | 1 +
.../test/query-tests/Diagnostics/src/.eslintrc.yml | 2 ++
.../ql/test/query-tests/Diagnostics/src/externs.js | 8 ++++++++
.../query-tests/Diagnostics/src/javascript_file.js | 7 +++++++
.../ql/test/query-tests/Diagnostics/src/package.json | 6 ++++++
.../query-tests/Diagnostics/src/typescript_file.ts | 7 +++++++
18 files changed, 98 insertions(+)
create mode 100644 javascript/ql/src/Diagnostics/Files.ql
create mode 100644 javascript/ql/src/Diagnostics/Lines.ql
create mode 100644 javascript/ql/src/Diagnostics/LinesOfCode.ql
create mode 100644 javascript/ql/src/Diagnostics/LinesOfCodePerFile.ql
create mode 100644 javascript/ql/src/Diagnostics/LinesPerFile.ql
create mode 100644 javascript/ql/test/query-tests/Diagnostics/Lines.expected
create mode 100644 javascript/ql/test/query-tests/Diagnostics/Lines.qlref
create mode 100644 javascript/ql/test/query-tests/Diagnostics/LinesOfCode.expected
create mode 100644 javascript/ql/test/query-tests/Diagnostics/LinesOfCode.qlref
create mode 100644 javascript/ql/test/query-tests/Diagnostics/LinesOfCodePerFile.expected
create mode 100644 javascript/ql/test/query-tests/Diagnostics/LinesOfCodePerFile.qlref
create mode 100644 javascript/ql/test/query-tests/Diagnostics/LinesPerFile.expected
create mode 100644 javascript/ql/test/query-tests/Diagnostics/LinesPerFile.qlref
create mode 100644 javascript/ql/test/query-tests/Diagnostics/src/.eslintrc.yml
create mode 100644 javascript/ql/test/query-tests/Diagnostics/src/externs.js
create mode 100644 javascript/ql/test/query-tests/Diagnostics/src/javascript_file.js
create mode 100644 javascript/ql/test/query-tests/Diagnostics/src/package.json
create mode 100644 javascript/ql/test/query-tests/Diagnostics/src/typescript_file.ts
diff --git a/javascript/ql/src/Diagnostics/Files.ql b/javascript/ql/src/Diagnostics/Files.ql
new file mode 100644
index 00000000000..61e5bf53fd5
--- /dev/null
+++ b/javascript/ql/src/Diagnostics/Files.ql
@@ -0,0 +1,10 @@
+/**
+ * @name Total source files
+ * @description The total number of source files.
+ * @kind metric
+ * @id js/metrics/files
+ */
+
+import javascript
+
+select count(File f | not f.getATopLevel().isExterns())
diff --git a/javascript/ql/src/Diagnostics/Lines.ql b/javascript/ql/src/Diagnostics/Lines.ql
new file mode 100644
index 00000000000..6b49b93cfc4
--- /dev/null
+++ b/javascript/ql/src/Diagnostics/Lines.ql
@@ -0,0 +1,10 @@
+/**
+ * @name Total lines of text
+ * @description The total number of lines of text across all source files.
+ * @kind metric
+ * @id js/metrics/lines
+ */
+
+import javascript
+
+select sum(File f | not f.getATopLevel().isExterns() | f.getNumberOfLines())
diff --git a/javascript/ql/src/Diagnostics/LinesOfCode.ql b/javascript/ql/src/Diagnostics/LinesOfCode.ql
new file mode 100644
index 00000000000..039f78798d7
--- /dev/null
+++ b/javascript/ql/src/Diagnostics/LinesOfCode.ql
@@ -0,0 +1,10 @@
+/**
+ * @name Total lines of code
+ * @description The total number of lines of code across all source files.
+ * @kind metric
+ * @id js/metrics/lines-of-code
+ */
+
+import javascript
+
+select sum(File f | not f.getATopLevel().isExterns() | f.getNumberOfLinesOfCode())
diff --git a/javascript/ql/src/Diagnostics/LinesOfCodePerFile.ql b/javascript/ql/src/Diagnostics/LinesOfCodePerFile.ql
new file mode 100644
index 00000000000..1b6fdbb4346
--- /dev/null
+++ b/javascript/ql/src/Diagnostics/LinesOfCodePerFile.ql
@@ -0,0 +1,12 @@
+/**
+ * @name Lines of code per source file
+ * @description The number of lines of code for each source file.
+ * @kind metric
+ * @id js/metrics/lines-of-code-per-file
+ */
+
+import javascript
+
+from File f
+where not f.getATopLevel().isExterns()
+select f, f.getNumberOfLinesOfCode()
diff --git a/javascript/ql/src/Diagnostics/LinesPerFile.ql b/javascript/ql/src/Diagnostics/LinesPerFile.ql
new file mode 100644
index 00000000000..639c77e2c4b
--- /dev/null
+++ b/javascript/ql/src/Diagnostics/LinesPerFile.ql
@@ -0,0 +1,12 @@
+/**
+ * @name Lines of text per source file
+ * @description The number of lines of text for each source file.
+ * @kind metric
+ * @id js/metrics/lines-per-file
+ */
+
+import javascript
+
+from File f
+where not f.getATopLevel().isExterns()
+select f, f.getNumberOfLines()
diff --git a/javascript/ql/test/query-tests/Diagnostics/Lines.expected b/javascript/ql/test/query-tests/Diagnostics/Lines.expected
new file mode 100644
index 00000000000..2e7d809d9b6
--- /dev/null
+++ b/javascript/ql/test/query-tests/Diagnostics/Lines.expected
@@ -0,0 +1 @@
+| 22 |
diff --git a/javascript/ql/test/query-tests/Diagnostics/Lines.qlref b/javascript/ql/test/query-tests/Diagnostics/Lines.qlref
new file mode 100644
index 00000000000..ecdef1053f4
--- /dev/null
+++ b/javascript/ql/test/query-tests/Diagnostics/Lines.qlref
@@ -0,0 +1 @@
+Diagnostics/Lines.ql
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Diagnostics/LinesOfCode.expected b/javascript/ql/test/query-tests/Diagnostics/LinesOfCode.expected
new file mode 100644
index 00000000000..0c89049708a
--- /dev/null
+++ b/javascript/ql/test/query-tests/Diagnostics/LinesOfCode.expected
@@ -0,0 +1 @@
+| 12 |
diff --git a/javascript/ql/test/query-tests/Diagnostics/LinesOfCode.qlref b/javascript/ql/test/query-tests/Diagnostics/LinesOfCode.qlref
new file mode 100644
index 00000000000..b07f3413688
--- /dev/null
+++ b/javascript/ql/test/query-tests/Diagnostics/LinesOfCode.qlref
@@ -0,0 +1 @@
+Diagnostics/LinesOfCode.ql
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Diagnostics/LinesOfCodePerFile.expected b/javascript/ql/test/query-tests/Diagnostics/LinesOfCodePerFile.expected
new file mode 100644
index 00000000000..2b4e70ee13b
--- /dev/null
+++ b/javascript/ql/test/query-tests/Diagnostics/LinesOfCodePerFile.expected
@@ -0,0 +1,4 @@
+| src/.eslintrc.yml:0:0:0:0 | src/.eslintrc.yml | 0 |
+| src/javascript_file.js:0:0:0:0 | src/javascript_file.js | 6 |
+| src/package.json:0:0:0:0 | src/package.json | 0 |
+| src/typescript_file.ts:0:0:0:0 | src/typescript_file.ts | 6 |
diff --git a/javascript/ql/test/query-tests/Diagnostics/LinesOfCodePerFile.qlref b/javascript/ql/test/query-tests/Diagnostics/LinesOfCodePerFile.qlref
new file mode 100644
index 00000000000..ea50143078c
--- /dev/null
+++ b/javascript/ql/test/query-tests/Diagnostics/LinesOfCodePerFile.qlref
@@ -0,0 +1 @@
+Diagnostics/LinesOfCodePerFile.ql
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Diagnostics/LinesPerFile.expected b/javascript/ql/test/query-tests/Diagnostics/LinesPerFile.expected
new file mode 100644
index 00000000000..cb0dc13db43
--- /dev/null
+++ b/javascript/ql/test/query-tests/Diagnostics/LinesPerFile.expected
@@ -0,0 +1,4 @@
+| src/.eslintrc.yml:0:0:0:0 | src/.eslintrc.yml | 2 |
+| src/javascript_file.js:0:0:0:0 | src/javascript_file.js | 7 |
+| src/package.json:0:0:0:0 | src/package.json | 6 |
+| src/typescript_file.ts:0:0:0:0 | src/typescript_file.ts | 7 |
diff --git a/javascript/ql/test/query-tests/Diagnostics/LinesPerFile.qlref b/javascript/ql/test/query-tests/Diagnostics/LinesPerFile.qlref
new file mode 100644
index 00000000000..768a83875e6
--- /dev/null
+++ b/javascript/ql/test/query-tests/Diagnostics/LinesPerFile.qlref
@@ -0,0 +1 @@
+Diagnostics/LinesPerFile.ql
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Diagnostics/src/.eslintrc.yml b/javascript/ql/test/query-tests/Diagnostics/src/.eslintrc.yml
new file mode 100644
index 00000000000..2b4028c44b8
--- /dev/null
+++ b/javascript/ql/test/query-tests/Diagnostics/src/.eslintrc.yml
@@ -0,0 +1,2 @@
+rules:
+ semi: error
diff --git a/javascript/ql/test/query-tests/Diagnostics/src/externs.js b/javascript/ql/test/query-tests/Diagnostics/src/externs.js
new file mode 100644
index 00000000000..c8e9cec9bc4
--- /dev/null
+++ b/javascript/ql/test/query-tests/Diagnostics/src/externs.js
@@ -0,0 +1,8 @@
+/**
+ * @externs
+ */
+
+// Should not be counted
+
+function Object() {}
+function String() {}
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Diagnostics/src/javascript_file.js b/javascript/ql/test/query-tests/Diagnostics/src/javascript_file.js
new file mode 100644
index 00000000000..e80d023d0d8
--- /dev/null
+++ b/javascript/ql/test/query-tests/Diagnostics/src/javascript_file.js
@@ -0,0 +1,7 @@
+function foo(x) {
+ return x;
+}
+
+function bar(y) {
+ return y;
+}
diff --git a/javascript/ql/test/query-tests/Diagnostics/src/package.json b/javascript/ql/test/query-tests/Diagnostics/src/package.json
new file mode 100644
index 00000000000..9ab61fa5ee4
--- /dev/null
+++ b/javascript/ql/test/query-tests/Diagnostics/src/package.json
@@ -0,0 +1,6 @@
+{
+ "private": true,
+ "devDependencies": {
+ "typescript": "*"
+ }
+}
diff --git a/javascript/ql/test/query-tests/Diagnostics/src/typescript_file.ts b/javascript/ql/test/query-tests/Diagnostics/src/typescript_file.ts
new file mode 100644
index 00000000000..e922c80dfdb
--- /dev/null
+++ b/javascript/ql/test/query-tests/Diagnostics/src/typescript_file.ts
@@ -0,0 +1,7 @@
+export interface Foo {
+ x: number;
+}
+
+export function getX(f: Foo) {
+ return f.x;
+}
From 0c0556bb38273722586f09b34f204a2719e4b97e Mon Sep 17 00:00:00 2001
From: Asger Feldthaus
Date: Fri, 19 Mar 2021 16:42:05 +0000
Subject: [PATCH 208/725] JS: Update LinesOfCode.ql to match the style from C++
---
javascript/ql/src/Diagnostics/LinesOfCode.ql | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/javascript/ql/src/Diagnostics/LinesOfCode.ql b/javascript/ql/src/Diagnostics/LinesOfCode.ql
index 039f78798d7..9f89e0e2163 100644
--- a/javascript/ql/src/Diagnostics/LinesOfCode.ql
+++ b/javascript/ql/src/Diagnostics/LinesOfCode.ql
@@ -1,8 +1,9 @@
/**
- * @name Total lines of code
- * @description The total number of lines of code across all source files.
+ * @id js/summary/lines-of-code
+ * @name Total lines of JavaScript and TypeScript code in the database
+ * @description The total number of lines of JavaScript or TypeScript code across all files checked into the repository, except in `node_modules`. This is a useful metric of the size of a database. For all files that were seen during extraction, this query counts the lines of code, excluding whitespace or comments.
* @kind metric
- * @id js/metrics/lines-of-code
+ * @tags summary
*/
import javascript
From 347cbe422daf851c31334541a055932e1f063055 Mon Sep 17 00:00:00 2001
From: Asger Feldthaus
Date: Fri, 19 Mar 2021 16:42:43 +0000
Subject: [PATCH 209/725] JS: Remove the other summary queries
---
javascript/ql/src/Diagnostics/Files.ql | 10 ----------
javascript/ql/src/Diagnostics/Lines.ql | 10 ----------
javascript/ql/src/Diagnostics/LinesOfCodePerFile.ql | 12 ------------
javascript/ql/src/Diagnostics/LinesPerFile.ql | 12 ------------
4 files changed, 44 deletions(-)
delete mode 100644 javascript/ql/src/Diagnostics/Files.ql
delete mode 100644 javascript/ql/src/Diagnostics/Lines.ql
delete mode 100644 javascript/ql/src/Diagnostics/LinesOfCodePerFile.ql
delete mode 100644 javascript/ql/src/Diagnostics/LinesPerFile.ql
diff --git a/javascript/ql/src/Diagnostics/Files.ql b/javascript/ql/src/Diagnostics/Files.ql
deleted file mode 100644
index 61e5bf53fd5..00000000000
--- a/javascript/ql/src/Diagnostics/Files.ql
+++ /dev/null
@@ -1,10 +0,0 @@
-/**
- * @name Total source files
- * @description The total number of source files.
- * @kind metric
- * @id js/metrics/files
- */
-
-import javascript
-
-select count(File f | not f.getATopLevel().isExterns())
diff --git a/javascript/ql/src/Diagnostics/Lines.ql b/javascript/ql/src/Diagnostics/Lines.ql
deleted file mode 100644
index 6b49b93cfc4..00000000000
--- a/javascript/ql/src/Diagnostics/Lines.ql
+++ /dev/null
@@ -1,10 +0,0 @@
-/**
- * @name Total lines of text
- * @description The total number of lines of text across all source files.
- * @kind metric
- * @id js/metrics/lines
- */
-
-import javascript
-
-select sum(File f | not f.getATopLevel().isExterns() | f.getNumberOfLines())
diff --git a/javascript/ql/src/Diagnostics/LinesOfCodePerFile.ql b/javascript/ql/src/Diagnostics/LinesOfCodePerFile.ql
deleted file mode 100644
index 1b6fdbb4346..00000000000
--- a/javascript/ql/src/Diagnostics/LinesOfCodePerFile.ql
+++ /dev/null
@@ -1,12 +0,0 @@
-/**
- * @name Lines of code per source file
- * @description The number of lines of code for each source file.
- * @kind metric
- * @id js/metrics/lines-of-code-per-file
- */
-
-import javascript
-
-from File f
-where not f.getATopLevel().isExterns()
-select f, f.getNumberOfLinesOfCode()
diff --git a/javascript/ql/src/Diagnostics/LinesPerFile.ql b/javascript/ql/src/Diagnostics/LinesPerFile.ql
deleted file mode 100644
index 639c77e2c4b..00000000000
--- a/javascript/ql/src/Diagnostics/LinesPerFile.ql
+++ /dev/null
@@ -1,12 +0,0 @@
-/**
- * @name Lines of text per source file
- * @description The number of lines of text for each source file.
- * @kind metric
- * @id js/metrics/lines-per-file
- */
-
-import javascript
-
-from File f
-where not f.getATopLevel().isExterns()
-select f, f.getNumberOfLines()
From fa2ae1420a8eaf30dcf3671f4528dd7eec2db0a5 Mon Sep 17 00:00:00 2001
From: Asger Feldthaus
Date: Fri, 19 Mar 2021 16:43:23 +0000
Subject: [PATCH 210/725] JS: Rename Diagnostics folder to Summary
---
javascript/ql/src/{Diagnostics => Summary}/LinesOfCode.ql | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename javascript/ql/src/{Diagnostics => Summary}/LinesOfCode.ql (100%)
diff --git a/javascript/ql/src/Diagnostics/LinesOfCode.ql b/javascript/ql/src/Summary/LinesOfCode.ql
similarity index 100%
rename from javascript/ql/src/Diagnostics/LinesOfCode.ql
rename to javascript/ql/src/Summary/LinesOfCode.ql
From 405c1f3fc7c731137672bdb0ce1d52672e08db14 Mon Sep 17 00:00:00 2001
From: Asger Feldthaus
Date: Fri, 19 Mar 2021 16:45:31 +0000
Subject: [PATCH 211/725] JS: Update test suite
---
javascript/ql/test/query-tests/Diagnostics/Lines.expected | 1 -
javascript/ql/test/query-tests/Diagnostics/Lines.qlref | 1 -
javascript/ql/test/query-tests/Diagnostics/LinesOfCode.qlref | 1 -
.../test/query-tests/Diagnostics/LinesOfCodePerFile.expected | 4 ----
.../ql/test/query-tests/Diagnostics/LinesOfCodePerFile.qlref | 1 -
.../ql/test/query-tests/Diagnostics/LinesPerFile.expected | 4 ----
javascript/ql/test/query-tests/Diagnostics/LinesPerFile.qlref | 1 -
.../query-tests/{Diagnostics => Summary}/LinesOfCode.expected | 0
javascript/ql/test/query-tests/Summary/LinesOfCode.qlref | 1 +
.../query-tests/{Diagnostics => Summary}/src/.eslintrc.yml | 0
.../test/query-tests/{Diagnostics => Summary}/src/externs.js | 0
.../{Diagnostics => Summary}/src/javascript_file.js | 0
.../query-tests/{Diagnostics => Summary}/src/package.json | 0
.../{Diagnostics => Summary}/src/typescript_file.ts | 0
14 files changed, 1 insertion(+), 13 deletions(-)
delete mode 100644 javascript/ql/test/query-tests/Diagnostics/Lines.expected
delete mode 100644 javascript/ql/test/query-tests/Diagnostics/Lines.qlref
delete mode 100644 javascript/ql/test/query-tests/Diagnostics/LinesOfCode.qlref
delete mode 100644 javascript/ql/test/query-tests/Diagnostics/LinesOfCodePerFile.expected
delete mode 100644 javascript/ql/test/query-tests/Diagnostics/LinesOfCodePerFile.qlref
delete mode 100644 javascript/ql/test/query-tests/Diagnostics/LinesPerFile.expected
delete mode 100644 javascript/ql/test/query-tests/Diagnostics/LinesPerFile.qlref
rename javascript/ql/test/query-tests/{Diagnostics => Summary}/LinesOfCode.expected (100%)
create mode 100644 javascript/ql/test/query-tests/Summary/LinesOfCode.qlref
rename javascript/ql/test/query-tests/{Diagnostics => Summary}/src/.eslintrc.yml (100%)
rename javascript/ql/test/query-tests/{Diagnostics => Summary}/src/externs.js (100%)
rename javascript/ql/test/query-tests/{Diagnostics => Summary}/src/javascript_file.js (100%)
rename javascript/ql/test/query-tests/{Diagnostics => Summary}/src/package.json (100%)
rename javascript/ql/test/query-tests/{Diagnostics => Summary}/src/typescript_file.ts (100%)
diff --git a/javascript/ql/test/query-tests/Diagnostics/Lines.expected b/javascript/ql/test/query-tests/Diagnostics/Lines.expected
deleted file mode 100644
index 2e7d809d9b6..00000000000
--- a/javascript/ql/test/query-tests/Diagnostics/Lines.expected
+++ /dev/null
@@ -1 +0,0 @@
-| 22 |
diff --git a/javascript/ql/test/query-tests/Diagnostics/Lines.qlref b/javascript/ql/test/query-tests/Diagnostics/Lines.qlref
deleted file mode 100644
index ecdef1053f4..00000000000
--- a/javascript/ql/test/query-tests/Diagnostics/Lines.qlref
+++ /dev/null
@@ -1 +0,0 @@
-Diagnostics/Lines.ql
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Diagnostics/LinesOfCode.qlref b/javascript/ql/test/query-tests/Diagnostics/LinesOfCode.qlref
deleted file mode 100644
index b07f3413688..00000000000
--- a/javascript/ql/test/query-tests/Diagnostics/LinesOfCode.qlref
+++ /dev/null
@@ -1 +0,0 @@
-Diagnostics/LinesOfCode.ql
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Diagnostics/LinesOfCodePerFile.expected b/javascript/ql/test/query-tests/Diagnostics/LinesOfCodePerFile.expected
deleted file mode 100644
index 2b4e70ee13b..00000000000
--- a/javascript/ql/test/query-tests/Diagnostics/LinesOfCodePerFile.expected
+++ /dev/null
@@ -1,4 +0,0 @@
-| src/.eslintrc.yml:0:0:0:0 | src/.eslintrc.yml | 0 |
-| src/javascript_file.js:0:0:0:0 | src/javascript_file.js | 6 |
-| src/package.json:0:0:0:0 | src/package.json | 0 |
-| src/typescript_file.ts:0:0:0:0 | src/typescript_file.ts | 6 |
diff --git a/javascript/ql/test/query-tests/Diagnostics/LinesOfCodePerFile.qlref b/javascript/ql/test/query-tests/Diagnostics/LinesOfCodePerFile.qlref
deleted file mode 100644
index ea50143078c..00000000000
--- a/javascript/ql/test/query-tests/Diagnostics/LinesOfCodePerFile.qlref
+++ /dev/null
@@ -1 +0,0 @@
-Diagnostics/LinesOfCodePerFile.ql
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Diagnostics/LinesPerFile.expected b/javascript/ql/test/query-tests/Diagnostics/LinesPerFile.expected
deleted file mode 100644
index cb0dc13db43..00000000000
--- a/javascript/ql/test/query-tests/Diagnostics/LinesPerFile.expected
+++ /dev/null
@@ -1,4 +0,0 @@
-| src/.eslintrc.yml:0:0:0:0 | src/.eslintrc.yml | 2 |
-| src/javascript_file.js:0:0:0:0 | src/javascript_file.js | 7 |
-| src/package.json:0:0:0:0 | src/package.json | 6 |
-| src/typescript_file.ts:0:0:0:0 | src/typescript_file.ts | 7 |
diff --git a/javascript/ql/test/query-tests/Diagnostics/LinesPerFile.qlref b/javascript/ql/test/query-tests/Diagnostics/LinesPerFile.qlref
deleted file mode 100644
index 768a83875e6..00000000000
--- a/javascript/ql/test/query-tests/Diagnostics/LinesPerFile.qlref
+++ /dev/null
@@ -1 +0,0 @@
-Diagnostics/LinesPerFile.ql
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Diagnostics/LinesOfCode.expected b/javascript/ql/test/query-tests/Summary/LinesOfCode.expected
similarity index 100%
rename from javascript/ql/test/query-tests/Diagnostics/LinesOfCode.expected
rename to javascript/ql/test/query-tests/Summary/LinesOfCode.expected
diff --git a/javascript/ql/test/query-tests/Summary/LinesOfCode.qlref b/javascript/ql/test/query-tests/Summary/LinesOfCode.qlref
new file mode 100644
index 00000000000..ac8650d6dcc
--- /dev/null
+++ b/javascript/ql/test/query-tests/Summary/LinesOfCode.qlref
@@ -0,0 +1 @@
+Summary/LinesOfCode.ql
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Diagnostics/src/.eslintrc.yml b/javascript/ql/test/query-tests/Summary/src/.eslintrc.yml
similarity index 100%
rename from javascript/ql/test/query-tests/Diagnostics/src/.eslintrc.yml
rename to javascript/ql/test/query-tests/Summary/src/.eslintrc.yml
diff --git a/javascript/ql/test/query-tests/Diagnostics/src/externs.js b/javascript/ql/test/query-tests/Summary/src/externs.js
similarity index 100%
rename from javascript/ql/test/query-tests/Diagnostics/src/externs.js
rename to javascript/ql/test/query-tests/Summary/src/externs.js
diff --git a/javascript/ql/test/query-tests/Diagnostics/src/javascript_file.js b/javascript/ql/test/query-tests/Summary/src/javascript_file.js
similarity index 100%
rename from javascript/ql/test/query-tests/Diagnostics/src/javascript_file.js
rename to javascript/ql/test/query-tests/Summary/src/javascript_file.js
diff --git a/javascript/ql/test/query-tests/Diagnostics/src/package.json b/javascript/ql/test/query-tests/Summary/src/package.json
similarity index 100%
rename from javascript/ql/test/query-tests/Diagnostics/src/package.json
rename to javascript/ql/test/query-tests/Summary/src/package.json
diff --git a/javascript/ql/test/query-tests/Diagnostics/src/typescript_file.ts b/javascript/ql/test/query-tests/Summary/src/typescript_file.ts
similarity index 100%
rename from javascript/ql/test/query-tests/Diagnostics/src/typescript_file.ts
rename to javascript/ql/test/query-tests/Summary/src/typescript_file.ts
From b565e3de91b2b6abe9f527b76c71ad00000f2041 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Fri, 19 Mar 2021 21:07:22 +0100
Subject: [PATCH 212/725] expand outDir support in tsconfig files
---
.../com/semmle/js/extractor/AutoBuild.java | 2 +-
.../src/com/semmle/js/extractor/Main.java | 2 +-
javascript/ql/src/semmle/javascript/Paths.qll | 115 +++++++++++-------
.../ImportOutDir/slashAndExtension/index.js | 2 +
.../slashAndExtension/package.json | 4 +
.../slashAndExtension/src/index.ts | 1 +
.../slashAndExtension/tsconfig.json | 8 ++
.../TypeScript/ImportOutDir/tests.expected | 5 +
.../TypeScript/ImportOutDir/tests.ql | 2 +
9 files changed, 98 insertions(+), 43 deletions(-)
create mode 100644 javascript/ql/test/library-tests/TypeScript/ImportOutDir/slashAndExtension/index.js
create mode 100644 javascript/ql/test/library-tests/TypeScript/ImportOutDir/slashAndExtension/package.json
create mode 100644 javascript/ql/test/library-tests/TypeScript/ImportOutDir/slashAndExtension/src/index.ts
create mode 100644 javascript/ql/test/library-tests/TypeScript/ImportOutDir/slashAndExtension/tsconfig.json
diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java
index 505c45832e2..94319e4998c 100644
--- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java
+++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java
@@ -407,7 +407,7 @@ public class AutoBuild {
// codeql-javascript-*.json files
patterns.add("**/.eslintrc*");
patterns.add("**/package.json");
- patterns.add("**/tsconfig.json");
+ patterns.add("**/tsconfig*.json");
patterns.add("**/codeql-javascript-*.json");
// include any explicitly specified extensions
diff --git a/javascript/extractor/src/com/semmle/js/extractor/Main.java b/javascript/extractor/src/com/semmle/js/extractor/Main.java
index 7b5df6946d4..7c5a8a25984 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-08";
+ public static final String EXTRACTOR_VERSION = "2021-03-19";
public static final Pattern NEWLINE = Pattern.compile("\n");
diff --git a/javascript/ql/src/semmle/javascript/Paths.qll b/javascript/ql/src/semmle/javascript/Paths.qll
index 6c25f8795d9..37098fc130f 100644
--- a/javascript/ql/src/semmle/javascript/Paths.qll
+++ b/javascript/ql/src/semmle/javascript/Paths.qll
@@ -141,29 +141,7 @@ abstract class PathString extends string {
* components of this path refers to when resolved relative to the
* given `root` folder.
*/
- Path resolveUpTo(int n, Folder root) {
- n = 0 and result.getContainer() = root and root = getARootFolder()
- or
- exists(Path base, string next | next = getComponent(this, n - 1, base, root) |
- // handle empty components and the special "." folder
- (next = "" or next = ".") and
- result = base
- or
- // handle the special ".." folder
- next = ".." and result = base.(ConsPath).getParent()
- or
- // special handling for Windows drive letters when resolving absolute path:
- // the extractor populates "C:/" as a folder that has path "C:/" but name ""
- n = 1 and
- next.regexpMatch("[A-Za-z]:") and
- root.getBaseName() = "" and
- root.toString() = next.toUpperCase() + "/" and
- result = base
- or
- // default case
- result = TConsPath(base, next)
- )
- }
+ Path resolveUpTo(int n, Folder root) { result = resolveUpTo(this, n, root, _) }
/**
* Gets the absolute path that this path refers to when resolved relative to
@@ -172,16 +150,57 @@ abstract class PathString extends string {
Path resolve(Folder root) { result = resolveUpTo(getNumComponent(), root) }
}
+/**
+ * Gets the absolute path that the sub-path consisting of the first `n`
+ * components of this path refers to when resolved relative to the
+ * given `root` folder.
+ */
+private Path resolveUpTo(PathString p, int n, Folder root, boolean inTS) {
+ n = 0 and result.getContainer() = root and root = p.getARootFolder() and inTS = false
+ or
+ exists(Path base, string next | next = getComponent(p, n - 1, base, root, inTS) |
+ // handle empty components and the special "." folder
+ (next = "" or next = ".") and
+ result = base
+ or
+ // handle the special ".." folder
+ next = ".." and result = base.(ConsPath).getParent()
+ or
+ // special handling for Windows drive letters when resolving absolute path:
+ // the extractor populates "C:/" as a folder that has path "C:/" but name ""
+ n = 1 and
+ next.regexpMatch("[A-Za-z]:") and
+ root.getBaseName() = "" and
+ root.toString() = next.toUpperCase() + "/" and
+ result = base
+ or
+ // default case
+ result = TConsPath(base, next)
+ )
+}
+
/**
* Gets the `i`th component of the path `str`, where `base` is the resolved path one level up.
* Supports that the root directory might be compiled output from TypeScript.
+ * `inTS` is true if the result is TypeScript that is compiled into the path specified by `str`.
*/
-private string getComponent(PathString str, int n, Path base, Folder root) {
- base = str.resolveUpTo(n, root) and
- (
- result = str.getComponent(n)
- or
- result = TypeScriptOutDir::getOriginalTypeScriptFolder(str.getComponent(n), base.getContainer())
+private string getComponent(PathString str, int n, Path base, Folder root, boolean inTS) {
+ exists(boolean prevTS |
+ base = resolveUpTo(str, n, root, prevTS) and
+ (
+ result = str.getComponent(n) and prevTS = inTS
+ or
+ // If we are in a TypeScript source folder, try to replace file endings with ".ts" or ".tsx"
+ n = str.getNumComponent() - 1 and
+ prevTS = true and
+ inTS = prevTS and
+ result = str.getComponent(n).regexpCapture("^(.*)\\.js$", 1) + "." + ["ts", "tsx"]
+ or
+ prevTS = false and
+ inTS = true and
+ result =
+ TypeScriptOutDir::getOriginalTypeScriptFolder(str.getComponent(n), base.getContainer())
+ )
)
}
@@ -194,21 +213,35 @@ private module TypeScriptOutDir {
*/
string getOriginalTypeScriptFolder(string outdir, Folder parent) {
exists(JSONObject tsconfig |
- tsconfig.getFile().getBaseName() = "tsconfig.json" and
- tsconfig.isTopLevel() and
- tsconfig.getFile().getParentContainer() = parent
- |
- outdir =
- tsconfig
- .getPropValue("compilerOptions")
- .(JSONObject)
- .getPropValue("outDir")
- .(JSONString)
- .getValue() and
- result = getEffectiveRootDirFromTSConfig(tsconfig)
+ outdir = removeLeadingSlash(getOutDir(tsconfig, parent)) and
+ result = removeLeadingSlash(getEffectiveRootDirFromTSConfig(tsconfig))
)
}
+ /**
+ * Removes a leading dot and/or slash from `raw`.
+ */
+ bindingset[raw]
+ private string removeLeadingSlash(string raw) {
+ result = raw.regexpCapture("^\\.?/?([\\w.\\-]+)$", 1)
+ }
+
+ /**
+ * Gets the `outDir` option from a tsconfig file from the folder `parent`.
+ */
+ private string getOutDir(JSONObject tsconfig, Folder parent) {
+ tsconfig.getFile().getBaseName().regexpMatch("tsconfig.*\\.json") and
+ tsconfig.isTopLevel() and
+ tsconfig.getFile().getParentContainer() = parent and
+ result =
+ tsconfig
+ .getPropValue("compilerOptions")
+ .(JSONObject)
+ .getPropValue("outDir")
+ .(JSONString)
+ .getValue()
+ }
+
/**
* Gets the directory that contains the TypeScript source files.
* Based on the tsconfig.json file `tsconfig`.
diff --git a/javascript/ql/test/library-tests/TypeScript/ImportOutDir/slashAndExtension/index.js b/javascript/ql/test/library-tests/TypeScript/ImportOutDir/slashAndExtension/index.js
new file mode 100644
index 00000000000..905a92e15a4
--- /dev/null
+++ b/javascript/ql/test/library-tests/TypeScript/ImportOutDir/slashAndExtension/index.js
@@ -0,0 +1,2 @@
+var foo1 = require('./lib/index.js');
+var foo2 = require('./src/index.ts');
\ No newline at end of file
diff --git a/javascript/ql/test/library-tests/TypeScript/ImportOutDir/slashAndExtension/package.json b/javascript/ql/test/library-tests/TypeScript/ImportOutDir/slashAndExtension/package.json
new file mode 100644
index 00000000000..4c8d1a457cb
--- /dev/null
+++ b/javascript/ql/test/library-tests/TypeScript/ImportOutDir/slashAndExtension/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "foo",
+ "main": "./lib/index.js"
+}
\ No newline at end of file
diff --git a/javascript/ql/test/library-tests/TypeScript/ImportOutDir/slashAndExtension/src/index.ts b/javascript/ql/test/library-tests/TypeScript/ImportOutDir/slashAndExtension/src/index.ts
new file mode 100644
index 00000000000..06f1c250ccd
--- /dev/null
+++ b/javascript/ql/test/library-tests/TypeScript/ImportOutDir/slashAndExtension/src/index.ts
@@ -0,0 +1 @@
+export default class Foo {}
\ No newline at end of file
diff --git a/javascript/ql/test/library-tests/TypeScript/ImportOutDir/slashAndExtension/tsconfig.json b/javascript/ql/test/library-tests/TypeScript/ImportOutDir/slashAndExtension/tsconfig.json
new file mode 100644
index 00000000000..0ae78b4a7cb
--- /dev/null
+++ b/javascript/ql/test/library-tests/TypeScript/ImportOutDir/slashAndExtension/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "./tsconfig",
+ "compilerOptions": {
+ "module": "commonjs",
+ "rootDir": "./src",
+ "outDir": "./lib"
+ }
+}
\ No newline at end of file
diff --git a/javascript/ql/test/library-tests/TypeScript/ImportOutDir/tests.expected b/javascript/ql/test/library-tests/TypeScript/ImportOutDir/tests.expected
index 4050e660521..af67932f33d 100644
--- a/javascript/ql/test/library-tests/TypeScript/ImportOutDir/tests.expected
+++ b/javascript/ql/test/library-tests/TypeScript/ImportOutDir/tests.expected
@@ -1,3 +1,4 @@
+resolveableImport
| nonUniqueInclude/index.js:1:12:1:38 | require ... oo.js') | nonUniqueInclude/src/foo.ts:1:1:1:27 | |
| nonUniqueInclude/index.js:2:12:2:39 | require ... oo.js') | nonUniqueInclude/src2/foo.ts:1:1:1:27 | |
| nonUniqueInclude/index.js:3:12:3:34 | require ... oo.ts') | nonUniqueInclude/src/foo.ts:1:1:1:27 | |
@@ -10,3 +11,7 @@
| simpleOutDir/index.js:2:12:2:36 | require ... ex.ts') | simpleOutDir/src/index.ts:1:1:1:27 | |
| simpleOutDir/index.js:4:12:4:33 | require ... index') | simpleOutDir/src/index.ts:1:1:1:27 | |
| simpleOutDir/index.js:5:12:5:33 | require ... index') | simpleOutDir/src/index.ts:1:1:1:27 | |
+| slashAndExtension/index.js:1:12:1:36 | require ... ex.js') | slashAndExtension/src/index.ts:1:1:1:27 | |
+| slashAndExtension/index.js:2:12:2:36 | require ... ex.ts') | slashAndExtension/src/index.ts:1:1:1:27 | |
+getMain
+| slashAndExtension/package.json:1:1:4:1 | {\\n " ... x.js"\\n} | slashAndExtension/src/index.ts:1:1:1:27 | |
diff --git a/javascript/ql/test/library-tests/TypeScript/ImportOutDir/tests.ql b/javascript/ql/test/library-tests/TypeScript/ImportOutDir/tests.ql
index 35d4a969236..450989f717b 100644
--- a/javascript/ql/test/library-tests/TypeScript/ImportOutDir/tests.ql
+++ b/javascript/ql/test/library-tests/TypeScript/ImportOutDir/tests.ql
@@ -5,3 +5,5 @@ query predicate resolveableImport(Import imp, Module mod) {
not imp.getTopLevel().isExterns() and
not mod.getTopLevel().isExterns()
}
+
+query Module getMain(PackageJSON json) { result = json.getMainModule() }
From 1385b2264234bc6fa3deaaeb7537ee47d89c47fe Mon Sep 17 00:00:00 2001
From: Dilan
Date: Fri, 19 Mar 2021 16:43:29 -0700
Subject: [PATCH 213/725] pr fixes, typo in qhelp file and helper method for
queries
---
.../Classes/NamingConventionsClasses.qhelp | 2 +-
.../Classes/NamingConventionsClasses.ql | 17 +++++++-----
.../NamingConventionsFunctions.qhelp | 2 +-
.../Functions/NamingConventionsFunctions.ql | 27 +++++++++++--------
4 files changed, 29 insertions(+), 19 deletions(-)
diff --git a/python/ql/src/experimental/Classes/NamingConventionsClasses.qhelp b/python/ql/src/experimental/Classes/NamingConventionsClasses.qhelp
index 1a7f4bc45a4..a928668dc6f 100644
--- a/python/ql/src/experimental/Classes/NamingConventionsClasses.qhelp
+++ b/python/ql/src/experimental/Classes/NamingConventionsClasses.qhelp
@@ -21,7 +21,7 @@ Write the class name beginning with an uppercase letter. For example, clas
- Guido van Rossum, Barry Warsaw, Nick Coghlan PEP 8 -- Style Guide for Python Code
+ Guido van Rossum, Barry Warsaw, Nick Coghlan PEP 8 -- Style Guide for Python Code
Python Class Names
diff --git a/python/ql/src/experimental/Classes/NamingConventionsClasses.ql b/python/ql/src/experimental/Classes/NamingConventionsClasses.ql
index d4919c3ece8..f16d242eadf 100644
--- a/python/ql/src/experimental/Classes/NamingConventionsClasses.ql
+++ b/python/ql/src/experimental/Classes/NamingConventionsClasses.ql
@@ -9,15 +9,20 @@
import python
-from Class c, string first_char
+predicate lower_case_class(Class c) {
+ exists(string first_char |
+ first_char = c.getName().prefix(1) and
+ not first_char = first_char.toUpperCase()
+ )
+}
+
+from Class c
where
c.inSource() and
- first_char = c.getName().prefix(1) and
- not first_char = first_char.toUpperCase() and
- not exists(Class c1, string first_char1 |
+ lower_case_class(c) and
+ not exists(Class c1 |
c1 != c and
c1.getLocation().getFile() = c.getLocation().getFile() and
- first_char1 = c1.getName().prefix(1) and
- not first_char1 = first_char1.toUpperCase()
+ lower_case_class(c1)
)
select c, "Class names should start in uppercase."
diff --git a/python/ql/src/experimental/Functions/NamingConventionsFunctions.qhelp b/python/ql/src/experimental/Functions/NamingConventionsFunctions.qhelp
index 46d948592ff..b4ff738782c 100644
--- a/python/ql/src/experimental/Functions/NamingConventionsFunctions.qhelp
+++ b/python/ql/src/experimental/Functions/NamingConventionsFunctions.qhelp
@@ -21,7 +21,7 @@ Write the function name beginning with an lowercase letter. For example, j
- Guido van Rossum, Barry Warsaw, Nick Coghlan PEP 8 -- Style Guide for Python Code
+ Guido van Rossum, Barry Warsaw, Nick Coghlan PEP 8 -- Style Guide for Python Code
Python Function and Variable Names
diff --git a/python/ql/src/experimental/Functions/NamingConventionsFunctions.ql b/python/ql/src/experimental/Functions/NamingConventionsFunctions.ql
index 80dc0e99cd8..d2868af22c9 100644
--- a/python/ql/src/experimental/Functions/NamingConventionsFunctions.ql
+++ b/python/ql/src/experimental/Functions/NamingConventionsFunctions.ql
@@ -9,15 +9,20 @@
import python
-from Function f, string first_char
-where
- f.inSource() and
- first_char = f.getName().prefix(1) and
- not first_char = first_char.toLowerCase() and
- not exists(Function f1, string first_char1 |
- f1 != f and
- f1.getLocation().getFile() = f.getLocation().getFile() and
- first_char1 = f1.getName().prefix(1) and
- not first_char1 = first_char1.toLowerCase()
+predicate upper_case_function(Function func) {
+ exists(string first_char |
+ first_char = func.getName().prefix(1) and
+ not first_char = first_char.toLowerCase()
)
-select f, "Function names should start in lowercase."
+}
+
+from Function func
+where
+ func.inSource() and
+ upper_case_function(func) and
+ not exists(Function func1 |
+ func1 != func and
+ func1.getLocation().getFile() = func.getLocation().getFile() and
+ upper_case_function(func1)
+ )
+select func, "Function names should start in lowercase."
From f4a476ea4e8e27d100b35ac8758c9b36dac7f3aa Mon Sep 17 00:00:00 2001
From: Asger Feldthaus
Date: Sat, 20 Mar 2021 09:05:04 +0000
Subject: [PATCH 214/725] JS: Change type ValueNode -> Node
---
.../ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll b/javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll
index 79ffecad221..2d9f05ce592 100644
--- a/javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll
+++ b/javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll
@@ -182,7 +182,7 @@ private module CachedSteps {
*/
cached
predicate argumentPassing(
- DataFlow::SourceNode invk, DataFlow::ValueNode arg, Function f, DataFlow::SourceNode parm
+ DataFlow::SourceNode invk, DataFlow::Node arg, Function f, DataFlow::SourceNode parm
) {
calls(invk, f) and
(
From a54e810804256e0abafd8c13a32fef818a330bec Mon Sep 17 00:00:00 2001
From: Asger Feldthaus
Date: Sat, 20 Mar 2021 11:37:15 +0000
Subject: [PATCH 215/725] JS: Include accessor-calls in CallGraph.ql
---
javascript/ql/src/meta/alerts/CallGraph.ql | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/javascript/ql/src/meta/alerts/CallGraph.ql b/javascript/ql/src/meta/alerts/CallGraph.ql
index ea86e7b474a..13cb8a94ecd 100644
--- a/javascript/ql/src/meta/alerts/CallGraph.ql
+++ b/javascript/ql/src/meta/alerts/CallGraph.ql
@@ -10,6 +10,7 @@
import javascript
-from DataFlow::InvokeNode invoke, Function f
-where invoke.getACallee() = f
-select invoke, "Call to $@", f, f.describe()
+from DataFlow::Node invoke, Function f, string kind
+where invoke.(DataFlow::InvokeNode).getACallee() = f and kind = "Call" or
+ invoke.(DataFlow::PropRef).getAnAccessorCallee().getFunction() = f and kind = "Accessor call"
+select invoke, kind + " to $@", f, f.describe()
From 26bac9f425c2ecab54759797da19bcc1586ed95d Mon Sep 17 00:00:00 2001
From: ihsinme
Date: Sun, 21 Mar 2021 15:25:29 +0300
Subject: [PATCH 216/725] Apply suggestions from code review
Co-authored-by: Robert Marsh
---
.../CWE-783/OperatorPrecedenceLogicErrorWhenUseBoolType.ql | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBoolType.ql b/cpp/ql/src/experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBoolType.ql
index 1a116a83dbf..034df703bc3 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBoolType.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBoolType.ql
@@ -15,12 +15,12 @@
import cpp
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
-/** Holds, if it is an expression, a boolean increment. */
+/** Holds if `exp` increments a boolean value. */
predicate incrementBoolType(Expr exp) {
exp.(IncrementOperation).getOperand().getType() instanceof BoolType
}
-/** Holds, if this is an expression, applies a minus to a boolean type. */
+/** Holds if `exp` applies the unary minus operator to a boolean type. */
predicate revertSignBoolType(Expr exp) {
exp.(AssignExpr).getRValue().(UnaryMinusExpr).getAnOperand().getType() instanceof BoolType and
exp.(AssignExpr).getLValue().getType() instanceof BoolType
From 0200aedc2efc939b96170951d848d440fcbb8e4a Mon Sep 17 00:00:00 2001
From: yo-h <55373593+yo-h@users.noreply.github.com>
Date: Sun, 21 Mar 2021 12:55:25 -0400
Subject: [PATCH 217/725] Java 16: adjust test `options`
---
java/ql/test/library-tests/dataflow/records/options | 2 +-
java/ql/test/library-tests/dataflow/taint-format/options | 1 -
java/ql/test/library-tests/printAst/options | 2 +-
java/ql/test/library-tests/ssa/options | 2 +-
java/ql/test/query-tests/StringFormat/options | 1 -
5 files changed, 3 insertions(+), 5 deletions(-)
delete mode 100644 java/ql/test/library-tests/dataflow/taint-format/options
delete mode 100644 java/ql/test/query-tests/StringFormat/options
diff --git a/java/ql/test/library-tests/dataflow/records/options b/java/ql/test/library-tests/dataflow/records/options
index 81817b37785..fc57fe025b9 100644
--- a/java/ql/test/library-tests/dataflow/records/options
+++ b/java/ql/test/library-tests/dataflow/records/options
@@ -1 +1 @@
-//semmle-extractor-options: --javac-args --enable-preview -source 15 -target 15
+//semmle-extractor-options: --javac-args -source 16 -target 16
diff --git a/java/ql/test/library-tests/dataflow/taint-format/options b/java/ql/test/library-tests/dataflow/taint-format/options
deleted file mode 100644
index 81817b37785..00000000000
--- a/java/ql/test/library-tests/dataflow/taint-format/options
+++ /dev/null
@@ -1 +0,0 @@
-//semmle-extractor-options: --javac-args --enable-preview -source 15 -target 15
diff --git a/java/ql/test/library-tests/printAst/options b/java/ql/test/library-tests/printAst/options
index 81817b37785..e41003a6e3c 100644
--- a/java/ql/test/library-tests/printAst/options
+++ b/java/ql/test/library-tests/printAst/options
@@ -1 +1 @@
-//semmle-extractor-options: --javac-args --enable-preview -source 15 -target 15
+//semmle-extractor-options: --javac-args --enable-preview -source 16 -target 16
diff --git a/java/ql/test/library-tests/ssa/options b/java/ql/test/library-tests/ssa/options
index 81817b37785..e41003a6e3c 100644
--- a/java/ql/test/library-tests/ssa/options
+++ b/java/ql/test/library-tests/ssa/options
@@ -1 +1 @@
-//semmle-extractor-options: --javac-args --enable-preview -source 15 -target 15
+//semmle-extractor-options: --javac-args --enable-preview -source 16 -target 16
diff --git a/java/ql/test/query-tests/StringFormat/options b/java/ql/test/query-tests/StringFormat/options
deleted file mode 100644
index 81817b37785..00000000000
--- a/java/ql/test/query-tests/StringFormat/options
+++ /dev/null
@@ -1 +0,0 @@
-//semmle-extractor-options: --javac-args --enable-preview -source 15 -target 15
From fa98443bb7e3fe3ca1c594d27ecb0c6661930c2b Mon Sep 17 00:00:00 2001
From: Marcono1234
Date: Wed, 17 Mar 2021 01:41:36 +0100
Subject: [PATCH 218/725] Java: Add value predicates for float and double
literals; improve tests
---
java/ql/src/semmle/code/java/Expr.qll | 12 ++
.../literals/literalBoolean.expected | 4 +-
.../library-tests/literals/literalBoolean.ql | 2 +-
.../literals/literalChar.expected | 11 +-
.../library-tests/literals/literalChar.ql | 2 +-
.../literals/literalDouble.expected | 17 ++-
.../library-tests/literals/literalDouble.ql | 2 +-
.../literals/literalFloat.expected | 17 ++-
.../library-tests/literals/literalFloat.ql | 2 +-
.../literals/literalInteger.expected | 28 +++-
.../library-tests/literals/literalInteger.ql | 2 +-
.../literals/literalLong.expected | 24 ++-
.../library-tests/literals/literalLong.ql | 2 +-
.../literals/literalString.expected | 26 +++-
.../library-tests/literals/literalString.ql | 2 +-
.../literals/literals/Literals.java | 137 ++++++++++++++++--
16 files changed, 248 insertions(+), 42 deletions(-)
diff --git a/java/ql/src/semmle/code/java/Expr.qll b/java/ql/src/semmle/code/java/Expr.qll
index 43ad6634fc1..054e41958be 100755
--- a/java/ql/src/semmle/code/java/Expr.qll
+++ b/java/ql/src/semmle/code/java/Expr.qll
@@ -653,11 +653,23 @@ class LongLiteral extends Literal, @longliteral {
/** A floating point literal. For example, `4.2f`. */
class FloatingPointLiteral extends Literal, @floatingpointliteral {
+ /**
+ * Gets the value of this literal as CodeQL 64-bit `float`. The value will
+ * be parsed as Java 32-bit `float` and then converted to a CodeQL `float`.
+ */
+ float getFloatValue() { result = getValue().toFloat() }
+
override string getAPrimaryQlClass() { result = "FloatingPointLiteral" }
}
/** A double literal. For example, `4.2`. */
class DoubleLiteral extends Literal, @doubleliteral {
+ /**
+ * Gets the value of this literal as CodeQL 64-bit `float`. The result will
+ * have the same effective value as the Java `double` literal.
+ */
+ float getDoubleValue() { result = getValue().toFloat() }
+
override string getAPrimaryQlClass() { result = "DoubleLiteral" }
}
diff --git a/java/ql/test/library-tests/literals/literalBoolean.expected b/java/ql/test/library-tests/literals/literalBoolean.expected
index 73460f4206b..bfc9f1a043f 100644
--- a/java/ql/test/library-tests/literals/literalBoolean.expected
+++ b/java/ql/test/library-tests/literals/literalBoolean.expected
@@ -1 +1,3 @@
-| literals/Literals.java:11:22:11:25 | true |
\ No newline at end of file
+| literals/Literals.java:11:22:11:25 | true | true | true |
+| literals/Literals.java:16:3:16:6 | true | true | true |
+| literals/Literals.java:17:3:17:7 | false | false | false |
diff --git a/java/ql/test/library-tests/literals/literalBoolean.ql b/java/ql/test/library-tests/literals/literalBoolean.ql
index bddc59b40a2..811d0a9aae8 100644
--- a/java/ql/test/library-tests/literals/literalBoolean.ql
+++ b/java/ql/test/library-tests/literals/literalBoolean.ql
@@ -2,4 +2,4 @@ import semmle.code.java.Expr
from BooleanLiteral lit
where lit.getCompilationUnit().fromSource()
-select lit
+select lit, lit.getValue(), lit.getBooleanValue()
diff --git a/java/ql/test/library-tests/literals/literalChar.expected b/java/ql/test/library-tests/literals/literalChar.expected
index c6ab2b79a5e..00eb45eff57 100644
--- a/java/ql/test/library-tests/literals/literalChar.expected
+++ b/java/ql/test/library-tests/literals/literalChar.expected
@@ -1 +1,10 @@
-| literals/Literals.java:12:22:12:24 | 'x' |
+| literals/Literals.java:12:22:12:24 | 'x' | x |
+| literals/Literals.java:21:3:21:5 | 'a' | a |
+| literals/Literals.java:22:3:22:10 | '\\u0061' | a |
+| literals/Literals.java:23:3:23:10 | '\\u0000' | \u0000 |
+| literals/Literals.java:24:3:24:6 | '\\0' | \u0000 |
+| literals/Literals.java:25:3:25:6 | '\\n' | \n |
+| literals/Literals.java:26:3:26:6 | '\\0' | \u0000 |
+| literals/Literals.java:27:3:27:6 | '\\\\' | \\ |
+| literals/Literals.java:28:3:28:6 | '\\'' | ' |
+| literals/Literals.java:29:3:29:8 | '\\123' | S |
diff --git a/java/ql/test/library-tests/literals/literalChar.ql b/java/ql/test/library-tests/literals/literalChar.ql
index 2253c5bb21e..104e2c03dc4 100644
--- a/java/ql/test/library-tests/literals/literalChar.ql
+++ b/java/ql/test/library-tests/literals/literalChar.ql
@@ -1,4 +1,4 @@
import semmle.code.java.Expr
from CharacterLiteral lit
-select lit
+select lit, lit.getValue()
diff --git a/java/ql/test/library-tests/literals/literalDouble.expected b/java/ql/test/library-tests/literals/literalDouble.expected
index 8a6d6e3e483..ba64bdd8446 100644
--- a/java/ql/test/library-tests/literals/literalDouble.expected
+++ b/java/ql/test/library-tests/literals/literalDouble.expected
@@ -1 +1,16 @@
-| literals/Literals.java:10:22:10:27 | 456.0D |
+| literals/Literals.java:10:22:10:27 | 456.0D | 456.0 | 456.0 |
+| literals/Literals.java:33:3:33:5 | 0.0 | 0.0 | 0.0 |
+| literals/Literals.java:34:3:34:4 | 0d | 0.0 | 0.0 |
+| literals/Literals.java:35:3:35:5 | .0d | 0.0 | 0.0 |
+| literals/Literals.java:36:3:36:4 | .0 | 0.0 | 0.0 |
+| literals/Literals.java:37:4:37:6 | 0.d | 0.0 | 0.0 |
+| literals/Literals.java:38:4:38:6 | 0.d | 0.0 | 0.0 |
+| literals/Literals.java:39:3:39:22 | 1.234567890123456789 | 1.2345678901234567 | 1.2345678901234567 |
+| literals/Literals.java:40:3:40:24 | 1.55555555555555555555 | 1.5555555555555556 | 1.5555555555555556 |
+| literals/Literals.java:42:3:42:5 | 1e1 | 10.0 | 10.0 |
+| literals/Literals.java:43:3:43:24 | 1.7976931348623157E308 | 1.7976931348623157E308 | 1.7976931348623157E308 |
+| literals/Literals.java:44:4:44:25 | 1.7976931348623157E308 | 1.7976931348623157E308 | 1.7976931348623157E308 |
+| literals/Literals.java:45:3:45:28 | 0x1.f_ffff_ffff_ffffP+1023 | 1.7976931348623157E308 | 1.7976931348623157E308 |
+| literals/Literals.java:46:3:46:10 | 4.9e-324 | 4.9E-324 | 4.9E-324 |
+| literals/Literals.java:47:3:47:28 | 0x0.0_0000_0000_0001P-1022 | 4.9E-324 | 4.9E-324 |
+| literals/Literals.java:48:3:48:13 | 0x1.0P-1074 | 4.9E-324 | 4.9E-324 |
diff --git a/java/ql/test/library-tests/literals/literalDouble.ql b/java/ql/test/library-tests/literals/literalDouble.ql
index 28c30eb439b..a3766e9676c 100644
--- a/java/ql/test/library-tests/literals/literalDouble.ql
+++ b/java/ql/test/library-tests/literals/literalDouble.ql
@@ -1,4 +1,4 @@
import semmle.code.java.Expr
from DoubleLiteral lit
-select lit
+select lit, lit.getValue(), lit.getDoubleValue()
diff --git a/java/ql/test/library-tests/literals/literalFloat.expected b/java/ql/test/library-tests/literals/literalFloat.expected
index d421532944b..f6221533fdb 100644
--- a/java/ql/test/library-tests/literals/literalFloat.expected
+++ b/java/ql/test/library-tests/literals/literalFloat.expected
@@ -1 +1,16 @@
-| literals/Literals.java:9:22:9:27 | 123.0F |
+| literals/Literals.java:9:22:9:27 | 123.0F | 123.0 | 123.0 |
+| literals/Literals.java:52:3:52:6 | 0.0f | 0.0 | 0.0 |
+| literals/Literals.java:53:3:53:4 | 0f | 0.0 | 0.0 |
+| literals/Literals.java:54:3:54:5 | .0f | 0.0 | 0.0 |
+| literals/Literals.java:55:4:55:6 | 0.f | 0.0 | 0.0 |
+| literals/Literals.java:56:4:56:6 | 0.f | 0.0 | 0.0 |
+| literals/Literals.java:57:3:57:10 | 1_0_0.0f | 100.0 | 100.0 |
+| literals/Literals.java:58:3:58:23 | 1.234567890123456789f | 1.2345679 | 1.2345679 |
+| literals/Literals.java:59:3:59:25 | 1.55555555555555555555f | 1.5555556 | 1.5555556 |
+| literals/Literals.java:61:3:61:6 | 1e1f | 10.0 | 10.0 |
+| literals/Literals.java:62:3:62:15 | 3.4028235e38f | 3.4028235E38 | 3.4028235E38 |
+| literals/Literals.java:63:4:63:16 | 3.4028235e38f | 3.4028235E38 | 3.4028235E38 |
+| literals/Literals.java:64:3:64:18 | 0x1.fffffeP+127f | 3.4028235E38 | 3.4028235E38 |
+| literals/Literals.java:65:3:65:10 | 1.4e-45f | 1.4E-45 | 1.4E-45 |
+| literals/Literals.java:66:3:66:18 | 0x0.000002P-126f | 1.4E-45 | 1.4E-45 |
+| literals/Literals.java:67:3:67:13 | 0x1.0P-149f | 1.4E-45 | 1.4E-45 |
diff --git a/java/ql/test/library-tests/literals/literalFloat.ql b/java/ql/test/library-tests/literals/literalFloat.ql
index e9d18be607a..6a4a9093225 100644
--- a/java/ql/test/library-tests/literals/literalFloat.ql
+++ b/java/ql/test/library-tests/literals/literalFloat.ql
@@ -1,4 +1,4 @@
import semmle.code.java.Expr
from FloatingPointLiteral lit
-select lit
+select lit, lit.getValue(), lit.getFloatValue()
diff --git a/java/ql/test/library-tests/literals/literalInteger.expected b/java/ql/test/library-tests/literals/literalInteger.expected
index 1f31642509a..40c6a89cd98 100644
--- a/java/ql/test/library-tests/literals/literalInteger.expected
+++ b/java/ql/test/library-tests/literals/literalInteger.expected
@@ -1,8 +1,20 @@
-| literals/Literals.java:7:22:7:24 | 123 |
-| literals/Literals.java:14:16:14:26 | -2147483648 |
-| literals/Literals.java:16:21:16:30 | 2147483647 |
-| literals/Literals.java:18:20:18:29 | 0x80000000 |
-| literals/Literals.java:20:10:20:11 | 23 |
-| literals/Literals.java:20:15:20:16 | 19 |
-| literals/Literals.java:21:10:21:11 | 23 |
-| literals/Literals.java:21:15:21:16 | 19 |
+| literals/Literals.java:7:22:7:24 | 123 | 123 | 123 |
+| literals/Literals.java:71:3:71:3 | 0 | 0 | 0 |
+| literals/Literals.java:72:3:72:5 | 0_0 | 0 | 0 |
+| literals/Literals.java:73:3:73:7 | 0___0 | 0 | 0 |
+| literals/Literals.java:74:3:74:6 | 0_12 | 10 | 10 |
+| literals/Literals.java:75:3:75:7 | 0X012 | 18 | 18 |
+| literals/Literals.java:76:3:76:10 | 0xaBcDeF | 11259375 | 11259375 |
+| literals/Literals.java:77:3:77:6 | 0B11 | 3 | 3 |
+| literals/Literals.java:78:3:78:12 | 0x80000000 | -2147483648 | -2147483648 |
+| literals/Literals.java:79:3:79:12 | 2147483647 | 2147483647 | 2147483647 |
+| literals/Literals.java:80:3:80:13 | -2147483648 | -2147483648 | -2147483648 |
+| literals/Literals.java:82:3:82:13 | 0x7fff_ffff | 2147483647 | 2147483647 |
+| literals/Literals.java:83:3:83:16 | 0177_7777_7777 | 2147483647 | 2147483647 |
+| literals/Literals.java:84:3:84:43 | 0b0111_1111_1111_1111_1111_1111_1111_1111 | 2147483647 | 2147483647 |
+| literals/Literals.java:85:3:85:13 | 0x8000_0000 | -2147483648 | -2147483648 |
+| literals/Literals.java:86:3:86:16 | 0200_0000_0000 | -2147483648 | -2147483648 |
+| literals/Literals.java:87:3:87:43 | 0b1000_0000_0000_0000_0000_0000_0000_0000 | -2147483648 | -2147483648 |
+| literals/Literals.java:88:3:88:13 | 0xffff_ffff | -1 | -1 |
+| literals/Literals.java:89:3:89:16 | 0377_7777_7777 | -1 | -1 |
+| literals/Literals.java:90:3:90:43 | 0b1111_1111_1111_1111_1111_1111_1111_1111 | -1 | -1 |
diff --git a/java/ql/test/library-tests/literals/literalInteger.ql b/java/ql/test/library-tests/literals/literalInteger.ql
index b654370a192..264f5b77b99 100644
--- a/java/ql/test/library-tests/literals/literalInteger.ql
+++ b/java/ql/test/library-tests/literals/literalInteger.ql
@@ -1,4 +1,4 @@
import semmle.code.java.Expr
from IntegerLiteral lit
-select lit
+select lit, lit.getValue(), lit.getIntValue()
diff --git a/java/ql/test/library-tests/literals/literalLong.expected b/java/ql/test/library-tests/literals/literalLong.expected
index 4caf96df7cb..f46c55f9f37 100644
--- a/java/ql/test/library-tests/literals/literalLong.expected
+++ b/java/ql/test/library-tests/literals/literalLong.expected
@@ -1,4 +1,20 @@
-| literals/Literals.java:8:22:8:25 | 456L |
-| literals/Literals.java:15:18:15:38 | -9223372036854775808l |
-| literals/Literals.java:17:23:17:42 | 9223372036854775807l |
-| literals/Literals.java:19:22:19:40 | 0x8000000000000000L |
+| literals/Literals.java:8:22:8:25 | 456L | 456 |
+| literals/Literals.java:94:3:94:4 | 0l | 0 |
+| literals/Literals.java:95:3:95:4 | 0L | 0 |
+| literals/Literals.java:96:3:96:6 | 0_0L | 0 |
+| literals/Literals.java:97:3:97:8 | 0___0L | 0 |
+| literals/Literals.java:98:3:98:7 | 0_12L | 10 |
+| literals/Literals.java:99:3:99:8 | 0X012L | 18 |
+| literals/Literals.java:100:3:100:11 | 0xaBcDeFL | 11259375 |
+| literals/Literals.java:101:3:101:7 | 0B11L | 3 |
+| literals/Literals.java:102:3:102:22 | 9223372036854775807L | 9223372036854775807 |
+| literals/Literals.java:103:3:103:23 | -9223372036854775808L | -9223372036854775808 |
+| literals/Literals.java:105:3:105:24 | 0x7fff_ffff_ffff_ffffL | 9223372036854775807 |
+| literals/Literals.java:106:3:106:30 | 07_7777_7777_7777_7777_7777L | 9223372036854775807 |
+| literals/Literals.java:107:3:107:84 | 0b0111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111L | 9223372036854775807 |
+| literals/Literals.java:108:3:108:24 | 0x8000_0000_0000_0000L | -9223372036854775808 |
+| literals/Literals.java:109:3:109:31 | 010_0000_0000_0000_0000_0000L | -9223372036854775808 |
+| literals/Literals.java:110:3:110:84 | 0b1000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000L | -9223372036854775808 |
+| literals/Literals.java:111:3:111:24 | 0xffff_ffff_ffff_ffffL | -1 |
+| literals/Literals.java:112:3:112:31 | 017_7777_7777_7777_7777_7777L | -1 |
+| literals/Literals.java:113:3:113:84 | 0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111L | -1 |
diff --git a/java/ql/test/library-tests/literals/literalLong.ql b/java/ql/test/library-tests/literals/literalLong.ql
index 9f0c5a789c9..54109369b3a 100644
--- a/java/ql/test/library-tests/literals/literalLong.ql
+++ b/java/ql/test/library-tests/literals/literalLong.ql
@@ -1,4 +1,4 @@
import semmle.code.java.Expr
from LongLiteral lit
-select lit
+select lit, lit.getValue()
diff --git a/java/ql/test/library-tests/literals/literalString.expected b/java/ql/test/library-tests/literals/literalString.expected
index addd494a5b1..bf980e80bf6 100644
--- a/java/ql/test/library-tests/literals/literalString.expected
+++ b/java/ql/test/library-tests/literals/literalString.expected
@@ -1,6 +1,20 @@
-| literals/Literals.java:6:22:6:37 | "literal string" |
-| literals/Literals.java:22:22:22:38 | "hello" + "world" |
-| literals/Literals.java:23:24:23:47 | "hello" + ", " + "world" |
-| literals/Literals.java:24:23:24:52 | "hello" + ", " + "world" + "!" |
-| literals/Literals.java:25:22:25:36 | "hello,\\tworld" |
-| literals/Literals.java:26:30:26:48 | "hello,\\u0009world" |
+| literals/Literals.java:6:22:6:37 | "literal string" | literal string | literal string |
+| literals/Literals.java:117:3:117:19 | "hello" + "world" | helloworld | helloworld |
+| literals/Literals.java:118:3:118:17 | "hello,\\tworld" | hello,\tworld | hello,\tworld |
+| literals/Literals.java:119:3:119:21 | "hello,\\u0009world" | hello,\tworld | hello,\tworld |
+| literals/Literals.java:120:3:120:10 | "\\u0061" | a | a |
+| literals/Literals.java:121:3:121:6 | "\\0" | \u0000 | \u0000 |
+| literals/Literals.java:122:3:122:9 | "\\0000" | \u00000 | \u00000 |
+| literals/Literals.java:123:3:123:6 | "\\"" | " | " |
+| literals/Literals.java:124:3:124:6 | "\\'" | ' | ' |
+| literals/Literals.java:125:3:125:6 | "\\n" | \n | \n |
+| literals/Literals.java:126:3:126:6 | "\\\\" | \\ | \\ |
+| literals/Literals.java:127:3:127:13 | "test \\123" | test S | test S |
+| literals/Literals.java:128:3:128:9 | "\\1234" | S4 | S4 |
+| literals/Literals.java:129:3:129:13 | "\\u0061567" | a567 | a567 |
+| literals/Literals.java:130:3:130:13 | "\\u1234567" | \u1234567 | \u1234567 |
+| literals/Literals.java:131:3:131:18 | "\\uaBcDeF\\u0aB1" | \uabcdeF\u0ab1 | \uabcdeF\u0ab1 |
+| literals/Literals.java:132:3:132:16 | "\\uD800\\uDC00" | \ud800\udc00 | \ud800\udc00 |
+| literals/Literals.java:134:3:134:10 | "\\uD800" | ? | ? |
+| literals/Literals.java:135:3:135:10 | "\\uDC00" | ? | ? |
+| literals/Literals.java:136:3:136:31 | "hello\\uD800hello\\uDC00world" | hello?hello?world | hello?hello?world |
diff --git a/java/ql/test/library-tests/literals/literalString.ql b/java/ql/test/library-tests/literals/literalString.ql
index e69d4f89b6b..e2128e54e0e 100644
--- a/java/ql/test/library-tests/literals/literalString.ql
+++ b/java/ql/test/library-tests/literals/literalString.ql
@@ -2,4 +2,4 @@ import semmle.code.java.Expr
from StringLiteral lit
where lit.getFile().(CompilationUnit).fromSource()
-select lit
+select lit, lit.getValue(), lit.getRepresentedString()
diff --git a/java/ql/test/library-tests/literals/literals/Literals.java b/java/ql/test/library-tests/literals/literals/Literals.java
index 82b21ae2763..082310acb80 100644
--- a/java/ql/test/library-tests/literals/literals/Literals.java
+++ b/java/ql/test/library-tests/literals/literals/Literals.java
@@ -11,17 +11,128 @@ public class Literals {
System.out.println(true);
System.out.println('x');
}
- int min_int = -2147483648;
- long min_long = -9223372036854775808l;
- int neg_max_int = -2147483647;
- long neg_max_long = -9223372036854775807l;
- int alt_min_int = 0x80000000;
- long alt_min_long = 0x8000000000000000L;
- int i = 23 + 19;
- int j = 23 +19;
- String twostrings = "hello" + "world";
- String threestrings = "hello" + ", " + "world";
- String fourstrings = "hello" + ", " + "world" + "!";
- String escape_seq = "hello,\tworld";
- String unicode_escape_seq = "hello,\u0009world";
+
+ boolean[] booleans = {
+ true,
+ false
+ };
+
+ char[] chars = {
+ 'a',
+ '\u0061', // 'a'
+ '\u0000',
+ '\0',
+ '\n',
+ '\0',
+ '\\',
+ '\'',
+ '\123' // octal escape sequence for 'S'
+ };
+
+ double[] doubles = {
+ 0.0,
+ 0d,
+ .0d,
+ .0,
+ -0.d,
+ +0.d,
+ 1.234567890123456789,
+ 1.55555555555555555555,
+ // From the JLS
+ 1e1,
+ 1.7976931348623157E308,
+ -1.7976931348623157E308,
+ 0x1.f_ffff_ffff_ffffP+1023,
+ 4.9e-324,
+ 0x0.0_0000_0000_0001P-1022,
+ 0x1.0P-1074
+ };
+
+ float[] floats = {
+ 0.0f,
+ 0f,
+ .0f,
+ -0.f,
+ +0.f,
+ 1_0_0.0f,
+ 1.234567890123456789f,
+ 1.55555555555555555555f,
+ // From the JLS
+ 1e1f,
+ 3.4028235e38f,
+ -3.4028235e38f,
+ 0x1.fffffeP+127f,
+ 1.4e-45f,
+ 0x0.000002P-126f,
+ 0x1.0P-149f
+ };
+
+ int[] ints = {
+ 0,
+ 0_0,
+ 0___0,
+ 0_12, // octal
+ 0X012, // hex
+ 0xaBcDeF, // hex
+ 0B11, // binary
+ 0x80000000,
+ 2147483647,
+ -2147483648,
+ // From the JLS
+ 0x7fff_ffff,
+ 0177_7777_7777, // octal
+ 0b0111_1111_1111_1111_1111_1111_1111_1111, // binary
+ 0x8000_0000,
+ 0200_0000_0000,
+ 0b1000_0000_0000_0000_0000_0000_0000_0000,
+ 0xffff_ffff,
+ 0377_7777_7777,
+ 0b1111_1111_1111_1111_1111_1111_1111_1111
+ };
+
+ long[] longs = {
+ 0l,
+ 0L,
+ 0_0L,
+ 0___0L,
+ 0_12L, // octal
+ 0X012L, // hex
+ 0xaBcDeFL, // hex
+ 0B11L, // binary
+ 9223372036854775807L,
+ -9223372036854775808L,
+ // From the JLS
+ 0x7fff_ffff_ffff_ffffL,
+ 07_7777_7777_7777_7777_7777L, // octal
+ 0b0111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111L, // binary
+ 0x8000_0000_0000_0000L,
+ 010_0000_0000_0000_0000_0000L,
+ 0b1000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000L,
+ 0xffff_ffff_ffff_ffffL,
+ 017_7777_7777_7777_7777_7777L,
+ 0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111L
+ };
+
+ String[] strings = {
+ "hello" + "world", // two separate literals
+ "hello,\tworld",
+ "hello,\u0009world",
+ "\u0061", // 'a'
+ "\0",
+ "\0000",
+ "\"",
+ "\'",
+ "\n",
+ "\\",
+ "test \123", // octal escape sequence for 'S'
+ "\1234", // octal escape followed by '4'
+ "\u0061567", // escape sequence for 'a' followed by "567"
+ "\u1234567", // '\u1234' followed by "567"
+ "\uaBcDeF\u0aB1", // '\uABCD' followed by "eF" followed by '\u0AB1'
+ "\uD800\uDC00", // surrogate pair
+ // Unpaired surrogates
+ "\uD800",
+ "\uDC00",
+ "hello\uD800hello\uDC00world"
+ };
}
From 73e940de74bb326664db451b97fa58e67e683400 Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Tue, 2 Feb 2021 17:37:14 +0100
Subject: [PATCH 219/725] Added query for Jakarta EL injections
- Added JakartaExpressionInjection.ql
- Added a qhelp file with examples
---
.../Security/CWE/CWE-094/InjectionLib.qll | 14 +++
.../CWE-094/JakartaExpressionInjection.qhelp | 65 ++++++++++++
.../CWE/CWE-094/JakartaExpressionInjection.ql | 20 ++++
.../CWE-094/JakartaExpressionInjectionLib.qll | 98 +++++++++++++++++++
.../Security/CWE/CWE-094/JexlInjectionLib.qll | 13 +--
.../SaferExpressionEvaluationWithJuel.java | 10 ++
.../UnsafeExpressionEvaluationWithJuel.java | 5 +
7 files changed, 213 insertions(+), 12 deletions(-)
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/InjectionLib.qll
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjection.qhelp
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjection.ql
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjectionLib.qll
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/SaferExpressionEvaluationWithJuel.java
create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/UnsafeExpressionEvaluationWithJuel.java
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/InjectionLib.qll b/java/ql/src/experimental/Security/CWE/CWE-094/InjectionLib.qll
new file mode 100644
index 00000000000..adab79d6f5c
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-094/InjectionLib.qll
@@ -0,0 +1,14 @@
+import java
+import semmle.code.java.dataflow.FlowSources
+
+/**
+ * Holds if `fromNode` to `toNode` is a dataflow step that returns data from
+ * a bean by calling one of its getters.
+ */
+predicate returnsDataFromBean(DataFlow::Node fromNode, DataFlow::Node toNode) {
+ exists(MethodAccess ma, Method m | ma.getMethod() = m |
+ m instanceof GetterMethod and
+ ma.getQualifier() = fromNode.asExpr() and
+ ma = toNode.asExpr()
+ )
+}
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjection.qhelp
new file mode 100644
index 00000000000..6e6b5f63394
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjection.qhelp
@@ -0,0 +1,65 @@
+
+
+
+
+
+Jakarta Expression Language (EL) is an expression language for Java applications.
+There are a single language specification and multiple implementations
+such as Glassfish, Juel, Apache Commons EL, etc.
+The language allows invocation of methods available in the JVM.
+If an expression is built using attacker-controlled data,
+and then evaluated, then it may allow the attacker to run arbitrary code.
+
+
+
+
+
+It is generally recommended to avoid using untrusted data in an EL expression.
+Before using untrusted data to build an EL expressoin, the data should be validated
+to ensure it is not evaluated as expression language. If the EL implementaion offers
+configuring a sandbox for EL expression, they should be run in a restircitive sandbox
+that allows accessing only explicitly allowed classes. If the EL implementation
+does not allow sandboxing, consider using other expressiong language implementations
+with sandboxing capabilities such as Apache Commons JEXL or the Spring Expression Language.
+
+
+
+
+
+The following example shows how untrusted data is used to build and run an expression
+using the JUEL interpreter:
+
+
+
+
+JUEL does not allow to run expression in a sandbox. To prevent running arbitrary code,
+incoming data has to be checked before including to an expression. The next example
+uses a Regex pattern to check whether a user tries to run an allowed exression or not:
+
+
+
+
+
+
+
+ Eclipse Foundation:
+ Jakarta Expression Language.
+
+
+ Jakarta EE documentation:
+ Jakarta Expression Language API
+
+
+ OWASP:
+ Expression Language Injection.
+
+
+ JUEL:
+ Home page
+
+
+ Apache Foundation:
+ Apache Commons EL
+
+
+
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjection.ql
new file mode 100644
index 00000000000..b94c98201de
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjection.ql
@@ -0,0 +1,20 @@
+/**
+ * @name Java EE Expression Language injection
+ * @description Evaluation of a user-controlled Jave EE expression
+ * may lead to arbitrary code execution.
+ * @kind path-problem
+ * @problem.severity error
+ * @precision high
+ * @id java/javaee-expression-injection
+ * @tags security
+ * external/cwe/cwe-094
+ */
+
+import java
+import JavaEEExpressionInjectionLib
+import DataFlow::PathGraph
+
+from DataFlow::PathNode source, DataFlow::PathNode sink, JavaEEExpressionInjectionConfig conf
+where conf.hasFlowPath(source, sink)
+select sink.getNode(), source, sink, "Java EE Expression Language injection from $@.",
+ source.getNode(), "this user input"
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjectionLib.qll b/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjectionLib.qll
new file mode 100644
index 00000000000..d43b31c9888
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjectionLib.qll
@@ -0,0 +1,98 @@
+import java
+import InjectionLib
+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 Java EE expression.
+ */
+class JavaEEExpressionInjectionConfig extends TaintTracking::Configuration {
+ JavaEEExpressionInjectionConfig() { this = "JavaEEExpressionInjectionConfig" }
+
+ override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
+
+ override predicate isSink(DataFlow::Node sink) { sink instanceof ExpressionEvaluationSink }
+
+ override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
+ any(TaintPropagatingCall c).taintFlow(fromNode, toNode) or
+ returnsDataFromBean(fromNode, toNode)
+ }
+}
+
+/**
+ * A sink for Expresssion Language injection vulnerabilities,
+ * i.e. method calls that run evaluation of a Java EE expression.
+ */
+private class ExpressionEvaluationSink extends DataFlow::ExprNode {
+ ExpressionEvaluationSink() {
+ exists(MethodAccess ma, Method m, Expr taintFrom |
+ ma.getMethod() = m and taintFrom = this.asExpr()
+ |
+ m.getDeclaringType() instanceof ValueExpression and
+ m.hasName(["getValue", "setValue"]) and
+ ma.getQualifier() = taintFrom
+ or
+ m.getDeclaringType() instanceof MethodExpression and
+ m.hasName("invoke") and
+ ma.getQualifier() = taintFrom
+ or
+ m.getDeclaringType() instanceof LambdaExpression and
+ m.hasName("invoke") and
+ ma.getQualifier() = taintFrom
+ or
+ m.getDeclaringType() instanceof ELProcessor and
+ m.hasName(["eval", "getValue", "setValue"]) and
+ ma.getArgument(0) = taintFrom
+ )
+ }
+}
+
+/**
+ * Defines method calls that propagate tainted expressions.
+ */
+private class TaintPropagatingCall extends Call {
+ Expr taintFromExpr;
+
+ TaintPropagatingCall() {
+ taintFromExpr = this.getArgument(1) and
+ exists(Method m | this.(MethodAccess).getMethod() = m |
+ m.getDeclaringType() instanceof ExpressionFactory and
+ m.hasName(["createValueExpression", "createMethodExpression"]) and
+ taintFromExpr.getType() instanceof TypeString
+ )
+ or
+ exists(Constructor c | this.(ConstructorCall).getConstructor() = c |
+ c.getDeclaringType() instanceof LambdaExpression and
+ taintFromExpr.getType() instanceof ValueExpression
+ )
+ }
+
+ /**
+ * Holds if `fromNode` to `toNode` is a dataflow step that propagates
+ * tainted data.
+ */
+ predicate taintFlow(DataFlow::Node fromNode, DataFlow::Node toNode) {
+ fromNode.asExpr() = taintFromExpr and toNode.asExpr() = this
+ }
+}
+
+private class ELProcessor extends RefType {
+ ELProcessor() { hasQualifiedName("javax.el", "ELProcessor") }
+}
+
+private class ExpressionFactory extends RefType {
+ ExpressionFactory() { hasQualifiedName("javax.el", "ExpressionFactory") }
+}
+
+private class ValueExpression extends RefType {
+ ValueExpression() { hasQualifiedName("javax.el", "ValueExpression") }
+}
+
+private class MethodExpression extends RefType {
+ MethodExpression() { hasQualifiedName("javax.el", "MethodExpression") }
+}
+
+private class LambdaExpression extends RefType {
+ LambdaExpression() { hasQualifiedName("javax.el", "LambdaExpression") }
+}
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 561d7e46ae9..51084a9862c 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjectionLib.qll
+++ b/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjectionLib.qll
@@ -1,4 +1,5 @@
import java
+import InjectionLib
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking
@@ -152,18 +153,6 @@ private predicate createsJexlEngine(DataFlow::Node fromNode, DataFlow::Node toNo
)
}
-/**
- * Holds if `fromNode` to `toNode` is a dataflow step that returns data from
- * a bean by calling one of its getters.
- */
-private predicate returnsDataFromBean(DataFlow::Node fromNode, DataFlow::Node toNode) {
- exists(MethodAccess ma, Method m | ma.getMethod() = m |
- m instanceof GetterMethod and
- ma.getQualifier() = fromNode.asExpr() and
- ma = toNode.asExpr()
- )
-}
-
/**
* A methods in the `JexlEngine` class that gets or sets a property with a JEXL expression.
*/
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/SaferExpressionEvaluationWithJuel.java b/java/ql/src/experimental/Security/CWE/CWE-094/SaferExpressionEvaluationWithJuel.java
new file mode 100644
index 00000000000..54fb9a0ed36
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-094/SaferExpressionEvaluationWithJuel.java
@@ -0,0 +1,10 @@
+String input = getRemoteUserInput();
+String pattern = "(inside|outside)\\.(temperature|humidity)";
+if (!input.matches(pattern)) {
+ throw new IllegalArgumentException("Unexpected exression");
+}
+String expression = "${" + input + "}";
+ExpressionFactory factory = new de.odysseus.el.ExpressionFactoryImpl();
+ValueExpression e = factory.createValueExpression(context, expression, Object.class);
+SimpleContext context = getContext();
+Object result = e.getValue(context);
\ No newline at end of file
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/UnsafeExpressionEvaluationWithJuel.java b/java/ql/src/experimental/Security/CWE/CWE-094/UnsafeExpressionEvaluationWithJuel.java
new file mode 100644
index 00000000000..27afa0fcb49
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-094/UnsafeExpressionEvaluationWithJuel.java
@@ -0,0 +1,5 @@
+String expression = "${" + getRemoteUserInput() + "}";
+ExpressionFactory factory = new de.odysseus.el.ExpressionFactoryImpl();
+ValueExpression e = factory.createValueExpression(context, expression, Object.class);
+SimpleContext context = getContext();
+Object result = e.getValue(context);
\ No newline at end of file
From adb1ed380ac4736860eac8ea6cfadb8f38497db5 Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Sun, 21 Mar 2021 20:36:47 +0300
Subject: [PATCH 220/725] Added tests for Jakarta expression injection
---
.../CWE/CWE-094/JakartaExpressionInjection.ql | 10 +--
.../CWE-094/JakartaExpressionInjectionLib.qll | 8 +-
.../JakartaExpressionInjection.expected | 59 +++++++++++++
.../CWE-094/JakartaExpressionInjection.java | 87 +++++++++++++++++++
.../CWE-094/JakartaExpressionInjection.qlref | 1 +
.../query-tests/security/CWE-094/options | 3 +-
.../stubs/java-ee-el/javax/el/ELContext.java | 3 +
.../stubs/java-ee-el/javax/el/ELManager.java | 5 ++
.../java-ee-el/javax/el/ELProcessor.java | 7 ++
.../javax/el/ExpressionFactory.java | 17 ++++
.../java-ee-el/javax/el/LambdaExpression.java | 8 ++
.../java-ee-el/javax/el/MethodExpression.java | 5 ++
.../javax/el/StandardELContext.java | 5 ++
.../java-ee-el/javax/el/ValueExpression.java | 6 ++
.../de/odysseus/el/ExpressionFactoryImpl.java | 5 ++
.../de/odysseus/el/util/SimpleContext.java | 5 ++
16 files changed, 223 insertions(+), 11 deletions(-)
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/JakartaExpressionInjection.expected
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/JakartaExpressionInjection.java
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/JakartaExpressionInjection.qlref
create mode 100644 java/ql/test/stubs/java-ee-el/javax/el/ELContext.java
create mode 100644 java/ql/test/stubs/java-ee-el/javax/el/ELManager.java
create mode 100644 java/ql/test/stubs/java-ee-el/javax/el/ELProcessor.java
create mode 100644 java/ql/test/stubs/java-ee-el/javax/el/ExpressionFactory.java
create mode 100644 java/ql/test/stubs/java-ee-el/javax/el/LambdaExpression.java
create mode 100644 java/ql/test/stubs/java-ee-el/javax/el/MethodExpression.java
create mode 100644 java/ql/test/stubs/java-ee-el/javax/el/StandardELContext.java
create mode 100644 java/ql/test/stubs/java-ee-el/javax/el/ValueExpression.java
create mode 100644 java/ql/test/stubs/juel-2.2/de/odysseus/el/ExpressionFactoryImpl.java
create mode 100644 java/ql/test/stubs/juel-2.2/de/odysseus/el/util/SimpleContext.java
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjection.ql
index b94c98201de..8190ec3d61f 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjection.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjection.ql
@@ -1,6 +1,6 @@
/**
- * @name Java EE Expression Language injection
- * @description Evaluation of a user-controlled Jave EE expression
+ * @name Jakarta Expression Language injection
+ * @description Evaluation of a user-controlled expression
* may lead to arbitrary code execution.
* @kind path-problem
* @problem.severity error
@@ -11,10 +11,10 @@
*/
import java
-import JavaEEExpressionInjectionLib
+import JakartaExpressionInjectionLib
import DataFlow::PathGraph
-from DataFlow::PathNode source, DataFlow::PathNode sink, JavaEEExpressionInjectionConfig conf
+from DataFlow::PathNode source, DataFlow::PathNode sink, JakartaExpressionInjectionConfig conf
where conf.hasFlowPath(source, sink)
-select sink.getNode(), source, sink, "Java EE Expression Language injection from $@.",
+select sink.getNode(), source, sink, "Jakarta Expression Language injection from $@.",
source.getNode(), "this user input"
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjectionLib.qll b/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjectionLib.qll
index d43b31c9888..bc22f7c3257 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjectionLib.qll
+++ b/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjectionLib.qll
@@ -5,10 +5,10 @@ import semmle.code.java.dataflow.TaintTracking
/**
* A taint-tracking configuration for unsafe user input
- * that is used to construct and evaluate a Java EE expression.
+ * that is used to construct and evaluate an expression.
*/
-class JavaEEExpressionInjectionConfig extends TaintTracking::Configuration {
- JavaEEExpressionInjectionConfig() { this = "JavaEEExpressionInjectionConfig" }
+class JakartaExpressionInjectionConfig extends TaintTracking::Configuration {
+ JakartaExpressionInjectionConfig() { this = "JakartaExpressionInjectionConfig" }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
@@ -22,7 +22,7 @@ class JavaEEExpressionInjectionConfig extends TaintTracking::Configuration {
/**
* A sink for Expresssion Language injection vulnerabilities,
- * i.e. method calls that run evaluation of a Java EE expression.
+ * i.e. method calls that run evaluation of an expression.
*/
private class ExpressionEvaluationSink extends DataFlow::ExprNode {
ExpressionEvaluationSink() {
diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JakartaExpressionInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-094/JakartaExpressionInjection.expected
new file mode 100644
index 00000000000..111cc81541c
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-094/JakartaExpressionInjection.expected
@@ -0,0 +1,59 @@
+edges
+| JakartaExpressionInjection.java:22:25:22:47 | getInputStream(...) : InputStream | JakartaExpressionInjection.java:24:31:24:40 | expression : String |
+| JakartaExpressionInjection.java:24:31:24:40 | expression : String | JakartaExpressionInjection.java:30:24:30:33 | expression : String |
+| JakartaExpressionInjection.java:24:31:24:40 | expression : String | JakartaExpressionInjection.java:37:24:37:33 | expression : String |
+| JakartaExpressionInjection.java:24:31:24:40 | expression : String | JakartaExpressionInjection.java:44:24:44:33 | expression : String |
+| JakartaExpressionInjection.java:24:31:24:40 | expression : String | JakartaExpressionInjection.java:54:24:54:33 | expression : String |
+| JakartaExpressionInjection.java:24:31:24:40 | expression : String | JakartaExpressionInjection.java:61:24:61:33 | expression : String |
+| JakartaExpressionInjection.java:24:31:24:40 | expression : String | JakartaExpressionInjection.java:70:24:70:33 | expression : String |
+| JakartaExpressionInjection.java:24:31:24:40 | expression : String | JakartaExpressionInjection.java:79:24:79:33 | expression : String |
+| JakartaExpressionInjection.java:30:24:30:33 | expression : String | JakartaExpressionInjection.java:32:28:32:37 | expression |
+| JakartaExpressionInjection.java:37:24:37:33 | expression : String | JakartaExpressionInjection.java:39:32:39:41 | expression |
+| JakartaExpressionInjection.java:44:24:44:33 | expression : String | JakartaExpressionInjection.java:49:13:49:28 | lambdaExpression |
+| JakartaExpressionInjection.java:48:49:48:104 | new LambdaExpression(...) : LambdaExpression | JakartaExpressionInjection.java:49:13:49:28 | lambdaExpression |
+| JakartaExpressionInjection.java:54:24:54:33 | expression : String | JakartaExpressionInjection.java:56:32:56:41 | expression |
+| JakartaExpressionInjection.java:61:24:61:33 | expression : String | JakartaExpressionInjection.java:64:33:64:96 | createValueExpression(...) : ValueExpression |
+| JakartaExpressionInjection.java:61:24:61:33 | expression : String | JakartaExpressionInjection.java:65:13:65:13 | e |
+| JakartaExpressionInjection.java:61:24:61:33 | expression : String | JakartaExpressionInjection.java:65:13:65:13 | e : ValueExpression |
+| JakartaExpressionInjection.java:64:33:64:96 | createValueExpression(...) : ValueExpression | JakartaExpressionInjection.java:48:49:48:104 | new LambdaExpression(...) : LambdaExpression |
+| JakartaExpressionInjection.java:64:33:64:96 | createValueExpression(...) : ValueExpression | JakartaExpressionInjection.java:65:13:65:13 | e |
+| JakartaExpressionInjection.java:64:33:64:96 | createValueExpression(...) : ValueExpression | JakartaExpressionInjection.java:65:13:65:13 | e : ValueExpression |
+| JakartaExpressionInjection.java:65:13:65:13 | e : ValueExpression | JakartaExpressionInjection.java:48:49:48:104 | new LambdaExpression(...) : LambdaExpression |
+| JakartaExpressionInjection.java:70:24:70:33 | expression : String | JakartaExpressionInjection.java:73:33:73:96 | createValueExpression(...) : ValueExpression |
+| JakartaExpressionInjection.java:70:24:70:33 | expression : String | JakartaExpressionInjection.java:74:13:74:13 | e |
+| JakartaExpressionInjection.java:70:24:70:33 | expression : String | JakartaExpressionInjection.java:74:13:74:13 | e : ValueExpression |
+| JakartaExpressionInjection.java:73:33:73:96 | createValueExpression(...) : ValueExpression | JakartaExpressionInjection.java:48:49:48:104 | new LambdaExpression(...) : LambdaExpression |
+| JakartaExpressionInjection.java:73:33:73:96 | createValueExpression(...) : ValueExpression | JakartaExpressionInjection.java:74:13:74:13 | e |
+| JakartaExpressionInjection.java:73:33:73:96 | createValueExpression(...) : ValueExpression | JakartaExpressionInjection.java:74:13:74:13 | e : ValueExpression |
+| JakartaExpressionInjection.java:74:13:74:13 | e : ValueExpression | JakartaExpressionInjection.java:48:49:48:104 | new LambdaExpression(...) : LambdaExpression |
+| JakartaExpressionInjection.java:79:24:79:33 | expression : String | JakartaExpressionInjection.java:83:13:83:13 | e |
+nodes
+| JakartaExpressionInjection.java:22:25:22:47 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
+| JakartaExpressionInjection.java:24:31:24:40 | expression : String | semmle.label | expression : String |
+| JakartaExpressionInjection.java:30:24:30:33 | expression : String | semmle.label | expression : String |
+| JakartaExpressionInjection.java:32:28:32:37 | expression | semmle.label | expression |
+| JakartaExpressionInjection.java:37:24:37:33 | expression : String | semmle.label | expression : String |
+| JakartaExpressionInjection.java:39:32:39:41 | expression | semmle.label | expression |
+| JakartaExpressionInjection.java:44:24:44:33 | expression : String | semmle.label | expression : String |
+| JakartaExpressionInjection.java:48:49:48:104 | new LambdaExpression(...) : LambdaExpression | semmle.label | new LambdaExpression(...) : LambdaExpression |
+| JakartaExpressionInjection.java:49:13:49:28 | lambdaExpression | semmle.label | lambdaExpression |
+| JakartaExpressionInjection.java:54:24:54:33 | expression : String | semmle.label | expression : String |
+| JakartaExpressionInjection.java:56:32:56:41 | expression | semmle.label | expression |
+| JakartaExpressionInjection.java:61:24:61:33 | expression : String | semmle.label | expression : String |
+| JakartaExpressionInjection.java:64:33:64:96 | createValueExpression(...) : ValueExpression | semmle.label | createValueExpression(...) : ValueExpression |
+| JakartaExpressionInjection.java:65:13:65:13 | e | semmle.label | e |
+| JakartaExpressionInjection.java:65:13:65:13 | e : ValueExpression | semmle.label | e : ValueExpression |
+| JakartaExpressionInjection.java:70:24:70:33 | expression : String | semmle.label | expression : String |
+| JakartaExpressionInjection.java:73:33:73:96 | createValueExpression(...) : ValueExpression | semmle.label | createValueExpression(...) : ValueExpression |
+| JakartaExpressionInjection.java:74:13:74:13 | e | semmle.label | e |
+| JakartaExpressionInjection.java:74:13:74:13 | e : ValueExpression | semmle.label | e : ValueExpression |
+| JakartaExpressionInjection.java:79:24:79:33 | expression : String | semmle.label | expression : String |
+| JakartaExpressionInjection.java:83:13:83:13 | e | semmle.label | e |
+#select
+| JakartaExpressionInjection.java:32:28:32:37 | expression | JakartaExpressionInjection.java:22:25:22:47 | getInputStream(...) : InputStream | JakartaExpressionInjection.java:32:28:32:37 | expression | Jakarta Expression Language injection from $@. | JakartaExpressionInjection.java:22:25:22:47 | getInputStream(...) | this user input |
+| JakartaExpressionInjection.java:39:32:39:41 | expression | JakartaExpressionInjection.java:22:25:22:47 | getInputStream(...) : InputStream | JakartaExpressionInjection.java:39:32:39:41 | expression | Jakarta Expression Language injection from $@. | JakartaExpressionInjection.java:22:25:22:47 | getInputStream(...) | this user input |
+| JakartaExpressionInjection.java:49:13:49:28 | lambdaExpression | JakartaExpressionInjection.java:22:25:22:47 | getInputStream(...) : InputStream | JakartaExpressionInjection.java:49:13:49:28 | lambdaExpression | Jakarta Expression Language injection from $@. | JakartaExpressionInjection.java:22:25:22:47 | getInputStream(...) | this user input |
+| JakartaExpressionInjection.java:56:32:56:41 | expression | JakartaExpressionInjection.java:22:25:22:47 | getInputStream(...) : InputStream | JakartaExpressionInjection.java:56:32:56:41 | expression | Jakarta Expression Language injection from $@. | JakartaExpressionInjection.java:22:25:22:47 | getInputStream(...) | this user input |
+| JakartaExpressionInjection.java:65:13:65:13 | e | JakartaExpressionInjection.java:22:25:22:47 | getInputStream(...) : InputStream | JakartaExpressionInjection.java:65:13:65:13 | e | Jakarta Expression Language injection from $@. | JakartaExpressionInjection.java:22:25:22:47 | getInputStream(...) | this user input |
+| JakartaExpressionInjection.java:74:13:74:13 | e | JakartaExpressionInjection.java:22:25:22:47 | getInputStream(...) : InputStream | JakartaExpressionInjection.java:74:13:74:13 | e | Jakarta Expression Language injection from $@. | JakartaExpressionInjection.java:22:25:22:47 | getInputStream(...) | this user input |
+| JakartaExpressionInjection.java:83:13:83:13 | e | JakartaExpressionInjection.java:22:25:22:47 | getInputStream(...) : InputStream | JakartaExpressionInjection.java:83:13:83:13 | e | Jakarta Expression Language injection from $@. | JakartaExpressionInjection.java:22:25:22:47 | getInputStream(...) | this user input |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JakartaExpressionInjection.java b/java/ql/test/experimental/query-tests/security/CWE-094/JakartaExpressionInjection.java
new file mode 100644
index 00000000000..a9fb2d45d6f
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-094/JakartaExpressionInjection.java
@@ -0,0 +1,87 @@
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+import javax.el.ELContext;
+import javax.el.ELManager;
+import javax.el.ELProcessor;
+import javax.el.ExpressionFactory;
+import javax.el.LambdaExpression;
+import javax.el.MethodExpression;
+import javax.el.StandardELContext;
+import javax.el.ValueExpression;
+
+public class JakartaExpressionInjection {
+
+ private static void testWithSocket(Consumer action) throws IOException {
+ try (ServerSocket serverSocket = new ServerSocket(0)) {
+ try (Socket socket = serverSocket.accept()) {
+ byte[] bytes = new byte[1024];
+ int n = socket.getInputStream().read(bytes);
+ String expression = new String(bytes, 0, n);
+ action.accept(expression);
+ }
+ }
+ }
+
+ private static void testWithELProcessorEval() throws IOException {
+ testWithSocket(expression -> {
+ ELProcessor processor = new ELProcessor();
+ processor.eval(expression);
+ });
+ }
+
+ private static void testWithELProcessorGetValue() throws IOException {
+ testWithSocket(expression -> {
+ ELProcessor processor = new ELProcessor();
+ processor.getValue(expression, Object.class);
+ });
+ }
+
+ private static void testWithLambdaExpressionInvoke() throws IOException {
+ testWithSocket(expression -> {
+ ExpressionFactory factory = ELManager.getExpressionFactory();
+ StandardELContext context = new StandardELContext(factory);
+ ValueExpression valueExpression = factory.createValueExpression(context, expression, Object.class);
+ LambdaExpression lambdaExpression = new LambdaExpression(new ArrayList<>(), valueExpression);
+ lambdaExpression.invoke(context, new Object[0]);
+ });
+ }
+
+ private static void testWithELProcessorSetValue() throws IOException {
+ testWithSocket(expression -> {
+ ELProcessor processor = new ELProcessor();
+ processor.setValue(expression, new Object());
+ });
+ }
+
+ private static void testWithJuelValueExpressionGetValue() throws IOException {
+ testWithSocket(expression -> {
+ ExpressionFactory factory = new de.odysseus.el.ExpressionFactoryImpl();
+ ELContext context = new de.odysseus.el.util.SimpleContext();
+ ValueExpression e = factory.createValueExpression(context, expression, Object.class);
+ e.getValue(context);
+ });
+ }
+
+ private static void testWithJuelValueExpressionSetValue() throws IOException {
+ testWithSocket(expression -> {
+ ExpressionFactory factory = new de.odysseus.el.ExpressionFactoryImpl();
+ ELContext context = new de.odysseus.el.util.SimpleContext();
+ ValueExpression e = factory.createValueExpression(context, expression, Object.class);
+ e.setValue(context, new Object());
+ });
+ }
+
+ private static void testWithJuelMethodExpressionInvoke() throws IOException {
+ testWithSocket(expression -> {
+ ExpressionFactory factory = new de.odysseus.el.ExpressionFactoryImpl();
+ ELContext context = new de.odysseus.el.util.SimpleContext();
+ MethodExpression e = factory.createMethodExpression(context, expression, Object.class, new Class[0]);
+ e.invoke(context, new Object[0]);
+ });
+ }
+
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JakartaExpressionInjection.qlref b/java/ql/test/experimental/query-tests/security/CWE-094/JakartaExpressionInjection.qlref
new file mode 100644
index 00000000000..3154ee5ccad
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-094/JakartaExpressionInjection.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-094/JakartaExpressionInjection.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 a8e30ce30b4..ec3354ea41c 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 @@
-//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
-
+//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
\ No newline at end of file
diff --git a/java/ql/test/stubs/java-ee-el/javax/el/ELContext.java b/java/ql/test/stubs/java-ee-el/javax/el/ELContext.java
new file mode 100644
index 00000000000..ce3840c69c8
--- /dev/null
+++ b/java/ql/test/stubs/java-ee-el/javax/el/ELContext.java
@@ -0,0 +1,3 @@
+package javax.el;
+
+public class ELContext {}
diff --git a/java/ql/test/stubs/java-ee-el/javax/el/ELManager.java b/java/ql/test/stubs/java-ee-el/javax/el/ELManager.java
new file mode 100644
index 00000000000..7d24f739a3f
--- /dev/null
+++ b/java/ql/test/stubs/java-ee-el/javax/el/ELManager.java
@@ -0,0 +1,5 @@
+package javax.el;
+
+public class ELManager {
+ public static ExpressionFactory getExpressionFactory() { return null; }
+}
\ No newline at end of file
diff --git a/java/ql/test/stubs/java-ee-el/javax/el/ELProcessor.java b/java/ql/test/stubs/java-ee-el/javax/el/ELProcessor.java
new file mode 100644
index 00000000000..3c523e685c5
--- /dev/null
+++ b/java/ql/test/stubs/java-ee-el/javax/el/ELProcessor.java
@@ -0,0 +1,7 @@
+package javax.el;
+
+public class ELProcessor {
+ public Object eval(String expression) { return null; }
+ public Object getValue(String expression, Class> expectedType) { return null; }
+ public void setValue(String expression, Object value) {}
+}
diff --git a/java/ql/test/stubs/java-ee-el/javax/el/ExpressionFactory.java b/java/ql/test/stubs/java-ee-el/javax/el/ExpressionFactory.java
new file mode 100644
index 00000000000..31ff79169ac
--- /dev/null
+++ b/java/ql/test/stubs/java-ee-el/javax/el/ExpressionFactory.java
@@ -0,0 +1,17 @@
+package javax.el;
+
+public class ExpressionFactory {
+ public MethodExpression createMethodExpression(ELContext context, String expression, Class> expectedReturnType,
+ Class>[] expectedParamTypes) {
+
+ return null;
+ }
+
+ public ValueExpression createValueExpression(ELContext context, String expression, Class> expectedType) {
+ return null;
+ }
+
+ public ValueExpression createValueExpression(Object instance, Class> expectedType) {
+ return null;
+ }
+}
diff --git a/java/ql/test/stubs/java-ee-el/javax/el/LambdaExpression.java b/java/ql/test/stubs/java-ee-el/javax/el/LambdaExpression.java
new file mode 100644
index 00000000000..4be01e9d2e4
--- /dev/null
+++ b/java/ql/test/stubs/java-ee-el/javax/el/LambdaExpression.java
@@ -0,0 +1,8 @@
+package javax.el;
+
+import java.util.List;
+
+public class LambdaExpression {
+ public LambdaExpression(List formalParameters, ValueExpression expression) {}
+ public Object invoke(Object... args) { return null; }
+}
diff --git a/java/ql/test/stubs/java-ee-el/javax/el/MethodExpression.java b/java/ql/test/stubs/java-ee-el/javax/el/MethodExpression.java
new file mode 100644
index 00000000000..ac50ece80e3
--- /dev/null
+++ b/java/ql/test/stubs/java-ee-el/javax/el/MethodExpression.java
@@ -0,0 +1,5 @@
+package javax.el;
+
+public class MethodExpression {
+ public Object invoke(ELContext context, Object[] params) { return null; }
+}
diff --git a/java/ql/test/stubs/java-ee-el/javax/el/StandardELContext.java b/java/ql/test/stubs/java-ee-el/javax/el/StandardELContext.java
new file mode 100644
index 00000000000..22e3f2a9fc1
--- /dev/null
+++ b/java/ql/test/stubs/java-ee-el/javax/el/StandardELContext.java
@@ -0,0 +1,5 @@
+package javax.el;
+
+public class StandardELContext extends ELContext {
+ public StandardELContext(ExpressionFactory factory) {}
+}
diff --git a/java/ql/test/stubs/java-ee-el/javax/el/ValueExpression.java b/java/ql/test/stubs/java-ee-el/javax/el/ValueExpression.java
new file mode 100644
index 00000000000..9a231215640
--- /dev/null
+++ b/java/ql/test/stubs/java-ee-el/javax/el/ValueExpression.java
@@ -0,0 +1,6 @@
+package javax.el;
+
+public class ValueExpression {
+ public Object getValue(ELContext context) { return null; }
+ public void setValue(ELContext context, Object value) {}
+}
diff --git a/java/ql/test/stubs/juel-2.2/de/odysseus/el/ExpressionFactoryImpl.java b/java/ql/test/stubs/juel-2.2/de/odysseus/el/ExpressionFactoryImpl.java
new file mode 100644
index 00000000000..a555cf88dee
--- /dev/null
+++ b/java/ql/test/stubs/juel-2.2/de/odysseus/el/ExpressionFactoryImpl.java
@@ -0,0 +1,5 @@
+package de.odysseus.el;
+
+import javax.el.ExpressionFactory;
+
+public class ExpressionFactoryImpl extends ExpressionFactory {}
diff --git a/java/ql/test/stubs/juel-2.2/de/odysseus/el/util/SimpleContext.java b/java/ql/test/stubs/juel-2.2/de/odysseus/el/util/SimpleContext.java
new file mode 100644
index 00000000000..aa4f449e5fa
--- /dev/null
+++ b/java/ql/test/stubs/juel-2.2/de/odysseus/el/util/SimpleContext.java
@@ -0,0 +1,5 @@
+package de.odysseus.el.util;
+
+import javax.el.ELContext;
+
+public class SimpleContext extends ELContext {}
From 6c246994035a35925d870f97abc58c04162d53f9 Mon Sep 17 00:00:00 2001
From: Artem Smotrakov
Date: Sun, 21 Mar 2021 21:16:00 +0300
Subject: [PATCH 221/725] Cover both javax.el and jakarta.el packages
---
.../CWE-094/JakartaExpressionInjectionLib.qll | 22 +++++++++++--------
1 file changed, 13 insertions(+), 9 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjectionLib.qll b/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjectionLib.qll
index bc22f7c3257..22f421b8bf8 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjectionLib.qll
+++ b/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjectionLib.qll
@@ -77,22 +77,26 @@ private class TaintPropagatingCall extends Call {
}
}
-private class ELProcessor extends RefType {
- ELProcessor() { hasQualifiedName("javax.el", "ELProcessor") }
+private class JakartaType extends RefType {
+ JakartaType() { getPackage().hasName(["javax.el", "jakarta.el"]) }
}
-private class ExpressionFactory extends RefType {
- ExpressionFactory() { hasQualifiedName("javax.el", "ExpressionFactory") }
+private class ELProcessor extends JakartaType {
+ ELProcessor() { hasName("ELProcessor") }
}
-private class ValueExpression extends RefType {
- ValueExpression() { hasQualifiedName("javax.el", "ValueExpression") }
+private class ExpressionFactory extends JakartaType {
+ ExpressionFactory() { hasName("ExpressionFactory") }
}
-private class MethodExpression extends RefType {
- MethodExpression() { hasQualifiedName("javax.el", "MethodExpression") }
+private class ValueExpression extends JakartaType {
+ ValueExpression() { hasName("ValueExpression") }
+}
+
+private class MethodExpression extends JakartaType {
+ MethodExpression() { hasName("MethodExpression") }
}
private class LambdaExpression extends RefType {
- LambdaExpression() { hasQualifiedName("javax.el", "LambdaExpression") }
+ LambdaExpression() { hasName("LambdaExpression") }
}
From cd059eb965238127ca5ce196afc9f01322c36ff0 Mon Sep 17 00:00:00 2001
From: Marcono1234
Date: Mon, 22 Mar 2021 00:19:23 +0100
Subject: [PATCH 222/725] Java: Add StringBuildingType
---
.../src/Likely Bugs/Likely Typos/StringBufferCharInit.ql | 7 +------
.../src/Security/CWE/CWE-327/MaybeBrokenCryptoAlgorithm.ql | 3 +--
java/ql/src/semmle/code/java/JDK.qll | 5 +++++
java/ql/src/semmle/code/java/StringFormat.qll | 5 +----
java/ql/src/semmle/code/java/dataflow/FlowSteps.qll | 3 +--
.../code/java/dataflow/internal/TaintTrackingUtil.qll | 5 +----
6 files changed, 10 insertions(+), 18 deletions(-)
diff --git a/java/ql/src/Likely Bugs/Likely Typos/StringBufferCharInit.ql b/java/ql/src/Likely Bugs/Likely Typos/StringBufferCharInit.ql
index c410c7745ee..d565c5bb7ad 100644
--- a/java/ql/src/Likely Bugs/Likely Typos/StringBufferCharInit.ql
+++ b/java/ql/src/Likely Bugs/Likely Typos/StringBufferCharInit.ql
@@ -13,12 +13,7 @@
import java
class NewStringBufferOrBuilder extends ClassInstanceExpr {
- NewStringBufferOrBuilder() {
- exists(Class c | c = this.getConstructedType() |
- c.hasQualifiedName("java.lang", "StringBuilder") or
- c.hasQualifiedName("java.lang", "StringBuffer")
- )
- }
+ NewStringBufferOrBuilder() { getConstructedType() instanceof StringBuildingType }
string getName() { result = this.getConstructedType().getName() }
}
diff --git a/java/ql/src/Security/CWE/CWE-327/MaybeBrokenCryptoAlgorithm.ql b/java/ql/src/Security/CWE/CWE-327/MaybeBrokenCryptoAlgorithm.ql
index efcd01548d8..c84abe1ff75 100644
--- a/java/ql/src/Security/CWE/CWE-327/MaybeBrokenCryptoAlgorithm.ql
+++ b/java/ql/src/Security/CWE/CWE-327/MaybeBrokenCryptoAlgorithm.ql
@@ -44,8 +44,7 @@ predicate objectToString(MethodAccess ma) {
class StringContainer extends RefType {
StringContainer() {
this instanceof TypeString or
- this.hasQualifiedName("java.lang", "StringBuilder") or
- this.hasQualifiedName("java.lang", "StringBuffer") or
+ this instanceof StringBuildingType or
this.hasQualifiedName("java.util", "StringTokenizer") or
this.(Array).getComponentType() instanceof StringContainer
}
diff --git a/java/ql/src/semmle/code/java/JDK.qll b/java/ql/src/semmle/code/java/JDK.qll
index 3b71396c920..cf2ae1ab207 100644
--- a/java/ql/src/semmle/code/java/JDK.qll
+++ b/java/ql/src/semmle/code/java/JDK.qll
@@ -46,6 +46,11 @@ class TypeStringBuilder extends Class {
TypeStringBuilder() { this.hasQualifiedName("java.lang", "StringBuilder") }
}
+/** Class `java.lang.StringBuffer` or `java.lang.StringBuilder`. */
+class StringBuildingType extends Class {
+ StringBuildingType() { this instanceof TypeStringBuffer or this instanceof TypeStringBuilder }
+}
+
/** The class `java.lang.System`. */
class TypeSystem extends Class {
TypeSystem() { this.hasQualifiedName("java.lang", "System") }
diff --git a/java/ql/src/semmle/code/java/StringFormat.qll b/java/ql/src/semmle/code/java/StringFormat.qll
index 000a074f31d..aa77feafeb9 100644
--- a/java/ql/src/semmle/code/java/StringFormat.qll
+++ b/java/ql/src/semmle/code/java/StringFormat.qll
@@ -210,10 +210,7 @@ private predicate printMethod(Method m, int i) {
(t.hasQualifiedName("java.io", "PrintWriter") or t.hasQualifiedName("java.io", "PrintStream")) and
(m.hasName("print") or m.hasName("println"))
or
- (
- t.hasQualifiedName("java.lang", "StringBuilder") or
- t.hasQualifiedName("java.lang", "StringBuffer")
- ) and
+ t instanceof StringBuildingType and
(m.hasName("append") or m.hasName("insert"))
or
t instanceof TypeString and m.hasName("valueOf")
diff --git a/java/ql/src/semmle/code/java/dataflow/FlowSteps.qll b/java/ql/src/semmle/code/java/dataflow/FlowSteps.qll
index 60308f44f65..d30cca6afe6 100644
--- a/java/ql/src/semmle/code/java/dataflow/FlowSteps.qll
+++ b/java/ql/src/semmle/code/java/dataflow/FlowSteps.qll
@@ -152,8 +152,7 @@ private class NumberTaintPreservingCallable extends TaintPreservingCallable {
/** Holds for the types `StringBuilder`, `StringBuffer`, and `StringWriter`. */
private predicate stringBuilderType(RefType t) {
- t.hasQualifiedName("java.lang", "StringBuilder") or
- t.hasQualifiedName("java.lang", "StringBuffer") or
+ t instanceof StringBuildingType or
t.hasQualifiedName("java.io", "StringWriter")
}
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 f8e11e8c770..2e27a390bdc 100644
--- a/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll
+++ b/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll
@@ -552,10 +552,7 @@ module StringBuilderVarModule {
* build up a query using string concatenation.
*/
class StringBuilderVar extends LocalVariableDecl {
- StringBuilderVar() {
- this.getType() instanceof TypeStringBuilder or
- this.getType() instanceof TypeStringBuffer
- }
+ StringBuilderVar() { getType() instanceof StringBuildingType }
/**
* Gets a call that adds something to this string builder, from the argument at the given index.
From 1534b387bb64cdcd6d88cf36f8516d2b830fd4d5 Mon Sep 17 00:00:00 2001
From: Marcono1234
Date: Mon, 22 Mar 2021 00:54:14 +0100
Subject: [PATCH 223/725] Java: Improve documentation regarding minus in front
of numeric literals
---
java/ql/src/semmle/code/java/Expr.qll | 45 ++++++++++++++++++++++++---
1 file changed, 41 insertions(+), 4 deletions(-)
diff --git a/java/ql/src/semmle/code/java/Expr.qll b/java/ql/src/semmle/code/java/Expr.qll
index 43ad6634fc1..2e90075c1eb 100755
--- a/java/ql/src/semmle/code/java/Expr.qll
+++ b/java/ql/src/semmle/code/java/Expr.qll
@@ -638,7 +638,20 @@ class BooleanLiteral extends Literal, @booleanliteral {
override string getAPrimaryQlClass() { result = "BooleanLiteral" }
}
-/** An integer literal. For example, `23`. */
+/**
+ * An integer literal. For example, `23`.
+ *
+ * An integer literal can never be negative except when:
+ * - It is written in binary, octal or hexadecimal notation
+ * - It is written in decimal notation, has the value `2147483648` and is preceded
+ * by a minus; in this case the value of the IntegerLiteral is -2147483648 and
+ * the preceding minus will *not* be modeled as `MinusExpr`.
+ * In all other cases the preceding minus, if any, will be modeled as separate
+ * `MinusExpr`.
+ *
+ * The last exception is necessary because `2147483648` on its own would not be
+ * a valid integer literal (and could also not be parsed as CodeQL `int`).
+ */
class IntegerLiteral extends Literal, @integerliteral {
/** Gets the int representation of this literal. */
int getIntValue() { result = getValue().toInt() }
@@ -646,17 +659,41 @@ class IntegerLiteral extends Literal, @integerliteral {
override string getAPrimaryQlClass() { result = "IntegerLiteral" }
}
-/** A long literal. For example, `23l`. */
+/**
+ * A long literal. For example, `23L`.
+ *
+ * A long literal can never be negative except when:
+ * - It is written in binary, octal or hexadecimal notation
+ * - It is written in decimal notation, has the value `9223372036854775808` and
+ * is preceded by a minus; in this case the value of the LongLiteral is
+ * -9223372036854775808 and the preceding minus will *not* be modeled as
+ * `MinusExpr`.
+ * In all other cases the preceding minus, if any, will be modeled as separate
+ * `MinusExpr`.
+ *
+ * The last exception is necessary because `9223372036854775808` on its own
+ * would not be a valid long literal.
+ */
class LongLiteral extends Literal, @longliteral {
override string getAPrimaryQlClass() { result = "LongLiteral" }
}
-/** A floating point literal. For example, `4.2f`. */
+/**
+ * A float literal. For example, `4.2f`.
+ *
+ * A float literal is never negative; a preceding minus, if any, will always
+ * be modeled as separate `MinusExpr`.
+ */
class FloatingPointLiteral extends Literal, @floatingpointliteral {
override string getAPrimaryQlClass() { result = "FloatingPointLiteral" }
}
-/** A double literal. For example, `4.2`. */
+/**
+ * A double literal. For example, `4.2`.
+ *
+ * A double literal is never negative; a preceding minus, if any, will always
+ * be modeled as separate `MinusExpr`.
+ */
class DoubleLiteral extends Literal, @doubleliteral {
override string getAPrimaryQlClass() { result = "DoubleLiteral" }
}
From 701b935564521d64cc35dc51753493f4dc2782f6 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Mon, 22 Mar 2021 00:57:43 +0100
Subject: [PATCH 224/725] Python: Add example of QuerySet chain (django)
---
python/ql/test/library-tests/frameworks/django/SqlExecution.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/python/ql/test/library-tests/frameworks/django/SqlExecution.py b/python/ql/test/library-tests/frameworks/django/SqlExecution.py
index 67a6ee81a5d..fba2ccdc73e 100644
--- a/python/ql/test/library-tests/frameworks/django/SqlExecution.py
+++ b/python/ql/test/library-tests/frameworks/django/SqlExecution.py
@@ -27,3 +27,6 @@ def test_model():
raw = RawSQL("so raw")
User.objects.annotate(val=raw) # $getSql="so raw"
+
+ # chaining QuerySet calls
+ User.objects.using("db-name").exclude(username="admin").extra("some sql") # $ MISSING: getSql="some sql"
From f800bf243fdbc573fbe97e4ecfe8c5ed997e1bb8 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Mon, 22 Mar 2021 01:39:19 +0100
Subject: [PATCH 225/725] Python: Better text for getSourceType in Django
---
python/ql/src/semmle/python/frameworks/Django.qll | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/python/ql/src/semmle/python/frameworks/Django.qll b/python/ql/src/semmle/python/frameworks/Django.qll
index 9f56bd5a299..10cd7d5c197 100644
--- a/python/ql/src/semmle/python/frameworks/Django.qll
+++ b/python/ql/src/semmle/python/frameworks/Django.qll
@@ -2393,7 +2393,7 @@ private module Django {
}
override string getSourceType() {
- result = "django.http.request.HttpRequest (attribute on self in View class)"
+ result = "django HttpRequest from self.request in View class"
}
}
@@ -2413,7 +2413,7 @@ private module Django {
}
override string getSourceType() {
- result = "django routed param from attribute on self in View class"
+ result = "django routed param from self.args/kwargs in View class"
}
}
From 98558c7c59bdb36a18b20f4756a32611a0389de6 Mon Sep 17 00:00:00 2001
From: Tom Hvitved
Date: Mon, 22 Mar 2021 09:42:27 +0100
Subject: [PATCH 226/725] Update docs/ql-libraries/dataflow/dataflow.md
Co-authored-by: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com>
---
docs/ql-libraries/dataflow/dataflow.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/ql-libraries/dataflow/dataflow.md b/docs/ql-libraries/dataflow/dataflow.md
index c4c09a4c949..3c46436a87a 100644
--- a/docs/ql-libraries/dataflow/dataflow.md
+++ b/docs/ql-libraries/dataflow/dataflow.md
@@ -199,7 +199,7 @@ predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c)
/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver)
-/** Extra data-flow steps needed for lamba flow analysis. */
+/** Extra data-flow steps needed for lambda flow analysis. */
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue)
```
From 3a83ecf067a5391bf4f54252fc0d2a2454b5fefb Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Mon, 22 Mar 2021 01:38:38 +0100
Subject: [PATCH 227/725] Python: Add test for taint in django forms/fields
---
.../django-v2-v3/TestTaint.expected | 11 +++++
.../frameworks/django-v2-v3/taint_forms.py | 46 +++++++++++++++++++
2 files changed, 57 insertions(+)
create mode 100644 python/ql/test/library-tests/frameworks/django-v2-v3/taint_forms.py
diff --git a/python/ql/test/library-tests/frameworks/django-v2-v3/TestTaint.expected b/python/ql/test/library-tests/frameworks/django-v2-v3/TestTaint.expected
index 0602985a392..95d0a58df3b 100644
--- a/python/ql/test/library-tests/frameworks/django-v2-v3/TestTaint.expected
+++ b/python/ql/test/library-tests/frameworks/django-v2-v3/TestTaint.expected
@@ -1,4 +1,15 @@
| response_test.py:61 | ok | get_redirect_url | foo |
+| taint_forms.py:6 | fail | to_python | value |
+| taint_forms.py:9 | fail | validate | value |
+| taint_forms.py:12 | fail | run_validators | value |
+| taint_forms.py:15 | fail | clean | value |
+| taint_forms.py:33 | fail | clean | cleaned_data |
+| taint_forms.py:34 | fail | clean | cleaned_data["key"] |
+| taint_forms.py:35 | fail | clean | cleaned_data.get(..) |
+| taint_forms.py:39 | fail | clean | self.cleaned_data |
+| taint_forms.py:40 | fail | clean | self.cleaned_data["key"] |
+| taint_forms.py:41 | fail | clean | self.cleaned_data.get(..) |
+| taint_forms.py:46 | fail | clean_foo | self.cleaned_data |
| taint_test.py:8 | ok | test_taint | bar |
| taint_test.py:8 | ok | test_taint | foo |
| taint_test.py:9 | ok | test_taint | baz |
diff --git a/python/ql/test/library-tests/frameworks/django-v2-v3/taint_forms.py b/python/ql/test/library-tests/frameworks/django-v2-v3/taint_forms.py
new file mode 100644
index 00000000000..a702f89ee26
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/django-v2-v3/taint_forms.py
@@ -0,0 +1,46 @@
+import django.forms
+
+
+class MyField(django.forms.Field):
+ def to_python(self, value):
+ ensure_tainted(value)
+
+ def validate(self, value):
+ ensure_tainted(value)
+
+ def run_validators(self, value):
+ ensure_tainted(value)
+
+ def clean(self, value):
+ ensure_tainted(value)
+
+ # # Base definition of `clean` looks like the following, so there is actually
+ # # _data flow_ from the methods, but we will ignore for simplicity.
+ # value = self.to_python(value)
+ # self.validate(value)
+ # self.run_validators(value)
+ # return value
+
+
+class MyForm(django.forms.Form):
+
+ foo = MyField()
+
+ def clean(self):
+ cleaned_data = super().clean()
+
+ ensure_tainted(
+ cleaned_data,
+ cleaned_data["key"],
+ cleaned_data.get("key"),
+ )
+
+ ensure_tainted(
+ self.cleaned_data,
+ self.cleaned_data["key"],
+ self.cleaned_data.get("key"),
+ )
+
+ def clean_foo(self):
+ # This method is supposed to clean a the `foo` field in context of this form.
+ ensure_tainted(self.cleaned_data)
From b422a972bffebd75ff23c8544cb1687638c4ad23 Mon Sep 17 00:00:00 2001
From: Shati Patel <42641846+shati-patel@users.noreply.github.com>
Date: Mon, 22 Mar 2021 10:00:18 +0000
Subject: [PATCH 228/725] Update conf.py
---
docs/codeql/support/conf.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/docs/codeql/support/conf.py b/docs/codeql/support/conf.py
index 45a8456ce9a..3d2f5d6cf81 100644
--- a/docs/codeql/support/conf.py
+++ b/docs/codeql/support/conf.py
@@ -16,7 +16,7 @@
##############################################################################
#
-# Modified 22052019.
+# Modified 22032021.
# The configuration values below are specific to the supported languages and frameworks project
# To amend html_theme_options, update version/release number, or add more sphinx extensions,
@@ -41,9 +41,9 @@ project = u'Supported languages and frameworks for LGTM Enterprise'
# The version info for this project, if different from version and release in main conf.py file.
# The short X.Y version.
-version = u'1.26'
+version = u'1.27'
# The full version, including alpha/beta/rc tags.
-release = u'1.26'
+release = u'1.27'
# -- Project-specifc options for HTML output ----------------------------------------------
@@ -95,4 +95,4 @@ html_favicon = '../images/site/favicon.ico'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
-exclude_patterns = ['read-me-project.rst', 'reusables/*']
\ No newline at end of file
+exclude_patterns = ['read-me-project.rst', 'reusables/*']
From 668841cefa83f984a53a31d6146cee238ea30ebf Mon Sep 17 00:00:00 2001
From: Cornelius Riemenschneider
Date: Mon, 22 Mar 2021 11:13:49 +0100
Subject: [PATCH 229/725] C++: Rename diagnostic queries.
---
cpp/ql/src/Diagnostics/ExtractionErrors.ql | 16 ++++++++++++++++
...ailedExtractions.qll => ExtractionErrors.qll} | 4 ++--
cpp/ql/src/Diagnostics/FailedExtractions.ql | 16 ----------------
...ocations.ql => FailedExtractorInvocations.ql} | 0
...ractions.ql => SuccessfullyExtractedFiles.ql} | 4 ++--
5 files changed, 20 insertions(+), 20 deletions(-)
create mode 100644 cpp/ql/src/Diagnostics/ExtractionErrors.ql
rename cpp/ql/src/Diagnostics/{FailedExtractions.qll => ExtractionErrors.qll} (96%)
delete mode 100644 cpp/ql/src/Diagnostics/FailedExtractions.ql
rename cpp/ql/src/Diagnostics/{FailedInvocations.ql => FailedExtractorInvocations.ql} (100%)
rename cpp/ql/src/Diagnostics/{SuccessfulExtractions.ql => SuccessfullyExtractedFiles.ql} (78%)
diff --git a/cpp/ql/src/Diagnostics/ExtractionErrors.ql b/cpp/ql/src/Diagnostics/ExtractionErrors.ql
new file mode 100644
index 00000000000..a445c3bc0c4
--- /dev/null
+++ b/cpp/ql/src/Diagnostics/ExtractionErrors.ql
@@ -0,0 +1,16 @@
+/**
+ * @name Extraction errors
+ * @description List all extraction errors for files in the source code directory.
+ * @kind diagnostic
+ * @id cpp/diagnostics/extraction-errors
+ */
+
+import cpp
+import ExtractionErrors
+
+from ExtractionError error
+where
+ error instanceof ExtractionUnknownError or
+ exists(error.getFile().getRelativePath())
+select error, "Extraction failed in " + error.getFile() + " with error " + error.getErrorMessage(),
+ error.getSeverity()
diff --git a/cpp/ql/src/Diagnostics/FailedExtractions.qll b/cpp/ql/src/Diagnostics/ExtractionErrors.qll
similarity index 96%
rename from cpp/ql/src/Diagnostics/FailedExtractions.qll
rename to cpp/ql/src/Diagnostics/ExtractionErrors.qll
index 62a89f7cdc1..4cf6f8145f8 100644
--- a/cpp/ql/src/Diagnostics/FailedExtractions.qll
+++ b/cpp/ql/src/Diagnostics/ExtractionErrors.qll
@@ -12,12 +12,12 @@ import cpp
* Thus, if the extractor emitted at least one diagnostic of severity discretionary
* error (or higher), it *also* emits a simple "There was an error during this compilation"
* error diagnostic, without location information.
- * In the common case, this means that a file with one (or more) errors also gets
+ * In the common case, this means that a compilation during which one or more errors happened also gets
* the catch-all diagnostic.
* This diagnostic has the empty string as file path.
* We filter out these useless diagnostics if there is at least one error-level diagnostic
* for the affected compilation in the database.
- * Otherwise, we show it to, to indicate that something went wrong, and we
+ * Otherwise, we show it to indicate that something went wrong and that we
* don't know what exactly happened.
*/
diff --git a/cpp/ql/src/Diagnostics/FailedExtractions.ql b/cpp/ql/src/Diagnostics/FailedExtractions.ql
deleted file mode 100644
index 52b07ca2559..00000000000
--- a/cpp/ql/src/Diagnostics/FailedExtractions.ql
+++ /dev/null
@@ -1,16 +0,0 @@
-/**
- * @name Failed extractions
- * @description List all files in the source code directory with extraction errors.
- * @kind diagnostic
- * @id cpp/diagnostics/failed-extractions
- */
-
-import cpp
-import FailedExtractions
-
-from ExtractionError error
-where
- error instanceof ExtractionUnknownError or
- exists(error.getFile().getRelativePath())
-select error, "Extracting failed in " + error.getFile() + " with error " + error.getErrorMessage(),
- error.getSeverity()
diff --git a/cpp/ql/src/Diagnostics/FailedInvocations.ql b/cpp/ql/src/Diagnostics/FailedExtractorInvocations.ql
similarity index 100%
rename from cpp/ql/src/Diagnostics/FailedInvocations.ql
rename to cpp/ql/src/Diagnostics/FailedExtractorInvocations.ql
diff --git a/cpp/ql/src/Diagnostics/SuccessfulExtractions.ql b/cpp/ql/src/Diagnostics/SuccessfullyExtractedFiles.ql
similarity index 78%
rename from cpp/ql/src/Diagnostics/SuccessfulExtractions.ql
rename to cpp/ql/src/Diagnostics/SuccessfullyExtractedFiles.ql
index 380f1ce8bbe..a920af8e767 100644
--- a/cpp/ql/src/Diagnostics/SuccessfulExtractions.ql
+++ b/cpp/ql/src/Diagnostics/SuccessfullyExtractedFiles.ql
@@ -1,12 +1,12 @@
/**
* @name Successfully extracted files
- * @description Lists all files in the source code directory that were extracted without encountering an error.
+ * @description Lists all files in the source code directory that were extracted without encountering an error in the file.
* @kind diagnostic
* @id cpp/diagnostics/successfully-extracted-files
*/
import cpp
-import FailedExtractions
+import ExtractionErrors
from File f
where
From 7ec86b5e7f1314f0649888651daff8df75fd6948 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Mon, 22 Mar 2021 11:35:29 +0100
Subject: [PATCH 230/725] C++: AdjustedConfiguration should not extend the same
dataflow configuration as FromGlobalVarTaintTrackingCfg as this causes
multiple configurations to be in scope for dataflow.
---
.../cpp/ir/dataflow/DefaultTaintTracking.qll | 21 ++--
.../code/cpp/ir/dataflow/TaintTracking3.qll | 15 +++
.../tainttracking3/TaintTrackingImpl.qll | 115 ++++++++++++++++++
.../tainttracking3/TaintTrackingParameter.qll | 5 +
4 files changed, 146 insertions(+), 10 deletions(-)
create mode 100644 cpp/ql/src/semmle/code/cpp/ir/dataflow/TaintTracking3.qll
create mode 100644 cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingImpl.qll
create mode 100644 cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingParameter.qll
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
index 7b97bc738f4..3092031cbc7 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
@@ -2,7 +2,7 @@ import cpp
import semmle.code.cpp.security.Security
private import semmle.code.cpp.ir.dataflow.DataFlow
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
-private import semmle.code.cpp.ir.dataflow.DataFlow2
+private import semmle.code.cpp.ir.dataflow.DataFlow3
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.ir.dataflow.internal.DataFlowDispatch as Dispatch
private import semmle.code.cpp.controlflow.IRGuards
@@ -10,6 +10,7 @@ private import semmle.code.cpp.models.interfaces.Taint
private import semmle.code.cpp.models.interfaces.DataFlow
private import semmle.code.cpp.ir.dataflow.TaintTracking
private import semmle.code.cpp.ir.dataflow.TaintTracking2
+private import semmle.code.cpp.ir.dataflow.TaintTracking3
private import semmle.code.cpp.ir.dataflow.internal.ModelUtil
/**
@@ -380,7 +381,7 @@ module TaintedWithPath {
string toString() { result = "TaintTrackingConfiguration" }
}
- private class AdjustedConfiguration extends TaintTracking2::Configuration {
+ private class AdjustedConfiguration extends TaintTracking3::Configuration {
AdjustedConfiguration() { this = "AdjustedConfiguration" }
override predicate isSource(DataFlow::Node source) {
@@ -438,11 +439,11 @@ module TaintedWithPath {
*/
private newtype TPathNode =
- TWrapPathNode(DataFlow2::PathNode n) or
+ TWrapPathNode(DataFlow3::PathNode n) or
// There's a single newtype constructor for both sources and sinks since
// that makes it easiest to deal with the case where source = sink.
TEndpointPathNode(Element e) {
- exists(AdjustedConfiguration cfg, DataFlow2::Node sourceNode, DataFlow2::Node sinkNode |
+ exists(AdjustedConfiguration cfg, DataFlow3::Node sourceNode, DataFlow3::Node sinkNode |
cfg.hasFlow(sourceNode, sinkNode)
|
sourceNode = getNodeForExpr(e) and
@@ -473,7 +474,7 @@ module TaintedWithPath {
}
private class WrapPathNode extends PathNode, TWrapPathNode {
- DataFlow2::PathNode inner() { this = TWrapPathNode(result) }
+ DataFlow3::PathNode inner() { this = TWrapPathNode(result) }
override string toString() { result = this.inner().toString() }
@@ -510,25 +511,25 @@ module TaintedWithPath {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
query predicate edges(PathNode a, PathNode b) {
- DataFlow2::PathGraph::edges(a.(WrapPathNode).inner(), b.(WrapPathNode).inner())
+ DataFlow3::PathGraph::edges(a.(WrapPathNode).inner(), b.(WrapPathNode).inner())
or
// To avoid showing trivial-looking steps, we _replace_ the last node instead
// of adding an edge out of it.
exists(WrapPathNode sinkNode |
- DataFlow2::PathGraph::edges(a.(WrapPathNode).inner(), sinkNode.inner()) and
+ DataFlow3::PathGraph::edges(a.(WrapPathNode).inner(), sinkNode.inner()) and
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
)
or
// Same for the first node
exists(WrapPathNode sourceNode |
- DataFlow2::PathGraph::edges(sourceNode.inner(), b.(WrapPathNode).inner()) and
+ DataFlow3::PathGraph::edges(sourceNode.inner(), b.(WrapPathNode).inner()) and
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner())
)
or
// Finally, handle the case where the path goes directly from a source to a
// sink, meaning that they both need to be translated.
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
- DataFlow2::PathGraph::edges(sourceNode.inner(), sinkNode.inner()) and
+ DataFlow3::PathGraph::edges(sourceNode.inner(), sinkNode.inner()) and
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner()) and
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
)
@@ -550,7 +551,7 @@ module TaintedWithPath {
* the computation.
*/
predicate taintedWithPath(Expr source, Element tainted, PathNode sourceNode, PathNode sinkNode) {
- exists(AdjustedConfiguration cfg, DataFlow2::Node flowSource, DataFlow2::Node flowSink |
+ exists(AdjustedConfiguration cfg, DataFlow3::Node flowSource, DataFlow3::Node flowSink |
source = sourceNode.(InitialPathNode).inner() and
flowSource = getNodeForExpr(source) and
cfg.hasFlow(flowSource, flowSink) and
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/TaintTracking3.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/TaintTracking3.qll
new file mode 100644
index 00000000000..98e1caebf38
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/TaintTracking3.qll
@@ -0,0 +1,15 @@
+/**
+ * Provides a `TaintTracking3` module, which is a copy of the `TaintTracking`
+ * module. Use this class when data-flow configurations or taint-tracking
+ * configurations must depend on each other. Two classes extending
+ * `DataFlow::Configuration` should never depend on each other, but one of them
+ * should instead depend on a `DataFlow2::Configuration`, a
+ * `DataFlow3::Configuration`, or a `DataFlow4::Configuration`. The
+ * `TaintTracking::Configuration` class extends `DataFlow::Configuration`, and
+ * `TaintTracking2::Configuration` extends `DataFlow2::Configuration`.
+ *
+ * See `semmle.code.cpp.ir.dataflow.TaintTracking` for the full documentation.
+ */
+module TaintTracking3 {
+ import semmle.code.cpp.ir.dataflow.internal.tainttracking3.TaintTrackingImpl
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingImpl.qll
new file mode 100644
index 00000000000..b509fad9cd2
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingImpl.qll
@@ -0,0 +1,115 @@
+/**
+ * Provides an implementation of global (interprocedural) taint tracking.
+ * This file re-exports the local (intraprocedural) taint-tracking analysis
+ * from `TaintTrackingParameter::Public` and adds a global analysis, mainly
+ * exposed through the `Configuration` class. For some languages, this file
+ * exists in several identical copies, allowing queries to use multiple
+ * `Configuration` classes that depend on each other without introducing
+ * mutual recursion among those configurations.
+ */
+
+import TaintTrackingParameter::Public
+private import TaintTrackingParameter::Private
+
+/**
+ * A configuration of interprocedural taint tracking analysis. This defines
+ * sources, sinks, and any other configurable aspect of the analysis. Each
+ * use of the taint tracking library must define its own unique extension of
+ * this abstract class.
+ *
+ * A taint-tracking configuration is a special data flow configuration
+ * (`DataFlow::Configuration`) that allows for flow through nodes that do not
+ * necessarily preserve values but are still relevant from a taint tracking
+ * perspective. (For example, string concatenation, where one of the operands
+ * is tainted.)
+ *
+ * To create a configuration, extend this class with a subclass whose
+ * characteristic predicate is a unique singleton string. For example, write
+ *
+ * ```ql
+ * class MyAnalysisConfiguration extends TaintTracking::Configuration {
+ * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
+ * // Override `isSource` and `isSink`.
+ * // Optionally override `isSanitizer`.
+ * // Optionally override `isSanitizerIn`.
+ * // Optionally override `isSanitizerOut`.
+ * // Optionally override `isSanitizerGuard`.
+ * // Optionally override `isAdditionalTaintStep`.
+ * }
+ * ```
+ *
+ * Then, to query whether there is flow between some `source` and `sink`,
+ * write
+ *
+ * ```ql
+ * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
+ * ```
+ *
+ * Multiple configurations can coexist, but it is unsupported to depend on
+ * another `TaintTracking::Configuration` or a `DataFlow::Configuration` in the
+ * overridden predicates that define sources, sinks, or additional steps.
+ * Instead, the dependency should go to a `TaintTracking2::Configuration` or a
+ * `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
+ */
+abstract class Configuration extends DataFlow::Configuration {
+ bindingset[this]
+ Configuration() { any() }
+
+ /**
+ * Holds if `source` is a relevant taint source.
+ *
+ * The smaller this predicate is, the faster `hasFlow()` will converge.
+ */
+ // overridden to provide taint-tracking specific qldoc
+ abstract override predicate isSource(DataFlow::Node source);
+
+ /**
+ * Holds if `sink` is a relevant taint sink.
+ *
+ * The smaller this predicate is, the faster `hasFlow()` will converge.
+ */
+ // overridden to provide taint-tracking specific qldoc
+ abstract override predicate isSink(DataFlow::Node sink);
+
+ /** Holds if the node `node` is a taint sanitizer. */
+ predicate isSanitizer(DataFlow::Node node) { none() }
+
+ final override predicate isBarrier(DataFlow::Node node) {
+ isSanitizer(node) or
+ defaultTaintSanitizer(node)
+ }
+
+ /** Holds if taint propagation into `node` is prohibited. */
+ predicate isSanitizerIn(DataFlow::Node node) { none() }
+
+ final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
+
+ /** Holds if taint propagation out of `node` is prohibited. */
+ predicate isSanitizerOut(DataFlow::Node node) { none() }
+
+ final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
+
+ /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
+ predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
+
+ final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
+
+ /**
+ * Holds if the additional taint propagation step from `node1` to `node2`
+ * must be taken into account in the analysis.
+ */
+ predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
+
+ final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
+ isAdditionalTaintStep(node1, node2) or
+ defaultAdditionalTaintStep(node1, node2)
+ }
+
+ /**
+ * Holds if taint may flow from `source` to `sink` for this configuration.
+ */
+ // overridden to provide taint-tracking specific qldoc
+ override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
+ super.hasFlow(source, sink)
+ }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingParameter.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingParameter.qll
new file mode 100644
index 00000000000..2a3b69f55cd
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingParameter.qll
@@ -0,0 +1,5 @@
+import semmle.code.cpp.ir.dataflow.internal.TaintTrackingUtil as Public
+
+module Private {
+ import semmle.code.cpp.ir.dataflow.DataFlow3::DataFlow3 as DataFlow
+}
From d09458a4868a86af333394bc45ee7d43d9c3c8fc Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen