From 5a4a63f8a93019119f81cbed3b466b16de338802 Mon Sep 17 00:00:00 2001
From: Nicky Mouha
Date: Mon, 30 Jan 2023 18:52:35 -0500
Subject: [PATCH 001/870] Create IfStatementAdditionOverflow.ql
---
.../CWE-190/IfStatementAdditionOverflow.ql | 28 +++++++++++++++++++
1 file changed, 28 insertions(+)
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-190/IfStatementAdditionOverflow.ql
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-190/IfStatementAdditionOverflow.ql b/cpp/ql/src/experimental/Security/CWE/CWE-190/IfStatementAdditionOverflow.ql
new file mode 100644
index 00000000000..4763504ca0d
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-190/IfStatementAdditionOverflow.ql
@@ -0,0 +1,28 @@
+/**
+ * @name Integer addition may overflow inside if statement
+ * @description Detects "if (a+b>c) a=c-b", which is incorrect if a+b overflows.
+ * Should be replaced by "if (a>c-b) a=c-b", which correctly
+ * implements a = min(a,c-b)". This integer overflow is the root
+ * cause of the buffer overflow in the SHA-3 reference implementation
+ * (CVE-2022-37454).
+ * @kind problem
+ * @problem.severity warning
+ * @id cpp/if-statement-addition-overflow
+ * @tags: experimental
+ * correctness
+ * security
+ * external/cwe/cwe-190
+ */
+
+import cpp
+
+from IfStmt ifstmt, GTExpr gtexpr, ExprStmt exprstmt, AssignExpr assignexpr, AddExpr addexpr, SubExpr subexpr
+where ifstmt.getCondition() = gtexpr and
+ gtexpr.getLeftOperand() = addexpr and
+ ifstmt.getThen() = exprstmt and
+ exprstmt.getExpr() = assignexpr and
+ assignexpr.getRValue() = subexpr and
+ addexpr.getLeftOperand().toString() = assignexpr.getLValue().toString() and
+ addexpr.getRightOperand().toString() = subexpr.getRightOperand().toString() and
+ gtexpr.getRightOperand().toString() = subexpr.getLeftOperand().toString()
+select ifstmt, "Integer addition may overflow inside if statement."
From f577a04eabd6f8a3f899647298534f445a4f2061 Mon Sep 17 00:00:00 2001
From: Nicky Mouha
Date: Sat, 18 Feb 2023 21:34:03 -0500
Subject: [PATCH 002/870] Update IfStatementAdditionOverflow.ql
---
.../CWE-190/IfStatementAdditionOverflow.ql | 40 +++++++++++--------
1 file changed, 24 insertions(+), 16 deletions(-)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-190/IfStatementAdditionOverflow.ql b/cpp/ql/src/experimental/Security/CWE/CWE-190/IfStatementAdditionOverflow.ql
index 4763504ca0d..cbfc2fc7f90 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-190/IfStatementAdditionOverflow.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-190/IfStatementAdditionOverflow.ql
@@ -1,10 +1,13 @@
/**
- * @name Integer addition may overflow inside if statement
- * @description Detects "if (a+b>c) a=c-b", which is incorrect if a+b overflows.
- * Should be replaced by "if (a>c-b) a=c-b", which correctly
- * implements a = min(a,c-b)". This integer overflow is the root
- * cause of the buffer overflow in the SHA-3 reference implementation
- * (CVE-2022-37454).
+ * @name Integer addition may overflow inside condition
+ * @description Detects "c-b" when the condition "a+b>c" has been imposed,
+ * which is not the same as the condition "a>b-c" if "a+b"
+ * overflows. Rewriting improves readability and optimizability
+ * (CSE elimination). Also detects "b+a>c" (swapped terms in
+ * addition), "c=", "<",
+ * "<=" instead of ">" (all operators). This integer overflow
+ * is the root cause of the buffer overflow in the SHA-3
+ * reference implementation (CVE-2022-37454).
* @kind problem
* @problem.severity warning
* @id cpp/if-statement-addition-overflow
@@ -15,14 +18,19 @@
*/
import cpp
+import semmle.code.cpp.controlflow.Guards
+import semmle.code.cpp.valuenumbering.GlobalValueNumbering
+import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
+import semmle.code.cpp.commons.Exclusions
-from IfStmt ifstmt, GTExpr gtexpr, ExprStmt exprstmt, AssignExpr assignexpr, AddExpr addexpr, SubExpr subexpr
-where ifstmt.getCondition() = gtexpr and
- gtexpr.getLeftOperand() = addexpr and
- ifstmt.getThen() = exprstmt and
- exprstmt.getExpr() = assignexpr and
- assignexpr.getRValue() = subexpr and
- addexpr.getLeftOperand().toString() = assignexpr.getLValue().toString() and
- addexpr.getRightOperand().toString() = subexpr.getRightOperand().toString() and
- gtexpr.getRightOperand().toString() = subexpr.getLeftOperand().toString()
-select ifstmt, "Integer addition may overflow inside if statement."
+from GuardCondition guard, BasicBlock block, RelationalOperation relop, AddExpr addexpr, SubExpr subexpr
+where guard.controls(block, _) and
+ guard.getAChild*() = relop and
+ pragma[only_bind_into](block) = subexpr.getBasicBlock() and
+ relop.getAnOperand() = addexpr and
+ addexpr.getUnspecifiedType() instanceof IntegralType and
+ not isFromMacroDefinition(relop) and
+ exprMightOverflowPositively(addexpr) and
+ globalValueNumber(addexpr.getAnOperand()) = globalValueNumber(subexpr.getRightOperand()) and
+ globalValueNumber(relop.getAnOperand()) = globalValueNumber(subexpr.getLeftOperand())
+select guard, "Integer addition may overflow inside condition."
From ed75172bdd555587fd9b74ebc812c1c347c51ec1 Mon Sep 17 00:00:00 2001
From: Nicky Mouha
Date: Tue, 21 Feb 2023 18:11:22 -0500
Subject: [PATCH 003/870] Update IfStatementAdditionOverflow.ql
---
.../CWE-190/IfStatementAdditionOverflow.ql | 42 +++++++++++--------
1 file changed, 25 insertions(+), 17 deletions(-)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-190/IfStatementAdditionOverflow.ql b/cpp/ql/src/experimental/Security/CWE/CWE-190/IfStatementAdditionOverflow.ql
index cbfc2fc7f90..5bfa265fca5 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-190/IfStatementAdditionOverflow.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-190/IfStatementAdditionOverflow.ql
@@ -1,13 +1,13 @@
/**
- * @name Integer addition may overflow inside condition
- * @description Detects "c-b" when the condition "a+b>c" has been imposed,
- * which is not the same as the condition "a>b-c" if "a+b"
- * overflows. Rewriting improves readability and optimizability
- * (CSE elimination). Also detects "b+a>c" (swapped terms in
- * addition), "c=", "<",
- * "<=" instead of ">" (all operators). This integer overflow
- * is the root cause of the buffer overflow in the SHA-3
- * reference implementation (CVE-2022-37454).
+ * @name Integer addition may overflow inside if statement
+ * @description Detects "if (a+b>c) a=c-b", which incorrectly implements
+ * a = min(a,c-b) if a+b overflows. Should be replaced by
+ * "if (a>c-b) a=c-b". Also detects "if (b+a>c) a=c-b"
+ * (swapped terms in addition), if (a+b>c) { a=c-b }"
+ * (assignment inside block), "c=", "<", "<=" instead of ">" (all operators). This
+ * integer overflow is the root cause of the buffer overflow
+ * in the SHA-3 reference implementation (CVE-2022-37454).
* @kind problem
* @problem.severity warning
* @id cpp/if-statement-addition-overflow
@@ -18,19 +18,27 @@
*/
import cpp
-import semmle.code.cpp.controlflow.Guards
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
+import semmle.code.cpp.valuenumbering.HashCons
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
import semmle.code.cpp.commons.Exclusions
-from GuardCondition guard, BasicBlock block, RelationalOperation relop, AddExpr addexpr, SubExpr subexpr
-where guard.controls(block, _) and
- guard.getAChild*() = relop and
- pragma[only_bind_into](block) = subexpr.getBasicBlock() and
+from IfStmt ifstmt, RelationalOperation relop, ExprStmt exprstmt, BlockStmt blockstmt, AssignExpr assignexpr, AddExpr addexpr, SubExpr subexpr
+where ifstmt.getCondition() = relop and
relop.getAnOperand() = addexpr and
addexpr.getUnspecifiedType() instanceof IntegralType and
+ subexpr.getUnspecifiedType() instanceof IntegralType and
not isFromMacroDefinition(relop) and
exprMightOverflowPositively(addexpr) and
- globalValueNumber(addexpr.getAnOperand()) = globalValueNumber(subexpr.getRightOperand()) and
- globalValueNumber(relop.getAnOperand()) = globalValueNumber(subexpr.getLeftOperand())
-select guard, "Integer addition may overflow inside condition."
+ (ifstmt.getThen() = exprstmt or
+ (ifstmt.getThen() = blockstmt and
+ blockstmt.getAStmt() = exprstmt)) and
+ exprstmt.getExpr() = assignexpr and
+ assignexpr.getRValue() = subexpr and
+ ((hashCons(addexpr.getLeftOperand()) = hashCons(assignexpr.getLValue()) and
+ globalValueNumber(addexpr.getRightOperand()) = globalValueNumber(subexpr.getRightOperand())) or
+ (hashCons(addexpr.getRightOperand()) = hashCons(assignexpr.getLValue()) and
+ globalValueNumber(addexpr.getLeftOperand()) = globalValueNumber(subexpr.getRightOperand()))) and
+ globalValueNumber(relop.getAnOperand()) = globalValueNumber(subexpr.getLeftOperand()) and
+ not globalValueNumber(addexpr.getAnOperand()) = globalValueNumber(relop.getAnOperand())
+select ifstmt, "Integer addition may overflow inside if statement."
From 08f04d53864d461155e536c5e86326864607eb43 Mon Sep 17 00:00:00 2001
From: Nicky Mouha
Date: Thu, 23 Feb 2023 17:50:02 -0500
Subject: [PATCH 004/870] Update IfStatementAdditionOverflow.ql
---
.../CWE/CWE-190/IfStatementAdditionOverflow.ql | 15 ++++-----------
1 file changed, 4 insertions(+), 11 deletions(-)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-190/IfStatementAdditionOverflow.ql b/cpp/ql/src/experimental/Security/CWE/CWE-190/IfStatementAdditionOverflow.ql
index 5bfa265fca5..20e77bb5ec0 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-190/IfStatementAdditionOverflow.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-190/IfStatementAdditionOverflow.ql
@@ -1,13 +1,8 @@
/**
* @name Integer addition may overflow inside if statement
- * @description Detects "if (a+b>c) a=c-b", which incorrectly implements
- * a = min(a,c-b) if a+b overflows. Should be replaced by
- * "if (a>c-b) a=c-b". Also detects "if (b+a>c) a=c-b"
- * (swapped terms in addition), if (a+b>c) { a=c-b }"
- * (assignment inside block), "c=", "<", "<=" instead of ">" (all operators). This
- * integer overflow is the root cause of the buffer overflow
- * in the SHA-3 reference implementation (CVE-2022-37454).
+ * @description "if (a+b>c) a=c-b" was detected where "a+b" may potentially
+ * produce an integer overflow (or wraparound). The code can be
+ * rewritten to "if (a>c-b) a=c-b" which avoids the overflow.
* @kind problem
* @problem.severity warning
* @id cpp/if-statement-addition-overflow
@@ -27,7 +22,6 @@ from IfStmt ifstmt, RelationalOperation relop, ExprStmt exprstmt, BlockStmt bloc
where ifstmt.getCondition() = relop and
relop.getAnOperand() = addexpr and
addexpr.getUnspecifiedType() instanceof IntegralType and
- subexpr.getUnspecifiedType() instanceof IntegralType and
not isFromMacroDefinition(relop) and
exprMightOverflowPositively(addexpr) and
(ifstmt.getThen() = exprstmt or
@@ -39,6 +33,5 @@ where ifstmt.getCondition() = relop and
globalValueNumber(addexpr.getRightOperand()) = globalValueNumber(subexpr.getRightOperand())) or
(hashCons(addexpr.getRightOperand()) = hashCons(assignexpr.getLValue()) and
globalValueNumber(addexpr.getLeftOperand()) = globalValueNumber(subexpr.getRightOperand()))) and
- globalValueNumber(relop.getAnOperand()) = globalValueNumber(subexpr.getLeftOperand()) and
- not globalValueNumber(addexpr.getAnOperand()) = globalValueNumber(relop.getAnOperand())
+ globalValueNumber(relop.getAnOperand()) = globalValueNumber(subexpr.getLeftOperand())
select ifstmt, "Integer addition may overflow inside if statement."
From dc09c9218ebb4bcb3f84797239a4028023804b1e Mon Sep 17 00:00:00 2001
From: Nicky Mouha
Date: Sun, 12 Mar 2023 01:05:18 -0500
Subject: [PATCH 005/870] Update IfStatementAdditionOverflow.ql
---
.../CWE/CWE-190/IfStatementAdditionOverflow.ql | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-190/IfStatementAdditionOverflow.ql b/cpp/ql/src/experimental/Security/CWE/CWE-190/IfStatementAdditionOverflow.ql
index 20e77bb5ec0..a45ba737bab 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-190/IfStatementAdditionOverflow.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-190/IfStatementAdditionOverflow.ql
@@ -1,8 +1,13 @@
/**
* @name Integer addition may overflow inside if statement
- * @description "if (a+b>c) a=c-b" was detected where "a+b" may potentially
- * produce an integer overflow (or wraparound). The code can be
- * rewritten to "if (a>c-b) a=c-b" which avoids the overflow.
+ * @description Detects "if (a+b>c) a=c-b", which incorrectly implements
+ * a = min(a,c-b) if a+b overflows. Should be replaced by
+ * "if (a>c-b) a=c-b". Also detects "if (b+a>c) a=c-b"
+ * (swapped terms in addition), if (a+b>c) { a=c-b }"
+ * (assignment inside block), "c=", "<", "<=" instead of ">" (all operators). This
+ * integer overflow is the root cause of the buffer overflow
+ * in the SHA-3 reference implementation (CVE-2022-37454).
* @kind problem
* @problem.severity warning
* @id cpp/if-statement-addition-overflow
@@ -34,4 +39,4 @@ where ifstmt.getCondition() = relop and
(hashCons(addexpr.getRightOperand()) = hashCons(assignexpr.getLValue()) and
globalValueNumber(addexpr.getLeftOperand()) = globalValueNumber(subexpr.getRightOperand()))) and
globalValueNumber(relop.getAnOperand()) = globalValueNumber(subexpr.getLeftOperand())
-select ifstmt, "Integer addition may overflow inside if statement."
+select ifstmt, "\"if (a+b>c) a=c-b\" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as \"if (a>c-b) a=c-b\" which avoids the overflow.", addexpr, "addition"
From 91a9a7eb32d3e4a0ea73eeb0d092861a4161f0c7 Mon Sep 17 00:00:00 2001
From: Nicky Mouha
Date: Sun, 12 Mar 2023 01:13:32 -0500
Subject: [PATCH 006/870] Create test.cpp
---
.../IfStatementAdditionOverflow/test.cpp | 59 +++++++++++++++++++
1 file changed, 59 insertions(+)
create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/IfStatementAdditionOverflow/test.cpp
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/IfStatementAdditionOverflow/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/IfStatementAdditionOverflow/test.cpp
new file mode 100644
index 00000000000..ca67c5578c7
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/IfStatementAdditionOverflow/test.cpp
@@ -0,0 +1,59 @@
+
+int getAnInt();
+double getADouble();
+unsigned short getAnUnsignedShort();
+
+void test()
+{
+ int a = getAnInt();
+ int b = getAnInt();
+ int c = getAnInt();
+ int x = getAnInt();
+ int y = getAnInt();
+ int d = getADouble();
+ int a1 = getAnUnsignedShort();
+ int b1 = getAnUnsignedShort();
+ int c1 = getAnUnsignedShort();
+
+ if (a+b>c) a = c-b; // BAD
+ if (a+b>c) { a = c-b; } // BAD
+ if (b+a>c) a = c-b; // BAD
+ if (b+a>c) { a = c-b; } // BAD
+ if (c>a+b) a = c-b; // BAD
+ if (c>a+b) { a = c-b; } // BAD
+ if (c>b+a) a = c-b; // BAD
+ if (c>b+a) { a = c-b; } // BAD
+
+ if (a+b>=c) a = c-b; // BAD
+ if (a+b>=c) { a = c-b; } // BAD
+ if (b+a>=c) a = c-b; // BAD
+ if (b+a>=c) { a = c-b; } // BAD
+ if (c>=a+b) a = c-b; // BAD
+ if (c>=a+b) { a = c-b; } // BAD
+ if (c>=b+a) a = c-b; // BAD
+ if (c>=b+a) { a = c-b; } // BAD
+
+ if (a+bd) a = d-b; // GOOD
+ if (a+(-x)>c) a = c-(-y); // GOOD
+ if (a+b>c) { b++; a = c-b; } // GOOD
+ if (a+d>c) a = c-d; // GOOD
+ if (a1+b1>c1) a1 = c1-b1; // GOOD
+}
From 2477c3a1c26d86c3b6c30c45fe8735f264bb5426 Mon Sep 17 00:00:00 2001
From: Nicky Mouha
Date: Sun, 12 Mar 2023 03:25:52 -0400
Subject: [PATCH 007/870] Update test.cpp
---
.../CWE/CWE-190/IfStatementAdditionOverflow/test.cpp | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/IfStatementAdditionOverflow/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/IfStatementAdditionOverflow/test.cpp
index ca67c5578c7..47c077a3c9b 100644
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/IfStatementAdditionOverflow/test.cpp
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/IfStatementAdditionOverflow/test.cpp
@@ -8,12 +8,12 @@ void test()
int a = getAnInt();
int b = getAnInt();
int c = getAnInt();
- int x = getAnInt();
+ int x = getAnInt();
int y = getAnInt();
- int d = getADouble();
- int a1 = getAnUnsignedShort();
- int b1 = getAnUnsignedShort();
- int c1 = getAnUnsignedShort();
+ double d = getADouble();
+ unsigned short a1 = getAnUnsignedShort();
+ unsigned short b1 = getAnUnsignedShort();
+ unsigned short c1 = getAnUnsignedShort();
if (a+b>c) a = c-b; // BAD
if (a+b>c) { a = c-b; } // BAD
@@ -51,7 +51,7 @@ void test()
if (c<=b+a) a = c-b; // BAD
if (c<=b+a) { a = c-b; } // BAD
- if (a+b>d) a = d-b; // GOOD
+ if (a+b>d) a = d-b; // BAD
if (a+(-x)>c) a = c-(-y); // GOOD
if (a+b>c) { b++; a = c-b; } // GOOD
if (a+d>c) a = c-d; // GOOD
From 59c1ae7734bb18431006c40841904cf132c95815 Mon Sep 17 00:00:00 2001
From: Nicky Mouha
Date: Sun, 12 Mar 2023 03:27:10 -0400
Subject: [PATCH 008/870] Update test.cpp
---
.../Security/CWE/CWE-190/IfStatementAdditionOverflow/test.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/IfStatementAdditionOverflow/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/IfStatementAdditionOverflow/test.cpp
index 47c077a3c9b..5879a7ca2a3 100644
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/IfStatementAdditionOverflow/test.cpp
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/IfStatementAdditionOverflow/test.cpp
@@ -8,7 +8,7 @@ void test()
int a = getAnInt();
int b = getAnInt();
int c = getAnInt();
- int x = getAnInt();
+ int x = getAnInt();
int y = getAnInt();
double d = getADouble();
unsigned short a1 = getAnUnsignedShort();
From 66710ad5a03fa6b1d60f08f6a90c297aa7835299 Mon Sep 17 00:00:00 2001
From: Nicky Mouha
Date: Sun, 12 Mar 2023 03:30:26 -0400
Subject: [PATCH 009/870] Create IfStatementAdditionOverflow.qlref
---
.../IfStatementAdditionOverflow.qlref | 1 +
1 file changed, 1 insertion(+)
create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/IfStatementAdditionOverflow/IfStatementAdditionOverflow.qlref
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/IfStatementAdditionOverflow/IfStatementAdditionOverflow.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/IfStatementAdditionOverflow/IfStatementAdditionOverflow.qlref
new file mode 100644
index 00000000000..0873051581d
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/IfStatementAdditionOverflow/IfStatementAdditionOverflow.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-190/IfStatementAdditionOverflow.ql
From a2b5fbf24c72f9b810f2ede79ba997c4b047aa17 Mon Sep 17 00:00:00 2001
From: Nicky Mouha
Date: Sun, 12 Mar 2023 03:31:48 -0400
Subject: [PATCH 010/870] Create IfStatementAdditionOverflow.expected
---
.../IfStatementAdditionOverflow.expected | 33 +++++++++++++++++++
1 file changed, 33 insertions(+)
create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/IfStatementAdditionOverflow/IfStatementAdditionOverflow.expected
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/IfStatementAdditionOverflow/IfStatementAdditionOverflow.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/IfStatementAdditionOverflow/IfStatementAdditionOverflow.expected
new file mode 100644
index 00000000000..12dbde04790
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/IfStatementAdditionOverflow/IfStatementAdditionOverflow.expected
@@ -0,0 +1,33 @@
+| test.cpp:18:2:18:20 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:18:6:18:8 | ... + ... | addition |
+| test.cpp:19:2:19:24 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:19:6:19:8 | ... + ... | addition |
+| test.cpp:20:2:20:20 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:20:6:20:8 | ... + ... | addition |
+| test.cpp:21:2:21:24 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:21:6:21:8 | ... + ... | addition |
+| test.cpp:22:2:22:20 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:22:8:22:10 | ... + ... | addition |
+| test.cpp:23:2:23:24 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:23:8:23:10 | ... + ... | addition |
+| test.cpp:24:2:24:20 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:24:8:24:10 | ... + ... | addition |
+| test.cpp:25:2:25:24 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:25:8:25:10 | ... + ... | addition |
+| test.cpp:27:2:27:21 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:27:6:27:8 | ... + ... | addition |
+| test.cpp:28:2:28:25 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:28:6:28:8 | ... + ... | addition |
+| test.cpp:29:2:29:21 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:29:6:29:8 | ... + ... | addition |
+| test.cpp:30:2:30:25 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:30:6:30:8 | ... + ... | addition |
+| test.cpp:31:2:31:21 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:31:9:31:11 | ... + ... | addition |
+| test.cpp:32:2:32:25 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:32:9:32:11 | ... + ... | addition |
+| test.cpp:33:2:33:21 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:33:9:33:11 | ... + ... | addition |
+| test.cpp:34:2:34:25 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:34:9:34:11 | ... + ... | addition |
+| test.cpp:36:2:36:20 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:36:6:36:8 | ... + ... | addition |
+| test.cpp:37:2:37:24 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:37:6:37:8 | ... + ... | addition |
+| test.cpp:38:2:38:20 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:38:6:38:8 | ... + ... | addition |
+| test.cpp:39:2:39:24 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:39:6:39:8 | ... + ... | addition |
+| test.cpp:40:2:40:20 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:40:8:40:10 | ... + ... | addition |
+| test.cpp:41:2:41:24 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:41:8:41:10 | ... + ... | addition |
+| test.cpp:42:2:42:20 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:42:8:42:10 | ... + ... | addition |
+| test.cpp:43:2:43:24 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:43:8:43:10 | ... + ... | addition |
+| test.cpp:45:2:45:21 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:45:6:45:8 | ... + ... | addition |
+| test.cpp:46:2:46:25 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:46:6:46:8 | ... + ... | addition |
+| test.cpp:47:2:47:21 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:47:6:47:8 | ... + ... | addition |
+| test.cpp:48:2:48:25 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:48:6:48:8 | ... + ... | addition |
+| test.cpp:49:2:49:21 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:49:9:49:11 | ... + ... | addition |
+| test.cpp:50:2:50:25 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:50:9:50:11 | ... + ... | addition |
+| test.cpp:51:2:51:21 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:51:9:51:11 | ... + ... | addition |
+| test.cpp:52:2:52:25 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:52:9:52:11 | ... + ... | addition |
+| test.cpp:54:2:54:20 | if (...) ... | "if (a+b>c) a=c-b" was detected where the $@ may potentially overflow/wraparound. The code can be rewritten as "if (a>c-b) a=c-b" which avoids the overflow. | test.cpp:54:6:54:8 | ... + ... | addition |
From 2de0e2209edde14d3fb930e44eb1d879e499fc80 Mon Sep 17 00:00:00 2001
From: Nicky Mouha
Date: Thu, 16 Mar 2023 02:34:40 -0400
Subject: [PATCH 011/870] Update test.cpp
---
.../Security/CWE/CWE-190/IfStatementAdditionOverflow/test.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/IfStatementAdditionOverflow/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/IfStatementAdditionOverflow/test.cpp
index 5879a7ca2a3..f1aac83122b 100644
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/IfStatementAdditionOverflow/test.cpp
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/IfStatementAdditionOverflow/test.cpp
@@ -52,6 +52,7 @@ void test()
if (c<=b+a) { a = c-b; } // BAD
if (a+b>d) a = d-b; // BAD
+ if (a+(double)b>c) a = c-b; // GOOD
if (a+(-x)>c) a = c-(-y); // GOOD
if (a+b>c) { b++; a = c-b; } // GOOD
if (a+d>c) a = c-d; // GOOD
From 99d634c8a4cd62d6265bd106d8bd93e40c4247af Mon Sep 17 00:00:00 2001
From: jarlob
Date: Mon, 3 Apr 2023 15:02:02 +0200
Subject: [PATCH 012/870] Add more sources, more unit tests, fixes to the
GitHub Actions injection query
---
.../ql/lib/semmle/javascript/Actions.qll | 4 +-
.../Security/CWE-094/ExpressionInjection.ql | 61 ++++++++++++++-----
.../.github/workflows/comment_issue.yml | 5 +-
.../.github/workflows/discussion.yml | 8 +++
.../.github/workflows/discussion_comment.yml | 9 +++
.../.github/workflows/gollum.yml | 11 ++++
.../.github/workflows/issues.yml | 8 +++
.../.github/workflows/pull_request_review.yml | 14 +++++
.../workflows/pull_request_review_comment.yml | 14 +++++
.../.github/workflows/pull_request_target.yml | 16 +++++
.../.github/workflows/push.yml | 16 +++++
.../.github/workflows/workflow_run.yml | 16 +++++
.../ExpressionInjection.expected | 57 ++++++++++++++++-
13 files changed, 220 insertions(+), 19 deletions(-)
create mode 100644 javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/discussion.yml
create mode 100644 javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/discussion_comment.yml
create mode 100644 javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/gollum.yml
create mode 100644 javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/issues.yml
create mode 100644 javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/pull_request_review.yml
create mode 100644 javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/pull_request_review_comment.yml
create mode 100644 javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/pull_request_target.yml
create mode 100644 javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/push.yml
create mode 100644 javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/workflow_run.yml
diff --git a/javascript/ql/lib/semmle/javascript/Actions.qll b/javascript/ql/lib/semmle/javascript/Actions.qll
index 7fd3952ac85..b1ab674924d 100644
--- a/javascript/ql/lib/semmle/javascript/Actions.qll
+++ b/javascript/ql/lib/semmle/javascript/Actions.qll
@@ -267,8 +267,8 @@ module Actions {
// not just the last (greedy match) or first (reluctant match).
result =
this.getValue()
- .regexpFind("\\$\\{\\{\\s*[A-Za-z0-9_\\.\\-]+\\s*\\}\\}", _, _)
- .regexpCapture("\\$\\{\\{\\s*([A-Za-z0-9_\\.\\-]+)\\s*\\}\\}", 1)
+ .regexpFind("\\$\\{\\{\\s*[A-Za-z0-9_\\[\\]\\*\\(\\)\\.\\-]+\\s*\\}\\}", _, _)
+ .regexpCapture("\\$\\{\\{\\s*([A-Za-z0-9_\\[\\]\\*\\((\\)\\.\\-]+)\\s*\\}\\}", 1)
}
}
}
diff --git a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
index 03c129711ad..c8c42b4122e 100644
--- a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
+++ b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
@@ -30,7 +30,10 @@ private predicate isExternalUserControlledPullRequest(string context) {
"\\bgithub\\s*\\.\\s*event\\s*\\.\\s*pull_request\\s*\\.\\s*body\\b",
"\\bgithub\\s*\\.\\s*event\\s*\\.\\s*pull_request\\s*\\.\\s*head\\s*\\.\\s*label\\b",
"\\bgithub\\s*\\.\\s*event\\s*\\.\\s*pull_request\\s*\\.\\s*head\\s*\\.\\s*repo\\s*\\.\\s*default_branch\\b",
+ "\\bgithub\\s*\\.\\s*event\\s*\\.\\s*pull_request\\s*\\.\\s*head\\s*\\.\\s*repo\\s*\\.\\s*description\\b",
+ "\\bgithub\\s*\\.\\s*event\\s*\\.\\s*pull_request\\s*\\.\\s*head\\s*\\.\\s*repo\\s*\\.\\s*homepage\\b",
"\\bgithub\\s*\\.\\s*event\\s*\\.\\s*pull_request\\s*\\.\\s*head\\s*\\.\\s*ref\\b",
+ "\\bgithub\\s*\\.\\s*head_ref\\b"
]
|
context.regexpMatch(reg)
@@ -39,8 +42,7 @@ private predicate isExternalUserControlledPullRequest(string context) {
bindingset[context]
private predicate isExternalUserControlledReview(string context) {
- context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*review\\s*\\.\\s*body\\b") or
- context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*review_comment\\s*\\.\\s*body\\b")
+ context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*review\\s*\\.\\s*body\\b")
}
bindingset[context]
@@ -50,8 +52,8 @@ private predicate isExternalUserControlledComment(string context) {
bindingset[context]
private predicate isExternalUserControlledGollum(string context) {
- context
- .regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*pages(?:\\[[0-9]\\]|\\s*\\.\\s*\\*)+\\s*\\.\\s*page_name\\b")
+ context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*pages\\[[0-9]+\\]\\s*\\.\\s*page_name\\b") or
+ context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*pages\\[[0-9]+\\]\\s*\\.\\s*title\\b")
}
bindingset[context]
@@ -59,13 +61,16 @@ private predicate isExternalUserControlledCommit(string context) {
exists(string reg |
reg =
[
- "\\bgithub\\s*\\.\\s*event\\s*\\.\\s*commits(?:\\[[0-9]\\]|\\s*\\.\\s*\\*)+\\s*\\.\\s*message\\b",
+ "\\bgithub\\s*\\.\\s*event\\s*\\.\\s*commits\\[[0-9]+\\]\\s*\\.\\s*message\\b",
"\\bgithub\\s*\\.\\s*event\\s*\\.\\s*head_commit\\s*\\.\\s*message\\b",
"\\bgithub\\s*\\.\\s*event\\s*\\.\\s*head_commit\\s*\\.\\s*author\\s*\\.\\s*email\\b",
"\\bgithub\\s*\\.\\s*event\\s*\\.\\s*head_commit\\s*\\.\\s*author\\s*\\.\\s*name\\b",
- "\\bgithub\\s*\\.\\s*event\\s*\\.\\s*commits(?:\\[[0-9]\\]|\\s*\\.\\s*\\*)+\\s*\\.\\s*author\\s*\\.\\s*email\\b",
- "\\bgithub\\s*\\.\\s*event\\s*\\.\\s*commits(?:\\[[0-9]\\]|\\s*\\.\\s*\\*)+\\s*\\.\\s*author\\s*\\.\\s*name\\b",
- "\\bgithub\\s*\\.\\s*head_ref\\b"
+ "\\bgithub\\s*\\.\\s*event\\s*\\.\\s*head_commit\\s*\\.\\s*committer\\s*\\.\\s*email\\b",
+ "\\bgithub\\s*\\.\\s*event\\s*\\.\\s*head_commit\\s*\\.\\s*committer\\s*\\.\\s*name\\b",
+ "\\bgithub\\s*\\.\\s*event\\s*\\.\\s*commits\\[[0-9]+\\]\\s*\\.\\s*author\\s*\\.\\s*email\\b",
+ "\\bgithub\\s*\\.\\s*event\\s*\\.\\s*commits\\[[0-9]+\\]\\s*\\.\\s*author\\s*\\.\\s*name\\b",
+ "\\bgithub\\s*\\.\\s*event\\s*\\.\\s*commits\\[[0-9]+\\]\\s*\\.\\s*committer\\s*\\.\\s*email\\b",
+ "\\bgithub\\s*\\.\\s*event\\s*\\.\\s*commits\\[[0-9]+\\]\\s*\\.\\s*committer\\s*\\.\\s*name\\b",
]
|
context.regexpMatch(reg)
@@ -78,6 +83,25 @@ private predicate isExternalUserControlledDiscussion(string context) {
context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*discussion\\s*\\.\\s*body\\b")
}
+bindingset[context]
+private predicate isExternalUserControlledWorkflowRun(string context) {
+ exists(string reg |
+ reg =
+ [
+ "\\bgithub\\s*\\.\\s*event\\s*\\.\\s*workflow_run\\s*\\.\\s*head_branch\\b",
+ "\\bgithub\\s*\\.\\s*event\\s*\\.\\s*workflow_run\\s*\\.\\s*display_title\\b",
+ "\\bgithub\\s*\\.\\s*event\\s*\\.\\s*workflow_run\\s*\\.\\s*head_repository\\b\\s*\\.\\s*description\\b",
+ "\\bgithub\\s*\\.\\s*event\\s*\\.\\s*workflow_run\\s*\\.\\s*head_commit\\b\\s*\\.\\s*message\\b",
+ "\\bgithub\\s*\\.\\s*event\\s*\\.\\s*workflow_run\\s*\\.\\s*head_commit\\b\\s*\\.\\s*author\\b\\s*\\.\\s*email\\b",
+ "\\bgithub\\s*\\.\\s*event\\s*\\.\\s*workflow_run\\s*\\.\\s*head_commit\\b\\s*\\.\\s*author\\b\\s*\\.\\s*name\\b",
+ "\\bgithub\\s*\\.\\s*event\\s*\\.\\s*workflow_run\\s*\\.\\s*head_commit\\b\\s*\\.\\s*committer\\b\\s*\\.\\s*email\\b",
+ "\\bgithub\\s*\\.\\s*event\\s*\\.\\s*workflow_run\\s*\\.\\s*head_commit\\b\\s*\\.\\s*committer\\b\\s*\\.\\s*name\\b",
+ ]
+ |
+ context.regexpMatch(reg)
+ )
+}
+
from Actions::Run run, string context, Actions::On on
where
run.getASimpleReferenceExpression() = context and
@@ -89,20 +113,29 @@ where
exists(on.getNode("pull_request_target")) and
isExternalUserControlledPullRequest(context)
or
- (exists(on.getNode("pull_request_review_comment")) or exists(on.getNode("pull_request_review"))) and
- isExternalUserControlledReview(context)
+ exists(on.getNode("pull_request_review")) and
+ (isExternalUserControlledReview(context) or isExternalUserControlledPullRequest(context))
or
- (exists(on.getNode("issue_comment")) or exists(on.getNode("pull_request_target"))) and
- isExternalUserControlledComment(context)
+ exists(on.getNode("pull_request_review_comment")) and
+ (isExternalUserControlledComment(context) or isExternalUserControlledPullRequest(context))
+ or
+ exists(on.getNode("issue_comment")) and
+ (isExternalUserControlledComment(context) or isExternalUserControlledIssue(context))
or
exists(on.getNode("gollum")) and
isExternalUserControlledGollum(context)
or
- exists(on.getNode("pull_request_target")) and
+ exists(on.getNode("push")) and
isExternalUserControlledCommit(context)
or
- (exists(on.getNode("discussion")) or exists(on.getNode("discussion_comment"))) and
+ exists(on.getNode("discussion")) and
isExternalUserControlledDiscussion(context)
+ or
+ exists(on.getNode("discussion_comment")) and
+ (isExternalUserControlledDiscussion(context) or isExternalUserControlledComment(context))
+ or
+ exists(on.getNode("workflow_run")) and
+ isExternalUserControlledWorkflowRun(context)
)
select run,
"Potential injection from the " + context +
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/comment_issue.yml b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/comment_issue.yml
index c19524f1191..fec20d7272f 100644
--- a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/comment_issue.yml
+++ b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/comment_issue.yml
@@ -10,5 +10,6 @@ jobs:
echo-chamber2:
runs-on: ubuntu-latest
steps:
- - run: |
- echo '${{ github.event.comment.body }}'
\ No newline at end of file
+ - run: echo '${{ github.event.comment.body }}'
+ - run: echo '${{ github.event.issue.body }}'
+ - run: echo '${{ github.event.issue.title }}'
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/discussion.yml b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/discussion.yml
new file mode 100644
index 00000000000..fdb140ec380
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/discussion.yml
@@ -0,0 +1,8 @@
+on: discussion
+
+jobs:
+ echo-chamber:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo '${{ github.event.discussion.title }}'
+ - run: echo '${{ github.event.discussion.body }}'
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/discussion_comment.yml b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/discussion_comment.yml
new file mode 100644
index 00000000000..649d3a6e131
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/discussion_comment.yml
@@ -0,0 +1,9 @@
+on: discussion_comment
+
+jobs:
+ echo-chamber:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo '${{ github.event.discussion.title }}'
+ - run: echo '${{ github.event.discussion.body }}'
+ - run: echo '${{ github.event.comment.body }}'
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/gollum.yml b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/gollum.yml
new file mode 100644
index 00000000000..a952c8c1ab8
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/gollum.yml
@@ -0,0 +1,11 @@
+on: gollum
+
+jobs:
+ echo-chamber:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo '${{ github.event.pages[1].title }}'
+ - run: echo '${{ github.event.pages[11].title }}'
+ - run: echo '${{ github.event.pages[0].page_name }}'
+ - run: echo '${{ github.event.pages[2222].page_name }}'
+ - run: echo '${{ toJSON(github.event.pages.*.title) }}' # safe
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/issues.yml b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/issues.yml
new file mode 100644
index 00000000000..2eae85278dd
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/issues.yml
@@ -0,0 +1,8 @@
+on: issues
+
+jobs:
+ echo-chamber:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo '${{ github.event.issue.title }}'
+ - run: echo '${{ github.event.issue.body }}'
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/pull_request_review.yml b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/pull_request_review.yml
new file mode 100644
index 00000000000..d4ce7885669
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/pull_request_review.yml
@@ -0,0 +1,14 @@
+on: pull_request_review
+
+jobs:
+ echo-chamber:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo '${{ github.event.pull_request.title }}'
+ - run: echo '${{ github.event.pull_request.body }}'
+ - run: echo '${{ github.event.pull_request.head.label }}'
+ - run: echo '${{ github.event.pull_request.head.repo.default_branch }}'
+ - run: echo '${{ github.event.pull_request.head.repo.description }}'
+ - run: echo '${{ github.event.pull_request.head.repo.homepage }}'
+ - run: echo '${{ github.event.pull_request.head.ref }}'
+ - run: echo '${{ github.event.review.body }}'
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/pull_request_review_comment.yml b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/pull_request_review_comment.yml
new file mode 100644
index 00000000000..5d288caad85
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/pull_request_review_comment.yml
@@ -0,0 +1,14 @@
+on: pull_request_review_comment
+
+jobs:
+ echo-chamber:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo '${{ github.event.pull_request.title }}'
+ - run: echo '${{ github.event.pull_request.body }}'
+ - run: echo '${{ github.event.pull_request.head.label }}'
+ - run: echo '${{ github.event.pull_request.head.repo.default_branch }}'
+ - run: echo '${{ github.event.pull_request.head.repo.description }}'
+ - run: echo '${{ github.event.pull_request.head.repo.homepage }}'
+ - run: echo '${{ github.event.pull_request.head.ref }}'
+ - run: echo '${{ github.event.comment.body }}'
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/pull_request_target.yml b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/pull_request_target.yml
new file mode 100644
index 00000000000..215b3252885
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/pull_request_target.yml
@@ -0,0 +1,16 @@
+on: pull_request_target
+
+jobs:
+ echo-chamber:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo '${{ github.event.issue.title }}' # not defined
+ - run: echo '${{ github.event.issue.body }}' # not defined
+ - run: echo '${{ github.event.pull_request.title }}'
+ - run: echo '${{ github.event.pull_request.body }}'
+ - run: echo '${{ github.event.pull_request.head.label }}'
+ - run: echo '${{ github.event.pull_request.head.repo.default_branch }}'
+ - run: echo '${{ github.event.pull_request.head.repo.description }}'
+ - run: echo '${{ github.event.pull_request.head.repo.homepage }}'
+ - run: echo '${{ github.event.pull_request.head.ref }}'
+ - run: echo '${{ github.head_ref }}'
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/push.yml b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/push.yml
new file mode 100644
index 00000000000..2006a7999da
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/push.yml
@@ -0,0 +1,16 @@
+on: push
+
+jobs:
+ echo-chamber:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo '${{ github.event.commits[11].message }}'
+ - run: echo '${{ github.event.commits[11].author.email }}'
+ - run: echo '${{ github.event.commits[11].author.name }}'
+ - run: echo '${{ github.event.head_commit.message }}'
+ - run: echo '${{ github.event.head_commit.author.email }}'
+ - run: echo '${{ github.event.head_commit.author.name }}'
+ - run: echo '${{ github.event.head_commit.committer.email }}'
+ - run: echo '${{ github.event.head_commit.committer.name }}'
+ - run: echo '${{ github.event.commits[11].committer.email }}'
+ - run: echo '${{ github.event.commits[11].committer.name }}'
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/workflow_run.yml b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/workflow_run.yml
new file mode 100644
index 00000000000..60e7645f60f
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/workflow_run.yml
@@ -0,0 +1,16 @@
+on:
+ workflow_run:
+ workflows: [test]
+
+jobs:
+ echo-chamber:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo '${{ github.event.workflow_run.display_title }}'
+ - run: echo '${{ github.event.workflow_run.head_commit.message }}'
+ - run: echo '${{ github.event.workflow_run.head_commit.author.email }}'
+ - run: echo '${{ github.event.workflow_run.head_commit.author.name }}'
+ - run: echo '${{ github.event.workflow_run.head_commit.committer.email }}'
+ - run: echo '${{ github.event.workflow_run.head_commit.committer.name }}'
+ - run: echo '${{ github.event.workflow_run.head_branch }}'
+ - run: echo '${{ github.event.workflow_run.head_repository.description }}'
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected
index 64451c37691..b89948010b8 100644
--- a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected
@@ -1,3 +1,58 @@
| .github/workflows/comment_issue.yml:7:12:8:48 | \| | Potential injection from the github.event.comment.body context, which may be controlled by an external user. |
-| .github/workflows/comment_issue.yml:13:12:14:47 | \| | Potential injection from the github.event.comment.body context, which may be controlled by an external user. |
+| .github/workflows/comment_issue.yml:13:12:13:50 | echo '$ ... ody }}' | Potential injection from the github.event.comment.body context, which may be controlled by an external user. |
+| .github/workflows/comment_issue.yml:14:12:14:48 | echo '$ ... ody }}' | Potential injection from the github.event.issue.body context, which may be controlled by an external user. |
+| .github/workflows/comment_issue.yml:15:12:15:49 | echo '$ ... tle }}' | Potential injection from the github.event.issue.title context, which may be controlled by an external user. |
| .github/workflows/comment_issue_newline.yml:9:14:10:50 | \| | Potential injection from the github.event.comment.body context, which may be controlled by an external user. |
+| .github/workflows/discussion.yml:7:12:7:54 | echo '$ ... tle }}' | Potential injection from the github.event.discussion.title context, which may be controlled by an external user. |
+| .github/workflows/discussion.yml:8:12:8:53 | echo '$ ... ody }}' | Potential injection from the github.event.discussion.body context, which may be controlled by an external user. |
+| .github/workflows/discussion_comment.yml:7:12:7:54 | echo '$ ... tle }}' | Potential injection from the github.event.discussion.title context, which may be controlled by an external user. |
+| .github/workflows/discussion_comment.yml:8:12:8:53 | echo '$ ... ody }}' | Potential injection from the github.event.discussion.body context, which may be controlled by an external user. |
+| .github/workflows/discussion_comment.yml:9:12:9:50 | echo '$ ... ody }}' | Potential injection from the github.event.comment.body context, which may be controlled by an external user. |
+| .github/workflows/gollum.yml:7:12:7:52 | echo '$ ... tle }}' | Potential injection from the github.event.pages[1].title context, which may be controlled by an external user. |
+| .github/workflows/gollum.yml:8:12:8:53 | echo '$ ... tle }}' | Potential injection from the github.event.pages[11].title context, which may be controlled by an external user. |
+| .github/workflows/gollum.yml:9:12:9:56 | echo '$ ... ame }}' | Potential injection from the github.event.pages[0].page_name context, which may be controlled by an external user. |
+| .github/workflows/gollum.yml:10:12:10:59 | echo '$ ... ame }}' | Potential injection from the github.event.pages[2222].page_name context, which may be controlled by an external user. |
+| .github/workflows/issues.yml:7:12:7:49 | echo '$ ... tle }}' | Potential injection from the github.event.issue.title context, which may be controlled by an external user. |
+| .github/workflows/issues.yml:8:12:8:48 | echo '$ ... ody }}' | Potential injection from the github.event.issue.body context, which may be controlled by an external user. |
+| .github/workflows/pull_request_review.yml:7:12:7:56 | echo '$ ... tle }}' | Potential injection from the github.event.pull_request.title context, which may be controlled by an external user. |
+| .github/workflows/pull_request_review.yml:8:12:8:55 | echo '$ ... ody }}' | Potential injection from the github.event.pull_request.body context, which may be controlled by an external user. |
+| .github/workflows/pull_request_review.yml:9:12:9:61 | echo '$ ... bel }}' | Potential injection from the github.event.pull_request.head.label context, which may be controlled by an external user. |
+| .github/workflows/pull_request_review.yml:10:12:10:75 | echo '$ ... nch }}' | Potential injection from the github.event.pull_request.head.repo.default_branch context, which may be controlled by an external user. |
+| .github/workflows/pull_request_review.yml:11:12:11:72 | echo '$ ... ion }}' | Potential injection from the github.event.pull_request.head.repo.description context, which may be controlled by an external user. |
+| .github/workflows/pull_request_review.yml:12:12:12:69 | echo '$ ... age }}' | Potential injection from the github.event.pull_request.head.repo.homepage context, which may be controlled by an external user. |
+| .github/workflows/pull_request_review.yml:13:12:13:59 | echo '$ ... ref }}' | Potential injection from the github.event.pull_request.head.ref context, which may be controlled by an external user. |
+| .github/workflows/pull_request_review.yml:14:12:14:49 | echo '$ ... ody }}' | Potential injection from the github.event.review.body context, which may be controlled by an external user. |
+| .github/workflows/pull_request_review_comment.yml:7:12:7:56 | echo '$ ... tle }}' | Potential injection from the github.event.pull_request.title context, which may be controlled by an external user. |
+| .github/workflows/pull_request_review_comment.yml:8:12:8:55 | echo '$ ... ody }}' | Potential injection from the github.event.pull_request.body context, which may be controlled by an external user. |
+| .github/workflows/pull_request_review_comment.yml:9:12:9:61 | echo '$ ... bel }}' | Potential injection from the github.event.pull_request.head.label context, which may be controlled by an external user. |
+| .github/workflows/pull_request_review_comment.yml:10:12:10:75 | echo '$ ... nch }}' | Potential injection from the github.event.pull_request.head.repo.default_branch context, which may be controlled by an external user. |
+| .github/workflows/pull_request_review_comment.yml:11:12:11:72 | echo '$ ... ion }}' | Potential injection from the github.event.pull_request.head.repo.description context, which may be controlled by an external user. |
+| .github/workflows/pull_request_review_comment.yml:12:12:12:69 | echo '$ ... age }}' | Potential injection from the github.event.pull_request.head.repo.homepage context, which may be controlled by an external user. |
+| .github/workflows/pull_request_review_comment.yml:13:12:13:59 | echo '$ ... ref }}' | Potential injection from the github.event.pull_request.head.ref context, which may be controlled by an external user. |
+| .github/workflows/pull_request_review_comment.yml:14:12:14:50 | echo '$ ... ody }}' | Potential injection from the github.event.comment.body context, which may be controlled by an external user. |
+| .github/workflows/pull_request_target.yml:9:12:9:56 | echo '$ ... tle }}' | Potential injection from the github.event.pull_request.title context, which may be controlled by an external user. |
+| .github/workflows/pull_request_target.yml:10:12:10:55 | echo '$ ... ody }}' | Potential injection from the github.event.pull_request.body context, which may be controlled by an external user. |
+| .github/workflows/pull_request_target.yml:11:12:11:61 | echo '$ ... bel }}' | Potential injection from the github.event.pull_request.head.label context, which may be controlled by an external user. |
+| .github/workflows/pull_request_target.yml:12:12:12:75 | echo '$ ... nch }}' | Potential injection from the github.event.pull_request.head.repo.default_branch context, which may be controlled by an external user. |
+| .github/workflows/pull_request_target.yml:13:12:13:72 | echo '$ ... ion }}' | Potential injection from the github.event.pull_request.head.repo.description context, which may be controlled by an external user. |
+| .github/workflows/pull_request_target.yml:14:12:14:69 | echo '$ ... age }}' | Potential injection from the github.event.pull_request.head.repo.homepage context, which may be controlled by an external user. |
+| .github/workflows/pull_request_target.yml:15:12:15:59 | echo '$ ... ref }}' | Potential injection from the github.event.pull_request.head.ref context, which may be controlled by an external user. |
+| .github/workflows/pull_request_target.yml:16:12:16:40 | echo '$ ... ref }}' | Potential injection from the github.head_ref context, which may be controlled by an external user. |
+| .github/workflows/push.yml:7:12:7:57 | echo '$ ... age }}' | Potential injection from the github.event.commits[11].message context, which may be controlled by an external user. |
+| .github/workflows/push.yml:8:12:8:62 | echo '$ ... ail }}' | Potential injection from the github.event.commits[11].author.email context, which may be controlled by an external user. |
+| .github/workflows/push.yml:9:12:9:61 | echo '$ ... ame }}' | Potential injection from the github.event.commits[11].author.name context, which may be controlled by an external user. |
+| .github/workflows/push.yml:10:12:10:57 | echo '$ ... age }}' | Potential injection from the github.event.head_commit.message context, which may be controlled by an external user. |
+| .github/workflows/push.yml:11:12:11:62 | echo '$ ... ail }}' | Potential injection from the github.event.head_commit.author.email context, which may be controlled by an external user. |
+| .github/workflows/push.yml:12:12:12:61 | echo '$ ... ame }}' | Potential injection from the github.event.head_commit.author.name context, which may be controlled by an external user. |
+| .github/workflows/push.yml:13:12:13:65 | echo '$ ... ail }}' | Potential injection from the github.event.head_commit.committer.email context, which may be controlled by an external user. |
+| .github/workflows/push.yml:14:12:14:64 | echo '$ ... ame }}' | Potential injection from the github.event.head_commit.committer.name context, which may be controlled by an external user. |
+| .github/workflows/push.yml:15:12:15:65 | echo '$ ... ail }}' | Potential injection from the github.event.commits[11].committer.email context, which may be controlled by an external user. |
+| .github/workflows/push.yml:16:12:16:64 | echo '$ ... ame }}' | Potential injection from the github.event.commits[11].committer.name context, which may be controlled by an external user. |
+| .github/workflows/workflow_run.yml:9:12:9:64 | echo '$ ... tle }}' | Potential injection from the github.event.workflow_run.display_title context, which may be controlled by an external user. |
+| .github/workflows/workflow_run.yml:10:12:10:70 | echo '$ ... age }}' | Potential injection from the github.event.workflow_run.head_commit.message context, which may be controlled by an external user. |
+| .github/workflows/workflow_run.yml:11:12:11:75 | echo '$ ... ail }}' | Potential injection from the github.event.workflow_run.head_commit.author.email context, which may be controlled by an external user. |
+| .github/workflows/workflow_run.yml:12:12:12:74 | echo '$ ... ame }}' | Potential injection from the github.event.workflow_run.head_commit.author.name context, which may be controlled by an external user. |
+| .github/workflows/workflow_run.yml:13:12:13:78 | echo '$ ... ail }}' | Potential injection from the github.event.workflow_run.head_commit.committer.email context, which may be controlled by an external user. |
+| .github/workflows/workflow_run.yml:14:12:14:77 | echo '$ ... ame }}' | Potential injection from the github.event.workflow_run.head_commit.committer.name context, which may be controlled by an external user. |
+| .github/workflows/workflow_run.yml:15:12:15:62 | echo '$ ... nch }}' | Potential injection from the github.event.workflow_run.head_branch context, which may be controlled by an external user. |
+| .github/workflows/workflow_run.yml:16:12:16:78 | echo '$ ... ion }}' | Potential injection from the github.event.workflow_run.head_repository.description context, which may be controlled by an external user. |
From c6eaf194a515de729ee03f74b5a4ba0f02ad11f8 Mon Sep 17 00:00:00 2001
From: jarlob
Date: Mon, 3 Apr 2023 15:09:40 +0200
Subject: [PATCH 013/870] Remove empty.js as it is not needed anymore
---
javascript/ql/test/experimental/Security/CWE-094/empty.js | 1 -
.../query-tests/Security/CWE-094/ExpressionInjection/empty.js | 1 -
2 files changed, 2 deletions(-)
delete mode 100644 javascript/ql/test/experimental/Security/CWE-094/empty.js
delete mode 100644 javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/empty.js
diff --git a/javascript/ql/test/experimental/Security/CWE-094/empty.js b/javascript/ql/test/experimental/Security/CWE-094/empty.js
deleted file mode 100644
index a243684db7f..00000000000
--- a/javascript/ql/test/experimental/Security/CWE-094/empty.js
+++ /dev/null
@@ -1 +0,0 @@
-console.log('test')
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/empty.js b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/empty.js
deleted file mode 100644
index a243684db7f..00000000000
--- a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/empty.js
+++ /dev/null
@@ -1 +0,0 @@
-console.log('test')
\ No newline at end of file
From ba5747dff382fb2c5f86e190746be92ec9f7c97d Mon Sep 17 00:00:00 2001
From: jarlob
Date: Mon, 3 Apr 2023 15:10:27 +0200
Subject: [PATCH 014/870] fix formatting
---
javascript/ql/src/Security/CWE-094/ExpressionInjection.ql | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
index c8c42b4122e..ce815c8e11d 100644
--- a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
+++ b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
@@ -52,7 +52,8 @@ private predicate isExternalUserControlledComment(string context) {
bindingset[context]
private predicate isExternalUserControlledGollum(string context) {
- context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*pages\\[[0-9]+\\]\\s*\\.\\s*page_name\\b") or
+ context
+ .regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*pages\\[[0-9]+\\]\\s*\\.\\s*page_name\\b") or
context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*pages\\[[0-9]+\\]\\s*\\.\\s*title\\b")
}
From e941218e307417476a22e2ef1396e0f333bd1ae9 Mon Sep 17 00:00:00 2001
From: jarlob
Date: Mon, 3 Apr 2023 15:15:00 +0200
Subject: [PATCH 015/870] change notes added
---
javascript/ql/lib/change-notes/2023-04-03-gh-injection.md | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 javascript/ql/lib/change-notes/2023-04-03-gh-injection.md
diff --git a/javascript/ql/lib/change-notes/2023-04-03-gh-injection.md b/javascript/ql/lib/change-notes/2023-04-03-gh-injection.md
new file mode 100644
index 00000000000..04a5a2f4b6f
--- /dev/null
+++ b/javascript/ql/lib/change-notes/2023-04-03-gh-injection.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* Fixes and improvements in GitHub Actions Injection query.
\ No newline at end of file
From 8ea418216c4cc997d6f73289fb37c41203310cf0 Mon Sep 17 00:00:00 2001
From: jarlob
Date: Mon, 3 Apr 2023 23:13:28 +0200
Subject: [PATCH 016/870] Look for script injections in actions/github-script
---
.../ql/lib/semmle/javascript/Actions.qll | 49 +++++++++++++------
.../Security/CWE-094/ExpressionInjection.ql | 22 +++++++--
.../.github/workflows/comment_issue.yml | 15 +++++-
.../ExpressionInjection.expected | 3 ++
4 files changed, 69 insertions(+), 20 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/Actions.qll b/javascript/ql/lib/semmle/javascript/Actions.qll
index b1ab674924d..3d1d4c589d1 100644
--- a/javascript/ql/lib/semmle/javascript/Actions.qll
+++ b/javascript/ql/lib/semmle/javascript/Actions.qll
@@ -244,6 +244,40 @@ module Actions {
With getWith() { result = with }
}
+ /**
+ * Holds if `${{ e }}` is a GitHub Actions expression evaluated within this YAML string.
+ * See https://docs.github.com/en/free-pro-team@latest/actions/reference/context-and-expression-syntax-for-github-actions.
+ * Only finds simple expressions like `${{ github.event.comment.body }}`, where the expression contains only alphanumeric characters, underscores, dots, or dashes.
+ * Does not identify more complicated expressions like `${{ fromJSON(env.time) }}`, or ${{ format('{{Hello {0}!}}', github.event.head_commit.author.name) }}
+ */
+ string getASimpleReferenceExpression(YamlString node) {
+ // We use `regexpFind` to obtain *all* matches of `${{...}}`,
+ // not just the last (greedy match) or first (reluctant match).
+ result =
+ node.getValue()
+ .regexpFind("\\$\\{\\{\\s*[A-Za-z0-9_\\[\\]\\*\\(\\)\\.\\-]+\\s*\\}\\}", _, _)
+ .regexpCapture("\\$\\{\\{\\s*([A-Za-z0-9_\\[\\]\\*\\((\\)\\.\\-]+)\\s*\\}\\}", 1)
+ }
+
+ /**
+ * A `script:` field within an Actions `with:` specific to `actions/github-script` action.
+ *
+ * For example:
+ * ```
+ * uses: actions/github-script@v3
+ * with:
+ * script: console.log('${{ github.event.pull_request.head.sha }}')
+ * ```
+ */
+ class Script extends YamlNode, YamlString {
+ With with;
+
+ Script() { with.lookup("script") = this }
+
+ /** Gets the `with` field this field belongs to. */
+ With getWith() { result = with }
+ }
+
/**
* A `run` field within an Actions job step, which runs command-line programs using an operating system shell.
* See https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsrun.
@@ -255,20 +289,5 @@ module Actions {
/** Gets the step that executes this `run` command. */
Step getStep() { result = step }
-
- /**
- * Holds if `${{ e }}` is a GitHub Actions expression evaluated within this `run` command.
- * See https://docs.github.com/en/free-pro-team@latest/actions/reference/context-and-expression-syntax-for-github-actions.
- * Only finds simple expressions like `${{ github.event.comment.body }}`, where the expression contains only alphanumeric characters, underscores, dots, or dashes.
- * Does not identify more complicated expressions like `${{ fromJSON(env.time) }}`, or ${{ format('{{Hello {0}!}}', github.event.head_commit.author.name) }}
- */
- string getASimpleReferenceExpression() {
- // We use `regexpFind` to obtain *all* matches of `${{...}}`,
- // not just the last (greedy match) or first (reluctant match).
- result =
- this.getValue()
- .regexpFind("\\$\\{\\{\\s*[A-Za-z0-9_\\[\\]\\*\\(\\)\\.\\-]+\\s*\\}\\}", _, _)
- .regexpCapture("\\$\\{\\{\\s*([A-Za-z0-9_\\[\\]\\*\\((\\)\\.\\-]+)\\s*\\}\\}", 1)
- }
}
}
diff --git a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
index ce815c8e11d..84e7215837e 100644
--- a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
+++ b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
@@ -103,10 +103,24 @@ private predicate isExternalUserControlledWorkflowRun(string context) {
)
}
-from Actions::Run run, string context, Actions::On on
+from YamlNode node, string context, Actions::On on
where
- run.getASimpleReferenceExpression() = context and
- run.getStep().getJob().getWorkflow().getOn() = on and
+ (
+ exists(Actions::Run run |
+ node = run and
+ Actions::getASimpleReferenceExpression(run) = context and
+ run.getStep().getJob().getWorkflow().getOn() = on
+ )
+ or
+ exists(Actions::Script script, Actions::Step step, Actions::Uses uses |
+ node = script and
+ script.getWith().getStep() = step and
+ uses.getStep() = step and
+ uses.getGitHubRepository() = "actions/github-script" and
+ Actions::getASimpleReferenceExpression(script) = context and
+ script.getWith().getStep().getJob().getWorkflow().getOn() = on
+ )
+ ) and
(
exists(on.getNode("issues")) and
isExternalUserControlledIssue(context)
@@ -138,6 +152,6 @@ where
exists(on.getNode("workflow_run")) and
isExternalUserControlledWorkflowRun(context)
)
-select run,
+select node,
"Potential injection from the " + context +
" context, which may be controlled by an external user."
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/comment_issue.yml b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/comment_issue.yml
index fec20d7272f..17ead9fdd20 100644
--- a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/comment_issue.yml
+++ b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/comment_issue.yml
@@ -12,4 +12,17 @@ jobs:
steps:
- run: echo '${{ github.event.comment.body }}'
- run: echo '${{ github.event.issue.body }}'
- - run: echo '${{ github.event.issue.title }}'
\ No newline at end of file
+ - run: echo '${{ github.event.issue.title }}'
+
+ echo-chamber3:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/github-script@v3
+ with:
+ script: console.log('${{ github.event.comment.body }}')
+ - uses: actions/github-script@v3
+ with:
+ script: console.log('${{ github.event.issue.body }}')
+ - uses: actions/github-script@v3
+ with:
+ script: console.log('${{ github.event.issue.title }}')
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected
index b89948010b8..775ec3ba640 100644
--- a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected
@@ -2,6 +2,9 @@
| .github/workflows/comment_issue.yml:13:12:13:50 | echo '$ ... ody }}' | Potential injection from the github.event.comment.body context, which may be controlled by an external user. |
| .github/workflows/comment_issue.yml:14:12:14:48 | echo '$ ... ody }}' | Potential injection from the github.event.issue.body context, which may be controlled by an external user. |
| .github/workflows/comment_issue.yml:15:12:15:49 | echo '$ ... tle }}' | Potential injection from the github.event.issue.title context, which may be controlled by an external user. |
+| .github/workflows/comment_issue.yml:22:17:22:63 | console ... dy }}') | Potential injection from the github.event.comment.body context, which may be controlled by an external user. |
+| .github/workflows/comment_issue.yml:25:17:25:61 | console ... dy }}') | Potential injection from the github.event.issue.body context, which may be controlled by an external user. |
+| .github/workflows/comment_issue.yml:28:17:28:62 | console ... le }}') | Potential injection from the github.event.issue.title context, which may be controlled by an external user. |
| .github/workflows/comment_issue_newline.yml:9:14:10:50 | \| | Potential injection from the github.event.comment.body context, which may be controlled by an external user. |
| .github/workflows/discussion.yml:7:12:7:54 | echo '$ ... tle }}' | Potential injection from the github.event.discussion.title context, which may be controlled by an external user. |
| .github/workflows/discussion.yml:8:12:8:53 | echo '$ ... ody }}' | Potential injection from the github.event.discussion.body context, which may be controlled by an external user. |
From 39ff3c72a29696058a994709b2f60d488ad96626 Mon Sep 17 00:00:00 2001
From: jarlob
Date: Mon, 3 Apr 2023 23:28:31 +0200
Subject: [PATCH 017/870] Remove label sanitizer because it is prone to race
conditions
---
.../Security/CWE-094/UntrustedCheckout.ql | 46 ++-----------------
.../CWE-094/UntrustedCheckout.expected | 6 +++
2 files changed, 10 insertions(+), 42 deletions(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-094/UntrustedCheckout.ql b/javascript/ql/src/experimental/Security/CWE-094/UntrustedCheckout.ql
index 8f9622fe6e7..a81c8c65d25 100644
--- a/javascript/ql/src/experimental/Security/CWE-094/UntrustedCheckout.ql
+++ b/javascript/ql/src/experimental/Security/CWE-094/UntrustedCheckout.ql
@@ -17,7 +17,7 @@ import javascript
import semmle.javascript.Actions
/**
- * An action step that doesn't contain `actor` or `label` check in `if:` or
+ * An action step that doesn't contain `actor` check in `if:` or
* the check requires manual analysis.
*/
class ProbableStep extends Actions::Step {
@@ -29,25 +29,13 @@ class ProbableStep extends Actions::Step {
// needs manual analysis if there is OR
this.getIf().getValue().matches("%||%")
or
- // labels can be assigned by owners only
- not exists(
- this.getIf()
- .getValue()
- .regexpFind("\\bcontains\\s*\\(\\s*github\\s*\\.\\s*event\\s*\\.\\s*(?:issue|pull_request)\\s*\\.\\s*labels\\b",
- _, _)
- ) and
- not exists(
- this.getIf()
- .getValue()
- .regexpFind("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*label\\s*\\.\\s*name\\s*==", _, _)
- ) and
// actor check means only the user is able to run it
not exists(this.getIf().getValue().regexpFind("\\bgithub\\s*\\.\\s*actor\\s*==", _, _))
}
}
/**
- * An action job that doesn't contain `actor` or `label` check in `if:` or
+ * An action job that doesn't contain `actor` check in `if:` or
* the check requires manual analysis.
*/
class ProbableJob extends Actions::Job {
@@ -59,45 +47,19 @@ class ProbableJob extends Actions::Job {
// needs manual analysis if there is OR
this.getIf().getValue().matches("%||%")
or
- // labels can be assigned by owners only
- not exists(
- this.getIf()
- .getValue()
- .regexpFind("\\bcontains\\s*\\(\\s*github\\s*\\.\\s*event\\s*\\.\\s*(?:issue|pull_request)\\s*\\.\\s*labels\\b",
- _, _)
- ) and
- not exists(
- this.getIf()
- .getValue()
- .regexpFind("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*label\\s*\\.\\s*name\\s*==", _, _)
- ) and
// actor check means only the user is able to run it
not exists(this.getIf().getValue().regexpFind("\\bgithub\\s*\\.\\s*actor\\s*==", _, _))
}
}
/**
- * An action step that doesn't contain `actor` or `label` check in `if:` or
+ * on: pull_request_target
*/
class ProbablePullRequestTarget extends Actions::On, YamlMappingLikeNode {
ProbablePullRequestTarget() {
exists(YamlNode prtNode |
// The `on:` is triggered on `pull_request_target`
- this.getNode("pull_request_target") = prtNode and
- (
- // and either doesn't contain `types` filter
- not exists(prtNode.getAChild())
- or
- // or has the filter, that is something else than just [labeled]
- exists(YamlMappingLikeNode prt, YamlMappingLikeNode types |
- types = prt.getNode("types") and
- prtNode = prt and
- (
- not types.getElementCount() = 1 or
- not exists(types.getNode("labeled"))
- )
- )
- )
+ this.getNode("pull_request_target") = prtNode
)
}
}
diff --git a/javascript/ql/test/experimental/Security/CWE-094/UntrustedCheckout.expected b/javascript/ql/test/experimental/Security/CWE-094/UntrustedCheckout.expected
index fc1f704c025..127ced2bb97 100644
--- a/javascript/ql/test/experimental/Security/CWE-094/UntrustedCheckout.expected
+++ b/javascript/ql/test/experimental/Security/CWE-094/UntrustedCheckout.expected
@@ -1,7 +1,13 @@
+| .github/workflows/pull_request_target_if_job.yml:9:7:12:2 | uses: a ... kout@v2 | Potential unsafe checkout of untrusted pull request on 'pull_request_target'. |
+| .github/workflows/pull_request_target_if_job.yml:16:7:19:2 | uses: a ... kout@v2 | Potential unsafe checkout of untrusted pull request on 'pull_request_target'. |
| .github/workflows/pull_request_target_if_job.yml:30:7:33:2 | uses: a ... kout@v2 | Potential unsafe checkout of untrusted pull request on 'pull_request_target'. |
| .github/workflows/pull_request_target_if_job.yml:36:7:38:54 | uses: a ... kout@v2 | Potential unsafe checkout of untrusted pull request on 'pull_request_target'. |
+| .github/workflows/pull_request_target_if_step.yml:9:7:14:4 | uses: a ... kout@v2 | Potential unsafe checkout of untrusted pull request on 'pull_request_target'. |
+| .github/workflows/pull_request_target_if_step.yml:14:7:19:4 | uses: a ... kout@v2 | Potential unsafe checkout of untrusted pull request on 'pull_request_target'. |
| .github/workflows/pull_request_target_if_step.yml:24:7:29:4 | uses: a ... kout@v2 | Potential unsafe checkout of untrusted pull request on 'pull_request_target'. |
| .github/workflows/pull_request_target_if_step.yml:29:7:31:54 | uses: a ... kout@v2 | Potential unsafe checkout of untrusted pull request on 'pull_request_target'. |
+| .github/workflows/pull_request_target_label_only.yml:10:7:12:54 | uses: a ... kout@v2 | Potential unsafe checkout of untrusted pull request on 'pull_request_target'. |
+| .github/workflows/pull_request_target_label_only_mapping.yml:11:7:13:54 | uses: a ... kout@v2 | Potential unsafe checkout of untrusted pull request on 'pull_request_target'. |
| .github/workflows/pull_request_target_labels_mapping.yml:13:7:15:54 | uses: a ... kout@v2 | Potential unsafe checkout of untrusted pull request on 'pull_request_target'. |
| .github/workflows/pull_request_target_labels_sequence.yml:10:7:12:54 | uses: a ... kout@v2 | Potential unsafe checkout of untrusted pull request on 'pull_request_target'. |
| .github/workflows/pull_request_target_mapping.yml:8:7:10:54 | uses: a ... kout@v2 | Potential unsafe checkout of untrusted pull request on 'pull_request_target'. |
From 5c5b9f99a83dfd328780e77d15892652556a5484 Mon Sep 17 00:00:00 2001
From: jarlob
Date: Wed, 5 Apr 2023 10:03:46 +0200
Subject: [PATCH 018/870] Add simple taint tracking for env variables
---
.../ql/lib/semmle/javascript/Actions.qll | 61 +++++++++++++++++++
.../CWE-094/ExpressionInjection.qhelp | 10 +--
.../Security/CWE-094/ExpressionInjection.ql | 28 +++++++--
.../.github/workflows/issues.yml | 12 ++++
.../ExpressionInjection.expected | 7 ++-
5 files changed, 106 insertions(+), 12 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/Actions.qll b/javascript/ql/lib/semmle/javascript/Actions.qll
index 3d1d4c589d1..3567414650b 100644
--- a/javascript/ql/lib/semmle/javascript/Actions.qll
+++ b/javascript/ql/lib/semmle/javascript/Actions.qll
@@ -28,6 +28,9 @@ module Actions {
/** Gets the `jobs` mapping from job IDs to job definitions in this workflow. */
YamlMapping getJobs() { result = this.lookup("jobs") }
+ /** Gets the 'global' `env` mapping in this workflow. */
+ YamlMapping getEnv() { result = this.lookup("env") }
+
/** Gets the name of the workflow. */
string getName() { result = this.lookup("name").(YamlString).getValue() }
@@ -54,6 +57,54 @@ module Actions {
Workflow getWorkflow() { result = workflow }
}
+ /** An environment variable in 'env:' */
+ abstract class Env extends YamlNode, YamlString {
+ /** Gets the name of this environment variable. */
+ abstract string getName();
+ }
+
+ /** Workflow level 'global' environment variable. */
+ class GlobalEnv extends Env {
+ string envName;
+ Workflow workflow;
+
+ GlobalEnv() { this = workflow.getEnv().lookup(envName) }
+
+ /** Gets the workflow this field belongs to. */
+ Workflow getWorkflow() { result = workflow }
+
+ /** Gets the name of this environment variable. */
+ override string getName() { result = envName }
+ }
+
+ /** Job level environment variable. */
+ class JobEnv extends Env {
+ string envName;
+ Job job;
+
+ JobEnv() { this = job.getEnv().lookup(envName) }
+
+ /** Gets the job this field belongs to. */
+ Job getJob() { result = job }
+
+ /** Gets the name of this environment variable. */
+ override string getName() { result = envName }
+ }
+
+ /** Step level environment variable. */
+ class StepEnv extends Env {
+ string envName;
+ Step step;
+
+ StepEnv() { this = step.getEnv().lookup(envName) }
+
+ /** Gets the step this field belongs to. */
+ Step getStep() { result = step }
+
+ /** Gets the name of this environment variable. */
+ override string getName() { result = envName }
+ }
+
/**
* An Actions job within a workflow.
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobs.
@@ -88,6 +139,9 @@ module Actions {
/** Gets the sequence of `steps` within this job. */
YamlSequence getSteps() { result = this.lookup("steps") }
+ /** Gets the `env` mapping in this job. */
+ YamlMapping getEnv() { result = this.lookup("env") }
+
/** Gets the workflow this job belongs to. */
Workflow getWorkflow() { result = workflow }
@@ -149,6 +203,9 @@ module Actions {
/** Gets the value of the `if` field in this step, if any. */
StepIf getIf() { result.getStep() = this }
+ /** Gets the value of the `env` field in this step, if any. */
+ YamlMapping getEnv() { result = this.lookup("env") }
+
/** Gets the ID of this step, if any. */
string getId() { result = this.lookup("id").(YamlString).getValue() }
}
@@ -259,6 +316,10 @@ module Actions {
.regexpCapture("\\$\\{\\{\\s*([A-Za-z0-9_\\[\\]\\*\\((\\)\\.\\-]+)\\s*\\}\\}", 1)
}
+ /** Extracts the 'name' part from env.name */
+ bindingset[name]
+ string getEnvName(string name) { result = name.regexpCapture("env\\.([A-Za-z0-9_]+)", 1) }
+
/**
* A `script:` field within an Actions `with:` specific to `actions/github-script` action.
*
diff --git a/javascript/ql/src/Security/CWE-094/ExpressionInjection.qhelp b/javascript/ql/src/Security/CWE-094/ExpressionInjection.qhelp
index 4424fe363a2..6e248eb380e 100644
--- a/javascript/ql/src/Security/CWE-094/ExpressionInjection.qhelp
+++ b/javascript/ql/src/Security/CWE-094/ExpressionInjection.qhelp
@@ -8,10 +8,11 @@
code injection in contexts like run: or script:.
- Code injection in GitHub Actions may allow an attacker to
- exfiltrate the temporary GitHub repository authorization token.
+ Code injection in GitHub Actions may allow an attacker to
+ exfiltrate any secrets used in the workflow and
+ the temporary GitHub repository authorization token.
The token might have write access to the repository, allowing an attacker
- to use the token to make changes to the repository.
+ to use the token to make changes to the repository.
@@ -19,7 +20,8 @@
The best practice to avoid code injection vulnerabilities
in GitHub workflows is to set the untrusted input value of the expression
- to an intermediate environment variable.
+ to an intermediate environment variable and then use the environment variable
+ using the native syntax of the shell/script interpreter (i.e. NOT the ${{ env.VAR }}).
It is also recommended to limit the permissions of any tokens used
diff --git a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
index 84e7215837e..cbeecf1405a 100644
--- a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
+++ b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
@@ -103,22 +103,38 @@ private predicate isExternalUserControlledWorkflowRun(string context) {
)
}
-from YamlNode node, string context, Actions::On on
+from YamlNode node, string injection, string context, Actions::On on
where
(
exists(Actions::Run run |
node = run and
- Actions::getASimpleReferenceExpression(run) = context and
- run.getStep().getJob().getWorkflow().getOn() = on
+ Actions::getASimpleReferenceExpression(run) = injection and
+ run.getStep().getJob().getWorkflow().getOn() = on and
+ (
+ injection = context
+ or
+ exists(Actions::Env env |
+ Actions::getEnvName(injection) = env.getName() and
+ Actions::getASimpleReferenceExpression(env) = context
+ )
+ )
)
or
exists(Actions::Script script, Actions::Step step, Actions::Uses uses |
node = script and
+ script.getWith().getStep().getJob().getWorkflow().getOn() = on and
script.getWith().getStep() = step and
uses.getStep() = step and
uses.getGitHubRepository() = "actions/github-script" and
- Actions::getASimpleReferenceExpression(script) = context and
- script.getWith().getStep().getJob().getWorkflow().getOn() = on
+ Actions::getASimpleReferenceExpression(script) = injection and
+ (
+ injection = context
+ or
+ exists(Actions::Env env |
+ Actions::getEnvName(injection) = env.getName() and
+ Actions::getASimpleReferenceExpression(env) = context
+ )
+ )
)
) and
(
@@ -153,5 +169,5 @@ where
isExternalUserControlledWorkflowRun(context)
)
select node,
- "Potential injection from the " + context +
+ "Potential injection from the " + injection +
" context, which may be controlled by an external user."
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/issues.yml b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/issues.yml
index 2eae85278dd..5e767ce0239 100644
--- a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/issues.yml
+++ b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/issues.yml
@@ -1,8 +1,20 @@
on: issues
+env:
+ global_env: ${{ github.event.issue.title }}
+ test: test
+
jobs:
echo-chamber:
+ env:
+ job_env: ${{ github.event.issue.title }}
runs-on: ubuntu-latest
steps:
- run: echo '${{ github.event.issue.title }}'
- run: echo '${{ github.event.issue.body }}'
+ - run: echo '${{ env.global_env }}'
+ - run: echo '${{ env.test }}'
+ - run: echo '${{ env.job_env }}'
+ - run: echo '${{ env.step_env }}'
+ env:
+ step_env: ${{ github.event.issue.title }}
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected
index 775ec3ba640..0665fe46a6b 100644
--- a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected
@@ -15,8 +15,11 @@
| .github/workflows/gollum.yml:8:12:8:53 | echo '$ ... tle }}' | Potential injection from the github.event.pages[11].title context, which may be controlled by an external user. |
| .github/workflows/gollum.yml:9:12:9:56 | echo '$ ... ame }}' | Potential injection from the github.event.pages[0].page_name context, which may be controlled by an external user. |
| .github/workflows/gollum.yml:10:12:10:59 | echo '$ ... ame }}' | Potential injection from the github.event.pages[2222].page_name context, which may be controlled by an external user. |
-| .github/workflows/issues.yml:7:12:7:49 | echo '$ ... tle }}' | Potential injection from the github.event.issue.title context, which may be controlled by an external user. |
-| .github/workflows/issues.yml:8:12:8:48 | echo '$ ... ody }}' | Potential injection from the github.event.issue.body context, which may be controlled by an external user. |
+| .github/workflows/issues.yml:13:12:13:49 | echo '$ ... tle }}' | Potential injection from the github.event.issue.title context, which may be controlled by an external user. |
+| .github/workflows/issues.yml:14:12:14:48 | echo '$ ... ody }}' | Potential injection from the github.event.issue.body context, which may be controlled by an external user. |
+| .github/workflows/issues.yml:15:12:15:39 | echo '$ ... env }}' | Potential injection from the env.global_env context, which may be controlled by an external user. |
+| .github/workflows/issues.yml:17:12:17:36 | echo '$ ... env }}' | Potential injection from the env.job_env context, which may be controlled by an external user. |
+| .github/workflows/issues.yml:18:12:18:37 | echo '$ ... env }}' | Potential injection from the env.step_env context, which may be controlled by an external user. |
| .github/workflows/pull_request_review.yml:7:12:7:56 | echo '$ ... tle }}' | Potential injection from the github.event.pull_request.title context, which may be controlled by an external user. |
| .github/workflows/pull_request_review.yml:8:12:8:55 | echo '$ ... ody }}' | Potential injection from the github.event.pull_request.body context, which may be controlled by an external user. |
| .github/workflows/pull_request_review.yml:9:12:9:61 | echo '$ ... bel }}' | Potential injection from the github.event.pull_request.head.label context, which may be controlled by an external user. |
From eef1973b93f8f727e9c791cca565547b892465f8 Mon Sep 17 00:00:00 2001
From: jarlob
Date: Wed, 5 Apr 2023 10:05:24 +0200
Subject: [PATCH 019/870] Change UI message
---
.../Security/CWE-094/ExpressionInjection.ql | 4 +-
.../ExpressionInjection.expected | 128 +++++++++---------
2 files changed, 66 insertions(+), 66 deletions(-)
diff --git a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
index cbeecf1405a..056d7204551 100644
--- a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
+++ b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
@@ -169,5 +169,5 @@ where
isExternalUserControlledWorkflowRun(context)
)
select node,
- "Potential injection from the " + injection +
- " context, which may be controlled by an external user."
+ "Potential injection from the ${ " + injection +
+ " }, which may be controlled by an external user."
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected
index 0665fe46a6b..457cb815e6a 100644
--- a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected
@@ -1,64 +1,64 @@
-| .github/workflows/comment_issue.yml:7:12:8:48 | \| | Potential injection from the github.event.comment.body context, which may be controlled by an external user. |
-| .github/workflows/comment_issue.yml:13:12:13:50 | echo '$ ... ody }}' | Potential injection from the github.event.comment.body context, which may be controlled by an external user. |
-| .github/workflows/comment_issue.yml:14:12:14:48 | echo '$ ... ody }}' | Potential injection from the github.event.issue.body context, which may be controlled by an external user. |
-| .github/workflows/comment_issue.yml:15:12:15:49 | echo '$ ... tle }}' | Potential injection from the github.event.issue.title context, which may be controlled by an external user. |
-| .github/workflows/comment_issue.yml:22:17:22:63 | console ... dy }}') | Potential injection from the github.event.comment.body context, which may be controlled by an external user. |
-| .github/workflows/comment_issue.yml:25:17:25:61 | console ... dy }}') | Potential injection from the github.event.issue.body context, which may be controlled by an external user. |
-| .github/workflows/comment_issue.yml:28:17:28:62 | console ... le }}') | Potential injection from the github.event.issue.title context, which may be controlled by an external user. |
-| .github/workflows/comment_issue_newline.yml:9:14:10:50 | \| | Potential injection from the github.event.comment.body context, which may be controlled by an external user. |
-| .github/workflows/discussion.yml:7:12:7:54 | echo '$ ... tle }}' | Potential injection from the github.event.discussion.title context, which may be controlled by an external user. |
-| .github/workflows/discussion.yml:8:12:8:53 | echo '$ ... ody }}' | Potential injection from the github.event.discussion.body context, which may be controlled by an external user. |
-| .github/workflows/discussion_comment.yml:7:12:7:54 | echo '$ ... tle }}' | Potential injection from the github.event.discussion.title context, which may be controlled by an external user. |
-| .github/workflows/discussion_comment.yml:8:12:8:53 | echo '$ ... ody }}' | Potential injection from the github.event.discussion.body context, which may be controlled by an external user. |
-| .github/workflows/discussion_comment.yml:9:12:9:50 | echo '$ ... ody }}' | Potential injection from the github.event.comment.body context, which may be controlled by an external user. |
-| .github/workflows/gollum.yml:7:12:7:52 | echo '$ ... tle }}' | Potential injection from the github.event.pages[1].title context, which may be controlled by an external user. |
-| .github/workflows/gollum.yml:8:12:8:53 | echo '$ ... tle }}' | Potential injection from the github.event.pages[11].title context, which may be controlled by an external user. |
-| .github/workflows/gollum.yml:9:12:9:56 | echo '$ ... ame }}' | Potential injection from the github.event.pages[0].page_name context, which may be controlled by an external user. |
-| .github/workflows/gollum.yml:10:12:10:59 | echo '$ ... ame }}' | Potential injection from the github.event.pages[2222].page_name context, which may be controlled by an external user. |
-| .github/workflows/issues.yml:13:12:13:49 | echo '$ ... tle }}' | Potential injection from the github.event.issue.title context, which may be controlled by an external user. |
-| .github/workflows/issues.yml:14:12:14:48 | echo '$ ... ody }}' | Potential injection from the github.event.issue.body context, which may be controlled by an external user. |
-| .github/workflows/issues.yml:15:12:15:39 | echo '$ ... env }}' | Potential injection from the env.global_env context, which may be controlled by an external user. |
-| .github/workflows/issues.yml:17:12:17:36 | echo '$ ... env }}' | Potential injection from the env.job_env context, which may be controlled by an external user. |
-| .github/workflows/issues.yml:18:12:18:37 | echo '$ ... env }}' | Potential injection from the env.step_env context, which may be controlled by an external user. |
-| .github/workflows/pull_request_review.yml:7:12:7:56 | echo '$ ... tle }}' | Potential injection from the github.event.pull_request.title context, which may be controlled by an external user. |
-| .github/workflows/pull_request_review.yml:8:12:8:55 | echo '$ ... ody }}' | Potential injection from the github.event.pull_request.body context, which may be controlled by an external user. |
-| .github/workflows/pull_request_review.yml:9:12:9:61 | echo '$ ... bel }}' | Potential injection from the github.event.pull_request.head.label context, which may be controlled by an external user. |
-| .github/workflows/pull_request_review.yml:10:12:10:75 | echo '$ ... nch }}' | Potential injection from the github.event.pull_request.head.repo.default_branch context, which may be controlled by an external user. |
-| .github/workflows/pull_request_review.yml:11:12:11:72 | echo '$ ... ion }}' | Potential injection from the github.event.pull_request.head.repo.description context, which may be controlled by an external user. |
-| .github/workflows/pull_request_review.yml:12:12:12:69 | echo '$ ... age }}' | Potential injection from the github.event.pull_request.head.repo.homepage context, which may be controlled by an external user. |
-| .github/workflows/pull_request_review.yml:13:12:13:59 | echo '$ ... ref }}' | Potential injection from the github.event.pull_request.head.ref context, which may be controlled by an external user. |
-| .github/workflows/pull_request_review.yml:14:12:14:49 | echo '$ ... ody }}' | Potential injection from the github.event.review.body context, which may be controlled by an external user. |
-| .github/workflows/pull_request_review_comment.yml:7:12:7:56 | echo '$ ... tle }}' | Potential injection from the github.event.pull_request.title context, which may be controlled by an external user. |
-| .github/workflows/pull_request_review_comment.yml:8:12:8:55 | echo '$ ... ody }}' | Potential injection from the github.event.pull_request.body context, which may be controlled by an external user. |
-| .github/workflows/pull_request_review_comment.yml:9:12:9:61 | echo '$ ... bel }}' | Potential injection from the github.event.pull_request.head.label context, which may be controlled by an external user. |
-| .github/workflows/pull_request_review_comment.yml:10:12:10:75 | echo '$ ... nch }}' | Potential injection from the github.event.pull_request.head.repo.default_branch context, which may be controlled by an external user. |
-| .github/workflows/pull_request_review_comment.yml:11:12:11:72 | echo '$ ... ion }}' | Potential injection from the github.event.pull_request.head.repo.description context, which may be controlled by an external user. |
-| .github/workflows/pull_request_review_comment.yml:12:12:12:69 | echo '$ ... age }}' | Potential injection from the github.event.pull_request.head.repo.homepage context, which may be controlled by an external user. |
-| .github/workflows/pull_request_review_comment.yml:13:12:13:59 | echo '$ ... ref }}' | Potential injection from the github.event.pull_request.head.ref context, which may be controlled by an external user. |
-| .github/workflows/pull_request_review_comment.yml:14:12:14:50 | echo '$ ... ody }}' | Potential injection from the github.event.comment.body context, which may be controlled by an external user. |
-| .github/workflows/pull_request_target.yml:9:12:9:56 | echo '$ ... tle }}' | Potential injection from the github.event.pull_request.title context, which may be controlled by an external user. |
-| .github/workflows/pull_request_target.yml:10:12:10:55 | echo '$ ... ody }}' | Potential injection from the github.event.pull_request.body context, which may be controlled by an external user. |
-| .github/workflows/pull_request_target.yml:11:12:11:61 | echo '$ ... bel }}' | Potential injection from the github.event.pull_request.head.label context, which may be controlled by an external user. |
-| .github/workflows/pull_request_target.yml:12:12:12:75 | echo '$ ... nch }}' | Potential injection from the github.event.pull_request.head.repo.default_branch context, which may be controlled by an external user. |
-| .github/workflows/pull_request_target.yml:13:12:13:72 | echo '$ ... ion }}' | Potential injection from the github.event.pull_request.head.repo.description context, which may be controlled by an external user. |
-| .github/workflows/pull_request_target.yml:14:12:14:69 | echo '$ ... age }}' | Potential injection from the github.event.pull_request.head.repo.homepage context, which may be controlled by an external user. |
-| .github/workflows/pull_request_target.yml:15:12:15:59 | echo '$ ... ref }}' | Potential injection from the github.event.pull_request.head.ref context, which may be controlled by an external user. |
-| .github/workflows/pull_request_target.yml:16:12:16:40 | echo '$ ... ref }}' | Potential injection from the github.head_ref context, which may be controlled by an external user. |
-| .github/workflows/push.yml:7:12:7:57 | echo '$ ... age }}' | Potential injection from the github.event.commits[11].message context, which may be controlled by an external user. |
-| .github/workflows/push.yml:8:12:8:62 | echo '$ ... ail }}' | Potential injection from the github.event.commits[11].author.email context, which may be controlled by an external user. |
-| .github/workflows/push.yml:9:12:9:61 | echo '$ ... ame }}' | Potential injection from the github.event.commits[11].author.name context, which may be controlled by an external user. |
-| .github/workflows/push.yml:10:12:10:57 | echo '$ ... age }}' | Potential injection from the github.event.head_commit.message context, which may be controlled by an external user. |
-| .github/workflows/push.yml:11:12:11:62 | echo '$ ... ail }}' | Potential injection from the github.event.head_commit.author.email context, which may be controlled by an external user. |
-| .github/workflows/push.yml:12:12:12:61 | echo '$ ... ame }}' | Potential injection from the github.event.head_commit.author.name context, which may be controlled by an external user. |
-| .github/workflows/push.yml:13:12:13:65 | echo '$ ... ail }}' | Potential injection from the github.event.head_commit.committer.email context, which may be controlled by an external user. |
-| .github/workflows/push.yml:14:12:14:64 | echo '$ ... ame }}' | Potential injection from the github.event.head_commit.committer.name context, which may be controlled by an external user. |
-| .github/workflows/push.yml:15:12:15:65 | echo '$ ... ail }}' | Potential injection from the github.event.commits[11].committer.email context, which may be controlled by an external user. |
-| .github/workflows/push.yml:16:12:16:64 | echo '$ ... ame }}' | Potential injection from the github.event.commits[11].committer.name context, which may be controlled by an external user. |
-| .github/workflows/workflow_run.yml:9:12:9:64 | echo '$ ... tle }}' | Potential injection from the github.event.workflow_run.display_title context, which may be controlled by an external user. |
-| .github/workflows/workflow_run.yml:10:12:10:70 | echo '$ ... age }}' | Potential injection from the github.event.workflow_run.head_commit.message context, which may be controlled by an external user. |
-| .github/workflows/workflow_run.yml:11:12:11:75 | echo '$ ... ail }}' | Potential injection from the github.event.workflow_run.head_commit.author.email context, which may be controlled by an external user. |
-| .github/workflows/workflow_run.yml:12:12:12:74 | echo '$ ... ame }}' | Potential injection from the github.event.workflow_run.head_commit.author.name context, which may be controlled by an external user. |
-| .github/workflows/workflow_run.yml:13:12:13:78 | echo '$ ... ail }}' | Potential injection from the github.event.workflow_run.head_commit.committer.email context, which may be controlled by an external user. |
-| .github/workflows/workflow_run.yml:14:12:14:77 | echo '$ ... ame }}' | Potential injection from the github.event.workflow_run.head_commit.committer.name context, which may be controlled by an external user. |
-| .github/workflows/workflow_run.yml:15:12:15:62 | echo '$ ... nch }}' | Potential injection from the github.event.workflow_run.head_branch context, which may be controlled by an external user. |
-| .github/workflows/workflow_run.yml:16:12:16:78 | echo '$ ... ion }}' | Potential injection from the github.event.workflow_run.head_repository.description context, which may be controlled by an external user. |
+| .github/workflows/comment_issue.yml:7:12:8:48 | \| | Potential injection from the ${ github.event.comment.body }, which may be controlled by an external user. |
+| .github/workflows/comment_issue.yml:13:12:13:50 | echo '$ ... ody }}' | Potential injection from the ${ github.event.comment.body }, which may be controlled by an external user. |
+| .github/workflows/comment_issue.yml:14:12:14:48 | echo '$ ... ody }}' | Potential injection from the ${ github.event.issue.body }, which may be controlled by an external user. |
+| .github/workflows/comment_issue.yml:15:12:15:49 | echo '$ ... tle }}' | Potential injection from the ${ github.event.issue.title }, which may be controlled by an external user. |
+| .github/workflows/comment_issue.yml:22:17:22:63 | console ... dy }}') | Potential injection from the ${ github.event.comment.body }, which may be controlled by an external user. |
+| .github/workflows/comment_issue.yml:25:17:25:61 | console ... dy }}') | Potential injection from the ${ github.event.issue.body }, which may be controlled by an external user. |
+| .github/workflows/comment_issue.yml:28:17:28:62 | console ... le }}') | Potential injection from the ${ github.event.issue.title }, which may be controlled by an external user. |
+| .github/workflows/comment_issue_newline.yml:9:14:10:50 | \| | Potential injection from the ${ github.event.comment.body }, which may be controlled by an external user. |
+| .github/workflows/discussion.yml:7:12:7:54 | echo '$ ... tle }}' | Potential injection from the ${ github.event.discussion.title }, which may be controlled by an external user. |
+| .github/workflows/discussion.yml:8:12:8:53 | echo '$ ... ody }}' | Potential injection from the ${ github.event.discussion.body }, which may be controlled by an external user. |
+| .github/workflows/discussion_comment.yml:7:12:7:54 | echo '$ ... tle }}' | Potential injection from the ${ github.event.discussion.title }, which may be controlled by an external user. |
+| .github/workflows/discussion_comment.yml:8:12:8:53 | echo '$ ... ody }}' | Potential injection from the ${ github.event.discussion.body }, which may be controlled by an external user. |
+| .github/workflows/discussion_comment.yml:9:12:9:50 | echo '$ ... ody }}' | Potential injection from the ${ github.event.comment.body }, which may be controlled by an external user. |
+| .github/workflows/gollum.yml:7:12:7:52 | echo '$ ... tle }}' | Potential injection from the ${ github.event.pages[1].title }, which may be controlled by an external user. |
+| .github/workflows/gollum.yml:8:12:8:53 | echo '$ ... tle }}' | Potential injection from the ${ github.event.pages[11].title }, which may be controlled by an external user. |
+| .github/workflows/gollum.yml:9:12:9:56 | echo '$ ... ame }}' | Potential injection from the ${ github.event.pages[0].page_name }, which may be controlled by an external user. |
+| .github/workflows/gollum.yml:10:12:10:59 | echo '$ ... ame }}' | Potential injection from the ${ github.event.pages[2222].page_name }, which may be controlled by an external user. |
+| .github/workflows/issues.yml:13:12:13:49 | echo '$ ... tle }}' | Potential injection from the ${ github.event.issue.title }, which may be controlled by an external user. |
+| .github/workflows/issues.yml:14:12:14:48 | echo '$ ... ody }}' | Potential injection from the ${ github.event.issue.body }, which may be controlled by an external user. |
+| .github/workflows/issues.yml:15:12:15:39 | echo '$ ... env }}' | Potential injection from the ${ env.global_env }, which may be controlled by an external user. |
+| .github/workflows/issues.yml:17:12:17:36 | echo '$ ... env }}' | Potential injection from the ${ env.job_env }, which may be controlled by an external user. |
+| .github/workflows/issues.yml:18:12:18:37 | echo '$ ... env }}' | Potential injection from the ${ env.step_env }, which may be controlled by an external user. |
+| .github/workflows/pull_request_review.yml:7:12:7:56 | echo '$ ... tle }}' | Potential injection from the ${ github.event.pull_request.title }, which may be controlled by an external user. |
+| .github/workflows/pull_request_review.yml:8:12:8:55 | echo '$ ... ody }}' | Potential injection from the ${ github.event.pull_request.body }, which may be controlled by an external user. |
+| .github/workflows/pull_request_review.yml:9:12:9:61 | echo '$ ... bel }}' | Potential injection from the ${ github.event.pull_request.head.label }, which may be controlled by an external user. |
+| .github/workflows/pull_request_review.yml:10:12:10:75 | echo '$ ... nch }}' | Potential injection from the ${ github.event.pull_request.head.repo.default_branch }, which may be controlled by an external user. |
+| .github/workflows/pull_request_review.yml:11:12:11:72 | echo '$ ... ion }}' | Potential injection from the ${ github.event.pull_request.head.repo.description }, which may be controlled by an external user. |
+| .github/workflows/pull_request_review.yml:12:12:12:69 | echo '$ ... age }}' | Potential injection from the ${ github.event.pull_request.head.repo.homepage }, which may be controlled by an external user. |
+| .github/workflows/pull_request_review.yml:13:12:13:59 | echo '$ ... ref }}' | Potential injection from the ${ github.event.pull_request.head.ref }, which may be controlled by an external user. |
+| .github/workflows/pull_request_review.yml:14:12:14:49 | echo '$ ... ody }}' | Potential injection from the ${ github.event.review.body }, which may be controlled by an external user. |
+| .github/workflows/pull_request_review_comment.yml:7:12:7:56 | echo '$ ... tle }}' | Potential injection from the ${ github.event.pull_request.title }, which may be controlled by an external user. |
+| .github/workflows/pull_request_review_comment.yml:8:12:8:55 | echo '$ ... ody }}' | Potential injection from the ${ github.event.pull_request.body }, which may be controlled by an external user. |
+| .github/workflows/pull_request_review_comment.yml:9:12:9:61 | echo '$ ... bel }}' | Potential injection from the ${ github.event.pull_request.head.label }, which may be controlled by an external user. |
+| .github/workflows/pull_request_review_comment.yml:10:12:10:75 | echo '$ ... nch }}' | Potential injection from the ${ github.event.pull_request.head.repo.default_branch }, which may be controlled by an external user. |
+| .github/workflows/pull_request_review_comment.yml:11:12:11:72 | echo '$ ... ion }}' | Potential injection from the ${ github.event.pull_request.head.repo.description }, which may be controlled by an external user. |
+| .github/workflows/pull_request_review_comment.yml:12:12:12:69 | echo '$ ... age }}' | Potential injection from the ${ github.event.pull_request.head.repo.homepage }, which may be controlled by an external user. |
+| .github/workflows/pull_request_review_comment.yml:13:12:13:59 | echo '$ ... ref }}' | Potential injection from the ${ github.event.pull_request.head.ref }, which may be controlled by an external user. |
+| .github/workflows/pull_request_review_comment.yml:14:12:14:50 | echo '$ ... ody }}' | Potential injection from the ${ github.event.comment.body }, which may be controlled by an external user. |
+| .github/workflows/pull_request_target.yml:9:12:9:56 | echo '$ ... tle }}' | Potential injection from the ${ github.event.pull_request.title }, which may be controlled by an external user. |
+| .github/workflows/pull_request_target.yml:10:12:10:55 | echo '$ ... ody }}' | Potential injection from the ${ github.event.pull_request.body }, which may be controlled by an external user. |
+| .github/workflows/pull_request_target.yml:11:12:11:61 | echo '$ ... bel }}' | Potential injection from the ${ github.event.pull_request.head.label }, which may be controlled by an external user. |
+| .github/workflows/pull_request_target.yml:12:12:12:75 | echo '$ ... nch }}' | Potential injection from the ${ github.event.pull_request.head.repo.default_branch }, which may be controlled by an external user. |
+| .github/workflows/pull_request_target.yml:13:12:13:72 | echo '$ ... ion }}' | Potential injection from the ${ github.event.pull_request.head.repo.description }, which may be controlled by an external user. |
+| .github/workflows/pull_request_target.yml:14:12:14:69 | echo '$ ... age }}' | Potential injection from the ${ github.event.pull_request.head.repo.homepage }, which may be controlled by an external user. |
+| .github/workflows/pull_request_target.yml:15:12:15:59 | echo '$ ... ref }}' | Potential injection from the ${ github.event.pull_request.head.ref }, which may be controlled by an external user. |
+| .github/workflows/pull_request_target.yml:16:12:16:40 | echo '$ ... ref }}' | Potential injection from the ${ github.head_ref }, which may be controlled by an external user. |
+| .github/workflows/push.yml:7:12:7:57 | echo '$ ... age }}' | Potential injection from the ${ github.event.commits[11].message }, which may be controlled by an external user. |
+| .github/workflows/push.yml:8:12:8:62 | echo '$ ... ail }}' | Potential injection from the ${ github.event.commits[11].author.email }, which may be controlled by an external user. |
+| .github/workflows/push.yml:9:12:9:61 | echo '$ ... ame }}' | Potential injection from the ${ github.event.commits[11].author.name }, which may be controlled by an external user. |
+| .github/workflows/push.yml:10:12:10:57 | echo '$ ... age }}' | Potential injection from the ${ github.event.head_commit.message }, which may be controlled by an external user. |
+| .github/workflows/push.yml:11:12:11:62 | echo '$ ... ail }}' | Potential injection from the ${ github.event.head_commit.author.email }, which may be controlled by an external user. |
+| .github/workflows/push.yml:12:12:12:61 | echo '$ ... ame }}' | Potential injection from the ${ github.event.head_commit.author.name }, which may be controlled by an external user. |
+| .github/workflows/push.yml:13:12:13:65 | echo '$ ... ail }}' | Potential injection from the ${ github.event.head_commit.committer.email }, which may be controlled by an external user. |
+| .github/workflows/push.yml:14:12:14:64 | echo '$ ... ame }}' | Potential injection from the ${ github.event.head_commit.committer.name }, which may be controlled by an external user. |
+| .github/workflows/push.yml:15:12:15:65 | echo '$ ... ail }}' | Potential injection from the ${ github.event.commits[11].committer.email }, which may be controlled by an external user. |
+| .github/workflows/push.yml:16:12:16:64 | echo '$ ... ame }}' | Potential injection from the ${ github.event.commits[11].committer.name }, which may be controlled by an external user. |
+| .github/workflows/workflow_run.yml:9:12:9:64 | echo '$ ... tle }}' | Potential injection from the ${ github.event.workflow_run.display_title }, which may be controlled by an external user. |
+| .github/workflows/workflow_run.yml:10:12:10:70 | echo '$ ... age }}' | Potential injection from the ${ github.event.workflow_run.head_commit.message }, which may be controlled by an external user. |
+| .github/workflows/workflow_run.yml:11:12:11:75 | echo '$ ... ail }}' | Potential injection from the ${ github.event.workflow_run.head_commit.author.email }, which may be controlled by an external user. |
+| .github/workflows/workflow_run.yml:12:12:12:74 | echo '$ ... ame }}' | Potential injection from the ${ github.event.workflow_run.head_commit.author.name }, which may be controlled by an external user. |
+| .github/workflows/workflow_run.yml:13:12:13:78 | echo '$ ... ail }}' | Potential injection from the ${ github.event.workflow_run.head_commit.committer.email }, which may be controlled by an external user. |
+| .github/workflows/workflow_run.yml:14:12:14:77 | echo '$ ... ame }}' | Potential injection from the ${ github.event.workflow_run.head_commit.committer.name }, which may be controlled by an external user. |
+| .github/workflows/workflow_run.yml:15:12:15:62 | echo '$ ... nch }}' | Potential injection from the ${ github.event.workflow_run.head_branch }, which may be controlled by an external user. |
+| .github/workflows/workflow_run.yml:16:12:16:78 | echo '$ ... ion }}' | Potential injection from the ${ github.event.workflow_run.head_repository.description }, which may be controlled by an external user. |
From 40b7910473176ec6aa97d0eda4c50faca6b819fd Mon Sep 17 00:00:00 2001
From: jarlob
Date: Wed, 5 Apr 2023 10:14:54 +0200
Subject: [PATCH 020/870] Fix QLDoc warnings
---
javascript/ql/lib/semmle/javascript/Actions.qll | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/Actions.qll b/javascript/ql/lib/semmle/javascript/Actions.qll
index 3567414650b..85b313af8a3 100644
--- a/javascript/ql/lib/semmle/javascript/Actions.qll
+++ b/javascript/ql/lib/semmle/javascript/Actions.qll
@@ -63,7 +63,7 @@ module Actions {
abstract string getName();
}
- /** Workflow level 'global' environment variable. */
+ /** A workflow level 'global' environment variable. */
class GlobalEnv extends Env {
string envName;
Workflow workflow;
@@ -77,7 +77,7 @@ module Actions {
override string getName() { result = envName }
}
- /** Job level environment variable. */
+ /** A job level environment variable. */
class JobEnv extends Env {
string envName;
Job job;
@@ -91,7 +91,7 @@ module Actions {
override string getName() { result = envName }
}
- /** Step level environment variable. */
+ /** A step level environment variable. */
class StepEnv extends Env {
string envName;
Step step;
From 9fba7d31f1cb8183e6617f24052efa7b6d1bef4c Mon Sep 17 00:00:00 2001
From: jarlob
Date: Wed, 5 Apr 2023 10:24:07 +0200
Subject: [PATCH 021/870] Improve documentation
---
.../src/Security/CWE-094/ExpressionInjection.qhelp | 14 +++++++++++++-
.../CWE-094/examples/comment_issue_bad_env.yml | 10 ++++++++++
2 files changed, 23 insertions(+), 1 deletion(-)
create mode 100644 javascript/ql/src/Security/CWE-094/examples/comment_issue_bad_env.yml
diff --git a/javascript/ql/src/Security/CWE-094/ExpressionInjection.qhelp b/javascript/ql/src/Security/CWE-094/ExpressionInjection.qhelp
index 6e248eb380e..dbc1196ae66 100644
--- a/javascript/ql/src/Security/CWE-094/ExpressionInjection.qhelp
+++ b/javascript/ql/src/Security/CWE-094/ExpressionInjection.qhelp
@@ -21,7 +21,7 @@
The best practice to avoid code injection vulnerabilities
in GitHub workflows is to set the untrusted input value of the expression
to an intermediate environment variable and then use the environment variable
- using the native syntax of the shell/script interpreter (i.e. NOT the ${{ env.VAR }}).
+ using the native syntax of the shell/script interpreter (i.e. NOT the ${{ env.VAR }}).
It is also recommended to limit the permissions of any tokens used
@@ -40,6 +40,18 @@
the environment variable and will prevent the attack:
+
+
+ The following example uses an environment variable, but
+ still allows injection because of the use of expression syntax:
+
+
+
+
+ The following example uses shell syntax to read
+ the environment variable and will prevent the attack:
+
+
diff --git a/javascript/ql/src/Security/CWE-094/examples/comment_issue_bad_env.yml b/javascript/ql/src/Security/CWE-094/examples/comment_issue_bad_env.yml
new file mode 100644
index 00000000000..b7698938de7
--- /dev/null
+++ b/javascript/ql/src/Security/CWE-094/examples/comment_issue_bad_env.yml
@@ -0,0 +1,10 @@
+on: issue_comment
+
+jobs:
+ echo-body:
+ runs-on: ubuntu-latest
+ steps:
+ - env:
+ BODY: ${{ github.event.issue.body }}
+ run: |
+ echo '${{ env.BODY }}'
\ No newline at end of file
From 40635e60d1df85f4deeabe4fc82f4509c6e414a0 Mon Sep 17 00:00:00 2001
From: jarlob
Date: Wed, 5 Apr 2023 10:26:02 +0200
Subject: [PATCH 022/870] Improve documentation
---
.../ql/src/Security/CWE-094/ExpressionInjection.qhelp | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/javascript/ql/src/Security/CWE-094/ExpressionInjection.qhelp b/javascript/ql/src/Security/CWE-094/ExpressionInjection.qhelp
index dbc1196ae66..d010a75a46b 100644
--- a/javascript/ql/src/Security/CWE-094/ExpressionInjection.qhelp
+++ b/javascript/ql/src/Security/CWE-094/ExpressionInjection.qhelp
@@ -35,15 +35,9 @@
-
- The following example uses shell syntax to read
- the environment variable and will prevent the attack:
-
-
-
The following example uses an environment variable, but
- still allows injection because of the use of expression syntax:
+ still allows the injection because of the use of expression syntax:
From 0a878d4db945f59f9ca8e9840e67b87a9712d21b Mon Sep 17 00:00:00 2001
From: jarlob
Date: Thu, 6 Apr 2023 19:07:38 +0200
Subject: [PATCH 023/870] Support yAml extensions
---
javascript/ql/lib/semmle/javascript/Actions.qll | 2 +-
.../.github/workflows/{issues.yml => issues.yaml} | 0
2 files changed, 1 insertion(+), 1 deletion(-)
rename javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/{issues.yml => issues.yaml} (100%)
diff --git a/javascript/ql/lib/semmle/javascript/Actions.qll b/javascript/ql/lib/semmle/javascript/Actions.qll
index 85b313af8a3..d8378ea347b 100644
--- a/javascript/ql/lib/semmle/javascript/Actions.qll
+++ b/javascript/ql/lib/semmle/javascript/Actions.qll
@@ -16,7 +16,7 @@ module Actions {
this.getLocation()
.getFile()
.getRelativePath()
- .regexpMatch("(^|.*/)\\.github/workflows/.*\\.yml$")
+ .regexpMatch("(^|.*/)\\.github/workflows/.*\\.y(?:a?)ml$")
}
}
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/issues.yml b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/issues.yaml
similarity index 100%
rename from javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/issues.yml
rename to javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/.github/workflows/issues.yaml
From baefeab2d13f204368f0ec4c706acc5e6a4f940b Mon Sep 17 00:00:00 2001
From: jarlob
Date: Thu, 6 Apr 2023 19:11:04 +0200
Subject: [PATCH 024/870] fix tests
---
.../ExpressionInjection/ExpressionInjection.expected | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected
index 457cb815e6a..21248e7f5b2 100644
--- a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected
@@ -15,11 +15,11 @@
| .github/workflows/gollum.yml:8:12:8:53 | echo '$ ... tle }}' | Potential injection from the ${ github.event.pages[11].title }, which may be controlled by an external user. |
| .github/workflows/gollum.yml:9:12:9:56 | echo '$ ... ame }}' | Potential injection from the ${ github.event.pages[0].page_name }, which may be controlled by an external user. |
| .github/workflows/gollum.yml:10:12:10:59 | echo '$ ... ame }}' | Potential injection from the ${ github.event.pages[2222].page_name }, which may be controlled by an external user. |
-| .github/workflows/issues.yml:13:12:13:49 | echo '$ ... tle }}' | Potential injection from the ${ github.event.issue.title }, which may be controlled by an external user. |
-| .github/workflows/issues.yml:14:12:14:48 | echo '$ ... ody }}' | Potential injection from the ${ github.event.issue.body }, which may be controlled by an external user. |
-| .github/workflows/issues.yml:15:12:15:39 | echo '$ ... env }}' | Potential injection from the ${ env.global_env }, which may be controlled by an external user. |
-| .github/workflows/issues.yml:17:12:17:36 | echo '$ ... env }}' | Potential injection from the ${ env.job_env }, which may be controlled by an external user. |
-| .github/workflows/issues.yml:18:12:18:37 | echo '$ ... env }}' | Potential injection from the ${ env.step_env }, which may be controlled by an external user. |
+| .github/workflows/issues.yaml:13:12:13:49 | echo '$ ... tle }}' | Potential injection from the ${ github.event.issue.title }, which may be controlled by an external user. |
+| .github/workflows/issues.yaml:14:12:14:48 | echo '$ ... ody }}' | Potential injection from the ${ github.event.issue.body }, which may be controlled by an external user. |
+| .github/workflows/issues.yaml:15:12:15:39 | echo '$ ... env }}' | Potential injection from the ${ env.global_env }, which may be controlled by an external user. |
+| .github/workflows/issues.yaml:17:12:17:36 | echo '$ ... env }}' | Potential injection from the ${ env.job_env }, which may be controlled by an external user. |
+| .github/workflows/issues.yaml:18:12:18:37 | echo '$ ... env }}' | Potential injection from the ${ env.step_env }, which may be controlled by an external user. |
| .github/workflows/pull_request_review.yml:7:12:7:56 | echo '$ ... tle }}' | Potential injection from the ${ github.event.pull_request.title }, which may be controlled by an external user. |
| .github/workflows/pull_request_review.yml:8:12:8:55 | echo '$ ... ody }}' | Potential injection from the ${ github.event.pull_request.body }, which may be controlled by an external user. |
| .github/workflows/pull_request_review.yml:9:12:9:61 | echo '$ ... bel }}' | Potential injection from the ${ github.event.pull_request.head.label }, which may be controlled by an external user. |
From 9c7eecf547e805f400d3d0334e1ca154ad0c4f49 Mon Sep 17 00:00:00 2001
From: jarlob
Date: Thu, 6 Apr 2023 22:53:59 +0200
Subject: [PATCH 025/870] Add support for composite actions
---
.../ql/lib/semmle/javascript/Actions.qll | 69 +++++--
.../Security/CWE-094/ExpressionInjection.ql | 172 ++++++++++++------
.../ExpressionInjection.expected | 1 +
.../CWE-094/ExpressionInjection/action.yml | 14 ++
4 files changed, 184 insertions(+), 72 deletions(-)
create mode 100644 javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/action.yml
diff --git a/javascript/ql/lib/semmle/javascript/Actions.qll b/javascript/ql/lib/semmle/javascript/Actions.qll
index d8378ea347b..6faeeb15987 100644
--- a/javascript/ql/lib/semmle/javascript/Actions.qll
+++ b/javascript/ql/lib/semmle/javascript/Actions.qll
@@ -10,16 +10,62 @@ import javascript
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions.
*/
module Actions {
- /** A YAML node in a GitHub Actions workflow file. */
+ /** A YAML node in a GitHub Actions workflow or custom action file. */
private class Node extends YamlNode {
Node() {
- this.getLocation()
- .getFile()
- .getRelativePath()
- .regexpMatch("(^|.*/)\\.github/workflows/.*\\.y(?:a?)ml$")
+ exists(File f |
+ f = this.getLocation().getFile() and
+ (
+ f.getRelativePath().regexpMatch("(^|.*/)\\.github/workflows/.*\\.y(?:a?)ml$")
+ or
+ f.getBaseName() = "action.yml"
+ )
+ )
}
}
+ /**
+ * A custom action. This is a mapping at the top level of an Actions YAML action file.
+ * See https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions.
+ */
+ class Action extends Node, YamlDocument, YamlMapping {
+ /** Gets the `runs` mapping. */
+ Runs getRuns() { result = this.lookup("runs") }
+ }
+
+ /**
+ * An `runs` mapping in a custom action YAML.
+ * See https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#runs
+ */
+ class Runs extends StepsContainer {
+ Action action;
+
+ Runs() { action.lookup("runs") = this }
+
+ /** Gets the action that this `runs` mapping is in. */
+ Action getAction() { result = action }
+ }
+
+ /**
+ * The parent class of the class that can contain `steps` mappings. (`Job` or `Runs` currently.)
+ */
+ abstract class StepsContainer extends YamlNode, YamlMapping {
+ /** Gets the sequence of `steps` within this YAML node. */
+ YamlSequence getSteps() { result = this.lookup("steps") }
+ }
+
+ /**
+ * A `using` mapping in a custom action YAML.
+ */
+ class Using extends YamlNode, YamlScalar {
+ Runs runs;
+
+ Using() { runs.lookup("using") = this }
+
+ /** Gets the `runs` mapping that this `using` mapping is in. */
+ Runs getRuns() { result = runs }
+ }
+
/**
* An Actions workflow. This is a mapping at the top level of an Actions YAML workflow file.
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions.
@@ -109,7 +155,7 @@ module Actions {
* An Actions job within a workflow.
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobs.
*/
- class Job extends YamlNode, YamlMapping {
+ class Job extends StepsContainer {
string jobId;
Workflow workflow;
@@ -136,9 +182,6 @@ module Actions {
/** Gets the step at the given index within this job. */
Step getStep(int index) { result.getJob() = this and result.getIndex() = index }
- /** Gets the sequence of `steps` within this job. */
- YamlSequence getSteps() { result = this.lookup("steps") }
-
/** Gets the `env` mapping in this job. */
YamlMapping getEnv() { result = this.lookup("env") }
@@ -184,15 +227,17 @@ module Actions {
*/
class Step extends YamlNode, YamlMapping {
int index;
- Job job;
+ StepsContainer parent;
- Step() { this = job.getSteps().getElement(index) }
+ Step() { this = parent.getSteps().getElement(index) }
/** Gets the 0-based position of this step within the sequence of `steps`. */
int getIndex() { result = index }
/** Gets the job this step belongs to. */
- Job getJob() { result = job }
+ Job getJob() { result = parent.(Job) }
+
+ Runs getRuns() { result = parent.(Runs) }
/** Gets the value of the `uses` field in this step, if any. */
Uses getUses() { result.getStep() = this }
diff --git a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
index 056d7204551..696fe0a0186 100644
--- a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
+++ b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
@@ -103,70 +103,122 @@ private predicate isExternalUserControlledWorkflowRun(string context) {
)
}
-from YamlNode node, string injection, string context, Actions::On on
+/**
+ * The env variable name in `${{ env.name }}`
+ * is where the external user controlled value was assigned to.
+ */
+bindingset[injection]
+predicate isEnvTainted(Actions::Env env, string injection, string context) {
+ Actions::getEnvName(injection) = env.getName() and
+ Actions::getASimpleReferenceExpression(env) = context
+}
+
+/**
+ * Holds if the `run` contains any expression interpolation `${{ e }}`.
+ * Sets `context` to the initial untrusted value assignment in case of `${{ env... }}` interpolation
+ */
+predicate isRunInjectable(Actions::Run run, string injection, string context) {
+ Actions::getASimpleReferenceExpression(run) = injection and
+ (
+ injection = context
+ or
+ exists(Actions::Env env | isEnvTainted(env, injection, context))
+ )
+}
+
+/**
+ * Holds if the `actions/github-script` contains any expression interpolation `${{ e }}`.
+ * Sets `context` to the initial untrusted value assignment in case of `${{ env... }}` interpolation
+ */
+predicate isScriptInjectable(Actions::Script script, string injection, string context) {
+ exists(Actions::Step step, Actions::Uses uses |
+ script.getWith().getStep() = step and
+ uses.getStep() = step and
+ uses.getGitHubRepository() = "actions/github-script" and
+ Actions::getASimpleReferenceExpression(script) = injection and
+ (
+ injection = context
+ or
+ exists(Actions::Env env | isEnvTainted(env, injection, context))
+ )
+ )
+}
+
+from YamlNode node, string injection, string context
where
- (
- exists(Actions::Run run |
- node = run and
- Actions::getASimpleReferenceExpression(run) = injection and
- run.getStep().getJob().getWorkflow().getOn() = on and
- (
- injection = context
- or
- exists(Actions::Env env |
- Actions::getEnvName(injection) = env.getName() and
- Actions::getASimpleReferenceExpression(env) = context
- )
+ exists(Actions::Using u, Actions::Runs runs |
+ u.getValue() = "composite" and
+ u.getRuns() = runs and
+ (
+ exists(Actions::Run run |
+ isRunInjectable(run, injection, context) and
+ node = run and
+ run.getStep().getRuns() = runs
)
- )
- or
- exists(Actions::Script script, Actions::Step step, Actions::Uses uses |
- node = script and
- script.getWith().getStep().getJob().getWorkflow().getOn() = on and
- script.getWith().getStep() = step and
- uses.getStep() = step and
- uses.getGitHubRepository() = "actions/github-script" and
- Actions::getASimpleReferenceExpression(script) = injection and
- (
- injection = context
- or
- exists(Actions::Env env |
- Actions::getEnvName(injection) = env.getName() and
- Actions::getASimpleReferenceExpression(env) = context
- )
+ or
+ exists(Actions::Script script |
+ node = script and
+ script.getWith().getStep().getRuns() = runs and
+ isScriptInjectable(script, injection, context)
)
+ ) and
+ (
+ isExternalUserControlledIssue(context) or
+ isExternalUserControlledPullRequest(context) or
+ isExternalUserControlledReview(context) or
+ isExternalUserControlledComment(context) or
+ isExternalUserControlledGollum(context) or
+ isExternalUserControlledCommit(context) or
+ isExternalUserControlledDiscussion(context) or
+ isExternalUserControlledWorkflowRun(context)
+ )
+ )
+ or
+ exists(Actions::On on |
+ (
+ exists(Actions::Run run |
+ isRunInjectable(run, injection, context) and
+ node = run and
+ run.getStep().getJob().getWorkflow().getOn() = on
+ )
+ or
+ exists(Actions::Script script |
+ node = script and
+ script.getWith().getStep().getJob().getWorkflow().getOn() = on and
+ isScriptInjectable(script, injection, context)
+ )
+ ) and
+ (
+ exists(on.getNode("issues")) and
+ isExternalUserControlledIssue(context)
+ or
+ exists(on.getNode("pull_request_target")) and
+ isExternalUserControlledPullRequest(context)
+ or
+ exists(on.getNode("pull_request_review")) and
+ (isExternalUserControlledReview(context) or isExternalUserControlledPullRequest(context))
+ or
+ exists(on.getNode("pull_request_review_comment")) and
+ (isExternalUserControlledComment(context) or isExternalUserControlledPullRequest(context))
+ or
+ exists(on.getNode("issue_comment")) and
+ (isExternalUserControlledComment(context) or isExternalUserControlledIssue(context))
+ or
+ exists(on.getNode("gollum")) and
+ isExternalUserControlledGollum(context)
+ or
+ exists(on.getNode("push")) and
+ isExternalUserControlledCommit(context)
+ or
+ exists(on.getNode("discussion")) and
+ isExternalUserControlledDiscussion(context)
+ or
+ exists(on.getNode("discussion_comment")) and
+ (isExternalUserControlledDiscussion(context) or isExternalUserControlledComment(context))
+ or
+ exists(on.getNode("workflow_run")) and
+ isExternalUserControlledWorkflowRun(context)
)
- ) and
- (
- exists(on.getNode("issues")) and
- isExternalUserControlledIssue(context)
- or
- exists(on.getNode("pull_request_target")) and
- isExternalUserControlledPullRequest(context)
- or
- exists(on.getNode("pull_request_review")) and
- (isExternalUserControlledReview(context) or isExternalUserControlledPullRequest(context))
- or
- exists(on.getNode("pull_request_review_comment")) and
- (isExternalUserControlledComment(context) or isExternalUserControlledPullRequest(context))
- or
- exists(on.getNode("issue_comment")) and
- (isExternalUserControlledComment(context) or isExternalUserControlledIssue(context))
- or
- exists(on.getNode("gollum")) and
- isExternalUserControlledGollum(context)
- or
- exists(on.getNode("push")) and
- isExternalUserControlledCommit(context)
- or
- exists(on.getNode("discussion")) and
- isExternalUserControlledDiscussion(context)
- or
- exists(on.getNode("discussion_comment")) and
- (isExternalUserControlledDiscussion(context) or isExternalUserControlledComment(context))
- or
- exists(on.getNode("workflow_run")) and
- isExternalUserControlledWorkflowRun(context)
)
select node,
"Potential injection from the ${ " + injection +
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected
index 21248e7f5b2..76ad174e16a 100644
--- a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected
@@ -62,3 +62,4 @@
| .github/workflows/workflow_run.yml:14:12:14:77 | echo '$ ... ame }}' | Potential injection from the ${ github.event.workflow_run.head_commit.committer.name }, which may be controlled by an external user. |
| .github/workflows/workflow_run.yml:15:12:15:62 | echo '$ ... nch }}' | Potential injection from the ${ github.event.workflow_run.head_branch }, which may be controlled by an external user. |
| .github/workflows/workflow_run.yml:16:12:16:78 | echo '$ ... ion }}' | Potential injection from the ${ github.event.workflow_run.head_repository.description }, which may be controlled by an external user. |
+| action.yml:14:12:14:50 | echo '$ ... ody }}' | Potential injection from the ${ github.event.comment.body }, which may be controlled by an external user. |
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/action.yml b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/action.yml
new file mode 100644
index 00000000000..e9a178cf3db
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/action.yml
@@ -0,0 +1,14 @@
+name: 'test'
+description: 'test'
+branding:
+ icon: 'test'
+ color: 'test'
+inputs:
+ test:
+ description: test
+ required: false
+ default: 'test'
+runs:
+ using: "composite"
+ steps:
+ - run: echo '${{ github.event.comment.body }}'
\ No newline at end of file
From af83d8af415fb6bae60cad0d728201af20cd347a Mon Sep 17 00:00:00 2001
From: jarlob
Date: Thu, 6 Apr 2023 22:59:09 +0200
Subject: [PATCH 026/870] Add comment
---
javascript/ql/lib/semmle/javascript/Actions.qll | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/javascript/ql/lib/semmle/javascript/Actions.qll b/javascript/ql/lib/semmle/javascript/Actions.qll
index 6faeeb15987..f0585b915b4 100644
--- a/javascript/ql/lib/semmle/javascript/Actions.qll
+++ b/javascript/ql/lib/semmle/javascript/Actions.qll
@@ -234,9 +234,10 @@ module Actions {
/** Gets the 0-based position of this step within the sequence of `steps`. */
int getIndex() { result = index }
- /** Gets the job this step belongs to. */
+ /** Gets the `job` this step belongs to. The step may belong to a `job` in a workflow or `runs` in a custom action. */
Job getJob() { result = parent.(Job) }
+ /** Gets the `runs` this step belongs to. The step may belong to a `job` in a workflow or `runs` in a custom action. */
Runs getRuns() { result = parent.(Runs) }
/** Gets the value of the `uses` field in this step, if any. */
From 3745ccceddaa0dd5dc2d34fa54f30bf08f9a89c1 Mon Sep 17 00:00:00 2001
From: jarlob
Date: Thu, 6 Apr 2023 23:02:08 +0200
Subject: [PATCH 027/870] Fix warnings
---
javascript/ql/lib/semmle/javascript/Actions.qll | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/Actions.qll b/javascript/ql/lib/semmle/javascript/Actions.qll
index f0585b915b4..50b11b18df6 100644
--- a/javascript/ql/lib/semmle/javascript/Actions.qll
+++ b/javascript/ql/lib/semmle/javascript/Actions.qll
@@ -10,7 +10,7 @@ import javascript
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions.
*/
module Actions {
- /** A YAML node in a GitHub Actions workflow or custom action file. */
+ /** A YAML node in a GitHub Actions workflow or a custom action file. */
private class Node extends YamlNode {
Node() {
exists(File f |
@@ -235,10 +235,10 @@ module Actions {
int getIndex() { result = index }
/** Gets the `job` this step belongs to. The step may belong to a `job` in a workflow or `runs` in a custom action. */
- Job getJob() { result = parent.(Job) }
+ Job getJob() { result = parent }
/** Gets the `runs` this step belongs to. The step may belong to a `job` in a workflow or `runs` in a custom action. */
- Runs getRuns() { result = parent.(Runs) }
+ Runs getRuns() { result = parent }
/** Gets the value of the `uses` field in this step, if any. */
Uses getUses() { result.getStep() = this }
From 7573c615f6dff90cb154285f1b8be0027ee87f48 Mon Sep 17 00:00:00 2001
From: jarlob
Date: Thu, 6 Apr 2023 23:07:22 +0200
Subject: [PATCH 028/870] Fix warnings
---
javascript/ql/src/Security/CWE-094/ExpressionInjection.ql | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
index 696fe0a0186..9e703d2d47c 100644
--- a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
+++ b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
@@ -104,7 +104,7 @@ private predicate isExternalUserControlledWorkflowRun(string context) {
}
/**
- * The env variable name in `${{ env.name }}`
+ * Holds if the env variable name in `${{ env.name }}`
* is where the external user controlled value was assigned to.
*/
bindingset[injection]
@@ -122,7 +122,7 @@ predicate isRunInjectable(Actions::Run run, string injection, string context) {
(
injection = context
or
- exists(Actions::Env env | isEnvTainted(env, injection, context))
+ isEnvTainted(_, injection, context)
)
}
@@ -139,7 +139,7 @@ predicate isScriptInjectable(Actions::Script script, string injection, string co
(
injection = context
or
- exists(Actions::Env env | isEnvTainted(env, injection, context))
+ isEnvTainted(_, injection, context)
)
)
}
From 72b66ffe97f153902d2b6483fec3e0bb9196c3bb Mon Sep 17 00:00:00 2001
From: jarlob
Date: Fri, 7 Apr 2023 10:01:14 +0200
Subject: [PATCH 029/870] Fix comment.
---
.../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 a81c8c65d25..71171af6ead 100644
--- a/javascript/ql/src/experimental/Security/CWE-094/UntrustedCheckout.ql
+++ b/javascript/ql/src/experimental/Security/CWE-094/UntrustedCheckout.ql
@@ -53,7 +53,7 @@ class ProbableJob extends Actions::Job {
}
/**
- * on: pull_request_target
+ * The `on: pull_request_target`.
*/
class ProbablePullRequestTarget extends Actions::On, YamlMappingLikeNode {
ProbablePullRequestTarget() {
From 8f1bccbb4d67c2d415c825d6b787f40e4b6c1269 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jaroslav=20Loba=C4=8Devski?=
Date: Thu, 13 Apr 2023 22:55:53 +0200
Subject: [PATCH 030/870] Apply suggestions from code review (comments)
Co-authored-by: Aditya Sharad <6874315+adityasharad@users.noreply.github.com>
---
javascript/ql/lib/change-notes/2023-04-03-gh-injection.md | 2 +-
javascript/ql/lib/semmle/javascript/Actions.qll | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/javascript/ql/lib/change-notes/2023-04-03-gh-injection.md b/javascript/ql/lib/change-notes/2023-04-03-gh-injection.md
index 04a5a2f4b6f..cabcb0b828d 100644
--- a/javascript/ql/lib/change-notes/2023-04-03-gh-injection.md
+++ b/javascript/ql/lib/change-notes/2023-04-03-gh-injection.md
@@ -1,4 +1,4 @@
---
category: minorAnalysis
---
-* Fixes and improvements in GitHub Actions Injection query.
\ No newline at end of file
+* Improved the queries for injection vulnerabilities in GitHub Actions workflows (`js/actions/command-injection` and `js/actions/pull-request-target`) and the associated library `semmle.javascript.Actions`. These now support steps defined in composite actions, in addition to steps defined in Actions workflow files.
\ No newline at end of file
diff --git a/javascript/ql/lib/semmle/javascript/Actions.qll b/javascript/ql/lib/semmle/javascript/Actions.qll
index 50b11b18df6..e770920d78c 100644
--- a/javascript/ql/lib/semmle/javascript/Actions.qll
+++ b/javascript/ql/lib/semmle/javascript/Actions.qll
@@ -234,10 +234,10 @@ module Actions {
/** Gets the 0-based position of this step within the sequence of `steps`. */
int getIndex() { result = index }
- /** Gets the `job` this step belongs to. The step may belong to a `job` in a workflow or `runs` in a custom action. */
+ /** Gets the `job` this step belongs to, if the step belongs to a `job` in a workflow. Has no result if the step belongs to `runs` in a custom action. */
Job getJob() { result = parent }
- /** Gets the `runs` this step belongs to. The step may belong to a `job` in a workflow or `runs` in a custom action. */
+ /** Gets the `runs` this step belongs to, if the step belongs to a `runs` in a custom action. Has no result if the step belongs to a `job` in a workflow. */
Runs getRuns() { result = parent }
/** Gets the value of the `uses` field in this step, if any. */
From 6790318769e5170054a67c6f77382672a9d1bad2 Mon Sep 17 00:00:00 2001
From: jarlob
Date: Thu, 13 Apr 2023 22:58:32 +0200
Subject: [PATCH 031/870] Added the composite word
---
javascript/ql/lib/semmle/javascript/Actions.qll | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/Actions.qll b/javascript/ql/lib/semmle/javascript/Actions.qll
index e770920d78c..009a195521a 100644
--- a/javascript/ql/lib/semmle/javascript/Actions.qll
+++ b/javascript/ql/lib/semmle/javascript/Actions.qll
@@ -10,7 +10,7 @@ import javascript
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions.
*/
module Actions {
- /** A YAML node in a GitHub Actions workflow or a custom action file. */
+ /** A YAML node in a GitHub Actions workflow or a custom composite action file. */
private class Node extends YamlNode {
Node() {
exists(File f |
@@ -25,7 +25,7 @@ module Actions {
}
/**
- * A custom action. This is a mapping at the top level of an Actions YAML action file.
+ * A custom composite action. This is a mapping at the top level of an Actions YAML action file.
* See https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions.
*/
class Action extends Node, YamlDocument, YamlMapping {
@@ -34,7 +34,7 @@ module Actions {
}
/**
- * An `runs` mapping in a custom action YAML.
+ * An `runs` mapping in a custom composite action YAML.
* See https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#runs
*/
class Runs extends StepsContainer {
@@ -55,7 +55,7 @@ module Actions {
}
/**
- * A `using` mapping in a custom action YAML.
+ * A `using` mapping in a custom composite action YAML.
*/
class Using extends YamlNode, YamlScalar {
Runs runs;
@@ -234,10 +234,10 @@ module Actions {
/** Gets the 0-based position of this step within the sequence of `steps`. */
int getIndex() { result = index }
- /** Gets the `job` this step belongs to, if the step belongs to a `job` in a workflow. Has no result if the step belongs to `runs` in a custom action. */
+ /** Gets the `job` this step belongs to, if the step belongs to a `job` in a workflow. Has no result if the step belongs to `runs` in a custom composite action. */
Job getJob() { result = parent }
- /** Gets the `runs` this step belongs to, if the step belongs to a `runs` in a custom action. Has no result if the step belongs to a `job` in a workflow. */
+ /** Gets the `runs` this step belongs to, if the step belongs to a `runs` in a custom composite action. Has no result if the step belongs to a `job` in a workflow. */
Runs getRuns() { result = parent }
/** Gets the value of the `uses` field in this step, if any. */
From 8234ea33f0ec39f4dc3978b4ca6f39a3a0eae83d Mon Sep 17 00:00:00 2001
From: jarlob
Date: Thu, 13 Apr 2023 23:05:32 +0200
Subject: [PATCH 032/870] More details in the changes file.
---
javascript/ql/lib/change-notes/2023-04-03-gh-injection.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/lib/change-notes/2023-04-03-gh-injection.md b/javascript/ql/lib/change-notes/2023-04-03-gh-injection.md
index cabcb0b828d..8cc9626e478 100644
--- a/javascript/ql/lib/change-notes/2023-04-03-gh-injection.md
+++ b/javascript/ql/lib/change-notes/2023-04-03-gh-injection.md
@@ -1,4 +1,4 @@
---
category: minorAnalysis
---
-* Improved the queries for injection vulnerabilities in GitHub Actions workflows (`js/actions/command-injection` and `js/actions/pull-request-target`) and the associated library `semmle.javascript.Actions`. These now support steps defined in composite actions, in addition to steps defined in Actions workflow files.
\ No newline at end of file
+* Improved the queries for injection vulnerabilities in GitHub Actions workflows (`js/actions/command-injection` and `js/actions/pull-request-target`) and the associated library `semmle.javascript.Actions`. These now support steps defined in composite actions, in addition to steps defined in Actions workflow files. It supports more potentially untrusted input values. Additioanlly to the shell injections it now also detects injections in `actions/github-script`. It also detects simple injections from user controlled `${{ env.name }}`. Additionally to the `yml` extension now it also supports workflows with the `yaml` extension.
\ No newline at end of file
From a8a6913512e84bc0c047faf06d46292e6c841d24 Mon Sep 17 00:00:00 2001
From: jarlob
Date: Thu, 13 Apr 2023 23:10:16 +0200
Subject: [PATCH 033/870] Simplify `exists` according to the warning
---
.../src/experimental/Security/CWE-094/UntrustedCheckout.ql | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-094/UntrustedCheckout.ql b/javascript/ql/src/experimental/Security/CWE-094/UntrustedCheckout.ql
index 71171af6ead..3f08f297c6a 100644
--- a/javascript/ql/src/experimental/Security/CWE-094/UntrustedCheckout.ql
+++ b/javascript/ql/src/experimental/Security/CWE-094/UntrustedCheckout.ql
@@ -57,10 +57,8 @@ class ProbableJob extends Actions::Job {
*/
class ProbablePullRequestTarget extends Actions::On, YamlMappingLikeNode {
ProbablePullRequestTarget() {
- exists(YamlNode prtNode |
- // The `on:` is triggered on `pull_request_target`
- this.getNode("pull_request_target") = prtNode
- )
+ // The `on:` is triggered on `pull_request_target`
+ exists(this.getNode("pull_request_target"))
}
}
From 76834cbe53760aabe65a55905756cbf873a60888 Mon Sep 17 00:00:00 2001
From: jarlob
Date: Thu, 13 Apr 2023 23:13:56 +0200
Subject: [PATCH 034/870] Rename GlobalEnv
---
javascript/ql/lib/semmle/javascript/Actions.qll | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/Actions.qll b/javascript/ql/lib/semmle/javascript/Actions.qll
index 009a195521a..bb27c9af068 100644
--- a/javascript/ql/lib/semmle/javascript/Actions.qll
+++ b/javascript/ql/lib/semmle/javascript/Actions.qll
@@ -110,11 +110,11 @@ module Actions {
}
/** A workflow level 'global' environment variable. */
- class GlobalEnv extends Env {
+ class WorkflowEnvVariable extends Env {
string envName;
Workflow workflow;
- GlobalEnv() { this = workflow.getEnv().lookup(envName) }
+ WorkflowEnvVariable() { this = workflow.getEnv().lookup(envName) }
/** Gets the workflow this field belongs to. */
Workflow getWorkflow() { result = workflow }
From dd52ef85cdcdf72857c3efc6386b7560ad0369e0 Mon Sep 17 00:00:00 2001
From: jarlob
Date: Thu, 13 Apr 2023 23:41:31 +0200
Subject: [PATCH 035/870] Rename Env
---
javascript/ql/lib/semmle/javascript/Actions.qll | 12 ++++++------
.../ql/src/Security/CWE-094/ExpressionInjection.ql | 2 +-
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/Actions.qll b/javascript/ql/lib/semmle/javascript/Actions.qll
index bb27c9af068..da8e9cfcee3 100644
--- a/javascript/ql/lib/semmle/javascript/Actions.qll
+++ b/javascript/ql/lib/semmle/javascript/Actions.qll
@@ -104,13 +104,13 @@ module Actions {
}
/** An environment variable in 'env:' */
- abstract class Env extends YamlNode, YamlString {
+ abstract class EnvVariable extends YamlNode, YamlString {
/** Gets the name of this environment variable. */
abstract string getName();
}
/** A workflow level 'global' environment variable. */
- class WorkflowEnvVariable extends Env {
+ class WorkflowEnvVariable extends EnvVariable {
string envName;
Workflow workflow;
@@ -124,11 +124,11 @@ module Actions {
}
/** A job level environment variable. */
- class JobEnv extends Env {
+ class JobEnvVariable extends EnvVariable {
string envName;
Job job;
- JobEnv() { this = job.getEnv().lookup(envName) }
+ JobEnvVariable() { this = job.getEnv().lookup(envName) }
/** Gets the job this field belongs to. */
Job getJob() { result = job }
@@ -138,11 +138,11 @@ module Actions {
}
/** A step level environment variable. */
- class StepEnv extends Env {
+ class StepEnvVariable extends EnvVariable {
string envName;
Step step;
- StepEnv() { this = step.getEnv().lookup(envName) }
+ StepEnvVariable() { this = step.getEnv().lookup(envName) }
/** Gets the step this field belongs to. */
Step getStep() { result = step }
diff --git a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
index 9e703d2d47c..9108b051966 100644
--- a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
+++ b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
@@ -108,7 +108,7 @@ private predicate isExternalUserControlledWorkflowRun(string context) {
* is where the external user controlled value was assigned to.
*/
bindingset[injection]
-predicate isEnvTainted(Actions::Env env, string injection, string context) {
+predicate isEnvTainted(Actions::EnvVariable env, string injection, string context) {
Actions::getEnvName(injection) = env.getName() and
Actions::getASimpleReferenceExpression(env) = context
}
From 79218a3946723945f9a99b43626f9d0816ae9e18 Mon Sep 17 00:00:00 2001
From: jarlob
Date: Fri, 14 Apr 2023 00:56:51 +0200
Subject: [PATCH 036/870] Use `YamlMapping` for modeling `Env`
---
.../ql/lib/semmle/javascript/Actions.qll | 42 ++++++-------------
.../Security/CWE-094/ExpressionInjection.ql | 13 +++---
2 files changed, 21 insertions(+), 34 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/Actions.qll b/javascript/ql/lib/semmle/javascript/Actions.qll
index da8e9cfcee3..efbcd8fd8f9 100644
--- a/javascript/ql/lib/semmle/javascript/Actions.qll
+++ b/javascript/ql/lib/semmle/javascript/Actions.qll
@@ -75,7 +75,7 @@ module Actions {
YamlMapping getJobs() { result = this.lookup("jobs") }
/** Gets the 'global' `env` mapping in this workflow. */
- YamlMapping getEnv() { result = this.lookup("env") }
+ WorkflowEnv getEnv() { result = this.lookup("env") }
/** Gets the name of the workflow. */
string getName() { result = this.lookup("name").(YamlString).getValue() }
@@ -103,52 +103,36 @@ module Actions {
Workflow getWorkflow() { result = workflow }
}
- /** An environment variable in 'env:' */
- abstract class EnvVariable extends YamlNode, YamlString {
- /** Gets the name of this environment variable. */
- abstract string getName();
- }
+ abstract class Env extends YamlNode, YamlMapping { }
- /** A workflow level 'global' environment variable. */
- class WorkflowEnvVariable extends EnvVariable {
- string envName;
+ /** A workflow level `env` mapping. */
+ class WorkflowEnv extends Env {
Workflow workflow;
- WorkflowEnvVariable() { this = workflow.getEnv().lookup(envName) }
+ WorkflowEnv() { workflow.lookup("env") = this }
/** Gets the workflow this field belongs to. */
Workflow getWorkflow() { result = workflow }
-
- /** Gets the name of this environment variable. */
- override string getName() { result = envName }
}
- /** A job level environment variable. */
- class JobEnvVariable extends EnvVariable {
- string envName;
+ /** A job level `env` mapping. */
+ class JobEnv extends Env {
Job job;
- JobEnvVariable() { this = job.getEnv().lookup(envName) }
+ JobEnv() { job.lookup("env") = this }
/** Gets the job this field belongs to. */
Job getJob() { result = job }
-
- /** Gets the name of this environment variable. */
- override string getName() { result = envName }
}
- /** A step level environment variable. */
- class StepEnvVariable extends EnvVariable {
- string envName;
+ /** A step level `env` mapping. */
+ class StepEnv extends Env {
Step step;
- StepEnvVariable() { this = step.getEnv().lookup(envName) }
+ StepEnv() { step.lookup("env") = this }
/** Gets the step this field belongs to. */
Step getStep() { result = step }
-
- /** Gets the name of this environment variable. */
- override string getName() { result = envName }
}
/**
@@ -183,7 +167,7 @@ module Actions {
Step getStep(int index) { result.getJob() = this and result.getIndex() = index }
/** Gets the `env` mapping in this job. */
- YamlMapping getEnv() { result = this.lookup("env") }
+ JobEnv getEnv() { result = this.lookup("env") }
/** Gets the workflow this job belongs to. */
Workflow getWorkflow() { result = workflow }
@@ -250,7 +234,7 @@ module Actions {
StepIf getIf() { result.getStep() = this }
/** Gets the value of the `env` field in this step, if any. */
- YamlMapping getEnv() { result = this.lookup("env") }
+ StepEnv getEnv() { result = this.lookup("env") }
/** Gets the ID of this step, if any. */
string getId() { result = this.lookup("id").(YamlString).getValue() }
diff --git a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
index 9108b051966..91405d673f5 100644
--- a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
+++ b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
@@ -108,9 +108,12 @@ private predicate isExternalUserControlledWorkflowRun(string context) {
* is where the external user controlled value was assigned to.
*/
bindingset[injection]
-predicate isEnvTainted(Actions::EnvVariable env, string injection, string context) {
- Actions::getEnvName(injection) = env.getName() and
- Actions::getASimpleReferenceExpression(env) = context
+predicate isEnvTainted(string injection, string context) {
+ exists(Actions::Env env, string envName, YamlString envValue |
+ envValue = env.lookup(envName) and
+ Actions::getEnvName(injection) = envName and
+ Actions::getASimpleReferenceExpression(envValue) = context
+ )
}
/**
@@ -122,7 +125,7 @@ predicate isRunInjectable(Actions::Run run, string injection, string context) {
(
injection = context
or
- isEnvTainted(_, injection, context)
+ isEnvTainted(injection, context)
)
}
@@ -139,7 +142,7 @@ predicate isScriptInjectable(Actions::Script script, string injection, string co
(
injection = context
or
- isEnvTainted(_, injection, context)
+ isEnvTainted(injection, context)
)
)
}
From 94065764d50952f191a83c6b3243d36de342b0ae Mon Sep 17 00:00:00 2001
From: jarlob
Date: Fri, 14 Apr 2023 01:05:21 +0200
Subject: [PATCH 037/870] Make predicate name clearer
---
.../ql/src/Security/CWE-094/ExpressionInjection.ql | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
index 91405d673f5..e04b8f7cb84 100644
--- a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
+++ b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
@@ -104,11 +104,11 @@ private predicate isExternalUserControlledWorkflowRun(string context) {
}
/**
- * Holds if the env variable name in `${{ env.name }}`
- * is where the external user controlled value was assigned to.
+ * Holds if environment name in the `injection` (in a form of `env.name`)
+ * is tainted by the `context` (in a form of `github.event.xxx.xxx`).
*/
bindingset[injection]
-predicate isEnvTainted(string injection, string context) {
+predicate isEnvInterpolationTainted(string injection, string context) {
exists(Actions::Env env, string envName, YamlString envValue |
envValue = env.lookup(envName) and
Actions::getEnvName(injection) = envName and
@@ -125,7 +125,7 @@ predicate isRunInjectable(Actions::Run run, string injection, string context) {
(
injection = context
or
- isEnvTainted(injection, context)
+ isEnvInterpolationTainted(injection, context)
)
}
@@ -142,7 +142,7 @@ predicate isScriptInjectable(Actions::Script script, string injection, string co
(
injection = context
or
- isEnvTainted(injection, context)
+ isEnvInterpolationTainted(injection, context)
)
)
}
From d80c541da640cac3f3d092f8bd14d6cf120c57fe Mon Sep 17 00:00:00 2001
From: jarlob
Date: Fri, 14 Apr 2023 10:06:35 +0200
Subject: [PATCH 038/870] Encapsulate composite actions
---
javascript/ql/lib/semmle/javascript/Actions.qll | 14 +++++++++++---
.../src/Security/CWE-094/ExpressionInjection.ql | 5 ++---
.../ExpressionInjection.expected | 2 +-
.../{ => action1}/action.yml | 0
.../ExpressionInjection/action2/action.yml | 17 +++++++++++++++++
5 files changed, 31 insertions(+), 7 deletions(-)
rename javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/{ => action1}/action.yml (100%)
create mode 100644 javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/action2/action.yml
diff --git a/javascript/ql/lib/semmle/javascript/Actions.qll b/javascript/ql/lib/semmle/javascript/Actions.qll
index efbcd8fd8f9..d57341da14e 100644
--- a/javascript/ql/lib/semmle/javascript/Actions.qll
+++ b/javascript/ql/lib/semmle/javascript/Actions.qll
@@ -28,7 +28,12 @@ module Actions {
* A custom composite action. This is a mapping at the top level of an Actions YAML action file.
* See https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions.
*/
- class Action extends Node, YamlDocument, YamlMapping {
+ class CompositeAction extends Node, YamlDocument, YamlMapping {
+ CompositeAction() {
+ this.getFile().getBaseName() = "action.yml" and
+ this.lookup("runs").(YamlMapping).lookup("using").(YamlScalar).getValue() = "composite"
+ }
+
/** Gets the `runs` mapping. */
Runs getRuns() { result = this.lookup("runs") }
}
@@ -38,12 +43,15 @@ module Actions {
* See https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#runs
*/
class Runs extends StepsContainer {
- Action action;
+ CompositeAction action;
Runs() { action.lookup("runs") = this }
/** Gets the action that this `runs` mapping is in. */
- Action getAction() { result = action }
+ CompositeAction getAction() { result = action }
+
+ /** Gets the `using` mapping. */
+ Using getUsing() { result = this.lookup("using") }
}
/**
diff --git a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
index e04b8f7cb84..bd16f60d070 100644
--- a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
+++ b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
@@ -149,9 +149,8 @@ predicate isScriptInjectable(Actions::Script script, string injection, string co
from YamlNode node, string injection, string context
where
- exists(Actions::Using u, Actions::Runs runs |
- u.getValue() = "composite" and
- u.getRuns() = runs and
+ exists(Actions::CompositeAction action, Actions::Runs runs |
+ action.getRuns() = runs and
(
exists(Actions::Run run |
isRunInjectable(run, injection, context) and
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected
index 76ad174e16a..a67188a976b 100644
--- a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected
@@ -62,4 +62,4 @@
| .github/workflows/workflow_run.yml:14:12:14:77 | echo '$ ... ame }}' | Potential injection from the ${ github.event.workflow_run.head_commit.committer.name }, which may be controlled by an external user. |
| .github/workflows/workflow_run.yml:15:12:15:62 | echo '$ ... nch }}' | Potential injection from the ${ github.event.workflow_run.head_branch }, which may be controlled by an external user. |
| .github/workflows/workflow_run.yml:16:12:16:78 | echo '$ ... ion }}' | Potential injection from the ${ github.event.workflow_run.head_repository.description }, which may be controlled by an external user. |
-| action.yml:14:12:14:50 | echo '$ ... ody }}' | Potential injection from the ${ github.event.comment.body }, which may be controlled by an external user. |
+| action1/action.yml:14:12:14:50 | echo '$ ... ody }}' | Potential injection from the ${ github.event.comment.body }, which may be controlled by an external user. |
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/action.yml b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/action1/action.yml
similarity index 100%
rename from javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/action.yml
rename to javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/action1/action.yml
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/action2/action.yml b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/action2/action.yml
new file mode 100644
index 00000000000..40fe0b31e6a
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/action2/action.yml
@@ -0,0 +1,17 @@
+name: 'Hello World'
+description: 'Greet someone and record the time'
+inputs:
+ who-to-greet: # id of input
+ description: 'Who to greet'
+ required: true
+ default: 'World'
+outputs:
+ time: # id of output
+ description: 'The time we greeted you'
+runs:
+ using: 'docker'
+ steps: # this is actually invalid, used to test we correctly identify composite actions
+ - run: echo '${{ github.event.comment.body }}'
+ image: 'Dockerfile'
+ args:
+ - ${{ inputs.who-to-greet }}
\ No newline at end of file
From ac1c20673d68c56add036583c62d8014a540bfbe Mon Sep 17 00:00:00 2001
From: jarlob
Date: Fri, 14 Apr 2023 10:23:49 +0200
Subject: [PATCH 039/870] Encapsulate github-script
---
.../ql/lib/semmle/javascript/Actions.qll | 27 ++++++++++++++++---
.../Security/CWE-094/ExpressionInjection.ql | 21 ++++++---------
2 files changed, 31 insertions(+), 17 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/Actions.qll b/javascript/ql/lib/semmle/javascript/Actions.qll
index d57341da14e..4bb52cb87cb 100644
--- a/javascript/ql/lib/semmle/javascript/Actions.qll
+++ b/javascript/ql/lib/semmle/javascript/Actions.qll
@@ -368,13 +368,32 @@ module Actions {
* script: console.log('${{ github.event.pull_request.head.sha }}')
* ```
*/
- class Script extends YamlNode, YamlString {
- With with;
+ class GitHubScript extends YamlNode, YamlString {
+ GitHubScriptWith with;
- Script() { with.lookup("script") = this }
+ GitHubScript() { with.lookup("script") = this }
/** Gets the `with` field this field belongs to. */
- With getWith() { result = with }
+ GitHubScriptWith getWith() { result = with }
+ }
+
+ /**
+ * A step that uses `actions/github-script` action.
+ */
+ class GitHubScriptStep extends Step {
+ GitHubScriptStep() { this.getUses().getGitHubRepository() = "actions/github-script" }
+ }
+
+ /**
+ * A `with:` field sibling to `uses: actions/github-script`.
+ */
+ class GitHubScriptWith extends YamlNode, YamlMapping {
+ GitHubScriptStep step;
+
+ GitHubScriptWith() { step.lookup("with") = this }
+
+ /** Gets the step this field belongs to. */
+ GitHubScriptStep getStep() { result = step }
}
/**
diff --git a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
index bd16f60d070..5b11e1e5adf 100644
--- a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
+++ b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
@@ -133,17 +133,12 @@ predicate isRunInjectable(Actions::Run run, string injection, string context) {
* Holds if the `actions/github-script` contains any expression interpolation `${{ e }}`.
* Sets `context` to the initial untrusted value assignment in case of `${{ env... }}` interpolation
*/
-predicate isScriptInjectable(Actions::Script script, string injection, string context) {
- exists(Actions::Step step, Actions::Uses uses |
- script.getWith().getStep() = step and
- uses.getStep() = step and
- uses.getGitHubRepository() = "actions/github-script" and
- Actions::getASimpleReferenceExpression(script) = injection and
- (
- injection = context
- or
- isEnvInterpolationTainted(injection, context)
- )
+predicate isScriptInjectable(Actions::GitHubScript script, string injection, string context) {
+ Actions::getASimpleReferenceExpression(script) = injection and
+ (
+ injection = context
+ or
+ isEnvInterpolationTainted(injection, context)
)
}
@@ -158,7 +153,7 @@ where
run.getStep().getRuns() = runs
)
or
- exists(Actions::Script script |
+ exists(Actions::GitHubScript script |
node = script and
script.getWith().getStep().getRuns() = runs and
isScriptInjectable(script, injection, context)
@@ -184,7 +179,7 @@ where
run.getStep().getJob().getWorkflow().getOn() = on
)
or
- exists(Actions::Script script |
+ exists(Actions::GitHubScript script |
node = script and
script.getWith().getStep().getJob().getWorkflow().getOn() = on and
isScriptInjectable(script, injection, context)
From 3724ea1a7b57e4bac09595c893bb9f75ef223922 Mon Sep 17 00:00:00 2001
From: jarlob
Date: Fri, 14 Apr 2023 10:49:56 +0200
Subject: [PATCH 040/870] Extract `where` parts into predicates
---
.../Security/CWE-094/ExpressionInjection.ql | 62 +++++++++++--------
1 file changed, 36 insertions(+), 26 deletions(-)
diff --git a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
index 5b11e1e5adf..5b0ab7fa823 100644
--- a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
+++ b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
@@ -142,23 +142,45 @@ predicate isScriptInjectable(Actions::GitHubScript script, string injection, str
)
}
+/**
+ * Holds if the composite action contains untrusted expression interpolation `${{ e }}`.
+ */
+YamlNode getInjectableCompositeActionNode(Actions::Runs runs, string injection, string context) {
+ exists(Actions::Run run |
+ isRunInjectable(run, injection, context) and
+ result = run and
+ run.getStep().getRuns() = runs
+ )
+ or
+ exists(Actions::GitHubScript script |
+ isScriptInjectable(script, injection, context) and
+ result = script and
+ script.getWith().getStep().getRuns() = runs
+ )
+}
+
+/**
+ * Holds if the workflow contains untrusted expression interpolation `${{ e }}`.
+ */
+YamlNode getInjectableWorkflowNode(Actions::On on, string injection, string context) {
+ exists(Actions::Run run |
+ isRunInjectable(run, injection, context) and
+ result = run and
+ run.getStep().getJob().getWorkflow().getOn() = on
+ )
+ or
+ exists(Actions::GitHubScript script |
+ isScriptInjectable(script, injection, context) and
+ result = script and
+ script.getWith().getStep().getJob().getWorkflow().getOn() = on
+ )
+}
+
from YamlNode node, string injection, string context
where
exists(Actions::CompositeAction action, Actions::Runs runs |
action.getRuns() = runs and
- (
- exists(Actions::Run run |
- isRunInjectable(run, injection, context) and
- node = run and
- run.getStep().getRuns() = runs
- )
- or
- exists(Actions::GitHubScript script |
- node = script and
- script.getWith().getStep().getRuns() = runs and
- isScriptInjectable(script, injection, context)
- )
- ) and
+ node = getInjectableCompositeActionNode(runs, injection, context) and
(
isExternalUserControlledIssue(context) or
isExternalUserControlledPullRequest(context) or
@@ -172,19 +194,7 @@ where
)
or
exists(Actions::On on |
- (
- exists(Actions::Run run |
- isRunInjectable(run, injection, context) and
- node = run and
- run.getStep().getJob().getWorkflow().getOn() = on
- )
- or
- exists(Actions::GitHubScript script |
- node = script and
- script.getWith().getStep().getJob().getWorkflow().getOn() = on and
- isScriptInjectable(script, injection, context)
- )
- ) and
+ node = getInjectableWorkflowNode(on, injection, context) and
(
exists(on.getNode("issues")) and
isExternalUserControlledIssue(context)
From 599ec5a3b441402d59d62ecdd14ebbc81f59f084 Mon Sep 17 00:00:00 2001
From: jarlob
Date: Fri, 14 Apr 2023 10:52:11 +0200
Subject: [PATCH 041/870] Add comment
---
javascript/ql/lib/semmle/javascript/Actions.qll | 1 +
1 file changed, 1 insertion(+)
diff --git a/javascript/ql/lib/semmle/javascript/Actions.qll b/javascript/ql/lib/semmle/javascript/Actions.qll
index 4bb52cb87cb..436369fc122 100644
--- a/javascript/ql/lib/semmle/javascript/Actions.qll
+++ b/javascript/ql/lib/semmle/javascript/Actions.qll
@@ -111,6 +111,7 @@ module Actions {
Workflow getWorkflow() { result = workflow }
}
+ /** A common class for `env` in workflow, job or step. */
abstract class Env extends YamlNode, YamlMapping { }
/** A workflow level `env` mapping. */
From e9dee3a185f47c10472a04d8c7bcd0ac7eba39c1 Mon Sep 17 00:00:00 2001
From: jarlob
Date: Fri, 14 Apr 2023 14:26:23 +0200
Subject: [PATCH 042/870] Move `actions/github-script` out of Actions.qll
---
.../ql/lib/semmle/javascript/Actions.qll | 38 ----------------
.../Security/CWE-094/ExpressionInjection.ql | 44 +++++++++++++++++--
2 files changed, 41 insertions(+), 41 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/Actions.qll b/javascript/ql/lib/semmle/javascript/Actions.qll
index 436369fc122..4bbc9816007 100644
--- a/javascript/ql/lib/semmle/javascript/Actions.qll
+++ b/javascript/ql/lib/semmle/javascript/Actions.qll
@@ -359,44 +359,6 @@ module Actions {
bindingset[name]
string getEnvName(string name) { result = name.regexpCapture("env\\.([A-Za-z0-9_]+)", 1) }
- /**
- * A `script:` field within an Actions `with:` specific to `actions/github-script` action.
- *
- * For example:
- * ```
- * uses: actions/github-script@v3
- * with:
- * script: console.log('${{ github.event.pull_request.head.sha }}')
- * ```
- */
- class GitHubScript extends YamlNode, YamlString {
- GitHubScriptWith with;
-
- GitHubScript() { with.lookup("script") = this }
-
- /** Gets the `with` field this field belongs to. */
- GitHubScriptWith getWith() { result = with }
- }
-
- /**
- * A step that uses `actions/github-script` action.
- */
- class GitHubScriptStep extends Step {
- GitHubScriptStep() { this.getUses().getGitHubRepository() = "actions/github-script" }
- }
-
- /**
- * A `with:` field sibling to `uses: actions/github-script`.
- */
- class GitHubScriptWith extends YamlNode, YamlMapping {
- GitHubScriptStep step;
-
- GitHubScriptWith() { step.lookup("with") = this }
-
- /** Gets the step this field belongs to. */
- GitHubScriptStep getStep() { result = step }
- }
-
/**
* A `run` field within an Actions job step, which runs command-line programs using an operating system shell.
* See https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsrun.
diff --git a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
index 5b0ab7fa823..7e00c6f2ac7 100644
--- a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
+++ b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
@@ -15,6 +15,44 @@
import javascript
import semmle.javascript.Actions
+/**
+ * A `script:` field within an Actions `with:` specific to `actions/github-script` action.
+ *
+ * For example:
+ * ```
+ * uses: actions/github-script@v3
+ * with:
+ * script: console.log('${{ github.event.pull_request.head.sha }}')
+ * ```
+ */
+class GitHubScript extends YamlNode, YamlString {
+ GitHubScriptWith with;
+
+ GitHubScript() { with.lookup("script") = this }
+
+ /** Gets the `with` field this field belongs to. */
+ GitHubScriptWith getWith() { result = with }
+}
+
+/**
+ * A step that uses `actions/github-script` action.
+ */
+class GitHubScriptStep extends Actions::Step {
+ GitHubScriptStep() { this.getUses().getGitHubRepository() = "actions/github-script" }
+}
+
+/**
+ * A `with:` field sibling to `uses: actions/github-script`.
+ */
+class GitHubScriptWith extends YamlNode, YamlMapping {
+ GitHubScriptStep step;
+
+ GitHubScriptWith() { step.lookup("with") = this }
+
+ /** Gets the step this field belongs to. */
+ GitHubScriptStep getStep() { result = step }
+}
+
bindingset[context]
private predicate isExternalUserControlledIssue(string context) {
context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*issue\\s*\\.\\s*title\\b") or
@@ -133,7 +171,7 @@ predicate isRunInjectable(Actions::Run run, string injection, string context) {
* Holds if the `actions/github-script` contains any expression interpolation `${{ e }}`.
* Sets `context` to the initial untrusted value assignment in case of `${{ env... }}` interpolation
*/
-predicate isScriptInjectable(Actions::GitHubScript script, string injection, string context) {
+predicate isScriptInjectable(GitHubScript script, string injection, string context) {
Actions::getASimpleReferenceExpression(script) = injection and
(
injection = context
@@ -152,7 +190,7 @@ YamlNode getInjectableCompositeActionNode(Actions::Runs runs, string injection,
run.getStep().getRuns() = runs
)
or
- exists(Actions::GitHubScript script |
+ exists(GitHubScript script |
isScriptInjectable(script, injection, context) and
result = script and
script.getWith().getStep().getRuns() = runs
@@ -169,7 +207,7 @@ YamlNode getInjectableWorkflowNode(Actions::On on, string injection, string cont
run.getStep().getJob().getWorkflow().getOn() = on
)
or
- exists(Actions::GitHubScript script |
+ exists(GitHubScript script |
isScriptInjectable(script, injection, context) and
result = script and
script.getWith().getStep().getJob().getWorkflow().getOn() = on
From 6e9f54ef557d6d138e3ec744dd8b72ae39fcb0bf Mon Sep 17 00:00:00 2001
From: jarlob
Date: Fri, 21 Apr 2023 19:03:38 +0200
Subject: [PATCH 043/870] Use double curly braces
---
.../Security/CWE-094/ExpressionInjection.ql | 4 +-
.../ExpressionInjection.expected | 130 +++++++++---------
2 files changed, 67 insertions(+), 67 deletions(-)
diff --git a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
index 7e00c6f2ac7..6c01edb330f 100644
--- a/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
+++ b/javascript/ql/src/Security/CWE-094/ExpressionInjection.ql
@@ -266,5 +266,5 @@ where
)
)
select node,
- "Potential injection from the ${ " + injection +
- " }, which may be controlled by an external user."
+ "Potential injection from the ${{ " + injection +
+ " }}, which may be controlled by an external user."
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected
index a67188a976b..414b9b9ae40 100644
--- a/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-094/ExpressionInjection/ExpressionInjection.expected
@@ -1,65 +1,65 @@
-| .github/workflows/comment_issue.yml:7:12:8:48 | \| | Potential injection from the ${ github.event.comment.body }, which may be controlled by an external user. |
-| .github/workflows/comment_issue.yml:13:12:13:50 | echo '$ ... ody }}' | Potential injection from the ${ github.event.comment.body }, which may be controlled by an external user. |
-| .github/workflows/comment_issue.yml:14:12:14:48 | echo '$ ... ody }}' | Potential injection from the ${ github.event.issue.body }, which may be controlled by an external user. |
-| .github/workflows/comment_issue.yml:15:12:15:49 | echo '$ ... tle }}' | Potential injection from the ${ github.event.issue.title }, which may be controlled by an external user. |
-| .github/workflows/comment_issue.yml:22:17:22:63 | console ... dy }}') | Potential injection from the ${ github.event.comment.body }, which may be controlled by an external user. |
-| .github/workflows/comment_issue.yml:25:17:25:61 | console ... dy }}') | Potential injection from the ${ github.event.issue.body }, which may be controlled by an external user. |
-| .github/workflows/comment_issue.yml:28:17:28:62 | console ... le }}') | Potential injection from the ${ github.event.issue.title }, which may be controlled by an external user. |
-| .github/workflows/comment_issue_newline.yml:9:14:10:50 | \| | Potential injection from the ${ github.event.comment.body }, which may be controlled by an external user. |
-| .github/workflows/discussion.yml:7:12:7:54 | echo '$ ... tle }}' | Potential injection from the ${ github.event.discussion.title }, which may be controlled by an external user. |
-| .github/workflows/discussion.yml:8:12:8:53 | echo '$ ... ody }}' | Potential injection from the ${ github.event.discussion.body }, which may be controlled by an external user. |
-| .github/workflows/discussion_comment.yml:7:12:7:54 | echo '$ ... tle }}' | Potential injection from the ${ github.event.discussion.title }, which may be controlled by an external user. |
-| .github/workflows/discussion_comment.yml:8:12:8:53 | echo '$ ... ody }}' | Potential injection from the ${ github.event.discussion.body }, which may be controlled by an external user. |
-| .github/workflows/discussion_comment.yml:9:12:9:50 | echo '$ ... ody }}' | Potential injection from the ${ github.event.comment.body }, which may be controlled by an external user. |
-| .github/workflows/gollum.yml:7:12:7:52 | echo '$ ... tle }}' | Potential injection from the ${ github.event.pages[1].title }, which may be controlled by an external user. |
-| .github/workflows/gollum.yml:8:12:8:53 | echo '$ ... tle }}' | Potential injection from the ${ github.event.pages[11].title }, which may be controlled by an external user. |
-| .github/workflows/gollum.yml:9:12:9:56 | echo '$ ... ame }}' | Potential injection from the ${ github.event.pages[0].page_name }, which may be controlled by an external user. |
-| .github/workflows/gollum.yml:10:12:10:59 | echo '$ ... ame }}' | Potential injection from the ${ github.event.pages[2222].page_name }, which may be controlled by an external user. |
-| .github/workflows/issues.yaml:13:12:13:49 | echo '$ ... tle }}' | Potential injection from the ${ github.event.issue.title }, which may be controlled by an external user. |
-| .github/workflows/issues.yaml:14:12:14:48 | echo '$ ... ody }}' | Potential injection from the ${ github.event.issue.body }, which may be controlled by an external user. |
-| .github/workflows/issues.yaml:15:12:15:39 | echo '$ ... env }}' | Potential injection from the ${ env.global_env }, which may be controlled by an external user. |
-| .github/workflows/issues.yaml:17:12:17:36 | echo '$ ... env }}' | Potential injection from the ${ env.job_env }, which may be controlled by an external user. |
-| .github/workflows/issues.yaml:18:12:18:37 | echo '$ ... env }}' | Potential injection from the ${ env.step_env }, which may be controlled by an external user. |
-| .github/workflows/pull_request_review.yml:7:12:7:56 | echo '$ ... tle }}' | Potential injection from the ${ github.event.pull_request.title }, which may be controlled by an external user. |
-| .github/workflows/pull_request_review.yml:8:12:8:55 | echo '$ ... ody }}' | Potential injection from the ${ github.event.pull_request.body }, which may be controlled by an external user. |
-| .github/workflows/pull_request_review.yml:9:12:9:61 | echo '$ ... bel }}' | Potential injection from the ${ github.event.pull_request.head.label }, which may be controlled by an external user. |
-| .github/workflows/pull_request_review.yml:10:12:10:75 | echo '$ ... nch }}' | Potential injection from the ${ github.event.pull_request.head.repo.default_branch }, which may be controlled by an external user. |
-| .github/workflows/pull_request_review.yml:11:12:11:72 | echo '$ ... ion }}' | Potential injection from the ${ github.event.pull_request.head.repo.description }, which may be controlled by an external user. |
-| .github/workflows/pull_request_review.yml:12:12:12:69 | echo '$ ... age }}' | Potential injection from the ${ github.event.pull_request.head.repo.homepage }, which may be controlled by an external user. |
-| .github/workflows/pull_request_review.yml:13:12:13:59 | echo '$ ... ref }}' | Potential injection from the ${ github.event.pull_request.head.ref }, which may be controlled by an external user. |
-| .github/workflows/pull_request_review.yml:14:12:14:49 | echo '$ ... ody }}' | Potential injection from the ${ github.event.review.body }, which may be controlled by an external user. |
-| .github/workflows/pull_request_review_comment.yml:7:12:7:56 | echo '$ ... tle }}' | Potential injection from the ${ github.event.pull_request.title }, which may be controlled by an external user. |
-| .github/workflows/pull_request_review_comment.yml:8:12:8:55 | echo '$ ... ody }}' | Potential injection from the ${ github.event.pull_request.body }, which may be controlled by an external user. |
-| .github/workflows/pull_request_review_comment.yml:9:12:9:61 | echo '$ ... bel }}' | Potential injection from the ${ github.event.pull_request.head.label }, which may be controlled by an external user. |
-| .github/workflows/pull_request_review_comment.yml:10:12:10:75 | echo '$ ... nch }}' | Potential injection from the ${ github.event.pull_request.head.repo.default_branch }, which may be controlled by an external user. |
-| .github/workflows/pull_request_review_comment.yml:11:12:11:72 | echo '$ ... ion }}' | Potential injection from the ${ github.event.pull_request.head.repo.description }, which may be controlled by an external user. |
-| .github/workflows/pull_request_review_comment.yml:12:12:12:69 | echo '$ ... age }}' | Potential injection from the ${ github.event.pull_request.head.repo.homepage }, which may be controlled by an external user. |
-| .github/workflows/pull_request_review_comment.yml:13:12:13:59 | echo '$ ... ref }}' | Potential injection from the ${ github.event.pull_request.head.ref }, which may be controlled by an external user. |
-| .github/workflows/pull_request_review_comment.yml:14:12:14:50 | echo '$ ... ody }}' | Potential injection from the ${ github.event.comment.body }, which may be controlled by an external user. |
-| .github/workflows/pull_request_target.yml:9:12:9:56 | echo '$ ... tle }}' | Potential injection from the ${ github.event.pull_request.title }, which may be controlled by an external user. |
-| .github/workflows/pull_request_target.yml:10:12:10:55 | echo '$ ... ody }}' | Potential injection from the ${ github.event.pull_request.body }, which may be controlled by an external user. |
-| .github/workflows/pull_request_target.yml:11:12:11:61 | echo '$ ... bel }}' | Potential injection from the ${ github.event.pull_request.head.label }, which may be controlled by an external user. |
-| .github/workflows/pull_request_target.yml:12:12:12:75 | echo '$ ... nch }}' | Potential injection from the ${ github.event.pull_request.head.repo.default_branch }, which may be controlled by an external user. |
-| .github/workflows/pull_request_target.yml:13:12:13:72 | echo '$ ... ion }}' | Potential injection from the ${ github.event.pull_request.head.repo.description }, which may be controlled by an external user. |
-| .github/workflows/pull_request_target.yml:14:12:14:69 | echo '$ ... age }}' | Potential injection from the ${ github.event.pull_request.head.repo.homepage }, which may be controlled by an external user. |
-| .github/workflows/pull_request_target.yml:15:12:15:59 | echo '$ ... ref }}' | Potential injection from the ${ github.event.pull_request.head.ref }, which may be controlled by an external user. |
-| .github/workflows/pull_request_target.yml:16:12:16:40 | echo '$ ... ref }}' | Potential injection from the ${ github.head_ref }, which may be controlled by an external user. |
-| .github/workflows/push.yml:7:12:7:57 | echo '$ ... age }}' | Potential injection from the ${ github.event.commits[11].message }, which may be controlled by an external user. |
-| .github/workflows/push.yml:8:12:8:62 | echo '$ ... ail }}' | Potential injection from the ${ github.event.commits[11].author.email }, which may be controlled by an external user. |
-| .github/workflows/push.yml:9:12:9:61 | echo '$ ... ame }}' | Potential injection from the ${ github.event.commits[11].author.name }, which may be controlled by an external user. |
-| .github/workflows/push.yml:10:12:10:57 | echo '$ ... age }}' | Potential injection from the ${ github.event.head_commit.message }, which may be controlled by an external user. |
-| .github/workflows/push.yml:11:12:11:62 | echo '$ ... ail }}' | Potential injection from the ${ github.event.head_commit.author.email }, which may be controlled by an external user. |
-| .github/workflows/push.yml:12:12:12:61 | echo '$ ... ame }}' | Potential injection from the ${ github.event.head_commit.author.name }, which may be controlled by an external user. |
-| .github/workflows/push.yml:13:12:13:65 | echo '$ ... ail }}' | Potential injection from the ${ github.event.head_commit.committer.email }, which may be controlled by an external user. |
-| .github/workflows/push.yml:14:12:14:64 | echo '$ ... ame }}' | Potential injection from the ${ github.event.head_commit.committer.name }, which may be controlled by an external user. |
-| .github/workflows/push.yml:15:12:15:65 | echo '$ ... ail }}' | Potential injection from the ${ github.event.commits[11].committer.email }, which may be controlled by an external user. |
-| .github/workflows/push.yml:16:12:16:64 | echo '$ ... ame }}' | Potential injection from the ${ github.event.commits[11].committer.name }, which may be controlled by an external user. |
-| .github/workflows/workflow_run.yml:9:12:9:64 | echo '$ ... tle }}' | Potential injection from the ${ github.event.workflow_run.display_title }, which may be controlled by an external user. |
-| .github/workflows/workflow_run.yml:10:12:10:70 | echo '$ ... age }}' | Potential injection from the ${ github.event.workflow_run.head_commit.message }, which may be controlled by an external user. |
-| .github/workflows/workflow_run.yml:11:12:11:75 | echo '$ ... ail }}' | Potential injection from the ${ github.event.workflow_run.head_commit.author.email }, which may be controlled by an external user. |
-| .github/workflows/workflow_run.yml:12:12:12:74 | echo '$ ... ame }}' | Potential injection from the ${ github.event.workflow_run.head_commit.author.name }, which may be controlled by an external user. |
-| .github/workflows/workflow_run.yml:13:12:13:78 | echo '$ ... ail }}' | Potential injection from the ${ github.event.workflow_run.head_commit.committer.email }, which may be controlled by an external user. |
-| .github/workflows/workflow_run.yml:14:12:14:77 | echo '$ ... ame }}' | Potential injection from the ${ github.event.workflow_run.head_commit.committer.name }, which may be controlled by an external user. |
-| .github/workflows/workflow_run.yml:15:12:15:62 | echo '$ ... nch }}' | Potential injection from the ${ github.event.workflow_run.head_branch }, which may be controlled by an external user. |
-| .github/workflows/workflow_run.yml:16:12:16:78 | echo '$ ... ion }}' | Potential injection from the ${ github.event.workflow_run.head_repository.description }, which may be controlled by an external user. |
-| action1/action.yml:14:12:14:50 | echo '$ ... ody }}' | Potential injection from the ${ github.event.comment.body }, which may be controlled by an external user. |
+| .github/workflows/comment_issue.yml:7:12:8:48 | \| | Potential injection from the ${{ github.event.comment.body }}, which may be controlled by an external user. |
+| .github/workflows/comment_issue.yml:13:12:13:50 | echo '$ ... ody }}' | Potential injection from the ${{ github.event.comment.body }}, which may be controlled by an external user. |
+| .github/workflows/comment_issue.yml:14:12:14:48 | echo '$ ... ody }}' | Potential injection from the ${{ github.event.issue.body }}, which may be controlled by an external user. |
+| .github/workflows/comment_issue.yml:15:12:15:49 | echo '$ ... tle }}' | Potential injection from the ${{ github.event.issue.title }}, which may be controlled by an external user. |
+| .github/workflows/comment_issue.yml:22:17:22:63 | console ... dy }}') | Potential injection from the ${{ github.event.comment.body }}, which may be controlled by an external user. |
+| .github/workflows/comment_issue.yml:25:17:25:61 | console ... dy }}') | Potential injection from the ${{ github.event.issue.body }}, which may be controlled by an external user. |
+| .github/workflows/comment_issue.yml:28:17:28:62 | console ... le }}') | Potential injection from the ${{ github.event.issue.title }}, which may be controlled by an external user. |
+| .github/workflows/comment_issue_newline.yml:9:14:10:50 | \| | Potential injection from the ${{ github.event.comment.body }}, which may be controlled by an external user. |
+| .github/workflows/discussion.yml:7:12:7:54 | echo '$ ... tle }}' | Potential injection from the ${{ github.event.discussion.title }}, which may be controlled by an external user. |
+| .github/workflows/discussion.yml:8:12:8:53 | echo '$ ... ody }}' | Potential injection from the ${{ github.event.discussion.body }}, which may be controlled by an external user. |
+| .github/workflows/discussion_comment.yml:7:12:7:54 | echo '$ ... tle }}' | Potential injection from the ${{ github.event.discussion.title }}, which may be controlled by an external user. |
+| .github/workflows/discussion_comment.yml:8:12:8:53 | echo '$ ... ody }}' | Potential injection from the ${{ github.event.discussion.body }}, which may be controlled by an external user. |
+| .github/workflows/discussion_comment.yml:9:12:9:50 | echo '$ ... ody }}' | Potential injection from the ${{ github.event.comment.body }}, which may be controlled by an external user. |
+| .github/workflows/gollum.yml:7:12:7:52 | echo '$ ... tle }}' | Potential injection from the ${{ github.event.pages[1].title }}, which may be controlled by an external user. |
+| .github/workflows/gollum.yml:8:12:8:53 | echo '$ ... tle }}' | Potential injection from the ${{ github.event.pages[11].title }}, which may be controlled by an external user. |
+| .github/workflows/gollum.yml:9:12:9:56 | echo '$ ... ame }}' | Potential injection from the ${{ github.event.pages[0].page_name }}, which may be controlled by an external user. |
+| .github/workflows/gollum.yml:10:12:10:59 | echo '$ ... ame }}' | Potential injection from the ${{ github.event.pages[2222].page_name }}, which may be controlled by an external user. |
+| .github/workflows/issues.yaml:13:12:13:49 | echo '$ ... tle }}' | Potential injection from the ${{ github.event.issue.title }}, which may be controlled by an external user. |
+| .github/workflows/issues.yaml:14:12:14:48 | echo '$ ... ody }}' | Potential injection from the ${{ github.event.issue.body }}, which may be controlled by an external user. |
+| .github/workflows/issues.yaml:15:12:15:39 | echo '$ ... env }}' | Potential injection from the ${{ env.global_env }}, which may be controlled by an external user. |
+| .github/workflows/issues.yaml:17:12:17:36 | echo '$ ... env }}' | Potential injection from the ${{ env.job_env }}, which may be controlled by an external user. |
+| .github/workflows/issues.yaml:18:12:18:37 | echo '$ ... env }}' | Potential injection from the ${{ env.step_env }}, which may be controlled by an external user. |
+| .github/workflows/pull_request_review.yml:7:12:7:56 | echo '$ ... tle }}' | Potential injection from the ${{ github.event.pull_request.title }}, which may be controlled by an external user. |
+| .github/workflows/pull_request_review.yml:8:12:8:55 | echo '$ ... ody }}' | Potential injection from the ${{ github.event.pull_request.body }}, which may be controlled by an external user. |
+| .github/workflows/pull_request_review.yml:9:12:9:61 | echo '$ ... bel }}' | Potential injection from the ${{ github.event.pull_request.head.label }}, which may be controlled by an external user. |
+| .github/workflows/pull_request_review.yml:10:12:10:75 | echo '$ ... nch }}' | Potential injection from the ${{ github.event.pull_request.head.repo.default_branch }}, which may be controlled by an external user. |
+| .github/workflows/pull_request_review.yml:11:12:11:72 | echo '$ ... ion }}' | Potential injection from the ${{ github.event.pull_request.head.repo.description }}, which may be controlled by an external user. |
+| .github/workflows/pull_request_review.yml:12:12:12:69 | echo '$ ... age }}' | Potential injection from the ${{ github.event.pull_request.head.repo.homepage }}, which may be controlled by an external user. |
+| .github/workflows/pull_request_review.yml:13:12:13:59 | echo '$ ... ref }}' | Potential injection from the ${{ github.event.pull_request.head.ref }}, which may be controlled by an external user. |
+| .github/workflows/pull_request_review.yml:14:12:14:49 | echo '$ ... ody }}' | Potential injection from the ${{ github.event.review.body }}, which may be controlled by an external user. |
+| .github/workflows/pull_request_review_comment.yml:7:12:7:56 | echo '$ ... tle }}' | Potential injection from the ${{ github.event.pull_request.title }}, which may be controlled by an external user. |
+| .github/workflows/pull_request_review_comment.yml:8:12:8:55 | echo '$ ... ody }}' | Potential injection from the ${{ github.event.pull_request.body }}, which may be controlled by an external user. |
+| .github/workflows/pull_request_review_comment.yml:9:12:9:61 | echo '$ ... bel }}' | Potential injection from the ${{ github.event.pull_request.head.label }}, which may be controlled by an external user. |
+| .github/workflows/pull_request_review_comment.yml:10:12:10:75 | echo '$ ... nch }}' | Potential injection from the ${{ github.event.pull_request.head.repo.default_branch }}, which may be controlled by an external user. |
+| .github/workflows/pull_request_review_comment.yml:11:12:11:72 | echo '$ ... ion }}' | Potential injection from the ${{ github.event.pull_request.head.repo.description }}, which may be controlled by an external user. |
+| .github/workflows/pull_request_review_comment.yml:12:12:12:69 | echo '$ ... age }}' | Potential injection from the ${{ github.event.pull_request.head.repo.homepage }}, which may be controlled by an external user. |
+| .github/workflows/pull_request_review_comment.yml:13:12:13:59 | echo '$ ... ref }}' | Potential injection from the ${{ github.event.pull_request.head.ref }}, which may be controlled by an external user. |
+| .github/workflows/pull_request_review_comment.yml:14:12:14:50 | echo '$ ... ody }}' | Potential injection from the ${{ github.event.comment.body }}, which may be controlled by an external user. |
+| .github/workflows/pull_request_target.yml:9:12:9:56 | echo '$ ... tle }}' | Potential injection from the ${{ github.event.pull_request.title }}, which may be controlled by an external user. |
+| .github/workflows/pull_request_target.yml:10:12:10:55 | echo '$ ... ody }}' | Potential injection from the ${{ github.event.pull_request.body }}, which may be controlled by an external user. |
+| .github/workflows/pull_request_target.yml:11:12:11:61 | echo '$ ... bel }}' | Potential injection from the ${{ github.event.pull_request.head.label }}, which may be controlled by an external user. |
+| .github/workflows/pull_request_target.yml:12:12:12:75 | echo '$ ... nch }}' | Potential injection from the ${{ github.event.pull_request.head.repo.default_branch }}, which may be controlled by an external user. |
+| .github/workflows/pull_request_target.yml:13:12:13:72 | echo '$ ... ion }}' | Potential injection from the ${{ github.event.pull_request.head.repo.description }}, which may be controlled by an external user. |
+| .github/workflows/pull_request_target.yml:14:12:14:69 | echo '$ ... age }}' | Potential injection from the ${{ github.event.pull_request.head.repo.homepage }}, which may be controlled by an external user. |
+| .github/workflows/pull_request_target.yml:15:12:15:59 | echo '$ ... ref }}' | Potential injection from the ${{ github.event.pull_request.head.ref }}, which may be controlled by an external user. |
+| .github/workflows/pull_request_target.yml:16:12:16:40 | echo '$ ... ref }}' | Potential injection from the ${{ github.head_ref }}, which may be controlled by an external user. |
+| .github/workflows/push.yml:7:12:7:57 | echo '$ ... age }}' | Potential injection from the ${{ github.event.commits[11].message }}, which may be controlled by an external user. |
+| .github/workflows/push.yml:8:12:8:62 | echo '$ ... ail }}' | Potential injection from the ${{ github.event.commits[11].author.email }}, which may be controlled by an external user. |
+| .github/workflows/push.yml:9:12:9:61 | echo '$ ... ame }}' | Potential injection from the ${{ github.event.commits[11].author.name }}, which may be controlled by an external user. |
+| .github/workflows/push.yml:10:12:10:57 | echo '$ ... age }}' | Potential injection from the ${{ github.event.head_commit.message }}, which may be controlled by an external user. |
+| .github/workflows/push.yml:11:12:11:62 | echo '$ ... ail }}' | Potential injection from the ${{ github.event.head_commit.author.email }}, which may be controlled by an external user. |
+| .github/workflows/push.yml:12:12:12:61 | echo '$ ... ame }}' | Potential injection from the ${{ github.event.head_commit.author.name }}, which may be controlled by an external user. |
+| .github/workflows/push.yml:13:12:13:65 | echo '$ ... ail }}' | Potential injection from the ${{ github.event.head_commit.committer.email }}, which may be controlled by an external user. |
+| .github/workflows/push.yml:14:12:14:64 | echo '$ ... ame }}' | Potential injection from the ${{ github.event.head_commit.committer.name }}, which may be controlled by an external user. |
+| .github/workflows/push.yml:15:12:15:65 | echo '$ ... ail }}' | Potential injection from the ${{ github.event.commits[11].committer.email }}, which may be controlled by an external user. |
+| .github/workflows/push.yml:16:12:16:64 | echo '$ ... ame }}' | Potential injection from the ${{ github.event.commits[11].committer.name }}, which may be controlled by an external user. |
+| .github/workflows/workflow_run.yml:9:12:9:64 | echo '$ ... tle }}' | Potential injection from the ${{ github.event.workflow_run.display_title }}, which may be controlled by an external user. |
+| .github/workflows/workflow_run.yml:10:12:10:70 | echo '$ ... age }}' | Potential injection from the ${{ github.event.workflow_run.head_commit.message }}, which may be controlled by an external user. |
+| .github/workflows/workflow_run.yml:11:12:11:75 | echo '$ ... ail }}' | Potential injection from the ${{ github.event.workflow_run.head_commit.author.email }}, which may be controlled by an external user. |
+| .github/workflows/workflow_run.yml:12:12:12:74 | echo '$ ... ame }}' | Potential injection from the ${{ github.event.workflow_run.head_commit.author.name }}, which may be controlled by an external user. |
+| .github/workflows/workflow_run.yml:13:12:13:78 | echo '$ ... ail }}' | Potential injection from the ${{ github.event.workflow_run.head_commit.committer.email }}, which may be controlled by an external user. |
+| .github/workflows/workflow_run.yml:14:12:14:77 | echo '$ ... ame }}' | Potential injection from the ${{ github.event.workflow_run.head_commit.committer.name }}, which may be controlled by an external user. |
+| .github/workflows/workflow_run.yml:15:12:15:62 | echo '$ ... nch }}' | Potential injection from the ${{ github.event.workflow_run.head_branch }}, which may be controlled by an external user. |
+| .github/workflows/workflow_run.yml:16:12:16:78 | echo '$ ... ion }}' | Potential injection from the ${{ github.event.workflow_run.head_repository.description }}, which may be controlled by an external user. |
+| action1/action.yml:14:12:14:50 | echo '$ ... ody }}' | Potential injection from the ${{ github.event.comment.body }}, which may be controlled by an external user. |
From 5f0d334b8dfb3dac217ca99d9a77912f6f211c81 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Mon, 17 Apr 2023 10:59:28 +0100
Subject: [PATCH 044/870] Swift: Add basic-query-for-swift-code.rst.
---
.../basic-query-for-swift-code.rst | 130 ++++++++++++++++++
.../codeql-for-swift.rst | 13 ++
docs/codeql/codeql-language-guides/index.rst | 1 +
3 files changed, 144 insertions(+)
create mode 100644 docs/codeql/codeql-language-guides/basic-query-for-swift-code.rst
create mode 100644 docs/codeql/codeql-language-guides/codeql-for-swift.rst
diff --git a/docs/codeql/codeql-language-guides/basic-query-for-swift-code.rst b/docs/codeql/codeql-language-guides/basic-query-for-swift-code.rst
new file mode 100644
index 00000000000..41208d7e6a5
--- /dev/null
+++ b/docs/codeql/codeql-language-guides/basic-query-for-swift-code.rst
@@ -0,0 +1,130 @@
+.. _basic-query-for-swift-code:
+
+Basic query for Swift code
+=========================
+
+Learn to write and run a simple CodeQL query using Visual Studio Code with the CodeQL extension.
+
+.. include:: ../reusables/vs-code-basic-instructions/setup-to-run-queries.rst
+
+About the query
+---------------
+
+The query we're going to run performs a basic search of the code for ``if`` expressions that are redundant, in the sense that they have an empty ``then`` branch. For example, code such as:
+
+.. code-block:: swift
+
+ if error {
+ // we should handle the error
+ }
+
+.. include:: ../reusables/vs-code-basic-instructions/find-database.rst
+
+Running a quick query
+---------------------
+
+.. include:: ../reusables/vs-code-basic-instructions/run-quick-query-1.rst
+
+#. In the quick query tab, delete the content and paste in the following query.
+
+ .. code-block:: ql
+
+ import swift
+
+ from IfStmt ifStmt
+ where ifStmt.getThen().(BraceStmt).getNumberOfElements() = 0
+ select ifStmt, "This 'if' statement is redundant."
+
+.. include:: ../reusables/vs-code-basic-instructions/run-quick-query-2.rst
+
+.. image:: ../images/codeql-for-visual-studio-code/basic-swift-query-results-1.png
+ :align: center
+
+If any matching code is found, click a link in the ``ifStmt`` column to open the file and highlight the matching ``if`` statement.
+
+.. image:: ../images/codeql-for-visual-studio-code/basic-swift-query-results-2.png
+ :align: center
+
+.. include:: ../reusables/vs-code-basic-instructions/note-store-quick-query.rst
+
+About the query structure
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+After the initial ``import`` statement, this simple query comprises three parts that serve similar purposes to the FROM, WHERE, and SELECT parts of an SQL query.
+
++------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------+
+| Query part | Purpose | Details |
++==================================================================+===================================================================================================================+=================================================================================================+
+| ``import swift`` | Imports the standard CodeQL AST libraries for Swift. | Every query begins with one or more ``import`` statements. |
++------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------+
+| ``from IfStmt ifStmt`` | Defines the variables for the query. | We use: an ``IfStmt`` variable for ``if`` statements. |
+| | Declarations are of the form: | |
+| | `` `` | |
++------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------+
+| ``where ifStmt.getThen().(BraceStmt).getNumberOfElements() = 0`` | Defines a condition on the variables. | ``ifStmt.getThen()``: gets the ``then`` branch of the ``if`` expression. |
+| | | ``.(BraceStmt)``: requires that the ``then`` branch is a brace statement (``{ }``). |
+| | | ``.getNumberOfElements() = 0``: requires that the brace statement contains no child statements. |
++------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------+
+| ``select ifStmt, "This 'if' statement is redundant."`` | Defines what to report for each match. | Reports the resulting ``if`` statement with a string that explains the problem. |
+| | | |
+| | ``select`` statements for queries that are used to find instances of poor coding practice are always in the form: | |
+| | ``select , ""`` | |
++------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------+
+
+Extend the query
+----------------
+
+Query writing is an inherently iterative process. You write a simple query and then, when you run it, you discover examples that you had not previously considered, or opportunities for improvement.
+
+Remove false positive results
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Browsing the results of our basic query shows that it could be improved. Among the results you are likely to find examples of ``if`` statements with an ``else`` branch, where an empty ``then`` branch does serve a purpose. For example:
+
+.. code-block:: swift
+
+ if (option == "-verbose") {
+ // nothing to do - handled earlier
+ } else {
+ handleError("unrecognized option")
+ }
+
+In this case, identifying the ``if`` statement with the empty ``then`` branch as redundant is a false positive. One solution to this is to modify the query to select ``if`` statements where both the ``then`` and ``else`` branches are missing.
+
+To exclude ``if`` statements that have an ``else`` branch:
+
+#. Add the following to the where clause:
+
+ .. code-block:: ql
+
+ and not exists(ifStmt.getElse())
+
+ The ``where`` clause is now:
+
+ .. code-block:: ql
+
+ where
+ ifStmt.getThen().(BraceStmt).getNumberOfElements() = 0 and
+ not exists(ifStmt.getElse())
+
+#. Re-run the query.
+
+ There are now fewer results because ``if`` expressions with an ``else`` branch are no longer included.
+
+Further reading
+---------------
+
+.. include:: ../reusables/swift-further-reading.rst
+.. include:: ../reusables/codeql-ref-tools-further-reading.rst
+
+.. Article-specific substitutions for the reusables used in docs/codeql/reusables/vs-code-basic-instructions
+
+.. |language-text| replace:: Swift
+
+.. |language-code| replace:: ``swift``
+
+.. |example-url| replace:: https://github.com/alamofire/alamofire
+
+.. |image-quick-query| image:: ../images/codeql-for-visual-studio-code/quick-query-tab-swift.png
+
+.. |result-col-1| replace:: The first column corresponds to the expression ``ifStmt`` and is linked to the location in the source code of the project where ``ifStmt`` occurs.
diff --git a/docs/codeql/codeql-language-guides/codeql-for-swift.rst b/docs/codeql/codeql-language-guides/codeql-for-swift.rst
new file mode 100644
index 00000000000..ccb3499b727
--- /dev/null
+++ b/docs/codeql/codeql-language-guides/codeql-for-swift.rst
@@ -0,0 +1,13 @@
+.. _codeql-for-swift:
+
+CodeQL for Swift
+===============
+
+Experiment and learn how to write effective and efficient queries for CodeQL databases generated from Swift codebases.
+
+.. toctree::
+ :hidden:
+
+ basic-query-for-swift-code
+
+- :doc:`Basic query for Swift code `: Learn to write and run a simple CodeQL query.
diff --git a/docs/codeql/codeql-language-guides/index.rst b/docs/codeql/codeql-language-guides/index.rst
index 79f3f79ac54..2b4fabc01a7 100644
--- a/docs/codeql/codeql-language-guides/index.rst
+++ b/docs/codeql/codeql-language-guides/index.rst
@@ -14,3 +14,4 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
codeql-for-javascript
codeql-for-python
codeql-for-ruby
+ codeql-for-swift
From 73b712a8c9ff96fefa07c840f684e31eb84940a9 Mon Sep 17 00:00:00 2001
From: Owen Mansel-Chan
Date: Tue, 25 Apr 2023 07:03:19 +0100
Subject: [PATCH 045/870] Allow data flow through varargs parameters
---
go/ql/lib/semmle/go/Expr.qll | 12 +++++++
.../go/dataflow/internal/ContainerFlow.qll | 8 +++--
.../go/dataflow/internal/DataFlowNodes.qll | 36 +++++++++++++++++++
3 files changed, 54 insertions(+), 2 deletions(-)
diff --git a/go/ql/lib/semmle/go/Expr.qll b/go/ql/lib/semmle/go/Expr.qll
index 439c19036e3..ad84b50ff0e 100644
--- a/go/ql/lib/semmle/go/Expr.qll
+++ b/go/ql/lib/semmle/go/Expr.qll
@@ -857,6 +857,18 @@ class CallExpr extends CallOrConversionExpr {
/** Gets the number of argument expressions of this call. */
int getNumArgument() { result = count(this.getAnArgument()) }
+ /**
+ * Gets an argument with an ellipsis after it which is passed to a varargs
+ * parameter, as in `f(x...)`.
+ *
+ * Note that if the varargs parameter is `...T` then the type of the argument
+ * must be assignable to the slice type `[]T`.
+ */
+ Expr getExplicitVarargsArgument() {
+ this.hasEllipsis() and
+ result = this.getArgument(this.getNumArgument() - 1)
+ }
+
/**
* Gets the name of the invoked function, method or variable if it can be
* determined syntactically.
diff --git a/go/ql/lib/semmle/go/dataflow/internal/ContainerFlow.qll b/go/ql/lib/semmle/go/dataflow/internal/ContainerFlow.qll
index b6c1005daac..9065cfdae11 100644
--- a/go/ql/lib/semmle/go/dataflow/internal/ContainerFlow.qll
+++ b/go/ql/lib/semmle/go/dataflow/internal/ContainerFlow.qll
@@ -10,7 +10,7 @@ private import semmle.go.dataflow.ExternalFlow
* Holds if the step from `node1` to `node2` stores a value in an array, a
* slice, a collection or a map. Thus, `node2` references an object with a
* content `c` that contains the value of `node1`. This covers array
- * assignments and initializers as well as implicit array creations for
+ * assignments and initializers as well as implicit slice creations for
* varargs.
*/
predicate containerStoreStep(Node node1, Node node2, Content c) {
@@ -20,7 +20,11 @@ predicate containerStoreStep(Node node1, Node node2, Content c) {
node2.getType() instanceof ArrayType or
node2.getType() instanceof SliceType
) and
- exists(Write w | w.writesElement(node2, _, node1))
+ (
+ exists(Write w | w.writesElement(node2, _, node1))
+ or
+ node1 = node2.(ImplicitVarargsSlice).getCallNode().getImplicitVarargsArgument(_)
+ )
)
or
c instanceof CollectionContent and
diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll
index 86c3651b0d3..5273d617741 100644
--- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll
+++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll
@@ -10,6 +10,7 @@ private newtype TNode =
MkInstructionNode(IR::Instruction insn) or
MkSsaNode(SsaDefinition ssa) or
MkGlobalFunctionNode(Function f) or
+ MkImplicitVarargsSlice(CallExpr c) { c.getTarget().isVariadic() and not c.hasEllipsis() } or
MkSummarizedParameterNode(SummarizedCallable c, int i) {
FlowSummaryImpl::Private::summaryParameterNodeRange(c, i)
} or
@@ -426,6 +427,41 @@ module Public {
override ResultNode getAResult() { result.getRoot() = this.getExpr() }
}
+ /**
+ * An implicit varargs slice creation expression.
+ *
+ * A variadic function like `f(t1 T1, ..., Tm tm, A... x)` actually sees the
+ * varargs parameter as a slice `[]A`. A call `f(t1, ..., tm, x1, ..., xn)`
+ * desugars to `f(t1, ..., tm, []A{x1, ..., xn})`, and this node corresponds
+ * to this implicit slice creation.
+ */
+ class ImplicitVarargsSlice extends Node, MkImplicitVarargsSlice {
+ CallNode call;
+
+ ImplicitVarargsSlice() { this = MkImplicitVarargsSlice(call.getCall()) }
+
+ override ControlFlow::Root getRoot() { result = call.getRoot() }
+
+ /** Gets the call containing this varargs slice creation argument. */
+ CallNode getCallNode() { result = call }
+
+ override Type getType() {
+ exists(Function f | f = call.getTarget() |
+ result = f.getParameterType(f.getNumParameter() - 1)
+ )
+ }
+
+ override string getNodeKind() { result = "implicit varargs slice" }
+
+ override string toString() { result = "[]type{args}" }
+
+ override predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ call.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ }
+ }
+
/**
* Gets a possible target of call `cn`.class
*
From 3e73e02175ad52088be555296916340bf562c088 Mon Sep 17 00:00:00 2001
From: Owen Mansel-Chan
Date: Wed, 4 Jan 2023 12:28:41 +0000
Subject: [PATCH 046/870] Update PostUpdateNodes for implicit varargs slices
We don't want a post update node for the implicit varargs slice, and we
do want one for each argument which is stored in the implicit varargs
slice.
---
go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll
index 5273d617741..70e9e00116a 100644
--- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll
+++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll
@@ -732,7 +732,11 @@ module Public {
or
preupd = getAWrittenNode()
or
- preupd instanceof ArgumentNode and
+ (
+ preupd instanceof ArgumentNode and not preupd instanceof ImplicitVarargsSlice
+ or
+ preupd = any(CallNode c).getImplicitVarargsArgument(_)
+ ) and
mutableType(preupd.getType())
) and
(
From 22507c156628ec3b547c796327281fb790909340 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Tue, 25 Apr 2023 22:47:48 +0100
Subject: [PATCH 047/870] Swift: Add a test for UITextField.
---
.../dataflow/flowsources/uikit.swift | 30 +++++++++++++++++++
1 file changed, 30 insertions(+)
create mode 100644 swift/ql/test/library-tests/dataflow/flowsources/uikit.swift
diff --git a/swift/ql/test/library-tests/dataflow/flowsources/uikit.swift b/swift/ql/test/library-tests/dataflow/flowsources/uikit.swift
new file mode 100644
index 00000000000..3031bafef5c
--- /dev/null
+++ b/swift/ql/test/library-tests/dataflow/flowsources/uikit.swift
@@ -0,0 +1,30 @@
+// --- stubs ---
+
+class NSObject { }
+class NSAttributedString: NSObject {}
+class UIResponder: NSObject {}
+class UIView: UIResponder {}
+class UIControl: UIView {}
+class UITextField: UIControl {
+ var text: String? {
+ get { nil }
+ set { }
+ }
+ var attributedText: NSAttributedString? {
+ get { nil }
+ set { }
+ }
+ var placeholder: String? {
+ get { nil }
+ set { }
+ }
+}
+
+// --- tests ---
+
+func testUITextField(textField: UITextField) {
+ _ = textField.text // $ MISSING: source=local
+ _ = textField.attributedText // $ MISSING: source=local
+ _ = textField.placeholder // GOOD (not input)
+ _ = textField.text?.uppercased() // $ MISSING: source=local
+}
From e16277ef43f13b31bde4511c6682b33cb52dd678 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Tue, 25 Apr 2023 23:11:26 +0100
Subject: [PATCH 048/870] Swift: Add source model for UITextField.
---
.../ql/lib/codeql/swift/dataflow/ExternalFlow.qll | 3 ++-
.../codeql/swift/frameworks/UIKit/UITextField.qll | 15 +++++++++++++++
.../dataflow/flowsources/uikit.swift | 6 +++---
3 files changed, 20 insertions(+), 4 deletions(-)
create mode 100644 swift/ql/lib/codeql/swift/frameworks/UIKit/UITextField.qll
diff --git a/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll b/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll
index 672405107d7..9fbfa322a9e 100644
--- a/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll
+++ b/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll
@@ -79,6 +79,7 @@ private import internal.FlowSummaryImplSpecific
* ensuring that they are visible to the taint tracking / data flow library.
*/
private module Frameworks {
+ private import codeql.swift.frameworks.Alamofire.Alamofire
private import codeql.swift.frameworks.StandardLibrary.Collection
private import codeql.swift.frameworks.StandardLibrary.CustomUrlSchemes
private import codeql.swift.frameworks.StandardLibrary.Data
@@ -94,7 +95,7 @@ private module Frameworks {
private import codeql.swift.frameworks.StandardLibrary.Url
private import codeql.swift.frameworks.StandardLibrary.UrlSession
private import codeql.swift.frameworks.StandardLibrary.WebView
- private import codeql.swift.frameworks.Alamofire.Alamofire
+ private import codeql.swift.frameworks.UIKit.UITextField
private import codeql.swift.security.CleartextLoggingExtensions
private import codeql.swift.security.CleartextStorageDatabaseExtensions
private import codeql.swift.security.ECBEncryptionExtensions
diff --git a/swift/ql/lib/codeql/swift/frameworks/UIKit/UITextField.qll b/swift/ql/lib/codeql/swift/frameworks/UIKit/UITextField.qll
new file mode 100644
index 00000000000..3fbbdb0fee1
--- /dev/null
+++ b/swift/ql/lib/codeql/swift/frameworks/UIKit/UITextField.qll
@@ -0,0 +1,15 @@
+/**
+ * Provides models for the `UITextField` Swift class.
+ */
+
+import swift
+private import codeql.swift.dataflow.ExternalFlow
+
+/**
+ * A model for `UITextField` members that are flow sources.
+ */
+private class UITextFieldSource extends SourceModelCsv {
+ override predicate row(string row) {
+ row = [";UITextField;true;text;;;;local", ";UITextField;true;attributedText;;;;local"]
+ }
+}
diff --git a/swift/ql/test/library-tests/dataflow/flowsources/uikit.swift b/swift/ql/test/library-tests/dataflow/flowsources/uikit.swift
index 3031bafef5c..2da74a21254 100644
--- a/swift/ql/test/library-tests/dataflow/flowsources/uikit.swift
+++ b/swift/ql/test/library-tests/dataflow/flowsources/uikit.swift
@@ -23,8 +23,8 @@ class UITextField: UIControl {
// --- tests ---
func testUITextField(textField: UITextField) {
- _ = textField.text // $ MISSING: source=local
- _ = textField.attributedText // $ MISSING: source=local
+ _ = textField.text // $ source=local
+ _ = textField.attributedText // $ source=local
_ = textField.placeholder // GOOD (not input)
- _ = textField.text?.uppercased() // $ MISSING: source=local
+ _ = textField.text?.uppercased() // $ source=local
}
From 33a6e722f62edce72e94afe571813aa4255ef236 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Tue, 25 Apr 2023 23:29:43 +0100
Subject: [PATCH 049/870] Swift: Add a test for UISearchTextField.
---
swift/ql/test/library-tests/dataflow/flowsources/uikit.swift | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/swift/ql/test/library-tests/dataflow/flowsources/uikit.swift b/swift/ql/test/library-tests/dataflow/flowsources/uikit.swift
index 2da74a21254..5d6ce674278 100644
--- a/swift/ql/test/library-tests/dataflow/flowsources/uikit.swift
+++ b/swift/ql/test/library-tests/dataflow/flowsources/uikit.swift
@@ -19,12 +19,15 @@ class UITextField: UIControl {
set { }
}
}
+class UISearchTextField : UITextField {
+}
// --- tests ---
-func testUITextField(textField: UITextField) {
+func testUITextField(textField: UITextField, searchTextField: UISearchTextField) {
_ = textField.text // $ source=local
_ = textField.attributedText // $ source=local
_ = textField.placeholder // GOOD (not input)
_ = textField.text?.uppercased() // $ source=local
+ _ = searchTextField.text // $ source=local
}
From 8b6593715937de30d66b9d144286661fceea5fa0 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Wed, 26 Apr 2023 12:11:08 +0200
Subject: [PATCH 050/870] Move ConstantStringExpr to RangeUtils.qll
---
.../semmle/code/java/dataflow/RangeUtils.qll | 19 +++++++++++++++
.../semmle/code/java/security/XmlParsers.qll | 24 +------------------
2 files changed, 20 insertions(+), 23 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/dataflow/RangeUtils.qll b/java/ql/lib/semmle/code/java/dataflow/RangeUtils.qll
index d073868b0f5..be7f1292091 100644
--- a/java/ql/lib/semmle/code/java/dataflow/RangeUtils.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/RangeUtils.qll
@@ -104,6 +104,17 @@ private predicate constantBooleanExpr(Expr e, boolean val) {
CalcConstants::calculateBooleanValue(e) = val
}
+pragma[nomagic]
+private predicate constantStringExpr(Expr e, string val) {
+ e.(CompileTimeConstantExpr).getStringValue() = val
+ or
+ exists(SsaExplicitUpdate v, Expr src |
+ e = v.getAUse() and
+ src = v.getDefiningExpr().(VariableAssign).getSource() and
+ constantStringExpr(src, val)
+ )
+}
+
private boolean getBoolValue(Expr e) { constantBooleanExpr(e, result) }
private int getIntValue(Expr e) { constantIntegerExpr(e, result) }
@@ -126,6 +137,14 @@ class ConstantBooleanExpr extends Expr {
boolean getBooleanValue() { constantBooleanExpr(this, result) }
}
+/** An expression that always has the same string value. */
+class ConstantStringExpr extends Expr {
+ ConstantStringExpr() { constantStringExpr(this, _) }
+
+ /** Get the string value of this expression. */
+ string getStringValue() { constantStringExpr(this, result) }
+}
+
/**
* Gets an expression that equals `v - d`.
*/
diff --git a/java/ql/lib/semmle/code/java/security/XmlParsers.qll b/java/ql/lib/semmle/code/java/security/XmlParsers.qll
index dd28d8b0117..b5a9750ed2e 100644
--- a/java/ql/lib/semmle/code/java/security/XmlParsers.qll
+++ b/java/ql/lib/semmle/code/java/security/XmlParsers.qll
@@ -4,9 +4,7 @@ import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.DataFlow2
import semmle.code.java.dataflow.DataFlow3
-import semmle.code.java.dataflow.DataFlow4
-import semmle.code.java.dataflow.DataFlow5
-private import semmle.code.java.dataflow.SSA
+private import semmle.code.java.dataflow.RangeUtils
/*
* Various XML parsers in Java.
@@ -130,26 +128,6 @@ class DocumentBuilderFactoryConfig extends ParserConfig {
}
}
-private predicate constantStringExpr(Expr e, string val) {
- e.(CompileTimeConstantExpr).getStringValue() = val
- or
- exists(SsaExplicitUpdate v, Expr src |
- e = v.getAUse() and
- src = v.getDefiningExpr().(VariableAssign).getSource() and
- constantStringExpr(src, val)
- )
-}
-
-/** An expression that always has the same string value. */
-private class ConstantStringExpr extends Expr {
- string value;
-
- ConstantStringExpr() { constantStringExpr(this, value) }
-
- /** Get the string value of this expression. */
- string getStringValue() { result = value }
-}
-
/**
* A general configuration that is safe when enabled.
*/
From 1e66a544fd7afce688de975f03a1700d849322e0 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Wed, 26 Apr 2023 12:11:48 +0200
Subject: [PATCH 051/870] Promote exxperimental XXE sinks
---
.../java/frameworks/apache/CommonsXml.qll | 90 +++++++++++++++++++
.../code/java/frameworks/javaee/Xml.qll | 64 +++++++++++++
.../code/java/frameworks/javase/Beans.qll | 24 +++++
.../java/frameworks/rundeck/RundeckXml.qll | 19 ++++
.../semmle/code/java/security/XmlParsers.qll | 12 +--
5 files changed, 204 insertions(+), 5 deletions(-)
create mode 100644 java/ql/lib/semmle/code/java/frameworks/apache/CommonsXml.qll
create mode 100644 java/ql/lib/semmle/code/java/frameworks/javaee/Xml.qll
create mode 100644 java/ql/lib/semmle/code/java/frameworks/javase/Beans.qll
create mode 100644 java/ql/lib/semmle/code/java/frameworks/rundeck/RundeckXml.qll
diff --git a/java/ql/lib/semmle/code/java/frameworks/apache/CommonsXml.qll b/java/ql/lib/semmle/code/java/frameworks/apache/CommonsXml.qll
new file mode 100644
index 00000000000..42ecc946e50
--- /dev/null
+++ b/java/ql/lib/semmle/code/java/frameworks/apache/CommonsXml.qll
@@ -0,0 +1,90 @@
+/** Provides XML definitions related to the `org.apache.commons` package. */
+
+import java
+private import semmle.code.java.dataflow.RangeUtils
+private import semmle.code.java.security.XmlParsers
+
+/**
+ * The classes `org.apache.commons.digester3.Digester`, `org.apache.commons.digester.Digester` or `org.apache.tomcat.util.digester.Digester`.
+ */
+private class Digester extends RefType {
+ Digester() {
+ this.hasQualifiedName([
+ "org.apache.commons.digester3", "org.apache.commons.digester",
+ "org.apache.tomcat.util.digester"
+ ], "Digester")
+ }
+}
+
+/** A call to `Digester.parse`. */
+private class DigesterParse extends XmlParserCall {
+ DigesterParse() {
+ exists(Method m |
+ this.getMethod() = m and
+ m.getDeclaringType() instanceof Digester and
+ m.hasName("parse")
+ )
+ }
+
+ override Expr getSink() { result = this.getArgument(0) }
+
+ override predicate isSafe() { SafeDigesterFlow::flowToExpr(this.getQualifier()) }
+}
+
+/** A `ParserConfig` that is specific to `Digester`. */
+private class DigesterConfig extends ParserConfig {
+ DigesterConfig() {
+ exists(Method m |
+ m = this.getMethod() and
+ m.getDeclaringType() instanceof Digester and
+ m.hasName("setFeature")
+ )
+ }
+}
+
+/**
+ * A safely configured `Digester`.
+ */
+private class SafeDigester extends VarAccess {
+ SafeDigester() {
+ exists(Variable v | v = this.getVariable() |
+ exists(DigesterConfig config | config.getQualifier() = v.getAnAccess() |
+ config.enables(singleSafeConfig())
+ )
+ or
+ exists(DigesterConfig config | config.getQualifier() = v.getAnAccess() |
+ config
+ .disables(any(ConstantStringExpr s |
+ s.getStringValue() = "http://xml.org/sax/features/external-general-entities"
+ ))
+ ) and
+ exists(DigesterConfig config | config.getQualifier() = v.getAnAccess() |
+ config
+ .disables(any(ConstantStringExpr s |
+ s.getStringValue() = "http://xml.org/sax/features/external-parameter-entities"
+ ))
+ ) and
+ exists(DigesterConfig config | config.getQualifier() = v.getAnAccess() |
+ config
+ .disables(any(ConstantStringExpr s |
+ s.getStringValue() =
+ "http://apache.org/xml/features/nonvalidating/load-external-dtd"
+ ))
+ )
+ )
+ }
+}
+
+private module SafeDigesterFlowConfig implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SafeDigester }
+
+ predicate isSink(DataFlow::Node sink) {
+ exists(MethodAccess ma |
+ sink.asExpr() = ma.getQualifier() and ma.getMethod().getDeclaringType() instanceof Digester
+ )
+ }
+
+ int fieldFlowBranchLimit() { result = 0 }
+}
+
+private module SafeDigesterFlow = DataFlow::Global;
diff --git a/java/ql/lib/semmle/code/java/frameworks/javaee/Xml.qll b/java/ql/lib/semmle/code/java/frameworks/javaee/Xml.qll
new file mode 100644
index 00000000000..590b172bffa
--- /dev/null
+++ b/java/ql/lib/semmle/code/java/frameworks/javaee/Xml.qll
@@ -0,0 +1,64 @@
+/** Provides definitions related to the `javax.xml` package. */
+
+import java
+private import semmle.code.java.security.XmlParsers
+
+/** A call to `Validator.validate`. */
+private class ValidatorValidate extends XmlParserCall {
+ ValidatorValidate() {
+ exists(Method m |
+ this.getMethod() = m and
+ m.getDeclaringType() instanceof Validator and
+ m.hasName("validate")
+ )
+ }
+
+ override Expr getSink() { result = this.getArgument(0) }
+
+ override predicate isSafe() { SafeValidatorFlow::flowToExpr(this.getQualifier()) }
+}
+
+/** A `TransformerConfig` specific to `Validator`. */
+private class ValidatorConfig extends TransformerConfig {
+ ValidatorConfig() {
+ exists(Method m |
+ this.getMethod() = m and
+ m.getDeclaringType() instanceof Validator and
+ m.hasName("setProperty")
+ )
+ }
+}
+
+/** The class `javax.xml.validation.Validator`. */
+private class Validator extends RefType {
+ Validator() { this.hasQualifiedName("javax.xml.validation", "Validator") }
+}
+
+/** A safely configured `Validator`. */
+private class SafeValidator extends VarAccess {
+ SafeValidator() {
+ exists(Variable v | v = this.getVariable() |
+ exists(ValidatorConfig config | config.getQualifier() = v.getAnAccess() |
+ config.disables(configAccessExternalDtd())
+ ) and
+ exists(ValidatorConfig config | config.getQualifier() = v.getAnAccess() |
+ config.disables(configAccessExternalSchema())
+ )
+ )
+ }
+}
+
+private module SafeValidatorFlowConfig implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SafeValidator }
+
+ predicate isSink(DataFlow::Node sink) {
+ exists(MethodAccess ma |
+ sink.asExpr() = ma.getQualifier() and
+ ma.getMethod().getDeclaringType() instanceof Validator
+ )
+ }
+
+ int fieldFlowBranchLimit() { result = 0 }
+}
+
+private module SafeValidatorFlow = DataFlow::Global;
diff --git a/java/ql/lib/semmle/code/java/frameworks/javase/Beans.qll b/java/ql/lib/semmle/code/java/frameworks/javase/Beans.qll
new file mode 100644
index 00000000000..dbdaf6960f3
--- /dev/null
+++ b/java/ql/lib/semmle/code/java/frameworks/javase/Beans.qll
@@ -0,0 +1,24 @@
+/** Provides definitions related to the `java.beans` package. */
+
+import java
+private import semmle.code.java.security.XmlParsers
+
+/** The class `java.beans.XMLDecoder`. */
+private class XmlDecoder extends RefType {
+ XmlDecoder() { this.hasQualifiedName("java.beans", "XMLDecoder") }
+}
+
+/** A call to `XMLDecoder.readObject`. */
+private class XmlDecoderReadObject extends XmlParserCall {
+ XmlDecoderReadObject() {
+ exists(Method m |
+ this.getMethod() = m and
+ m.getDeclaringType() instanceof XmlDecoder and
+ m.hasName("readObject")
+ )
+ }
+
+ override Expr getSink() { result = this.getQualifier() }
+
+ override predicate isSafe() { none() }
+}
diff --git a/java/ql/lib/semmle/code/java/frameworks/rundeck/RundeckXml.qll b/java/ql/lib/semmle/code/java/frameworks/rundeck/RundeckXml.qll
new file mode 100644
index 00000000000..0f271e073e6
--- /dev/null
+++ b/java/ql/lib/semmle/code/java/frameworks/rundeck/RundeckXml.qll
@@ -0,0 +1,19 @@
+/** Provides definitions related to XML parsing in Rundeck. */
+
+import java
+private import semmle.code.java.security.XmlParsers
+
+/** A call to `ParserHelper.loadDocument`. */
+private class ParserHelperLoadDocument extends XmlParserCall {
+ ParserHelperLoadDocument() {
+ exists(Method m |
+ this.getMethod() = m and
+ m.getDeclaringType().hasQualifiedName("org.rundeck.api.parser", "ParserHelper") and
+ m.hasName("loadDocument")
+ )
+ }
+
+ override Expr getSink() { result = this.getArgument(0) }
+
+ override predicate isSafe() { none() }
+}
diff --git a/java/ql/lib/semmle/code/java/security/XmlParsers.qll b/java/ql/lib/semmle/code/java/security/XmlParsers.qll
index b5a9750ed2e..86c2458cd3e 100644
--- a/java/ql/lib/semmle/code/java/security/XmlParsers.qll
+++ b/java/ql/lib/semmle/code/java/security/XmlParsers.qll
@@ -2,13 +2,15 @@
import java
import semmle.code.java.dataflow.DataFlow
-import semmle.code.java.dataflow.DataFlow2
import semmle.code.java.dataflow.DataFlow3
private import semmle.code.java.dataflow.RangeUtils
-/*
- * Various XML parsers in Java.
- */
+private module Frameworks {
+ private import semmle.code.java.frameworks.apache.CommonsXml
+ private import semmle.code.java.frameworks.javaee.Xml
+ private import semmle.code.java.frameworks.javase.Beans
+ private import semmle.code.java.frameworks.rundeck.RundeckXml
+}
/**
* An abstract type representing a call to parse XML files.
@@ -946,7 +948,7 @@ class TransformerFactorySource extends XmlParserCall {
exists(Method m |
this.getMethod() = m and
m.getDeclaringType() instanceof TransformerFactory and
- m.hasName("newTransformer")
+ m.hasName(["newTransformer", "newTransformerHandler"])
)
}
From db73e16b70b6ce50cf6d68764782393c7711c07e Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Wed, 26 Apr 2023 12:12:10 +0200
Subject: [PATCH 052/870] Add tests
---
.../security/CWE-611/DigesterTests.java | 33 +++++++++++++++
.../security/CWE-611/ParserHelperTests.java | 14 +++++++
.../security/CWE-611/ValidatorTests.java | 41 +++++++++++++++++++
.../security/CWE-611/XMLDecoderTests.java | 32 +++++++++++++++
.../query-tests/security/CWE-611/XXE.expected | 22 ++++++++++
.../test/query-tests/security/CWE-611/options | 2 +-
6 files changed, 143 insertions(+), 1 deletion(-)
create mode 100644 java/ql/test/query-tests/security/CWE-611/DigesterTests.java
create mode 100644 java/ql/test/query-tests/security/CWE-611/ParserHelperTests.java
create mode 100644 java/ql/test/query-tests/security/CWE-611/ValidatorTests.java
create mode 100644 java/ql/test/query-tests/security/CWE-611/XMLDecoderTests.java
diff --git a/java/ql/test/query-tests/security/CWE-611/DigesterTests.java b/java/ql/test/query-tests/security/CWE-611/DigesterTests.java
new file mode 100644
index 00000000000..5ba8b74b709
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-611/DigesterTests.java
@@ -0,0 +1,33 @@
+import java.io.BufferedReader;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.digester3.Digester;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PostMapping;
+
+@Controller
+public class DigesterTests {
+
+ @PostMapping(value = "bad")
+ public void bad1(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ ServletInputStream servletInputStream = request.getInputStream();
+ Digester digester = new Digester();
+ digester.parse(servletInputStream); // bad
+ }
+
+ @PostMapping(value = "good")
+ public void good1(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ BufferedReader br = request.getReader();
+ String str = "";
+ StringBuilder listString = new StringBuilder();
+ while ((str = br.readLine()) != null) {
+ listString.append(str);
+ }
+ Digester digester = new Digester();
+ digester.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+ digester.setFeature("http://xml.org/sax/features/external-general-entities", false);
+ digester.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+ digester.parse(listString.toString());
+ }
+}
diff --git a/java/ql/test/query-tests/security/CWE-611/ParserHelperTests.java b/java/ql/test/query-tests/security/CWE-611/ParserHelperTests.java
new file mode 100644
index 00000000000..cdb65b780e3
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-611/ParserHelperTests.java
@@ -0,0 +1,14 @@
+import javax.servlet.http.HttpServletRequest;
+import org.dom4j.Document;
+import org.rundeck.api.parser.ParserHelper;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PostMapping;
+
+@Controller
+public class ParserHelperTests {
+
+ @PostMapping(value = "bad4")
+ public void bad4(HttpServletRequest request) throws Exception {
+ Document document = ParserHelper.loadDocument(request.getInputStream()); // bad
+ }
+}
diff --git a/java/ql/test/query-tests/security/CWE-611/ValidatorTests.java b/java/ql/test/query-tests/security/CWE-611/ValidatorTests.java
new file mode 100644
index 00000000000..06d32ab95ae
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-611/ValidatorTests.java
@@ -0,0 +1,41 @@
+import java.io.BufferedReader;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import javax.xml.validation.Validator;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PostMapping;
+
+@Controller
+public class ValidatorTests {
+
+ @PostMapping(value = "bad")
+ public void bad2(HttpServletRequest request) throws Exception {
+ ServletInputStream servletInputStream = request.getInputStream();
+ SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
+ Schema schema = factory.newSchema();
+ Validator validator = schema.newValidator();
+ StreamSource source = new StreamSource(servletInputStream);
+ validator.validate(source); // bad
+ }
+
+ @PostMapping(value = "good")
+ public void good2(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ BufferedReader br = request.getReader();
+ String str = "";
+ StringBuilder listString = new StringBuilder();
+ while ((str = br.readLine()) != null) {
+ listString.append(str).append("\n");
+ }
+ SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
+ Schema schema = factory.newSchema();
+ Validator validator = schema.newValidator();
+ validator.setProperty("http://javax.xml.XMLConstants/property/accessExternalDTD", "");
+ validator.setProperty("http://javax.xml.XMLConstants/property/accessExternalSchema", "");
+ StreamSource source = new StreamSource(listString.toString());
+ validator.validate(source);
+ }
+}
diff --git a/java/ql/test/query-tests/security/CWE-611/XMLDecoderTests.java b/java/ql/test/query-tests/security/CWE-611/XMLDecoderTests.java
new file mode 100644
index 00000000000..434bfe9daed
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-611/XMLDecoderTests.java
@@ -0,0 +1,32 @@
+import java.beans.XMLDecoder;
+import java.io.BufferedReader;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.dom4j.Document;
+import org.dom4j.DocumentHelper;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PostMapping;
+
+@Controller
+public class XMLDecoderTests {
+
+ @PostMapping(value = "bad")
+ public void bad3(HttpServletRequest request) throws Exception {
+ ServletInputStream servletInputStream = request.getInputStream();
+ XMLDecoder xmlDecoder = new XMLDecoder(servletInputStream);
+ xmlDecoder.readObject(); // bad
+ }
+
+ @PostMapping(value = "good")
+ public void good3(HttpServletRequest request) throws Exception {
+ BufferedReader br = request.getReader();
+ String str = "";
+ StringBuilder listString = new StringBuilder();
+ while ((str = br.readLine()) != null) {
+ listString.append(str).append("\n");
+ }
+ // parseText falls back to a default SAXReader, which is safe
+ Document document = DocumentHelper.parseText(listString.toString()); // Safe
+ }
+}
diff --git a/java/ql/test/query-tests/security/CWE-611/XXE.expected b/java/ql/test/query-tests/security/CWE-611/XXE.expected
index 6304e3582a2..dec71c301d5 100644
--- a/java/ql/test/query-tests/security/CWE-611/XXE.expected
+++ b/java/ql/test/query-tests/security/CWE-611/XXE.expected
@@ -1,4 +1,5 @@
edges
+| DigesterTests.java:14:49:14:72 | getInputStream(...) : ServletInputStream | DigesterTests.java:16:24:16:41 | servletInputStream |
| DocumentBuilderTests.java:93:21:93:73 | new SAXSource(...) : SAXSource | DocumentBuilderTests.java:94:16:94:21 | source : SAXSource |
| DocumentBuilderTests.java:93:35:93:72 | new InputSource(...) : InputSource | DocumentBuilderTests.java:93:21:93:73 | new SAXSource(...) : SAXSource |
| DocumentBuilderTests.java:93:51:93:71 | getInputStream(...) : InputStream | DocumentBuilderTests.java:93:35:93:72 | new InputSource(...) : InputSource |
@@ -66,6 +67,12 @@ edges
| TransformerTests.java:136:38:136:58 | getInputStream(...) : InputStream | TransformerTests.java:136:21:136:59 | new StreamSource(...) |
| TransformerTests.java:141:32:141:69 | new InputSource(...) : InputSource | TransformerTests.java:141:18:141:70 | new SAXSource(...) |
| TransformerTests.java:141:48:141:68 | getInputStream(...) : InputStream | TransformerTests.java:141:32:141:69 | new InputSource(...) : InputSource |
+| ValidatorTests.java:17:49:17:72 | getInputStream(...) : ServletInputStream | ValidatorTests.java:21:48:21:65 | servletInputStream : ServletInputStream |
+| ValidatorTests.java:21:31:21:66 | new StreamSource(...) : StreamSource | ValidatorTests.java:22:28:22:33 | source |
+| ValidatorTests.java:21:48:21:65 | servletInputStream : ServletInputStream | ValidatorTests.java:21:31:21:66 | new StreamSource(...) : StreamSource |
+| XMLDecoderTests.java:16:49:16:72 | getInputStream(...) : ServletInputStream | XMLDecoderTests.java:17:48:17:65 | servletInputStream : ServletInputStream |
+| XMLDecoderTests.java:17:33:17:66 | new XMLDecoder(...) : XMLDecoder | XMLDecoderTests.java:18:9:18:18 | xmlDecoder |
+| XMLDecoderTests.java:17:48:17:65 | servletInputStream : ServletInputStream | XMLDecoderTests.java:17:33:17:66 | new XMLDecoder(...) : XMLDecoder |
| XMLReaderTests.java:16:34:16:54 | getInputStream(...) : InputStream | XMLReaderTests.java:16:18:16:55 | new InputSource(...) |
| XMLReaderTests.java:56:34:56:54 | getInputStream(...) : InputStream | XMLReaderTests.java:56:18:56:55 | new InputSource(...) |
| XMLReaderTests.java:63:34:63:54 | getInputStream(...) : InputStream | XMLReaderTests.java:63:18:63:55 | new InputSource(...) |
@@ -76,6 +83,8 @@ edges
| XMLReaderTests.java:100:34:100:54 | getInputStream(...) : InputStream | XMLReaderTests.java:100:18:100:55 | new InputSource(...) |
| XPathExpressionTests.java:27:37:27:57 | getInputStream(...) : InputStream | XPathExpressionTests.java:27:21:27:58 | new InputSource(...) |
nodes
+| DigesterTests.java:14:49:14:72 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
+| DigesterTests.java:16:24:16:41 | servletInputStream | semmle.label | servletInputStream |
| DocumentBuilderTests.java:14:19:14:39 | getInputStream(...) | semmle.label | getInputStream(...) |
| DocumentBuilderTests.java:28:19:28:39 | getInputStream(...) | semmle.label | getInputStream(...) |
| DocumentBuilderTests.java:35:19:35:39 | getInputStream(...) | semmle.label | getInputStream(...) |
@@ -96,6 +105,7 @@ nodes
| DocumentBuilderTests.java:101:46:101:51 | source : StreamSource | semmle.label | source : StreamSource |
| DocumentBuilderTests.java:102:16:102:21 | source : StreamSource | semmle.label | source : StreamSource |
| DocumentBuilderTests.java:102:16:102:38 | getInputStream(...) | semmle.label | getInputStream(...) |
+| ParserHelperTests.java:12:55:12:78 | getInputStream(...) | semmle.label | getInputStream(...) |
| SAXBuilderTests.java:8:19:8:39 | getInputStream(...) | semmle.label | getInputStream(...) |
| SAXBuilderTests.java:20:19:20:39 | getInputStream(...) | semmle.label | getInputStream(...) |
| SAXParserTests.java:13:18:13:38 | getInputStream(...) | semmle.label | getInputStream(...) |
@@ -219,6 +229,14 @@ nodes
| TransformerTests.java:141:32:141:69 | new InputSource(...) : InputSource | semmle.label | new InputSource(...) : InputSource |
| TransformerTests.java:141:48:141:68 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
| UnmarshallerTests.java:28:18:28:38 | getInputStream(...) | semmle.label | getInputStream(...) |
+| ValidatorTests.java:17:49:17:72 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
+| ValidatorTests.java:21:31:21:66 | new StreamSource(...) : StreamSource | semmle.label | new StreamSource(...) : StreamSource |
+| ValidatorTests.java:21:48:21:65 | servletInputStream : ServletInputStream | semmle.label | servletInputStream : ServletInputStream |
+| ValidatorTests.java:22:28:22:33 | source | semmle.label | source |
+| XMLDecoderTests.java:16:49:16:72 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
+| XMLDecoderTests.java:17:33:17:66 | new XMLDecoder(...) : XMLDecoder | semmle.label | new XMLDecoder(...) : XMLDecoder |
+| XMLDecoderTests.java:17:48:17:65 | servletInputStream : ServletInputStream | semmle.label | servletInputStream : ServletInputStream |
+| XMLDecoderTests.java:18:9:18:18 | xmlDecoder | semmle.label | xmlDecoder |
| XMLReaderTests.java:16:18:16:55 | new InputSource(...) | semmle.label | new InputSource(...) |
| XMLReaderTests.java:16:34:16:54 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
| XMLReaderTests.java:56:18:56:55 | new InputSource(...) | semmle.label | new InputSource(...) |
@@ -251,6 +269,7 @@ nodes
| XmlInputFactoryTests.java:56:34:56:54 | getInputStream(...) | semmle.label | getInputStream(...) |
subpaths
#select
+| DigesterTests.java:16:24:16:41 | servletInputStream | DigesterTests.java:14:49:14:72 | getInputStream(...) : ServletInputStream | DigesterTests.java:16:24:16:41 | servletInputStream | XML parsing depends on a $@ without guarding against external entity expansion. | DigesterTests.java:14:49:14:72 | getInputStream(...) | user-provided value |
| DocumentBuilderTests.java:14:19:14:39 | getInputStream(...) | DocumentBuilderTests.java:14:19:14:39 | getInputStream(...) | DocumentBuilderTests.java:14:19:14:39 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | DocumentBuilderTests.java:14:19:14:39 | getInputStream(...) | user-provided value |
| DocumentBuilderTests.java:28:19:28:39 | getInputStream(...) | DocumentBuilderTests.java:28:19:28:39 | getInputStream(...) | DocumentBuilderTests.java:28:19:28:39 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | DocumentBuilderTests.java:28:19:28:39 | getInputStream(...) | user-provided value |
| DocumentBuilderTests.java:35:19:35:39 | getInputStream(...) | DocumentBuilderTests.java:35:19:35:39 | getInputStream(...) | DocumentBuilderTests.java:35:19:35:39 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | DocumentBuilderTests.java:35:19:35:39 | getInputStream(...) | user-provided value |
@@ -263,6 +282,7 @@ subpaths
| DocumentBuilderTests.java:94:16:94:38 | getInputSource(...) | DocumentBuilderTests.java:93:51:93:71 | getInputStream(...) : InputStream | DocumentBuilderTests.java:94:16:94:38 | getInputSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | DocumentBuilderTests.java:93:51:93:71 | getInputStream(...) | user-provided value |
| DocumentBuilderTests.java:101:16:101:52 | sourceToInputSource(...) | DocumentBuilderTests.java:100:41:100:61 | getInputStream(...) : InputStream | DocumentBuilderTests.java:101:16:101:52 | sourceToInputSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | DocumentBuilderTests.java:100:41:100:61 | getInputStream(...) | user-provided value |
| DocumentBuilderTests.java:102:16:102:38 | getInputStream(...) | DocumentBuilderTests.java:100:41:100:61 | getInputStream(...) : InputStream | DocumentBuilderTests.java:102:16:102:38 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | DocumentBuilderTests.java:100:41:100:61 | getInputStream(...) | user-provided value |
+| ParserHelperTests.java:12:55:12:78 | getInputStream(...) | ParserHelperTests.java:12:55:12:78 | getInputStream(...) | ParserHelperTests.java:12:55:12:78 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | ParserHelperTests.java:12:55:12:78 | getInputStream(...) | user-provided value |
| SAXBuilderTests.java:8:19:8:39 | getInputStream(...) | SAXBuilderTests.java:8:19:8:39 | getInputStream(...) | SAXBuilderTests.java:8:19:8:39 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SAXBuilderTests.java:8:19:8:39 | getInputStream(...) | user-provided value |
| SAXBuilderTests.java:20:19:20:39 | getInputStream(...) | SAXBuilderTests.java:20:19:20:39 | getInputStream(...) | SAXBuilderTests.java:20:19:20:39 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SAXBuilderTests.java:20:19:20:39 | getInputStream(...) | user-provided value |
| SAXParserTests.java:13:18:13:38 | getInputStream(...) | SAXParserTests.java:13:18:13:38 | getInputStream(...) | SAXParserTests.java:13:18:13:38 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SAXParserTests.java:13:18:13:38 | getInputStream(...) | user-provided value |
@@ -328,6 +348,8 @@ subpaths
| TransformerTests.java:136:21:136:59 | new StreamSource(...) | TransformerTests.java:136:38:136:58 | getInputStream(...) : InputStream | TransformerTests.java:136:21:136:59 | new StreamSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | TransformerTests.java:136:38:136:58 | getInputStream(...) | user-provided value |
| TransformerTests.java:141:18:141:70 | new SAXSource(...) | TransformerTests.java:141:48:141:68 | getInputStream(...) : InputStream | TransformerTests.java:141:18:141:70 | new SAXSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | TransformerTests.java:141:48:141:68 | getInputStream(...) | user-provided value |
| UnmarshallerTests.java:28:18:28:38 | getInputStream(...) | UnmarshallerTests.java:28:18:28:38 | getInputStream(...) | UnmarshallerTests.java:28:18:28:38 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | UnmarshallerTests.java:28:18:28:38 | getInputStream(...) | user-provided value |
+| ValidatorTests.java:22:28:22:33 | source | ValidatorTests.java:17:49:17:72 | getInputStream(...) : ServletInputStream | ValidatorTests.java:22:28:22:33 | source | XML parsing depends on a $@ without guarding against external entity expansion. | ValidatorTests.java:17:49:17:72 | getInputStream(...) | user-provided value |
+| XMLDecoderTests.java:18:9:18:18 | xmlDecoder | XMLDecoderTests.java:16:49:16:72 | getInputStream(...) : ServletInputStream | XMLDecoderTests.java:18:9:18:18 | xmlDecoder | XML parsing depends on a $@ without guarding against external entity expansion. | XMLDecoderTests.java:16:49:16:72 | getInputStream(...) | user-provided value |
| XMLReaderTests.java:16:18:16:55 | new InputSource(...) | XMLReaderTests.java:16:34:16:54 | getInputStream(...) : InputStream | XMLReaderTests.java:16:18:16:55 | new InputSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | XMLReaderTests.java:16:34:16:54 | getInputStream(...) | user-provided value |
| XMLReaderTests.java:56:18:56:55 | new InputSource(...) | XMLReaderTests.java:56:34:56:54 | getInputStream(...) : InputStream | XMLReaderTests.java:56:18:56:55 | new InputSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | XMLReaderTests.java:56:34:56:54 | getInputStream(...) | user-provided value |
| XMLReaderTests.java:63:18:63:55 | new InputSource(...) | XMLReaderTests.java:63:34:63:54 | getInputStream(...) : InputStream | XMLReaderTests.java:63:18:63:55 | new InputSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | XMLReaderTests.java:63:34:63:54 | getInputStream(...) | user-provided value |
diff --git a/java/ql/test/query-tests/security/CWE-611/options b/java/ql/test/query-tests/security/CWE-611/options
index c3935792c6b..bec95f19163 100644
--- a/java/ql/test/query-tests/security/CWE-611/options
+++ b/java/ql/test/query-tests/security/CWE-611/options
@@ -1 +1 @@
-//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/jdom-1.1.3:${testdir}/../../../stubs/dom4j-2.1.1:${testdir}/../../../stubs/simple-xml-2.7.1:${testdir}/../../../stubs/jaxb-api-2.3.1:${testdir}/../../../stubs/jaxen-1.2.0
+//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/jdom-1.1.3:${testdir}/../../../stubs/dom4j-2.1.1:${testdir}/../../../stubs/simple-xml-2.7.1:${testdir}/../../../stubs/jaxb-api-2.3.1:${testdir}/../../../stubs/jaxen-1.2.0:${testdir}/../../../stubs/apache-commons-digester3-3.2:${testdir}/../../../stubs/servlet-api-2.4/:${testdir}/../../../stubs/rundeck-api-java-client-13.2:${testdir}/../../../stubs/springframework-5.3.8/
From e54eaed26f3babe80f67fd86fe96d8a170c00e86 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Wed, 26 Apr 2023 12:19:59 +0200
Subject: [PATCH 053/870] Refactor tests to use InlineFlowTest
---
.../security/CWE-611/DigesterTests.java | 2 +-
.../CWE-611/DocumentBuilderTests.java | 67 ++--
.../security/CWE-611/ParserHelperTests.java | 2 +-
.../security/CWE-611/SAXBuilderTests.java | 12 +-
.../security/CWE-611/SAXParserTests.java | 36 +-
.../security/CWE-611/SAXReaderTests.java | 42 +-
.../security/CWE-611/SAXSourceTests.java | 4 +-
.../security/CWE-611/SchemaTests.java | 12 +-
.../security/CWE-611/SimpleXMLTests.java | 96 ++---
.../security/CWE-611/TransformerTests.java | 78 ++--
.../security/CWE-611/UnmarshallerTests.java | 7 +-
.../security/CWE-611/ValidatorTests.java | 2 +-
.../security/CWE-611/XMLDecoderTests.java | 2 +-
.../security/CWE-611/XMLReaderTests.java | 42 +-
.../CWE-611/XPathExpressionTests.java | 20 +-
.../query-tests/security/CWE-611/XXE.expected | 373 ------------------
.../test/query-tests/security/CWE-611/XXE.ql | 11 +
.../query-tests/security/CWE-611/XXE.qlref | 1 -
.../CWE-611/XmlInputFactoryTests.java | 42 +-
19 files changed, 246 insertions(+), 605 deletions(-)
create mode 100644 java/ql/test/query-tests/security/CWE-611/XXE.ql
delete mode 100644 java/ql/test/query-tests/security/CWE-611/XXE.qlref
diff --git a/java/ql/test/query-tests/security/CWE-611/DigesterTests.java b/java/ql/test/query-tests/security/CWE-611/DigesterTests.java
index 5ba8b74b709..bace07a9b30 100644
--- a/java/ql/test/query-tests/security/CWE-611/DigesterTests.java
+++ b/java/ql/test/query-tests/security/CWE-611/DigesterTests.java
@@ -13,7 +13,7 @@ public class DigesterTests {
public void bad1(HttpServletRequest request, HttpServletResponse response) throws Exception {
ServletInputStream servletInputStream = request.getInputStream();
Digester digester = new Digester();
- digester.parse(servletInputStream); // bad
+ digester.parse(servletInputStream); // $ hasTaintFlow
}
@PostMapping(value = "good")
diff --git a/java/ql/test/query-tests/security/CWE-611/DocumentBuilderTests.java b/java/ql/test/query-tests/security/CWE-611/DocumentBuilderTests.java
index 0018e41346a..98d95686301 100644
--- a/java/ql/test/query-tests/security/CWE-611/DocumentBuilderTests.java
+++ b/java/ql/test/query-tests/security/CWE-611/DocumentBuilderTests.java
@@ -11,42 +11,44 @@ class DocumentBuilderTests {
public void unconfiguredParse(Socket sock) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
- builder.parse(sock.getInputStream()); //unsafe
+ builder.parse(sock.getInputStream()); // $ hasTaintFlow
}
public void disableDTD(Socket sock) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
DocumentBuilder builder = factory.newDocumentBuilder();
- builder.parse(sock.getInputStream()); //safe
+ builder.parse(sock.getInputStream()); // safe
}
public void enableSecurityFeature(Socket sock) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
DocumentBuilder builder = factory.newDocumentBuilder();
- builder.parse(sock.getInputStream()); //unsafe -- secure-processing by itself is insufficient
+ builder.parse(sock.getInputStream()); // $ hasTaintFlow -- secure-processing by itself is
+ // insufficient
}
public void enableSecurityFeature2(Socket sock) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature("http://javax.xml.XMLConstants/feature/secure-processing", true);
DocumentBuilder builder = factory.newDocumentBuilder();
- builder.parse(sock.getInputStream()); //unsafe -- secure-processing by itself is insufficient
+ builder.parse(sock.getInputStream()); // $ hasTaintFlow -- secure-processing by itself is
+ // insufficient
}
public void enableDTD(Socket sock) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false);
DocumentBuilder builder = factory.newDocumentBuilder();
- builder.parse(sock.getInputStream()); //unsafe
+ builder.parse(sock.getInputStream()); // $ hasTaintFlow
}
public void disableSecurityFeature(Socket sock) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature("http://javax.xml.XMLConstants/feature/secure-processing", false);
DocumentBuilder builder = factory.newDocumentBuilder();
- builder.parse(sock.getInputStream()); //unsafe
+ builder.parse(sock.getInputStream()); // $ hasTaintFlow
}
public void disableExternalEntities(Socket sock) throws Exception {
@@ -54,21 +56,21 @@ class DocumentBuilderTests {
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
DocumentBuilder builder = factory.newDocumentBuilder();
- builder.parse(sock.getInputStream()); //safe
+ builder.parse(sock.getInputStream()); // safe
}
public void partialDisableExternalEntities(Socket sock) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
DocumentBuilder builder = factory.newDocumentBuilder();
- builder.parse(sock.getInputStream()); //unsafe
+ builder.parse(sock.getInputStream()); // $ hasTaintFlow
}
public void partialDisableExternalEntities2(Socket sock) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
DocumentBuilder builder = factory.newDocumentBuilder();
- builder.parse(sock.getInputStream()); //unsafe
+ builder.parse(sock.getInputStream()); // $ hasTaintFlow
}
public void misConfigureExternalEntities1(Socket sock) throws Exception {
@@ -76,7 +78,7 @@ class DocumentBuilderTests {
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", true);
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
DocumentBuilder builder = factory.newDocumentBuilder();
- builder.parse(sock.getInputStream()); //unsafe
+ builder.parse(sock.getInputStream()); // $ hasTaintFlow
}
public void misConfigureExternalEntities2(Socket sock) throws Exception {
@@ -84,22 +86,22 @@ class DocumentBuilderTests {
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
factory.setFeature("http://xml.org/sax/features/external-general-entities", true);
DocumentBuilder builder = factory.newDocumentBuilder();
- builder.parse(sock.getInputStream()); //unsafe
+ builder.parse(sock.getInputStream()); // $ hasTaintFlow
}
public void taintedSAXInputSource1(Socket sock) throws Exception {
- DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
- DocumentBuilder builder = factory.newDocumentBuilder();
- SAXSource source = new SAXSource(new InputSource(sock.getInputStream()));
- builder.parse(source.getInputSource()); //unsafe
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ SAXSource source = new SAXSource(new InputSource(sock.getInputStream()));
+ builder.parse(source.getInputSource()); // $ hasTaintFlow
}
public void taintedSAXInputSource2(Socket sock) throws Exception {
- DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
- DocumentBuilder builder = factory.newDocumentBuilder();
- StreamSource source = new StreamSource(sock.getInputStream());
- builder.parse(SAXSource.sourceToInputSource(source)); //unsafe
- builder.parse(source.getInputStream()); //unsafe
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ StreamSource source = new StreamSource(sock.getInputStream());
+ builder.parse(SAXSource.sourceToInputSource(source)); // $ hasTaintFlow
+ builder.parse(source.getInputStream()); // $ hasTaintFlow
}
private static DocumentBuilderFactory getDocumentBuilderFactory() throws Exception {
@@ -112,21 +114,22 @@ class DocumentBuilderTests {
return factory;
}
- private static final ThreadLocal XML_DOCUMENT_BUILDER = new ThreadLocal() {
- @Override
- protected DocumentBuilder initialValue() {
- try {
- DocumentBuilderFactory factory = getDocumentBuilderFactory();
- return factory.newDocumentBuilder();
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
- }
- };
+ private static final ThreadLocal XML_DOCUMENT_BUILDER =
+ new ThreadLocal() {
+ @Override
+ protected DocumentBuilder initialValue() {
+ try {
+ DocumentBuilderFactory factory = getDocumentBuilderFactory();
+ return factory.newDocumentBuilder();
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ };
public void disableExternalEntities2(Socket sock) throws Exception {
DocumentBuilder builder = XML_DOCUMENT_BUILDER.get();
- builder.parse(sock.getInputStream()); //safe
+ builder.parse(sock.getInputStream()); // safe
}
}
diff --git a/java/ql/test/query-tests/security/CWE-611/ParserHelperTests.java b/java/ql/test/query-tests/security/CWE-611/ParserHelperTests.java
index cdb65b780e3..6b43c224d94 100644
--- a/java/ql/test/query-tests/security/CWE-611/ParserHelperTests.java
+++ b/java/ql/test/query-tests/security/CWE-611/ParserHelperTests.java
@@ -9,6 +9,6 @@ public class ParserHelperTests {
@PostMapping(value = "bad4")
public void bad4(HttpServletRequest request) throws Exception {
- Document document = ParserHelper.loadDocument(request.getInputStream()); // bad
+ Document document = ParserHelper.loadDocument(request.getInputStream()); // $ hasTaintFlow
}
}
diff --git a/java/ql/test/query-tests/security/CWE-611/SAXBuilderTests.java b/java/ql/test/query-tests/security/CWE-611/SAXBuilderTests.java
index c0a58bfc18d..2b25540b85b 100644
--- a/java/ql/test/query-tests/security/CWE-611/SAXBuilderTests.java
+++ b/java/ql/test/query-tests/security/CWE-611/SAXBuilderTests.java
@@ -5,18 +5,18 @@ public class SAXBuilderTests {
public void unconfiguredSAXBuilder(Socket sock) throws Exception {
SAXBuilder builder = new SAXBuilder();
- builder.build(sock.getInputStream()); //unsafe
+ builder.build(sock.getInputStream()); // $ hasTaintFlow
}
-
+
public void safeBuilder(Socket sock) throws Exception {
SAXBuilder builder = new SAXBuilder();
- builder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true);
- builder.build(sock.getInputStream()); //safe
+ builder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+ builder.build(sock.getInputStream()); // safe
}
public void misConfiguredBuilder(Socket sock) throws Exception {
SAXBuilder builder = new SAXBuilder();
- builder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",false);
- builder.build(sock.getInputStream()); //unsafe
+ builder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false);
+ builder.build(sock.getInputStream()); // $ hasTaintFlow
}
}
diff --git a/java/ql/test/query-tests/security/CWE-611/SAXParserTests.java b/java/ql/test/query-tests/security/CWE-611/SAXParserTests.java
index f8079dd1bc8..a6de7709aed 100644
--- a/java/ql/test/query-tests/security/CWE-611/SAXParserTests.java
+++ b/java/ql/test/query-tests/security/CWE-611/SAXParserTests.java
@@ -6,78 +6,78 @@ import javax.xml.XMLConstants;
import org.xml.sax.helpers.DefaultHandler;
public class SAXParserTests {
-
+
public void unconfiguredParser(Socket sock) throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
- parser.parse(sock.getInputStream(), new DefaultHandler()); //unsafe
+ parser.parse(sock.getInputStream(), new DefaultHandler()); // $ hasTaintFlow
}
-
+
public void safeParser(Socket sock) throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
SAXParser parser = factory.newSAXParser();
- parser.parse(sock.getInputStream(), new DefaultHandler()); //safe
+ parser.parse(sock.getInputStream(), new DefaultHandler()); // safe
}
-
+
public void partialConfiguredParser1(Socket sock) throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
SAXParser parser = factory.newSAXParser();
- parser.parse(sock.getInputStream(), new DefaultHandler()); //unsafe
+ parser.parse(sock.getInputStream(), new DefaultHandler()); // $ hasTaintFlow
}
-
+
public void partialConfiguredParser2(Socket sock) throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
SAXParser parser = factory.newSAXParser();
- parser.parse(sock.getInputStream(), new DefaultHandler()); //unsafe
+ parser.parse(sock.getInputStream(), new DefaultHandler()); // $ hasTaintFlow
}
-
+
public void partialConfiguredParser3(Socket sock) throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
SAXParser parser = factory.newSAXParser();
- parser.parse(sock.getInputStream(), new DefaultHandler()); //unsafe
+ parser.parse(sock.getInputStream(), new DefaultHandler()); // $ hasTaintFlow
}
-
+
public void misConfiguredParser1(Socket sock) throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setFeature("http://xml.org/sax/features/external-general-entities", true);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
SAXParser parser = factory.newSAXParser();
- parser.parse(sock.getInputStream(), new DefaultHandler()); //unsafe
+ parser.parse(sock.getInputStream(), new DefaultHandler()); // $ hasTaintFlow
}
-
+
public void misConfiguredParser2(Socket sock) throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", true);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
SAXParser parser = factory.newSAXParser();
- parser.parse(sock.getInputStream(), new DefaultHandler()); //unsafe
+ parser.parse(sock.getInputStream(), new DefaultHandler()); // $ hasTaintFlow
}
-
+
public void misConfiguredParser3(Socket sock) throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", true);
SAXParser parser = factory.newSAXParser();
- parser.parse(sock.getInputStream(), new DefaultHandler()); //unsafe
+ parser.parse(sock.getInputStream(), new DefaultHandler()); // $ hasTaintFlow
}
public void safeParser2(Socket sock) throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
- factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+ factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
SAXParser parser = factory.newSAXParser();
- parser.parse(sock.getInputStream(), new DefaultHandler()); //safe
+ parser.parse(sock.getInputStream(), new DefaultHandler()); // safe
}
}
diff --git a/java/ql/test/query-tests/security/CWE-611/SAXReaderTests.java b/java/ql/test/query-tests/security/CWE-611/SAXReaderTests.java
index ba0bfac5a29..f436074f65f 100644
--- a/java/ql/test/query-tests/security/CWE-611/SAXReaderTests.java
+++ b/java/ql/test/query-tests/security/CWE-611/SAXReaderTests.java
@@ -5,59 +5,59 @@ public class SAXReaderTests {
public void unconfiguredReader(Socket sock) throws Exception {
SAXReader reader = new SAXReader();
- reader.read(sock.getInputStream()); //unsafe
+ reader.read(sock.getInputStream()); // $ hasTaintFlow
}
-
+
public void safeReader(Socket sock) throws Exception {
SAXReader reader = new SAXReader();
reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
reader.setFeature("http://xml.org/sax/features/external-general-entities", false);
- reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
- reader.read(sock.getInputStream()); //safe
+ reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+ reader.read(sock.getInputStream()); // safe
}
-
+
public void partialConfiguredReader1(Socket sock) throws Exception {
SAXReader reader = new SAXReader();
reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
reader.setFeature("http://xml.org/sax/features/external-general-entities", false);
- reader.read(sock.getInputStream()); //unsafe
+ reader.read(sock.getInputStream()); // $ hasTaintFlow
}
-
+
public void partialConfiguredReader2(Socket sock) throws Exception {
SAXReader reader = new SAXReader();
reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
- reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
- reader.read(sock.getInputStream()); //unsafe
+ reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+ reader.read(sock.getInputStream()); // $ hasTaintFlow
}
-
+
public void partialConfiguredReader3(Socket sock) throws Exception {
SAXReader reader = new SAXReader();
reader.setFeature("http://xml.org/sax/features/external-general-entities", false);
- reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
- reader.read(sock.getInputStream()); //unsafe
+ reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+ reader.read(sock.getInputStream()); // $ hasTaintFlow
}
-
+
public void misConfiguredReader1(Socket sock) throws Exception {
SAXReader reader = new SAXReader();
reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
reader.setFeature("http://xml.org/sax/features/external-general-entities", true);
- reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
- reader.read(sock.getInputStream()); //unsafe
+ reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+ reader.read(sock.getInputStream()); // $ hasTaintFlow
}
-
+
public void misConfiguredReader2(Socket sock) throws Exception {
SAXReader reader = new SAXReader();
reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false);
reader.setFeature("http://xml.org/sax/features/external-general-entities", false);
- reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
- reader.read(sock.getInputStream()); //unsafe
+ reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+ reader.read(sock.getInputStream()); // $ hasTaintFlow
}
-
+
public void misConfiguredReader3(Socket sock) throws Exception {
SAXReader reader = new SAXReader();
reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
reader.setFeature("http://xml.org/sax/features/external-general-entities", false);
- reader.setFeature("http://xml.org/sax/features/external-parameter-entities", true);
- reader.read(sock.getInputStream()); //unsafe
+ reader.setFeature("http://xml.org/sax/features/external-parameter-entities", true);
+ reader.read(sock.getInputStream()); // $ hasTaintFlow
}
}
diff --git a/java/ql/test/query-tests/security/CWE-611/SAXSourceTests.java b/java/ql/test/query-tests/security/CWE-611/SAXSourceTests.java
index 06a4b5a43f3..721f596457d 100644
--- a/java/ql/test/query-tests/security/CWE-611/SAXSourceTests.java
+++ b/java/ql/test/query-tests/security/CWE-611/SAXSourceTests.java
@@ -17,14 +17,14 @@ public class SAXSourceTests {
SAXSource source = new SAXSource(reader, new InputSource(sock.getInputStream()));
JAXBContext jc = JAXBContext.newInstance(Object.class);
Unmarshaller um = jc.createUnmarshaller();
- um.unmarshal(source); // BAD
+ um.unmarshal(source); // $ hasTaintFlow
}
public void explicitlySafeSource1(Socket sock) throws Exception {
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setFeature("http://xml.org/sax/features/external-general-entities", false);
reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
- reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",false);
+ reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
SAXSource source = new SAXSource(reader, new InputSource(sock.getInputStream())); // GOOD
}
diff --git a/java/ql/test/query-tests/security/CWE-611/SchemaTests.java b/java/ql/test/query-tests/security/CWE-611/SchemaTests.java
index f41e0017af1..d98aeb4a3ba 100644
--- a/java/ql/test/query-tests/security/CWE-611/SchemaTests.java
+++ b/java/ql/test/query-tests/security/CWE-611/SchemaTests.java
@@ -9,39 +9,39 @@ public class SchemaTests {
public void unconfiguredSchemaFactory(Socket sock) throws Exception {
SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
- Schema schema = factory.newSchema(new StreamSource(sock.getInputStream())); //unsafe
+ Schema schema = factory.newSchema(new StreamSource(sock.getInputStream())); // $ hasTaintFlow
}
public void safeSchemaFactory(Socket sock) throws Exception {
SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
- Schema schema = factory.newSchema(new StreamSource(sock.getInputStream())); //safe
+ Schema schema = factory.newSchema(new StreamSource(sock.getInputStream())); // safe
}
public void partialConfiguredSchemaFactory1(Socket sock) throws Exception {
SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
- Schema schema = factory.newSchema(new StreamSource(sock.getInputStream())); //unsafe
+ Schema schema = factory.newSchema(new StreamSource(sock.getInputStream())); // $ hasTaintFlow
}
public void partialConfiguredSchemaFactory2(Socket sock) throws Exception {
SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
- Schema schema = factory.newSchema(new StreamSource(sock.getInputStream())); //unsafe
+ Schema schema = factory.newSchema(new StreamSource(sock.getInputStream())); // $ hasTaintFlow
}
public void misConfiguredSchemaFactory1(Socket sock) throws Exception {
SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "ab");
- Schema schema = factory.newSchema(new StreamSource(sock.getInputStream())); //unsafe
+ Schema schema = factory.newSchema(new StreamSource(sock.getInputStream())); // $ hasTaintFlow
}
public void misConfiguredSchemaFactory2(Socket sock) throws Exception {
SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "cd");
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
- Schema schema = factory.newSchema(new StreamSource(sock.getInputStream())); //unsafe
+ Schema schema = factory.newSchema(new StreamSource(sock.getInputStream())); // $ hasTaintFlow
}
}
diff --git a/java/ql/test/query-tests/security/CWE-611/SimpleXMLTests.java b/java/ql/test/query-tests/security/CWE-611/SimpleXMLTests.java
index baefeadfbe6..65c759acbf4 100644
--- a/java/ql/test/query-tests/security/CWE-611/SimpleXMLTests.java
+++ b/java/ql/test/query-tests/security/CWE-611/SimpleXMLTests.java
@@ -11,145 +11,145 @@ public class SimpleXMLTests {
public void persisterValidate1(Socket sock) throws Exception {
Persister persister = new Persister();
- persister.validate(this.getClass(), sock.getInputStream());
+ persister.validate(this.getClass(), sock.getInputStream()); // $ hasTaintFlow
}
-
+
public void persisterValidate2(Socket sock) throws Exception {
Persister persister = new Persister();
- persister.validate(this.getClass(), sock.getInputStream(), true);
+ persister.validate(this.getClass(), sock.getInputStream(), true); // $ hasTaintFlow
}
public void persisterValidate3(Socket sock) throws Exception {
Persister persister = new Persister();
- persister.validate(this.getClass(), new InputStreamReader(sock.getInputStream()));
+ persister.validate(this.getClass(), new InputStreamReader(sock.getInputStream())); // $ hasTaintFlow
}
public void persisterValidate4(Socket sock) throws Exception {
Persister persister = new Persister();
- byte[] b = new byte[]{};
+ byte[] b = new byte[] {};
sock.getInputStream().read(b);
- persister.validate(this.getClass(), new String(b));
+ persister.validate(this.getClass(), new String(b)); // $ hasTaintFlow
}
public void persisterValidate5(Socket sock) throws Exception {
Persister persister = new Persister();
- byte[] b = new byte[]{};
+ byte[] b = new byte[] {};
sock.getInputStream().read(b);
- persister.validate(this.getClass(), new String(b), true);
+ persister.validate(this.getClass(), new String(b), true); // $ hasTaintFlow
}
public void persisterValidate6(Socket sock) throws Exception {
Persister persister = new Persister();
- persister.validate(this.getClass(), new InputStreamReader(sock.getInputStream()), true);
+ persister.validate(this.getClass(), new InputStreamReader(sock.getInputStream()), true); // $ hasTaintFlow
}
public void persisterRead1(Socket sock) throws Exception {
Persister persister = new Persister();
- persister.read(this.getClass(), sock.getInputStream());
+ persister.read(this.getClass(), sock.getInputStream()); // $ hasTaintFlow
}
-
+
public void persisterRead2(Socket sock) throws Exception {
Persister persister = new Persister();
- persister.read(this.getClass(), sock.getInputStream(), true);
+ persister.read(this.getClass(), sock.getInputStream(), true); // $ hasTaintFlow
}
-
+
public void persisterRead3(Socket sock) throws Exception {
Persister persister = new Persister();
- persister.read(this, sock.getInputStream());
+ persister.read(this, sock.getInputStream()); // $ hasTaintFlow
}
-
+
public void persisterRead4(Socket sock) throws Exception {
Persister persister = new Persister();
- persister.read(this, sock.getInputStream(), true);
+ persister.read(this, sock.getInputStream(), true); // $ hasTaintFlow
}
-
+
public void persisterRead5(Socket sock) throws Exception {
Persister persister = new Persister();
- persister.read(this.getClass(), new InputStreamReader(sock.getInputStream()));
+ persister.read(this.getClass(), new InputStreamReader(sock.getInputStream())); // $ hasTaintFlow
}
public void persisterRead6(Socket sock) throws Exception {
Persister persister = new Persister();
- persister.read(this.getClass(), new InputStreamReader(sock.getInputStream()), true);
+ persister.read(this.getClass(), new InputStreamReader(sock.getInputStream()), true); // $ hasTaintFlow
}
public void persisterRead7(Socket sock) throws Exception {
Persister persister = new Persister();
- persister.read(this, new InputStreamReader(sock.getInputStream()));
+ persister.read(this, new InputStreamReader(sock.getInputStream())); // $ hasTaintFlow
}
public void persisterRead8(Socket sock) throws Exception {
Persister persister = new Persister();
- persister.read(this, new InputStreamReader(sock.getInputStream()), true);
+ persister.read(this, new InputStreamReader(sock.getInputStream()), true); // $ hasTaintFlow
}
-
+
public void persisterRead9(Socket sock) throws Exception {
Persister persister = new Persister();
- byte[] b = new byte[]{};
+ byte[] b = new byte[] {};
sock.getInputStream().read(b);
- persister.read(this.getClass(), new String(b));
+ persister.read(this.getClass(), new String(b)); // $ hasTaintFlow
}
-
+
public void persisterRead10(Socket sock) throws Exception {
Persister persister = new Persister();
- byte[] b = new byte[]{};
+ byte[] b = new byte[] {};
sock.getInputStream().read(b);
- persister.read(this.getClass(), new String(b), true);
+ persister.read(this.getClass(), new String(b), true); // $ hasTaintFlow
}
-
+
public void persisterRead11(Socket sock) throws Exception {
Persister persister = new Persister();
- byte[] b = new byte[]{};
+ byte[] b = new byte[] {};
sock.getInputStream().read(b);
- persister.read(this, new String(b));
+ persister.read(this, new String(b)); // $ hasTaintFlow
}
-
+
public void persisterRead12(Socket sock) throws Exception {
Persister persister = new Persister();
- byte[] b = new byte[]{};
+ byte[] b = new byte[] {};
sock.getInputStream().read(b);
- persister.read(this, new String(b), true);
+ persister.read(this, new String(b), true); // $ hasTaintFlow
}
-
+
public void nodeBuilderRead1(Socket sock) throws Exception {
- NodeBuilder.read(sock.getInputStream());
+ NodeBuilder.read(sock.getInputStream()); // $ hasTaintFlow
}
-
+
public void nodeBuilderRead2(Socket sock) throws Exception {
- NodeBuilder.read(new InputStreamReader(sock.getInputStream()));
+ NodeBuilder.read(new InputStreamReader(sock.getInputStream())); // $ hasTaintFlow
}
-
+
public void documentProviderProvide1(Socket sock) throws Exception {
DocumentProvider provider = new DocumentProvider();
- provider.provide(sock.getInputStream());
+ provider.provide(sock.getInputStream()); // $ hasTaintFlow
}
-
+
public void documentProviderProvide2(Socket sock) throws Exception {
DocumentProvider provider = new DocumentProvider();
- provider.provide(new InputStreamReader(sock.getInputStream()));
+ provider.provide(new InputStreamReader(sock.getInputStream())); // $ hasTaintFlow
}
public void streamProviderProvide1(Socket sock) throws Exception {
StreamProvider provider = new StreamProvider();
- provider.provide(sock.getInputStream());
+ provider.provide(sock.getInputStream()); // $ hasTaintFlow
}
public void streamProviderProvide2(Socket sock) throws Exception {
StreamProvider provider = new StreamProvider();
- provider.provide(new InputStreamReader(sock.getInputStream()));
+ provider.provide(new InputStreamReader(sock.getInputStream())); // $ hasTaintFlow
}
public void formatterFormat1(Socket sock) throws Exception {
Formatter formatter = new Formatter();
- byte[] b = new byte[]{};
+ byte[] b = new byte[] {};
sock.getInputStream().read(b);
- formatter.format(new String(b), null);
+ formatter.format(new String(b), null); // $ hasTaintFlow
}
-
+
public void formatterFormat2(Socket sock) throws Exception {
Formatter formatter = new Formatter();
- byte[] b = new byte[]{};
+ byte[] b = new byte[] {};
sock.getInputStream().read(b);
- formatter.format(new String(b));
+ formatter.format(new String(b)); // $ hasTaintFlow
}
}
diff --git a/java/ql/test/query-tests/security/CWE-611/TransformerTests.java b/java/ql/test/query-tests/security/CWE-611/TransformerTests.java
index 696d00c3fcf..afba1790f0c 100644
--- a/java/ql/test/query-tests/security/CWE-611/TransformerTests.java
+++ b/java/ql/test/query-tests/security/CWE-611/TransformerTests.java
@@ -17,8 +17,8 @@ public class TransformerTests {
public void unconfiguredTransformerFactory(Socket sock) throws Exception {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
- transformer.transform(new StreamSource(sock.getInputStream()), null); //unsafe
- tf.newTransformer(new StreamSource(sock.getInputStream())); //unsafe
+ transformer.transform(new StreamSource(sock.getInputStream()), null); // $ hasTaintFlow
+ tf.newTransformer(new StreamSource(sock.getInputStream())); // $ hasTaintFlow
}
public void safeTransformerFactory1(Socket sock) throws Exception {
@@ -26,8 +26,8 @@ public class TransformerTests {
tf.setAttribute("http://javax.xml.XMLConstants/property/accessExternalDTD", "");
tf.setAttribute("http://javax.xml.XMLConstants/property/accessExternalStylesheet", "");
Transformer transformer = tf.newTransformer();
- transformer.transform(new StreamSource(sock.getInputStream()), null); //safe
- tf.newTransformer(new StreamSource(sock.getInputStream())); //safe
+ transformer.transform(new StreamSource(sock.getInputStream()), null); // safe
+ tf.newTransformer(new StreamSource(sock.getInputStream())); // safe
}
public void safeTransformerFactory2(Socket sock) throws Exception {
@@ -35,49 +35,49 @@ public class TransformerTests {
tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
Transformer transformer = tf.newTransformer();
- transformer.transform(new StreamSource(sock.getInputStream()), null); //safe
- tf.newTransformer(new StreamSource(sock.getInputStream())); //safe
+ transformer.transform(new StreamSource(sock.getInputStream()), null); // safe
+ tf.newTransformer(new StreamSource(sock.getInputStream())); // safe
}
public void safeTransformerFactory3(Socket sock) throws Exception {
- TransformerFactory tf = TransformerFactory.newInstance();
- Transformer transformer = tf.newTransformer();
+ TransformerFactory tf = TransformerFactory.newInstance();
+ Transformer transformer = tf.newTransformer();
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setFeature("http://xml.org/sax/features/external-general-entities", false);
reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
- reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",false);
- SAXSource source = new SAXSource(reader, new InputSource(sock.getInputStream())); //safe
- transformer.transform(source, null); //safe
- tf.newTransformer(source); //safe
+ reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+ SAXSource source = new SAXSource(reader, new InputSource(sock.getInputStream())); // safe
+ transformer.transform(source, null); // safe
+ tf.newTransformer(source); // safe
}
public void safeTransformerFactory4(Socket sock) throws Exception {
- TransformerFactory tf = TransformerFactory.newInstance();
- Transformer transformer = tf.newTransformer();
+ TransformerFactory tf = TransformerFactory.newInstance();
+ Transformer transformer = tf.newTransformer();
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setFeature("http://xml.org/sax/features/external-general-entities", false);
reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
- reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",false);
+ reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
SAXSource source = new SAXSource(new InputSource(sock.getInputStream()));
source.setXMLReader(reader);
- transformer.transform(source, null); //safe
- tf.newTransformer(source); //safe
+ transformer.transform(source, null); // safe
+ tf.newTransformer(source); // safe
}
public void partialConfiguredTransformerFactory1(Socket sock) throws Exception {
TransformerFactory tf = TransformerFactory.newInstance();
tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
Transformer transformer = tf.newTransformer();
- transformer.transform(new StreamSource(sock.getInputStream()), null); //unsafe
- tf.newTransformer(new StreamSource(sock.getInputStream())); //unsafe
+ transformer.transform(new StreamSource(sock.getInputStream()), null); // $ hasTaintFlow
+ tf.newTransformer(new StreamSource(sock.getInputStream())); // $ hasTaintFlow
}
public void partialConfiguredTransformerFactory2(Socket sock) throws Exception {
TransformerFactory tf = TransformerFactory.newInstance();
tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
Transformer transformer = tf.newTransformer();
- transformer.transform(new StreamSource(sock.getInputStream()), null); //unsafe
- tf.newTransformer(new StreamSource(sock.getInputStream())); //unsafe
+ transformer.transform(new StreamSource(sock.getInputStream()), null); // $ hasTaintFlow
+ tf.newTransformer(new StreamSource(sock.getInputStream())); // $ hasTaintFlow
}
public void misConfiguredTransformerFactory1(Socket sock) throws Exception {
@@ -85,8 +85,8 @@ public class TransformerTests {
Transformer transformer = tf.newTransformer();
tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "ab");
tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
- transformer.transform(new StreamSource(sock.getInputStream()), null); //unsafe
- tf.newTransformer(new StreamSource(sock.getInputStream())); //unsafe
+ transformer.transform(new StreamSource(sock.getInputStream()), null); // $ hasTaintFlow
+ tf.newTransformer(new StreamSource(sock.getInputStream())); // $ hasTaintFlow
}
public void misConfiguredTransformerFactory2(Socket sock) throws Exception {
@@ -94,50 +94,50 @@ public class TransformerTests {
Transformer transformer = tf.newTransformer();
tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "cd");
- transformer.transform(new StreamSource(sock.getInputStream()), null); //unsafe
- tf.newTransformer(new StreamSource(sock.getInputStream())); //unsafe
+ transformer.transform(new StreamSource(sock.getInputStream()), null); // $ hasTaintFlow
+ tf.newTransformer(new StreamSource(sock.getInputStream())); // $ hasTaintFlow
}
public void unconfiguredSAXTransformerFactory(Socket sock) throws Exception {
- SAXTransformerFactory sf = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
- sf.newXMLFilter(new StreamSource(sock.getInputStream())); //unsafe
+ SAXTransformerFactory sf = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
+ sf.newXMLFilter(new StreamSource(sock.getInputStream())); // $ hasTaintFlow
}
public void safeSAXTransformerFactory(Socket sock) throws Exception {
- SAXTransformerFactory sf = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
+ SAXTransformerFactory sf = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
sf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
sf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
- sf.newXMLFilter(new StreamSource(sock.getInputStream())); //safe
+ sf.newXMLFilter(new StreamSource(sock.getInputStream())); // safe
}
public void partialConfiguredSAXTransformerFactory1(Socket sock) throws Exception {
- SAXTransformerFactory sf = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
+ SAXTransformerFactory sf = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
sf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
- sf.newXMLFilter(new StreamSource(sock.getInputStream())); //unsafe
+ sf.newXMLFilter(new StreamSource(sock.getInputStream())); // $ hasTaintFlow
}
public void partialConfiguredSAXTransformerFactory2(Socket sock) throws Exception {
- SAXTransformerFactory sf = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
+ SAXTransformerFactory sf = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
sf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
- sf.newXMLFilter(new StreamSource(sock.getInputStream())); //unsafe
+ sf.newXMLFilter(new StreamSource(sock.getInputStream())); // $ hasTaintFlow
}
public void misConfiguredSAXTransformerFactory1(Socket sock) throws Exception {
- SAXTransformerFactory sf = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
+ SAXTransformerFactory sf = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
sf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "ab");
sf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
- sf.newXMLFilter(new StreamSource(sock.getInputStream())); //unsafe
+ sf.newXMLFilter(new StreamSource(sock.getInputStream())); // $ hasTaintFlow
}
public void misConfiguredSAXTransformerFactory2(Socket sock) throws Exception {
- SAXTransformerFactory sf = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
+ SAXTransformerFactory sf = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
sf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
sf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "cd");
- sf.newXMLFilter(new StreamSource(sock.getInputStream())); //unsafe
+ sf.newXMLFilter(new StreamSource(sock.getInputStream())); // $ hasTaintFlow
}
public void taintedSAXSource(Socket sock) throws Exception {
- SAXTransformerFactory sf = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
- sf.newXMLFilter(new SAXSource(new InputSource(sock.getInputStream()))); //unsafe
+ SAXTransformerFactory sf = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
+ sf.newXMLFilter(new SAXSource(new InputSource(sock.getInputStream()))); // $ hasTaintFlow
}
}
diff --git a/java/ql/test/query-tests/security/CWE-611/UnmarshallerTests.java b/java/ql/test/query-tests/security/CWE-611/UnmarshallerTests.java
index f29018d599a..54efa567aa3 100644
--- a/java/ql/test/query-tests/security/CWE-611/UnmarshallerTests.java
+++ b/java/ql/test/query-tests/security/CWE-611/UnmarshallerTests.java
@@ -16,15 +16,16 @@ public class UnmarshallerTests {
spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
JAXBContext jc = JAXBContext.newInstance(Object.class);
- Source xmlSource = new SAXSource(spf.newSAXParser().getXMLReader(), new InputSource(sock.getInputStream()));
+ Source xmlSource =
+ new SAXSource(spf.newSAXParser().getXMLReader(), new InputSource(sock.getInputStream()));
Unmarshaller um = jc.createUnmarshaller();
- um.unmarshal(xmlSource); //safe
+ um.unmarshal(xmlSource); // safe
}
public void unsafeUnmarshal(Socket sock) throws Exception {
SAXParserFactory spf = SAXParserFactory.newInstance();
JAXBContext jc = JAXBContext.newInstance(Object.class);
Unmarshaller um = jc.createUnmarshaller();
- um.unmarshal(sock.getInputStream()); //unsafe
+ um.unmarshal(sock.getInputStream()); // $ hasTaintFlow
}
}
diff --git a/java/ql/test/query-tests/security/CWE-611/ValidatorTests.java b/java/ql/test/query-tests/security/CWE-611/ValidatorTests.java
index 06d32ab95ae..091be21676a 100644
--- a/java/ql/test/query-tests/security/CWE-611/ValidatorTests.java
+++ b/java/ql/test/query-tests/security/CWE-611/ValidatorTests.java
@@ -19,7 +19,7 @@ public class ValidatorTests {
Schema schema = factory.newSchema();
Validator validator = schema.newValidator();
StreamSource source = new StreamSource(servletInputStream);
- validator.validate(source); // bad
+ validator.validate(source); // $ hasTaintFlow
}
@PostMapping(value = "good")
diff --git a/java/ql/test/query-tests/security/CWE-611/XMLDecoderTests.java b/java/ql/test/query-tests/security/CWE-611/XMLDecoderTests.java
index 434bfe9daed..8e75ebc1401 100644
--- a/java/ql/test/query-tests/security/CWE-611/XMLDecoderTests.java
+++ b/java/ql/test/query-tests/security/CWE-611/XMLDecoderTests.java
@@ -15,7 +15,7 @@ public class XMLDecoderTests {
public void bad3(HttpServletRequest request) throws Exception {
ServletInputStream servletInputStream = request.getInputStream();
XMLDecoder xmlDecoder = new XMLDecoder(servletInputStream);
- xmlDecoder.readObject(); // bad
+ xmlDecoder.readObject(); // $ hasTaintFlow
}
@PostMapping(value = "good")
diff --git a/java/ql/test/query-tests/security/CWE-611/XMLReaderTests.java b/java/ql/test/query-tests/security/CWE-611/XMLReaderTests.java
index 9f63e64d0c9..15536b766b7 100644
--- a/java/ql/test/query-tests/security/CWE-611/XMLReaderTests.java
+++ b/java/ql/test/query-tests/security/CWE-611/XMLReaderTests.java
@@ -13,23 +13,23 @@ public class XMLReaderTests {
public void unconfiguredReader(Socket sock) throws Exception {
XMLReader reader = XMLReaderFactory.createXMLReader();
- reader.parse(new InputSource(sock.getInputStream())); //unsafe
+ reader.parse(new InputSource(sock.getInputStream())); // $ hasTaintFlow
}
public void safeReaderFromConfig1(Socket sock) throws Exception {
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setFeature("http://xml.org/sax/features/external-general-entities", false);
reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
- reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",false);
- reader.parse(new InputSource(sock.getInputStream())); //safe
+ reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+ reader.parse(new InputSource(sock.getInputStream())); // safe
}
public void safeReaderFromConfig2(Socket sock) throws Exception {
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
- reader.parse(new InputSource(sock.getInputStream())); //safe
+ reader.parse(new InputSource(sock.getInputStream())); // safe
}
-
+
public void safeReaderFromSAXParser(Socket sock) throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
@@ -37,66 +37,66 @@ public class XMLReaderTests {
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
SAXParser parser = factory.newSAXParser();
XMLReader reader = parser.getXMLReader();
- reader.parse(new InputSource(sock.getInputStream())); //safe
+ reader.parse(new InputSource(sock.getInputStream())); // safe
}
public void safeReaderFromSAXReader(Socket sock) throws Exception {
SAXReader reader = new SAXReader();
reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
reader.setFeature("http://xml.org/sax/features/external-general-entities", false);
- reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+ reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
XMLReader xmlReader = reader.getXMLReader();
- xmlReader.parse(new InputSource(sock.getInputStream())); //safe
+ xmlReader.parse(new InputSource(sock.getInputStream())); // safe
}
public void partialConfiguredXMLReader1(Socket sock) throws Exception {
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setFeature("http://xml.org/sax/features/external-general-entities", false);
reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
- reader.parse(new InputSource(sock.getInputStream())); //unsafe
+ reader.parse(new InputSource(sock.getInputStream())); // $ hasTaintFlow
}
public void partialConfiguredXMLReader2(Socket sock) throws Exception {
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setFeature("http://xml.org/sax/features/external-general-entities", false);
- reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",false);
- reader.parse(new InputSource(sock.getInputStream())); //unsafe
+ reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+ reader.parse(new InputSource(sock.getInputStream())); // $ hasTaintFlow
}
public void partilaConfiguredXMLReader3(Socket sock) throws Exception {
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
- reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",false);
- reader.parse(new InputSource(sock.getInputStream())); //unsafe
+ reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+ reader.parse(new InputSource(sock.getInputStream())); // $ hasTaintFlow
}
public void misConfiguredXMLReader1(Socket sock) throws Exception {
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setFeature("http://xml.org/sax/features/external-general-entities", true);
reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
- reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",false);
- reader.parse(new InputSource(sock.getInputStream())); //unsafe
+ reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+ reader.parse(new InputSource(sock.getInputStream())); // $ hasTaintFlow
}
public void misConfiguredXMLReader2(Socket sock) throws Exception {
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setFeature("http://xml.org/sax/features/external-general-entities", false);
reader.setFeature("http://xml.org/sax/features/external-parameter-entities", true);
- reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",false);
- reader.parse(new InputSource(sock.getInputStream())); //unsafe
+ reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+ reader.parse(new InputSource(sock.getInputStream())); // $ hasTaintFlow
}
public void misConfiguredXMLReader3(Socket sock) throws Exception {
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setFeature("http://xml.org/sax/features/external-general-entities", false);
reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
- reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", true);
- reader.parse(new InputSource(sock.getInputStream())); //unsafe
+ reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", true);
+ reader.parse(new InputSource(sock.getInputStream())); // $ hasTaintFlow
}
-
+
public void misConfiguredXMLReader4(Socket sock) throws Exception {
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false);
- reader.parse(new InputSource(sock.getInputStream())); //unsafe
+ reader.parse(new InputSource(sock.getInputStream())); // $ hasTaintFlow
}
}
diff --git a/java/ql/test/query-tests/security/CWE-611/XPathExpressionTests.java b/java/ql/test/query-tests/security/CWE-611/XPathExpressionTests.java
index 1d67b9a055f..2fd56736e67 100644
--- a/java/ql/test/query-tests/security/CWE-611/XPathExpressionTests.java
+++ b/java/ql/test/query-tests/security/CWE-611/XPathExpressionTests.java
@@ -12,18 +12,18 @@ public class XPathExpressionTests {
public void safeXPathExpression(Socket sock) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
- factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
- DocumentBuilder builder = factory.newDocumentBuilder();
- XPathFactory xFactory = XPathFactory.newInstance();
- XPath path = xFactory.newXPath();
- XPathExpression expr = path.compile("");
- expr.evaluate(builder.parse(sock.getInputStream())); //safe
+ factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ XPathFactory xFactory = XPathFactory.newInstance();
+ XPath path = xFactory.newXPath();
+ XPathExpression expr = path.compile("");
+ expr.evaluate(builder.parse(sock.getInputStream())); // safe
}
public void unsafeExpressionTests(Socket sock) throws Exception {
- XPathFactory xFactory = XPathFactory.newInstance();
- XPath path = xFactory.newXPath();
- XPathExpression expr = path.compile("");
- expr.evaluate(new InputSource(sock.getInputStream())); //unsafe
+ XPathFactory xFactory = XPathFactory.newInstance();
+ XPath path = xFactory.newXPath();
+ XPathExpression expr = path.compile("");
+ expr.evaluate(new InputSource(sock.getInputStream())); // $ hasTaintFlow
}
}
diff --git a/java/ql/test/query-tests/security/CWE-611/XXE.expected b/java/ql/test/query-tests/security/CWE-611/XXE.expected
index dec71c301d5..e69de29bb2d 100644
--- a/java/ql/test/query-tests/security/CWE-611/XXE.expected
+++ b/java/ql/test/query-tests/security/CWE-611/XXE.expected
@@ -1,373 +0,0 @@
-edges
-| DigesterTests.java:14:49:14:72 | getInputStream(...) : ServletInputStream | DigesterTests.java:16:24:16:41 | servletInputStream |
-| DocumentBuilderTests.java:93:21:93:73 | new SAXSource(...) : SAXSource | DocumentBuilderTests.java:94:16:94:21 | source : SAXSource |
-| DocumentBuilderTests.java:93:35:93:72 | new InputSource(...) : InputSource | DocumentBuilderTests.java:93:21:93:73 | new SAXSource(...) : SAXSource |
-| DocumentBuilderTests.java:93:51:93:71 | getInputStream(...) : InputStream | DocumentBuilderTests.java:93:35:93:72 | new InputSource(...) : InputSource |
-| DocumentBuilderTests.java:94:16:94:21 | source : SAXSource | DocumentBuilderTests.java:94:16:94:38 | getInputSource(...) |
-| DocumentBuilderTests.java:100:24:100:62 | new StreamSource(...) : StreamSource | DocumentBuilderTests.java:101:46:101:51 | source : StreamSource |
-| DocumentBuilderTests.java:100:24:100:62 | new StreamSource(...) : StreamSource | DocumentBuilderTests.java:102:16:102:21 | source : StreamSource |
-| DocumentBuilderTests.java:100:41:100:61 | getInputStream(...) : InputStream | DocumentBuilderTests.java:100:24:100:62 | new StreamSource(...) : StreamSource |
-| DocumentBuilderTests.java:101:46:101:51 | source : StreamSource | DocumentBuilderTests.java:101:16:101:52 | sourceToInputSource(...) |
-| DocumentBuilderTests.java:102:16:102:21 | source : StreamSource | DocumentBuilderTests.java:102:16:102:38 | getInputStream(...) |
-| SAXSourceTests.java:17:24:17:84 | new SAXSource(...) : SAXSource | SAXSourceTests.java:20:18:20:23 | source |
-| SAXSourceTests.java:17:46:17:83 | new InputSource(...) : InputSource | SAXSourceTests.java:17:24:17:84 | new SAXSource(...) : SAXSource |
-| SAXSourceTests.java:17:62:17:82 | getInputStream(...) : InputStream | SAXSourceTests.java:17:46:17:83 | new InputSource(...) : InputSource |
-| SchemaTests.java:12:56:12:76 | getInputStream(...) : InputStream | SchemaTests.java:12:39:12:77 | new StreamSource(...) |
-| SchemaTests.java:25:56:25:76 | getInputStream(...) : InputStream | SchemaTests.java:25:39:25:77 | new StreamSource(...) |
-| SchemaTests.java:31:56:31:76 | getInputStream(...) : InputStream | SchemaTests.java:31:39:31:77 | new StreamSource(...) |
-| SchemaTests.java:38:56:38:76 | getInputStream(...) : InputStream | SchemaTests.java:38:39:38:77 | new StreamSource(...) |
-| SchemaTests.java:45:56:45:76 | getInputStream(...) : InputStream | SchemaTests.java:45:39:45:77 | new StreamSource(...) |
-| SimpleXMLTests.java:24:63:24:83 | getInputStream(...) : InputStream | SimpleXMLTests.java:24:41:24:84 | new InputStreamReader(...) |
-| SimpleXMLTests.java:30:5:30:25 | getInputStream(...) : InputStream | SimpleXMLTests.java:30:32:30:32 | b [post update] : byte[] |
-| SimpleXMLTests.java:30:32:30:32 | b [post update] : byte[] | SimpleXMLTests.java:31:52:31:52 | b : byte[] |
-| SimpleXMLTests.java:31:52:31:52 | b : byte[] | SimpleXMLTests.java:31:41:31:53 | new String(...) |
-| SimpleXMLTests.java:37:5:37:25 | getInputStream(...) : InputStream | SimpleXMLTests.java:37:32:37:32 | b [post update] : byte[] |
-| SimpleXMLTests.java:37:32:37:32 | b [post update] : byte[] | SimpleXMLTests.java:38:52:38:52 | b : byte[] |
-| SimpleXMLTests.java:38:52:38:52 | b : byte[] | SimpleXMLTests.java:38:41:38:53 | new String(...) |
-| SimpleXMLTests.java:43:63:43:83 | getInputStream(...) : InputStream | SimpleXMLTests.java:43:41:43:84 | new InputStreamReader(...) |
-| SimpleXMLTests.java:68:59:68:79 | getInputStream(...) : InputStream | SimpleXMLTests.java:68:37:68:80 | new InputStreamReader(...) |
-| SimpleXMLTests.java:73:59:73:79 | getInputStream(...) : InputStream | SimpleXMLTests.java:73:37:73:80 | new InputStreamReader(...) |
-| SimpleXMLTests.java:78:48:78:68 | getInputStream(...) : InputStream | SimpleXMLTests.java:78:26:78:69 | new InputStreamReader(...) |
-| SimpleXMLTests.java:83:48:83:68 | getInputStream(...) : InputStream | SimpleXMLTests.java:83:26:83:69 | new InputStreamReader(...) |
-| SimpleXMLTests.java:89:5:89:25 | getInputStream(...) : InputStream | SimpleXMLTests.java:89:32:89:32 | b [post update] : byte[] |
-| SimpleXMLTests.java:89:32:89:32 | b [post update] : byte[] | SimpleXMLTests.java:90:48:90:48 | b : byte[] |
-| SimpleXMLTests.java:90:48:90:48 | b : byte[] | SimpleXMLTests.java:90:37:90:49 | new String(...) |
-| SimpleXMLTests.java:96:5:96:25 | getInputStream(...) : InputStream | SimpleXMLTests.java:96:32:96:32 | b [post update] : byte[] |
-| SimpleXMLTests.java:96:32:96:32 | b [post update] : byte[] | SimpleXMLTests.java:97:48:97:48 | b : byte[] |
-| SimpleXMLTests.java:97:48:97:48 | b : byte[] | SimpleXMLTests.java:97:37:97:49 | new String(...) |
-| SimpleXMLTests.java:103:5:103:25 | getInputStream(...) : InputStream | SimpleXMLTests.java:103:32:103:32 | b [post update] : byte[] |
-| SimpleXMLTests.java:103:32:103:32 | b [post update] : byte[] | SimpleXMLTests.java:104:37:104:37 | b : byte[] |
-| SimpleXMLTests.java:104:37:104:37 | b : byte[] | SimpleXMLTests.java:104:26:104:38 | new String(...) |
-| SimpleXMLTests.java:110:5:110:25 | getInputStream(...) : InputStream | SimpleXMLTests.java:110:32:110:32 | b [post update] : byte[] |
-| SimpleXMLTests.java:110:32:110:32 | b [post update] : byte[] | SimpleXMLTests.java:111:37:111:37 | b : byte[] |
-| SimpleXMLTests.java:111:37:111:37 | b : byte[] | SimpleXMLTests.java:111:26:111:38 | new String(...) |
-| SimpleXMLTests.java:119:44:119:64 | getInputStream(...) : InputStream | SimpleXMLTests.java:119:22:119:65 | new InputStreamReader(...) |
-| SimpleXMLTests.java:129:44:129:64 | getInputStream(...) : InputStream | SimpleXMLTests.java:129:22:129:65 | new InputStreamReader(...) |
-| SimpleXMLTests.java:139:44:139:64 | getInputStream(...) : InputStream | SimpleXMLTests.java:139:22:139:65 | new InputStreamReader(...) |
-| SimpleXMLTests.java:145:5:145:25 | getInputStream(...) : InputStream | SimpleXMLTests.java:145:32:145:32 | b [post update] : byte[] |
-| SimpleXMLTests.java:145:32:145:32 | b [post update] : byte[] | SimpleXMLTests.java:146:33:146:33 | b : byte[] |
-| SimpleXMLTests.java:146:33:146:33 | b : byte[] | SimpleXMLTests.java:146:22:146:34 | new String(...) |
-| SimpleXMLTests.java:152:5:152:25 | getInputStream(...) : InputStream | SimpleXMLTests.java:152:32:152:32 | b [post update] : byte[] |
-| SimpleXMLTests.java:152:32:152:32 | b [post update] : byte[] | SimpleXMLTests.java:153:33:153:33 | b : byte[] |
-| SimpleXMLTests.java:153:33:153:33 | b : byte[] | SimpleXMLTests.java:153:22:153:34 | new String(...) |
-| TransformerTests.java:20:44:20:64 | getInputStream(...) : InputStream | TransformerTests.java:20:27:20:65 | new StreamSource(...) |
-| TransformerTests.java:21:40:21:60 | getInputStream(...) : InputStream | TransformerTests.java:21:23:21:61 | new StreamSource(...) |
-| TransformerTests.java:71:44:71:64 | getInputStream(...) : InputStream | TransformerTests.java:71:27:71:65 | new StreamSource(...) |
-| TransformerTests.java:72:40:72:60 | getInputStream(...) : InputStream | TransformerTests.java:72:23:72:61 | new StreamSource(...) |
-| TransformerTests.java:79:44:79:64 | getInputStream(...) : InputStream | TransformerTests.java:79:27:79:65 | new StreamSource(...) |
-| TransformerTests.java:80:40:80:60 | getInputStream(...) : InputStream | TransformerTests.java:80:23:80:61 | new StreamSource(...) |
-| TransformerTests.java:88:44:88:64 | getInputStream(...) : InputStream | TransformerTests.java:88:27:88:65 | new StreamSource(...) |
-| TransformerTests.java:89:40:89:60 | getInputStream(...) : InputStream | TransformerTests.java:89:23:89:61 | new StreamSource(...) |
-| TransformerTests.java:97:44:97:64 | getInputStream(...) : InputStream | TransformerTests.java:97:27:97:65 | new StreamSource(...) |
-| TransformerTests.java:98:40:98:60 | getInputStream(...) : InputStream | TransformerTests.java:98:23:98:61 | new StreamSource(...) |
-| TransformerTests.java:103:38:103:58 | getInputStream(...) : InputStream | TransformerTests.java:103:21:103:59 | new StreamSource(...) |
-| TransformerTests.java:116:38:116:58 | getInputStream(...) : InputStream | TransformerTests.java:116:21:116:59 | new StreamSource(...) |
-| TransformerTests.java:122:38:122:58 | getInputStream(...) : InputStream | TransformerTests.java:122:21:122:59 | new StreamSource(...) |
-| TransformerTests.java:129:38:129:58 | getInputStream(...) : InputStream | TransformerTests.java:129:21:129:59 | new StreamSource(...) |
-| TransformerTests.java:136:38:136:58 | getInputStream(...) : InputStream | TransformerTests.java:136:21:136:59 | new StreamSource(...) |
-| TransformerTests.java:141:32:141:69 | new InputSource(...) : InputSource | TransformerTests.java:141:18:141:70 | new SAXSource(...) |
-| TransformerTests.java:141:48:141:68 | getInputStream(...) : InputStream | TransformerTests.java:141:32:141:69 | new InputSource(...) : InputSource |
-| ValidatorTests.java:17:49:17:72 | getInputStream(...) : ServletInputStream | ValidatorTests.java:21:48:21:65 | servletInputStream : ServletInputStream |
-| ValidatorTests.java:21:31:21:66 | new StreamSource(...) : StreamSource | ValidatorTests.java:22:28:22:33 | source |
-| ValidatorTests.java:21:48:21:65 | servletInputStream : ServletInputStream | ValidatorTests.java:21:31:21:66 | new StreamSource(...) : StreamSource |
-| XMLDecoderTests.java:16:49:16:72 | getInputStream(...) : ServletInputStream | XMLDecoderTests.java:17:48:17:65 | servletInputStream : ServletInputStream |
-| XMLDecoderTests.java:17:33:17:66 | new XMLDecoder(...) : XMLDecoder | XMLDecoderTests.java:18:9:18:18 | xmlDecoder |
-| XMLDecoderTests.java:17:48:17:65 | servletInputStream : ServletInputStream | XMLDecoderTests.java:17:33:17:66 | new XMLDecoder(...) : XMLDecoder |
-| XMLReaderTests.java:16:34:16:54 | getInputStream(...) : InputStream | XMLReaderTests.java:16:18:16:55 | new InputSource(...) |
-| XMLReaderTests.java:56:34:56:54 | getInputStream(...) : InputStream | XMLReaderTests.java:56:18:56:55 | new InputSource(...) |
-| XMLReaderTests.java:63:34:63:54 | getInputStream(...) : InputStream | XMLReaderTests.java:63:18:63:55 | new InputSource(...) |
-| XMLReaderTests.java:70:34:70:54 | getInputStream(...) : InputStream | XMLReaderTests.java:70:18:70:55 | new InputSource(...) |
-| XMLReaderTests.java:78:34:78:54 | getInputStream(...) : InputStream | XMLReaderTests.java:78:18:78:55 | new InputSource(...) |
-| XMLReaderTests.java:86:34:86:54 | getInputStream(...) : InputStream | XMLReaderTests.java:86:18:86:55 | new InputSource(...) |
-| XMLReaderTests.java:94:34:94:54 | getInputStream(...) : InputStream | XMLReaderTests.java:94:18:94:55 | new InputSource(...) |
-| XMLReaderTests.java:100:34:100:54 | getInputStream(...) : InputStream | XMLReaderTests.java:100:18:100:55 | new InputSource(...) |
-| XPathExpressionTests.java:27:37:27:57 | getInputStream(...) : InputStream | XPathExpressionTests.java:27:21:27:58 | new InputSource(...) |
-nodes
-| DigesterTests.java:14:49:14:72 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
-| DigesterTests.java:16:24:16:41 | servletInputStream | semmle.label | servletInputStream |
-| DocumentBuilderTests.java:14:19:14:39 | getInputStream(...) | semmle.label | getInputStream(...) |
-| DocumentBuilderTests.java:28:19:28:39 | getInputStream(...) | semmle.label | getInputStream(...) |
-| DocumentBuilderTests.java:35:19:35:39 | getInputStream(...) | semmle.label | getInputStream(...) |
-| DocumentBuilderTests.java:42:19:42:39 | getInputStream(...) | semmle.label | getInputStream(...) |
-| DocumentBuilderTests.java:49:19:49:39 | getInputStream(...) | semmle.label | getInputStream(...) |
-| DocumentBuilderTests.java:64:19:64:39 | getInputStream(...) | semmle.label | getInputStream(...) |
-| DocumentBuilderTests.java:71:19:71:39 | getInputStream(...) | semmle.label | getInputStream(...) |
-| DocumentBuilderTests.java:79:19:79:39 | getInputStream(...) | semmle.label | getInputStream(...) |
-| DocumentBuilderTests.java:87:19:87:39 | getInputStream(...) | semmle.label | getInputStream(...) |
-| DocumentBuilderTests.java:93:21:93:73 | new SAXSource(...) : SAXSource | semmle.label | new SAXSource(...) : SAXSource |
-| DocumentBuilderTests.java:93:35:93:72 | new InputSource(...) : InputSource | semmle.label | new InputSource(...) : InputSource |
-| DocumentBuilderTests.java:93:51:93:71 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| DocumentBuilderTests.java:94:16:94:21 | source : SAXSource | semmle.label | source : SAXSource |
-| DocumentBuilderTests.java:94:16:94:38 | getInputSource(...) | semmle.label | getInputSource(...) |
-| DocumentBuilderTests.java:100:24:100:62 | new StreamSource(...) : StreamSource | semmle.label | new StreamSource(...) : StreamSource |
-| DocumentBuilderTests.java:100:41:100:61 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| DocumentBuilderTests.java:101:16:101:52 | sourceToInputSource(...) | semmle.label | sourceToInputSource(...) |
-| DocumentBuilderTests.java:101:46:101:51 | source : StreamSource | semmle.label | source : StreamSource |
-| DocumentBuilderTests.java:102:16:102:21 | source : StreamSource | semmle.label | source : StreamSource |
-| DocumentBuilderTests.java:102:16:102:38 | getInputStream(...) | semmle.label | getInputStream(...) |
-| ParserHelperTests.java:12:55:12:78 | getInputStream(...) | semmle.label | getInputStream(...) |
-| SAXBuilderTests.java:8:19:8:39 | getInputStream(...) | semmle.label | getInputStream(...) |
-| SAXBuilderTests.java:20:19:20:39 | getInputStream(...) | semmle.label | getInputStream(...) |
-| SAXParserTests.java:13:18:13:38 | getInputStream(...) | semmle.label | getInputStream(...) |
-| SAXParserTests.java:30:18:30:38 | getInputStream(...) | semmle.label | getInputStream(...) |
-| SAXParserTests.java:38:18:38:38 | getInputStream(...) | semmle.label | getInputStream(...) |
-| SAXParserTests.java:46:18:46:38 | getInputStream(...) | semmle.label | getInputStream(...) |
-| SAXParserTests.java:55:18:55:38 | getInputStream(...) | semmle.label | getInputStream(...) |
-| SAXParserTests.java:64:18:64:38 | getInputStream(...) | semmle.label | getInputStream(...) |
-| SAXParserTests.java:73:18:73:38 | getInputStream(...) | semmle.label | getInputStream(...) |
-| SAXReaderTests.java:8:17:8:37 | getInputStream(...) | semmle.label | getInputStream(...) |
-| SAXReaderTests.java:23:17:23:37 | getInputStream(...) | semmle.label | getInputStream(...) |
-| SAXReaderTests.java:30:17:30:37 | getInputStream(...) | semmle.label | getInputStream(...) |
-| SAXReaderTests.java:37:17:37:37 | getInputStream(...) | semmle.label | getInputStream(...) |
-| SAXReaderTests.java:45:17:45:37 | getInputStream(...) | semmle.label | getInputStream(...) |
-| SAXReaderTests.java:53:17:53:37 | getInputStream(...) | semmle.label | getInputStream(...) |
-| SAXReaderTests.java:61:17:61:37 | getInputStream(...) | semmle.label | getInputStream(...) |
-| SAXSourceTests.java:17:24:17:84 | new SAXSource(...) : SAXSource | semmle.label | new SAXSource(...) : SAXSource |
-| SAXSourceTests.java:17:46:17:83 | new InputSource(...) : InputSource | semmle.label | new InputSource(...) : InputSource |
-| SAXSourceTests.java:17:62:17:82 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| SAXSourceTests.java:20:18:20:23 | source | semmle.label | source |
-| SchemaTests.java:12:39:12:77 | new StreamSource(...) | semmle.label | new StreamSource(...) |
-| SchemaTests.java:12:56:12:76 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| SchemaTests.java:25:39:25:77 | new StreamSource(...) | semmle.label | new StreamSource(...) |
-| SchemaTests.java:25:56:25:76 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| SchemaTests.java:31:39:31:77 | new StreamSource(...) | semmle.label | new StreamSource(...) |
-| SchemaTests.java:31:56:31:76 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| SchemaTests.java:38:39:38:77 | new StreamSource(...) | semmle.label | new StreamSource(...) |
-| SchemaTests.java:38:56:38:76 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| SchemaTests.java:45:39:45:77 | new StreamSource(...) | semmle.label | new StreamSource(...) |
-| SchemaTests.java:45:56:45:76 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| SimpleXMLTests.java:14:41:14:61 | getInputStream(...) | semmle.label | getInputStream(...) |
-| SimpleXMLTests.java:19:41:19:61 | getInputStream(...) | semmle.label | getInputStream(...) |
-| SimpleXMLTests.java:24:41:24:84 | new InputStreamReader(...) | semmle.label | new InputStreamReader(...) |
-| SimpleXMLTests.java:24:63:24:83 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| SimpleXMLTests.java:30:5:30:25 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| SimpleXMLTests.java:30:32:30:32 | b [post update] : byte[] | semmle.label | b [post update] : byte[] |
-| SimpleXMLTests.java:31:41:31:53 | new String(...) | semmle.label | new String(...) |
-| SimpleXMLTests.java:31:52:31:52 | b : byte[] | semmle.label | b : byte[] |
-| SimpleXMLTests.java:37:5:37:25 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| SimpleXMLTests.java:37:32:37:32 | b [post update] : byte[] | semmle.label | b [post update] : byte[] |
-| SimpleXMLTests.java:38:41:38:53 | new String(...) | semmle.label | new String(...) |
-| SimpleXMLTests.java:38:52:38:52 | b : byte[] | semmle.label | b : byte[] |
-| SimpleXMLTests.java:43:41:43:84 | new InputStreamReader(...) | semmle.label | new InputStreamReader(...) |
-| SimpleXMLTests.java:43:63:43:83 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| SimpleXMLTests.java:48:37:48:57 | getInputStream(...) | semmle.label | getInputStream(...) |
-| SimpleXMLTests.java:53:37:53:57 | getInputStream(...) | semmle.label | getInputStream(...) |
-| SimpleXMLTests.java:58:26:58:46 | getInputStream(...) | semmle.label | getInputStream(...) |
-| SimpleXMLTests.java:63:26:63:46 | getInputStream(...) | semmle.label | getInputStream(...) |
-| SimpleXMLTests.java:68:37:68:80 | new InputStreamReader(...) | semmle.label | new InputStreamReader(...) |
-| SimpleXMLTests.java:68:59:68:79 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| SimpleXMLTests.java:73:37:73:80 | new InputStreamReader(...) | semmle.label | new InputStreamReader(...) |
-| SimpleXMLTests.java:73:59:73:79 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| SimpleXMLTests.java:78:26:78:69 | new InputStreamReader(...) | semmle.label | new InputStreamReader(...) |
-| SimpleXMLTests.java:78:48:78:68 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| SimpleXMLTests.java:83:26:83:69 | new InputStreamReader(...) | semmle.label | new InputStreamReader(...) |
-| SimpleXMLTests.java:83:48:83:68 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| SimpleXMLTests.java:89:5:89:25 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| SimpleXMLTests.java:89:32:89:32 | b [post update] : byte[] | semmle.label | b [post update] : byte[] |
-| SimpleXMLTests.java:90:37:90:49 | new String(...) | semmle.label | new String(...) |
-| SimpleXMLTests.java:90:48:90:48 | b : byte[] | semmle.label | b : byte[] |
-| SimpleXMLTests.java:96:5:96:25 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| SimpleXMLTests.java:96:32:96:32 | b [post update] : byte[] | semmle.label | b [post update] : byte[] |
-| SimpleXMLTests.java:97:37:97:49 | new String(...) | semmle.label | new String(...) |
-| SimpleXMLTests.java:97:48:97:48 | b : byte[] | semmle.label | b : byte[] |
-| SimpleXMLTests.java:103:5:103:25 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| SimpleXMLTests.java:103:32:103:32 | b [post update] : byte[] | semmle.label | b [post update] : byte[] |
-| SimpleXMLTests.java:104:26:104:38 | new String(...) | semmle.label | new String(...) |
-| SimpleXMLTests.java:104:37:104:37 | b : byte[] | semmle.label | b : byte[] |
-| SimpleXMLTests.java:110:5:110:25 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| SimpleXMLTests.java:110:32:110:32 | b [post update] : byte[] | semmle.label | b [post update] : byte[] |
-| SimpleXMLTests.java:111:26:111:38 | new String(...) | semmle.label | new String(...) |
-| SimpleXMLTests.java:111:37:111:37 | b : byte[] | semmle.label | b : byte[] |
-| SimpleXMLTests.java:115:22:115:42 | getInputStream(...) | semmle.label | getInputStream(...) |
-| SimpleXMLTests.java:119:22:119:65 | new InputStreamReader(...) | semmle.label | new InputStreamReader(...) |
-| SimpleXMLTests.java:119:44:119:64 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| SimpleXMLTests.java:124:22:124:42 | getInputStream(...) | semmle.label | getInputStream(...) |
-| SimpleXMLTests.java:129:22:129:65 | new InputStreamReader(...) | semmle.label | new InputStreamReader(...) |
-| SimpleXMLTests.java:129:44:129:64 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| SimpleXMLTests.java:134:22:134:42 | getInputStream(...) | semmle.label | getInputStream(...) |
-| SimpleXMLTests.java:139:22:139:65 | new InputStreamReader(...) | semmle.label | new InputStreamReader(...) |
-| SimpleXMLTests.java:139:44:139:64 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| SimpleXMLTests.java:145:5:145:25 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| SimpleXMLTests.java:145:32:145:32 | b [post update] : byte[] | semmle.label | b [post update] : byte[] |
-| SimpleXMLTests.java:146:22:146:34 | new String(...) | semmle.label | new String(...) |
-| SimpleXMLTests.java:146:33:146:33 | b : byte[] | semmle.label | b : byte[] |
-| SimpleXMLTests.java:152:5:152:25 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| SimpleXMLTests.java:152:32:152:32 | b [post update] : byte[] | semmle.label | b [post update] : byte[] |
-| SimpleXMLTests.java:153:22:153:34 | new String(...) | semmle.label | new String(...) |
-| SimpleXMLTests.java:153:33:153:33 | b : byte[] | semmle.label | b : byte[] |
-| TransformerTests.java:20:27:20:65 | new StreamSource(...) | semmle.label | new StreamSource(...) |
-| TransformerTests.java:20:44:20:64 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| TransformerTests.java:21:23:21:61 | new StreamSource(...) | semmle.label | new StreamSource(...) |
-| TransformerTests.java:21:40:21:60 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| TransformerTests.java:71:27:71:65 | new StreamSource(...) | semmle.label | new StreamSource(...) |
-| TransformerTests.java:71:44:71:64 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| TransformerTests.java:72:23:72:61 | new StreamSource(...) | semmle.label | new StreamSource(...) |
-| TransformerTests.java:72:40:72:60 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| TransformerTests.java:79:27:79:65 | new StreamSource(...) | semmle.label | new StreamSource(...) |
-| TransformerTests.java:79:44:79:64 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| TransformerTests.java:80:23:80:61 | new StreamSource(...) | semmle.label | new StreamSource(...) |
-| TransformerTests.java:80:40:80:60 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| TransformerTests.java:88:27:88:65 | new StreamSource(...) | semmle.label | new StreamSource(...) |
-| TransformerTests.java:88:44:88:64 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| TransformerTests.java:89:23:89:61 | new StreamSource(...) | semmle.label | new StreamSource(...) |
-| TransformerTests.java:89:40:89:60 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| TransformerTests.java:97:27:97:65 | new StreamSource(...) | semmle.label | new StreamSource(...) |
-| TransformerTests.java:97:44:97:64 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| TransformerTests.java:98:23:98:61 | new StreamSource(...) | semmle.label | new StreamSource(...) |
-| TransformerTests.java:98:40:98:60 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| TransformerTests.java:103:21:103:59 | new StreamSource(...) | semmle.label | new StreamSource(...) |
-| TransformerTests.java:103:38:103:58 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| TransformerTests.java:116:21:116:59 | new StreamSource(...) | semmle.label | new StreamSource(...) |
-| TransformerTests.java:116:38:116:58 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| TransformerTests.java:122:21:122:59 | new StreamSource(...) | semmle.label | new StreamSource(...) |
-| TransformerTests.java:122:38:122:58 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| TransformerTests.java:129:21:129:59 | new StreamSource(...) | semmle.label | new StreamSource(...) |
-| TransformerTests.java:129:38:129:58 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| TransformerTests.java:136:21:136:59 | new StreamSource(...) | semmle.label | new StreamSource(...) |
-| TransformerTests.java:136:38:136:58 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| TransformerTests.java:141:18:141:70 | new SAXSource(...) | semmle.label | new SAXSource(...) |
-| TransformerTests.java:141:32:141:69 | new InputSource(...) : InputSource | semmle.label | new InputSource(...) : InputSource |
-| TransformerTests.java:141:48:141:68 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| UnmarshallerTests.java:28:18:28:38 | getInputStream(...) | semmle.label | getInputStream(...) |
-| ValidatorTests.java:17:49:17:72 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
-| ValidatorTests.java:21:31:21:66 | new StreamSource(...) : StreamSource | semmle.label | new StreamSource(...) : StreamSource |
-| ValidatorTests.java:21:48:21:65 | servletInputStream : ServletInputStream | semmle.label | servletInputStream : ServletInputStream |
-| ValidatorTests.java:22:28:22:33 | source | semmle.label | source |
-| XMLDecoderTests.java:16:49:16:72 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
-| XMLDecoderTests.java:17:33:17:66 | new XMLDecoder(...) : XMLDecoder | semmle.label | new XMLDecoder(...) : XMLDecoder |
-| XMLDecoderTests.java:17:48:17:65 | servletInputStream : ServletInputStream | semmle.label | servletInputStream : ServletInputStream |
-| XMLDecoderTests.java:18:9:18:18 | xmlDecoder | semmle.label | xmlDecoder |
-| XMLReaderTests.java:16:18:16:55 | new InputSource(...) | semmle.label | new InputSource(...) |
-| XMLReaderTests.java:16:34:16:54 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| XMLReaderTests.java:56:18:56:55 | new InputSource(...) | semmle.label | new InputSource(...) |
-| XMLReaderTests.java:56:34:56:54 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| XMLReaderTests.java:63:18:63:55 | new InputSource(...) | semmle.label | new InputSource(...) |
-| XMLReaderTests.java:63:34:63:54 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| XMLReaderTests.java:70:18:70:55 | new InputSource(...) | semmle.label | new InputSource(...) |
-| XMLReaderTests.java:70:34:70:54 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| XMLReaderTests.java:78:18:78:55 | new InputSource(...) | semmle.label | new InputSource(...) |
-| XMLReaderTests.java:78:34:78:54 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| XMLReaderTests.java:86:18:86:55 | new InputSource(...) | semmle.label | new InputSource(...) |
-| XMLReaderTests.java:86:34:86:54 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| XMLReaderTests.java:94:18:94:55 | new InputSource(...) | semmle.label | new InputSource(...) |
-| XMLReaderTests.java:94:34:94:54 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| XMLReaderTests.java:100:18:100:55 | new InputSource(...) | semmle.label | new InputSource(...) |
-| XMLReaderTests.java:100:34:100:54 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| XPathExpressionTests.java:27:21:27:58 | new InputSource(...) | semmle.label | new InputSource(...) |
-| XPathExpressionTests.java:27:37:27:57 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| XmlInputFactoryTests.java:9:35:9:55 | getInputStream(...) | semmle.label | getInputStream(...) |
-| XmlInputFactoryTests.java:10:34:10:54 | getInputStream(...) | semmle.label | getInputStream(...) |
-| XmlInputFactoryTests.java:24:35:24:55 | getInputStream(...) | semmle.label | getInputStream(...) |
-| XmlInputFactoryTests.java:25:34:25:54 | getInputStream(...) | semmle.label | getInputStream(...) |
-| XmlInputFactoryTests.java:31:35:31:55 | getInputStream(...) | semmle.label | getInputStream(...) |
-| XmlInputFactoryTests.java:32:34:32:54 | getInputStream(...) | semmle.label | getInputStream(...) |
-| XmlInputFactoryTests.java:39:35:39:55 | getInputStream(...) | semmle.label | getInputStream(...) |
-| XmlInputFactoryTests.java:40:34:40:54 | getInputStream(...) | semmle.label | getInputStream(...) |
-| XmlInputFactoryTests.java:47:35:47:55 | getInputStream(...) | semmle.label | getInputStream(...) |
-| XmlInputFactoryTests.java:48:34:48:54 | getInputStream(...) | semmle.label | getInputStream(...) |
-| XmlInputFactoryTests.java:55:35:55:55 | getInputStream(...) | semmle.label | getInputStream(...) |
-| XmlInputFactoryTests.java:56:34:56:54 | getInputStream(...) | semmle.label | getInputStream(...) |
-subpaths
-#select
-| DigesterTests.java:16:24:16:41 | servletInputStream | DigesterTests.java:14:49:14:72 | getInputStream(...) : ServletInputStream | DigesterTests.java:16:24:16:41 | servletInputStream | XML parsing depends on a $@ without guarding against external entity expansion. | DigesterTests.java:14:49:14:72 | getInputStream(...) | user-provided value |
-| DocumentBuilderTests.java:14:19:14:39 | getInputStream(...) | DocumentBuilderTests.java:14:19:14:39 | getInputStream(...) | DocumentBuilderTests.java:14:19:14:39 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | DocumentBuilderTests.java:14:19:14:39 | getInputStream(...) | user-provided value |
-| DocumentBuilderTests.java:28:19:28:39 | getInputStream(...) | DocumentBuilderTests.java:28:19:28:39 | getInputStream(...) | DocumentBuilderTests.java:28:19:28:39 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | DocumentBuilderTests.java:28:19:28:39 | getInputStream(...) | user-provided value |
-| DocumentBuilderTests.java:35:19:35:39 | getInputStream(...) | DocumentBuilderTests.java:35:19:35:39 | getInputStream(...) | DocumentBuilderTests.java:35:19:35:39 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | DocumentBuilderTests.java:35:19:35:39 | getInputStream(...) | user-provided value |
-| DocumentBuilderTests.java:42:19:42:39 | getInputStream(...) | DocumentBuilderTests.java:42:19:42:39 | getInputStream(...) | DocumentBuilderTests.java:42:19:42:39 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | DocumentBuilderTests.java:42:19:42:39 | getInputStream(...) | user-provided value |
-| DocumentBuilderTests.java:49:19:49:39 | getInputStream(...) | DocumentBuilderTests.java:49:19:49:39 | getInputStream(...) | DocumentBuilderTests.java:49:19:49:39 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | DocumentBuilderTests.java:49:19:49:39 | getInputStream(...) | user-provided value |
-| DocumentBuilderTests.java:64:19:64:39 | getInputStream(...) | DocumentBuilderTests.java:64:19:64:39 | getInputStream(...) | DocumentBuilderTests.java:64:19:64:39 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | DocumentBuilderTests.java:64:19:64:39 | getInputStream(...) | user-provided value |
-| DocumentBuilderTests.java:71:19:71:39 | getInputStream(...) | DocumentBuilderTests.java:71:19:71:39 | getInputStream(...) | DocumentBuilderTests.java:71:19:71:39 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | DocumentBuilderTests.java:71:19:71:39 | getInputStream(...) | user-provided value |
-| DocumentBuilderTests.java:79:19:79:39 | getInputStream(...) | DocumentBuilderTests.java:79:19:79:39 | getInputStream(...) | DocumentBuilderTests.java:79:19:79:39 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | DocumentBuilderTests.java:79:19:79:39 | getInputStream(...) | user-provided value |
-| DocumentBuilderTests.java:87:19:87:39 | getInputStream(...) | DocumentBuilderTests.java:87:19:87:39 | getInputStream(...) | DocumentBuilderTests.java:87:19:87:39 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | DocumentBuilderTests.java:87:19:87:39 | getInputStream(...) | user-provided value |
-| DocumentBuilderTests.java:94:16:94:38 | getInputSource(...) | DocumentBuilderTests.java:93:51:93:71 | getInputStream(...) : InputStream | DocumentBuilderTests.java:94:16:94:38 | getInputSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | DocumentBuilderTests.java:93:51:93:71 | getInputStream(...) | user-provided value |
-| DocumentBuilderTests.java:101:16:101:52 | sourceToInputSource(...) | DocumentBuilderTests.java:100:41:100:61 | getInputStream(...) : InputStream | DocumentBuilderTests.java:101:16:101:52 | sourceToInputSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | DocumentBuilderTests.java:100:41:100:61 | getInputStream(...) | user-provided value |
-| DocumentBuilderTests.java:102:16:102:38 | getInputStream(...) | DocumentBuilderTests.java:100:41:100:61 | getInputStream(...) : InputStream | DocumentBuilderTests.java:102:16:102:38 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | DocumentBuilderTests.java:100:41:100:61 | getInputStream(...) | user-provided value |
-| ParserHelperTests.java:12:55:12:78 | getInputStream(...) | ParserHelperTests.java:12:55:12:78 | getInputStream(...) | ParserHelperTests.java:12:55:12:78 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | ParserHelperTests.java:12:55:12:78 | getInputStream(...) | user-provided value |
-| SAXBuilderTests.java:8:19:8:39 | getInputStream(...) | SAXBuilderTests.java:8:19:8:39 | getInputStream(...) | SAXBuilderTests.java:8:19:8:39 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SAXBuilderTests.java:8:19:8:39 | getInputStream(...) | user-provided value |
-| SAXBuilderTests.java:20:19:20:39 | getInputStream(...) | SAXBuilderTests.java:20:19:20:39 | getInputStream(...) | SAXBuilderTests.java:20:19:20:39 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SAXBuilderTests.java:20:19:20:39 | getInputStream(...) | user-provided value |
-| SAXParserTests.java:13:18:13:38 | getInputStream(...) | SAXParserTests.java:13:18:13:38 | getInputStream(...) | SAXParserTests.java:13:18:13:38 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SAXParserTests.java:13:18:13:38 | getInputStream(...) | user-provided value |
-| SAXParserTests.java:30:18:30:38 | getInputStream(...) | SAXParserTests.java:30:18:30:38 | getInputStream(...) | SAXParserTests.java:30:18:30:38 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SAXParserTests.java:30:18:30:38 | getInputStream(...) | user-provided value |
-| SAXParserTests.java:38:18:38:38 | getInputStream(...) | SAXParserTests.java:38:18:38:38 | getInputStream(...) | SAXParserTests.java:38:18:38:38 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SAXParserTests.java:38:18:38:38 | getInputStream(...) | user-provided value |
-| SAXParserTests.java:46:18:46:38 | getInputStream(...) | SAXParserTests.java:46:18:46:38 | getInputStream(...) | SAXParserTests.java:46:18:46:38 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SAXParserTests.java:46:18:46:38 | getInputStream(...) | user-provided value |
-| SAXParserTests.java:55:18:55:38 | getInputStream(...) | SAXParserTests.java:55:18:55:38 | getInputStream(...) | SAXParserTests.java:55:18:55:38 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SAXParserTests.java:55:18:55:38 | getInputStream(...) | user-provided value |
-| SAXParserTests.java:64:18:64:38 | getInputStream(...) | SAXParserTests.java:64:18:64:38 | getInputStream(...) | SAXParserTests.java:64:18:64:38 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SAXParserTests.java:64:18:64:38 | getInputStream(...) | user-provided value |
-| SAXParserTests.java:73:18:73:38 | getInputStream(...) | SAXParserTests.java:73:18:73:38 | getInputStream(...) | SAXParserTests.java:73:18:73:38 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SAXParserTests.java:73:18:73:38 | getInputStream(...) | user-provided value |
-| SAXReaderTests.java:8:17:8:37 | getInputStream(...) | SAXReaderTests.java:8:17:8:37 | getInputStream(...) | SAXReaderTests.java:8:17:8:37 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SAXReaderTests.java:8:17:8:37 | getInputStream(...) | user-provided value |
-| SAXReaderTests.java:23:17:23:37 | getInputStream(...) | SAXReaderTests.java:23:17:23:37 | getInputStream(...) | SAXReaderTests.java:23:17:23:37 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SAXReaderTests.java:23:17:23:37 | getInputStream(...) | user-provided value |
-| SAXReaderTests.java:30:17:30:37 | getInputStream(...) | SAXReaderTests.java:30:17:30:37 | getInputStream(...) | SAXReaderTests.java:30:17:30:37 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SAXReaderTests.java:30:17:30:37 | getInputStream(...) | user-provided value |
-| SAXReaderTests.java:37:17:37:37 | getInputStream(...) | SAXReaderTests.java:37:17:37:37 | getInputStream(...) | SAXReaderTests.java:37:17:37:37 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SAXReaderTests.java:37:17:37:37 | getInputStream(...) | user-provided value |
-| SAXReaderTests.java:45:17:45:37 | getInputStream(...) | SAXReaderTests.java:45:17:45:37 | getInputStream(...) | SAXReaderTests.java:45:17:45:37 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SAXReaderTests.java:45:17:45:37 | getInputStream(...) | user-provided value |
-| SAXReaderTests.java:53:17:53:37 | getInputStream(...) | SAXReaderTests.java:53:17:53:37 | getInputStream(...) | SAXReaderTests.java:53:17:53:37 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SAXReaderTests.java:53:17:53:37 | getInputStream(...) | user-provided value |
-| SAXReaderTests.java:61:17:61:37 | getInputStream(...) | SAXReaderTests.java:61:17:61:37 | getInputStream(...) | SAXReaderTests.java:61:17:61:37 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SAXReaderTests.java:61:17:61:37 | getInputStream(...) | user-provided value |
-| SAXSourceTests.java:20:18:20:23 | source | SAXSourceTests.java:17:62:17:82 | getInputStream(...) : InputStream | SAXSourceTests.java:20:18:20:23 | source | XML parsing depends on a $@ without guarding against external entity expansion. | SAXSourceTests.java:17:62:17:82 | getInputStream(...) | user-provided value |
-| SchemaTests.java:12:39:12:77 | new StreamSource(...) | SchemaTests.java:12:56:12:76 | getInputStream(...) : InputStream | SchemaTests.java:12:39:12:77 | new StreamSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SchemaTests.java:12:56:12:76 | getInputStream(...) | user-provided value |
-| SchemaTests.java:25:39:25:77 | new StreamSource(...) | SchemaTests.java:25:56:25:76 | getInputStream(...) : InputStream | SchemaTests.java:25:39:25:77 | new StreamSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SchemaTests.java:25:56:25:76 | getInputStream(...) | user-provided value |
-| SchemaTests.java:31:39:31:77 | new StreamSource(...) | SchemaTests.java:31:56:31:76 | getInputStream(...) : InputStream | SchemaTests.java:31:39:31:77 | new StreamSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SchemaTests.java:31:56:31:76 | getInputStream(...) | user-provided value |
-| SchemaTests.java:38:39:38:77 | new StreamSource(...) | SchemaTests.java:38:56:38:76 | getInputStream(...) : InputStream | SchemaTests.java:38:39:38:77 | new StreamSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SchemaTests.java:38:56:38:76 | getInputStream(...) | user-provided value |
-| SchemaTests.java:45:39:45:77 | new StreamSource(...) | SchemaTests.java:45:56:45:76 | getInputStream(...) : InputStream | SchemaTests.java:45:39:45:77 | new StreamSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SchemaTests.java:45:56:45:76 | getInputStream(...) | user-provided value |
-| SimpleXMLTests.java:14:41:14:61 | getInputStream(...) | SimpleXMLTests.java:14:41:14:61 | getInputStream(...) | SimpleXMLTests.java:14:41:14:61 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SimpleXMLTests.java:14:41:14:61 | getInputStream(...) | user-provided value |
-| SimpleXMLTests.java:19:41:19:61 | getInputStream(...) | SimpleXMLTests.java:19:41:19:61 | getInputStream(...) | SimpleXMLTests.java:19:41:19:61 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SimpleXMLTests.java:19:41:19:61 | getInputStream(...) | user-provided value |
-| SimpleXMLTests.java:24:41:24:84 | new InputStreamReader(...) | SimpleXMLTests.java:24:63:24:83 | getInputStream(...) : InputStream | SimpleXMLTests.java:24:41:24:84 | new InputStreamReader(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SimpleXMLTests.java:24:63:24:83 | getInputStream(...) | user-provided value |
-| SimpleXMLTests.java:31:41:31:53 | new String(...) | SimpleXMLTests.java:30:5:30:25 | getInputStream(...) : InputStream | SimpleXMLTests.java:31:41:31:53 | new String(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SimpleXMLTests.java:30:5:30:25 | getInputStream(...) | user-provided value |
-| SimpleXMLTests.java:38:41:38:53 | new String(...) | SimpleXMLTests.java:37:5:37:25 | getInputStream(...) : InputStream | SimpleXMLTests.java:38:41:38:53 | new String(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SimpleXMLTests.java:37:5:37:25 | getInputStream(...) | user-provided value |
-| SimpleXMLTests.java:43:41:43:84 | new InputStreamReader(...) | SimpleXMLTests.java:43:63:43:83 | getInputStream(...) : InputStream | SimpleXMLTests.java:43:41:43:84 | new InputStreamReader(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SimpleXMLTests.java:43:63:43:83 | getInputStream(...) | user-provided value |
-| SimpleXMLTests.java:48:37:48:57 | getInputStream(...) | SimpleXMLTests.java:48:37:48:57 | getInputStream(...) | SimpleXMLTests.java:48:37:48:57 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SimpleXMLTests.java:48:37:48:57 | getInputStream(...) | user-provided value |
-| SimpleXMLTests.java:53:37:53:57 | getInputStream(...) | SimpleXMLTests.java:53:37:53:57 | getInputStream(...) | SimpleXMLTests.java:53:37:53:57 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SimpleXMLTests.java:53:37:53:57 | getInputStream(...) | user-provided value |
-| SimpleXMLTests.java:58:26:58:46 | getInputStream(...) | SimpleXMLTests.java:58:26:58:46 | getInputStream(...) | SimpleXMLTests.java:58:26:58:46 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SimpleXMLTests.java:58:26:58:46 | getInputStream(...) | user-provided value |
-| SimpleXMLTests.java:63:26:63:46 | getInputStream(...) | SimpleXMLTests.java:63:26:63:46 | getInputStream(...) | SimpleXMLTests.java:63:26:63:46 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SimpleXMLTests.java:63:26:63:46 | getInputStream(...) | user-provided value |
-| SimpleXMLTests.java:68:37:68:80 | new InputStreamReader(...) | SimpleXMLTests.java:68:59:68:79 | getInputStream(...) : InputStream | SimpleXMLTests.java:68:37:68:80 | new InputStreamReader(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SimpleXMLTests.java:68:59:68:79 | getInputStream(...) | user-provided value |
-| SimpleXMLTests.java:73:37:73:80 | new InputStreamReader(...) | SimpleXMLTests.java:73:59:73:79 | getInputStream(...) : InputStream | SimpleXMLTests.java:73:37:73:80 | new InputStreamReader(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SimpleXMLTests.java:73:59:73:79 | getInputStream(...) | user-provided value |
-| SimpleXMLTests.java:78:26:78:69 | new InputStreamReader(...) | SimpleXMLTests.java:78:48:78:68 | getInputStream(...) : InputStream | SimpleXMLTests.java:78:26:78:69 | new InputStreamReader(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SimpleXMLTests.java:78:48:78:68 | getInputStream(...) | user-provided value |
-| SimpleXMLTests.java:83:26:83:69 | new InputStreamReader(...) | SimpleXMLTests.java:83:48:83:68 | getInputStream(...) : InputStream | SimpleXMLTests.java:83:26:83:69 | new InputStreamReader(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SimpleXMLTests.java:83:48:83:68 | getInputStream(...) | user-provided value |
-| SimpleXMLTests.java:90:37:90:49 | new String(...) | SimpleXMLTests.java:89:5:89:25 | getInputStream(...) : InputStream | SimpleXMLTests.java:90:37:90:49 | new String(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SimpleXMLTests.java:89:5:89:25 | getInputStream(...) | user-provided value |
-| SimpleXMLTests.java:97:37:97:49 | new String(...) | SimpleXMLTests.java:96:5:96:25 | getInputStream(...) : InputStream | SimpleXMLTests.java:97:37:97:49 | new String(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SimpleXMLTests.java:96:5:96:25 | getInputStream(...) | user-provided value |
-| SimpleXMLTests.java:104:26:104:38 | new String(...) | SimpleXMLTests.java:103:5:103:25 | getInputStream(...) : InputStream | SimpleXMLTests.java:104:26:104:38 | new String(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SimpleXMLTests.java:103:5:103:25 | getInputStream(...) | user-provided value |
-| SimpleXMLTests.java:111:26:111:38 | new String(...) | SimpleXMLTests.java:110:5:110:25 | getInputStream(...) : InputStream | SimpleXMLTests.java:111:26:111:38 | new String(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SimpleXMLTests.java:110:5:110:25 | getInputStream(...) | user-provided value |
-| SimpleXMLTests.java:115:22:115:42 | getInputStream(...) | SimpleXMLTests.java:115:22:115:42 | getInputStream(...) | SimpleXMLTests.java:115:22:115:42 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SimpleXMLTests.java:115:22:115:42 | getInputStream(...) | user-provided value |
-| SimpleXMLTests.java:119:22:119:65 | new InputStreamReader(...) | SimpleXMLTests.java:119:44:119:64 | getInputStream(...) : InputStream | SimpleXMLTests.java:119:22:119:65 | new InputStreamReader(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SimpleXMLTests.java:119:44:119:64 | getInputStream(...) | user-provided value |
-| SimpleXMLTests.java:124:22:124:42 | getInputStream(...) | SimpleXMLTests.java:124:22:124:42 | getInputStream(...) | SimpleXMLTests.java:124:22:124:42 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SimpleXMLTests.java:124:22:124:42 | getInputStream(...) | user-provided value |
-| SimpleXMLTests.java:129:22:129:65 | new InputStreamReader(...) | SimpleXMLTests.java:129:44:129:64 | getInputStream(...) : InputStream | SimpleXMLTests.java:129:22:129:65 | new InputStreamReader(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SimpleXMLTests.java:129:44:129:64 | getInputStream(...) | user-provided value |
-| SimpleXMLTests.java:134:22:134:42 | getInputStream(...) | SimpleXMLTests.java:134:22:134:42 | getInputStream(...) | SimpleXMLTests.java:134:22:134:42 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SimpleXMLTests.java:134:22:134:42 | getInputStream(...) | user-provided value |
-| SimpleXMLTests.java:139:22:139:65 | new InputStreamReader(...) | SimpleXMLTests.java:139:44:139:64 | getInputStream(...) : InputStream | SimpleXMLTests.java:139:22:139:65 | new InputStreamReader(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SimpleXMLTests.java:139:44:139:64 | getInputStream(...) | user-provided value |
-| SimpleXMLTests.java:146:22:146:34 | new String(...) | SimpleXMLTests.java:145:5:145:25 | getInputStream(...) : InputStream | SimpleXMLTests.java:146:22:146:34 | new String(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SimpleXMLTests.java:145:5:145:25 | getInputStream(...) | user-provided value |
-| SimpleXMLTests.java:153:22:153:34 | new String(...) | SimpleXMLTests.java:152:5:152:25 | getInputStream(...) : InputStream | SimpleXMLTests.java:153:22:153:34 | new String(...) | XML parsing depends on a $@ without guarding against external entity expansion. | SimpleXMLTests.java:152:5:152:25 | getInputStream(...) | user-provided value |
-| TransformerTests.java:20:27:20:65 | new StreamSource(...) | TransformerTests.java:20:44:20:64 | getInputStream(...) : InputStream | TransformerTests.java:20:27:20:65 | new StreamSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | TransformerTests.java:20:44:20:64 | getInputStream(...) | user-provided value |
-| TransformerTests.java:21:23:21:61 | new StreamSource(...) | TransformerTests.java:21:40:21:60 | getInputStream(...) : InputStream | TransformerTests.java:21:23:21:61 | new StreamSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | TransformerTests.java:21:40:21:60 | getInputStream(...) | user-provided value |
-| TransformerTests.java:71:27:71:65 | new StreamSource(...) | TransformerTests.java:71:44:71:64 | getInputStream(...) : InputStream | TransformerTests.java:71:27:71:65 | new StreamSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | TransformerTests.java:71:44:71:64 | getInputStream(...) | user-provided value |
-| TransformerTests.java:72:23:72:61 | new StreamSource(...) | TransformerTests.java:72:40:72:60 | getInputStream(...) : InputStream | TransformerTests.java:72:23:72:61 | new StreamSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | TransformerTests.java:72:40:72:60 | getInputStream(...) | user-provided value |
-| TransformerTests.java:79:27:79:65 | new StreamSource(...) | TransformerTests.java:79:44:79:64 | getInputStream(...) : InputStream | TransformerTests.java:79:27:79:65 | new StreamSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | TransformerTests.java:79:44:79:64 | getInputStream(...) | user-provided value |
-| TransformerTests.java:80:23:80:61 | new StreamSource(...) | TransformerTests.java:80:40:80:60 | getInputStream(...) : InputStream | TransformerTests.java:80:23:80:61 | new StreamSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | TransformerTests.java:80:40:80:60 | getInputStream(...) | user-provided value |
-| TransformerTests.java:88:27:88:65 | new StreamSource(...) | TransformerTests.java:88:44:88:64 | getInputStream(...) : InputStream | TransformerTests.java:88:27:88:65 | new StreamSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | TransformerTests.java:88:44:88:64 | getInputStream(...) | user-provided value |
-| TransformerTests.java:89:23:89:61 | new StreamSource(...) | TransformerTests.java:89:40:89:60 | getInputStream(...) : InputStream | TransformerTests.java:89:23:89:61 | new StreamSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | TransformerTests.java:89:40:89:60 | getInputStream(...) | user-provided value |
-| TransformerTests.java:97:27:97:65 | new StreamSource(...) | TransformerTests.java:97:44:97:64 | getInputStream(...) : InputStream | TransformerTests.java:97:27:97:65 | new StreamSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | TransformerTests.java:97:44:97:64 | getInputStream(...) | user-provided value |
-| TransformerTests.java:98:23:98:61 | new StreamSource(...) | TransformerTests.java:98:40:98:60 | getInputStream(...) : InputStream | TransformerTests.java:98:23:98:61 | new StreamSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | TransformerTests.java:98:40:98:60 | getInputStream(...) | user-provided value |
-| TransformerTests.java:103:21:103:59 | new StreamSource(...) | TransformerTests.java:103:38:103:58 | getInputStream(...) : InputStream | TransformerTests.java:103:21:103:59 | new StreamSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | TransformerTests.java:103:38:103:58 | getInputStream(...) | user-provided value |
-| TransformerTests.java:116:21:116:59 | new StreamSource(...) | TransformerTests.java:116:38:116:58 | getInputStream(...) : InputStream | TransformerTests.java:116:21:116:59 | new StreamSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | TransformerTests.java:116:38:116:58 | getInputStream(...) | user-provided value |
-| TransformerTests.java:122:21:122:59 | new StreamSource(...) | TransformerTests.java:122:38:122:58 | getInputStream(...) : InputStream | TransformerTests.java:122:21:122:59 | new StreamSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | TransformerTests.java:122:38:122:58 | getInputStream(...) | user-provided value |
-| TransformerTests.java:129:21:129:59 | new StreamSource(...) | TransformerTests.java:129:38:129:58 | getInputStream(...) : InputStream | TransformerTests.java:129:21:129:59 | new StreamSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | TransformerTests.java:129:38:129:58 | getInputStream(...) | user-provided value |
-| TransformerTests.java:136:21:136:59 | new StreamSource(...) | TransformerTests.java:136:38:136:58 | getInputStream(...) : InputStream | TransformerTests.java:136:21:136:59 | new StreamSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | TransformerTests.java:136:38:136:58 | getInputStream(...) | user-provided value |
-| TransformerTests.java:141:18:141:70 | new SAXSource(...) | TransformerTests.java:141:48:141:68 | getInputStream(...) : InputStream | TransformerTests.java:141:18:141:70 | new SAXSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | TransformerTests.java:141:48:141:68 | getInputStream(...) | user-provided value |
-| UnmarshallerTests.java:28:18:28:38 | getInputStream(...) | UnmarshallerTests.java:28:18:28:38 | getInputStream(...) | UnmarshallerTests.java:28:18:28:38 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | UnmarshallerTests.java:28:18:28:38 | getInputStream(...) | user-provided value |
-| ValidatorTests.java:22:28:22:33 | source | ValidatorTests.java:17:49:17:72 | getInputStream(...) : ServletInputStream | ValidatorTests.java:22:28:22:33 | source | XML parsing depends on a $@ without guarding against external entity expansion. | ValidatorTests.java:17:49:17:72 | getInputStream(...) | user-provided value |
-| XMLDecoderTests.java:18:9:18:18 | xmlDecoder | XMLDecoderTests.java:16:49:16:72 | getInputStream(...) : ServletInputStream | XMLDecoderTests.java:18:9:18:18 | xmlDecoder | XML parsing depends on a $@ without guarding against external entity expansion. | XMLDecoderTests.java:16:49:16:72 | getInputStream(...) | user-provided value |
-| XMLReaderTests.java:16:18:16:55 | new InputSource(...) | XMLReaderTests.java:16:34:16:54 | getInputStream(...) : InputStream | XMLReaderTests.java:16:18:16:55 | new InputSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | XMLReaderTests.java:16:34:16:54 | getInputStream(...) | user-provided value |
-| XMLReaderTests.java:56:18:56:55 | new InputSource(...) | XMLReaderTests.java:56:34:56:54 | getInputStream(...) : InputStream | XMLReaderTests.java:56:18:56:55 | new InputSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | XMLReaderTests.java:56:34:56:54 | getInputStream(...) | user-provided value |
-| XMLReaderTests.java:63:18:63:55 | new InputSource(...) | XMLReaderTests.java:63:34:63:54 | getInputStream(...) : InputStream | XMLReaderTests.java:63:18:63:55 | new InputSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | XMLReaderTests.java:63:34:63:54 | getInputStream(...) | user-provided value |
-| XMLReaderTests.java:70:18:70:55 | new InputSource(...) | XMLReaderTests.java:70:34:70:54 | getInputStream(...) : InputStream | XMLReaderTests.java:70:18:70:55 | new InputSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | XMLReaderTests.java:70:34:70:54 | getInputStream(...) | user-provided value |
-| XMLReaderTests.java:78:18:78:55 | new InputSource(...) | XMLReaderTests.java:78:34:78:54 | getInputStream(...) : InputStream | XMLReaderTests.java:78:18:78:55 | new InputSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | XMLReaderTests.java:78:34:78:54 | getInputStream(...) | user-provided value |
-| XMLReaderTests.java:86:18:86:55 | new InputSource(...) | XMLReaderTests.java:86:34:86:54 | getInputStream(...) : InputStream | XMLReaderTests.java:86:18:86:55 | new InputSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | XMLReaderTests.java:86:34:86:54 | getInputStream(...) | user-provided value |
-| XMLReaderTests.java:94:18:94:55 | new InputSource(...) | XMLReaderTests.java:94:34:94:54 | getInputStream(...) : InputStream | XMLReaderTests.java:94:18:94:55 | new InputSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | XMLReaderTests.java:94:34:94:54 | getInputStream(...) | user-provided value |
-| XMLReaderTests.java:100:18:100:55 | new InputSource(...) | XMLReaderTests.java:100:34:100:54 | getInputStream(...) : InputStream | XMLReaderTests.java:100:18:100:55 | new InputSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | XMLReaderTests.java:100:34:100:54 | getInputStream(...) | user-provided value |
-| XPathExpressionTests.java:27:21:27:58 | new InputSource(...) | XPathExpressionTests.java:27:37:27:57 | getInputStream(...) : InputStream | XPathExpressionTests.java:27:21:27:58 | new InputSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | XPathExpressionTests.java:27:37:27:57 | getInputStream(...) | user-provided value |
-| XmlInputFactoryTests.java:9:35:9:55 | getInputStream(...) | XmlInputFactoryTests.java:9:35:9:55 | getInputStream(...) | XmlInputFactoryTests.java:9:35:9:55 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | XmlInputFactoryTests.java:9:35:9:55 | getInputStream(...) | user-provided value |
-| XmlInputFactoryTests.java:10:34:10:54 | getInputStream(...) | XmlInputFactoryTests.java:10:34:10:54 | getInputStream(...) | XmlInputFactoryTests.java:10:34:10:54 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | XmlInputFactoryTests.java:10:34:10:54 | getInputStream(...) | user-provided value |
-| XmlInputFactoryTests.java:24:35:24:55 | getInputStream(...) | XmlInputFactoryTests.java:24:35:24:55 | getInputStream(...) | XmlInputFactoryTests.java:24:35:24:55 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | XmlInputFactoryTests.java:24:35:24:55 | getInputStream(...) | user-provided value |
-| XmlInputFactoryTests.java:25:34:25:54 | getInputStream(...) | XmlInputFactoryTests.java:25:34:25:54 | getInputStream(...) | XmlInputFactoryTests.java:25:34:25:54 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | XmlInputFactoryTests.java:25:34:25:54 | getInputStream(...) | user-provided value |
-| XmlInputFactoryTests.java:31:35:31:55 | getInputStream(...) | XmlInputFactoryTests.java:31:35:31:55 | getInputStream(...) | XmlInputFactoryTests.java:31:35:31:55 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | XmlInputFactoryTests.java:31:35:31:55 | getInputStream(...) | user-provided value |
-| XmlInputFactoryTests.java:32:34:32:54 | getInputStream(...) | XmlInputFactoryTests.java:32:34:32:54 | getInputStream(...) | XmlInputFactoryTests.java:32:34:32:54 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | XmlInputFactoryTests.java:32:34:32:54 | getInputStream(...) | user-provided value |
-| XmlInputFactoryTests.java:39:35:39:55 | getInputStream(...) | XmlInputFactoryTests.java:39:35:39:55 | getInputStream(...) | XmlInputFactoryTests.java:39:35:39:55 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | XmlInputFactoryTests.java:39:35:39:55 | getInputStream(...) | user-provided value |
-| XmlInputFactoryTests.java:40:34:40:54 | getInputStream(...) | XmlInputFactoryTests.java:40:34:40:54 | getInputStream(...) | XmlInputFactoryTests.java:40:34:40:54 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | XmlInputFactoryTests.java:40:34:40:54 | getInputStream(...) | user-provided value |
-| XmlInputFactoryTests.java:47:35:47:55 | getInputStream(...) | XmlInputFactoryTests.java:47:35:47:55 | getInputStream(...) | XmlInputFactoryTests.java:47:35:47:55 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | XmlInputFactoryTests.java:47:35:47:55 | getInputStream(...) | user-provided value |
-| XmlInputFactoryTests.java:48:34:48:54 | getInputStream(...) | XmlInputFactoryTests.java:48:34:48:54 | getInputStream(...) | XmlInputFactoryTests.java:48:34:48:54 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | XmlInputFactoryTests.java:48:34:48:54 | getInputStream(...) | user-provided value |
-| XmlInputFactoryTests.java:55:35:55:55 | getInputStream(...) | XmlInputFactoryTests.java:55:35:55:55 | getInputStream(...) | XmlInputFactoryTests.java:55:35:55:55 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | XmlInputFactoryTests.java:55:35:55:55 | getInputStream(...) | user-provided value |
-| XmlInputFactoryTests.java:56:34:56:54 | getInputStream(...) | XmlInputFactoryTests.java:56:34:56:54 | getInputStream(...) | XmlInputFactoryTests.java:56:34:56:54 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | XmlInputFactoryTests.java:56:34:56:54 | getInputStream(...) | user-provided value |
diff --git a/java/ql/test/query-tests/security/CWE-611/XXE.ql b/java/ql/test/query-tests/security/CWE-611/XXE.ql
new file mode 100644
index 00000000000..f1463f561f3
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-611/XXE.ql
@@ -0,0 +1,11 @@
+import java
+import TestUtilities.InlineFlowTest
+import semmle.code.java.security.XxeRemoteQuery
+
+class HasFlowTest extends InlineFlowTest {
+ override predicate hasTaintFlow(DataFlow::Node src, DataFlow::Node sink) {
+ XxeFlow::flow(src, sink)
+ }
+
+ override predicate hasValueFlow(DataFlow::Node src, DataFlow::Node sink) { none() }
+}
diff --git a/java/ql/test/query-tests/security/CWE-611/XXE.qlref b/java/ql/test/query-tests/security/CWE-611/XXE.qlref
deleted file mode 100644
index dc71ddf9ddb..00000000000
--- a/java/ql/test/query-tests/security/CWE-611/XXE.qlref
+++ /dev/null
@@ -1 +0,0 @@
-Security/CWE/CWE-611/XXE.ql
\ No newline at end of file
diff --git a/java/ql/test/query-tests/security/CWE-611/XmlInputFactoryTests.java b/java/ql/test/query-tests/security/CWE-611/XmlInputFactoryTests.java
index ce0f9c43e19..a75bcde8c1f 100644
--- a/java/ql/test/query-tests/security/CWE-611/XmlInputFactoryTests.java
+++ b/java/ql/test/query-tests/security/CWE-611/XmlInputFactoryTests.java
@@ -6,53 +6,53 @@ public class XmlInputFactoryTests {
public void unconfigureFactory(Socket sock) throws Exception {
XMLInputFactory factory = XMLInputFactory.newFactory();
- factory.createXMLStreamReader(sock.getInputStream()); //unsafe
- factory.createXMLEventReader(sock.getInputStream()); //unsafe
+ factory.createXMLStreamReader(sock.getInputStream()); // $ hasTaintFlow
+ factory.createXMLEventReader(sock.getInputStream()); // $ hasTaintFlow
}
-
+
public void safeFactory(Socket sock) throws Exception {
XMLInputFactory factory = XMLInputFactory.newFactory();
factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
factory.setProperty("javax.xml.stream.isSupportingExternalEntities", false);
- factory.createXMLStreamReader(sock.getInputStream()); //safe
- factory.createXMLEventReader(sock.getInputStream()); //safe
+ factory.createXMLStreamReader(sock.getInputStream()); // safe
+ factory.createXMLEventReader(sock.getInputStream()); // safe
}
-
+
public void misConfiguredFactory(Socket sock) throws Exception {
XMLInputFactory factory = XMLInputFactory.newFactory();
factory.setProperty("javax.xml.stream.isSupportingExternalEntities", false);
- factory.createXMLStreamReader(sock.getInputStream()); //unsafe
- factory.createXMLEventReader(sock.getInputStream()); //unsafe
+ factory.createXMLStreamReader(sock.getInputStream()); // $ hasTaintFlow
+ factory.createXMLEventReader(sock.getInputStream()); // $ hasTaintFlow
}
-
+
public void misConfiguredFactory2(Socket sock) throws Exception {
XMLInputFactory factory = XMLInputFactory.newFactory();
factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
- factory.createXMLStreamReader(sock.getInputStream()); //unsafe
- factory.createXMLEventReader(sock.getInputStream()); //unsafe
+ factory.createXMLStreamReader(sock.getInputStream()); // $ hasTaintFlow
+ factory.createXMLEventReader(sock.getInputStream()); // $ hasTaintFlow
}
-
+
public void misConfiguredFactory3(Socket sock) throws Exception {
XMLInputFactory factory = XMLInputFactory.newFactory();
factory.setProperty("javax.xml.stream.isSupportingExternalEntities", true);
factory.setProperty(XMLInputFactory.SUPPORT_DTD, true);
- factory.createXMLStreamReader(sock.getInputStream()); //unsafe
- factory.createXMLEventReader(sock.getInputStream()); //unsafe
+ factory.createXMLStreamReader(sock.getInputStream()); // $ hasTaintFlow
+ factory.createXMLEventReader(sock.getInputStream()); // $ hasTaintFlow
}
-
+
public void misConfiguredFactory4(Socket sock) throws Exception {
XMLInputFactory factory = XMLInputFactory.newFactory();
factory.setProperty("javax.xml.stream.isSupportingExternalEntities", false);
factory.setProperty(XMLInputFactory.SUPPORT_DTD, true);
- factory.createXMLStreamReader(sock.getInputStream()); //unsafe
- factory.createXMLEventReader(sock.getInputStream()); //unsafe
+ factory.createXMLStreamReader(sock.getInputStream()); // $ hasTaintFlow
+ factory.createXMLEventReader(sock.getInputStream()); // $ hasTaintFlow
}
-
+
public void misConfiguredFactory5(Socket sock) throws Exception {
XMLInputFactory factory = XMLInputFactory.newFactory();
factory.setProperty("javax.xml.stream.isSupportingExternalEntities", true);
factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
- factory.createXMLStreamReader(sock.getInputStream()); //unsafe
- factory.createXMLEventReader(sock.getInputStream()); //unsafe
- }
+ factory.createXMLStreamReader(sock.getInputStream()); // $ hasTaintFlow
+ factory.createXMLEventReader(sock.getInputStream()); // $ hasTaintFlow
+ }
}
From fba61d51ed02ae1d7ed042fa071b55253338662b Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Wed, 26 Apr 2023 12:20:12 +0200
Subject: [PATCH 054/870] Remove experimental files
---
.../Security/CWE/CWE-611/XXE.java | 85 ------
.../Security/CWE/CWE-611/XXE.qhelp | 67 -----
.../experimental/Security/CWE/CWE-611/XXE.ql | 32 ---
.../Security/CWE/CWE-611/XXELib.qll | 246 ------------------
.../Security/CWE/CWE-611/XXELocal.qhelp | 5 -
.../Security/CWE/CWE-611/XXELocal.ql | 34 ---
.../query-tests/security/CWE-611/XXE.expected | 26 --
.../query-tests/security/CWE-611/XXE.java | 92 -------
.../query-tests/security/CWE-611/XXE.qlref | 1 -
.../query-tests/security/CWE-611/options | 1 -
10 files changed, 589 deletions(-)
delete mode 100644 java/ql/src/experimental/Security/CWE/CWE-611/XXE.java
delete mode 100644 java/ql/src/experimental/Security/CWE/CWE-611/XXE.qhelp
delete mode 100644 java/ql/src/experimental/Security/CWE/CWE-611/XXE.ql
delete mode 100644 java/ql/src/experimental/Security/CWE/CWE-611/XXELib.qll
delete mode 100644 java/ql/src/experimental/Security/CWE/CWE-611/XXELocal.qhelp
delete mode 100644 java/ql/src/experimental/Security/CWE/CWE-611/XXELocal.ql
delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-611/XXE.expected
delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-611/XXE.java
delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-611/XXE.qlref
delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-611/options
diff --git a/java/ql/src/experimental/Security/CWE/CWE-611/XXE.java b/java/ql/src/experimental/Security/CWE/CWE-611/XXE.java
deleted file mode 100644
index b56914235a7..00000000000
--- a/java/ql/src/experimental/Security/CWE/CWE-611/XXE.java
+++ /dev/null
@@ -1,85 +0,0 @@
-import java.beans.XMLDecoder;
-import java.io.BufferedReader;
-import javax.servlet.ServletInputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.xml.transform.stream.StreamSource;
-import javax.xml.validation.Schema;
-import javax.xml.validation.SchemaFactory;
-import javax.xml.validation.Validator;
-import org.apache.commons.digester3.Digester;
-import org.dom4j.Document;
-import org.dom4j.DocumentHelper;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.PostMapping;
-
-@Controller
-public class XxeController {
-
- @PostMapping(value = "xxe1")
- public void bad1(HttpServletRequest request, HttpServletResponse response) throws Exception {
- ServletInputStream servletInputStream = request.getInputStream();
- Digester digester = new Digester();
- digester.parse(servletInputStream);
- }
-
- @PostMapping(value = "xxe2")
- public void bad2(HttpServletRequest request) throws Exception {
- BufferedReader br = request.getReader();
- String str = "";
- StringBuilder listString = new StringBuilder();
- while ((str = br.readLine()) != null) {
- listString.append(str).append("\n");
- }
- Document document = DocumentHelper.parseText(listString.toString());
- }
-
- @PostMapping(value = "xxe3")
- public void bad3(HttpServletRequest request) throws Exception {
- ServletInputStream servletInputStream = request.getInputStream();
- SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
- Schema schema = factory.newSchema();
- Validator validator = schema.newValidator();
- StreamSource source = new StreamSource(servletInputStream);
- validator.validate(source);
- }
-
- @PostMapping(value = "xxe4")
- public void bad4(HttpServletRequest request) throws Exception {
- ServletInputStream servletInputStream = request.getInputStream();
- XMLDecoder xmlDecoder = new XMLDecoder(servletInputStream);
- xmlDecoder.readObject();
- }
-
- @PostMapping(value = "good1")
- public void good1(HttpServletRequest request, HttpServletResponse response) throws Exception {
- BufferedReader br = request.getReader();
- String str = "";
- StringBuilder listString = new StringBuilder();
- while ((str = br.readLine()) != null) {
- listString.append(str);
- }
- Digester digester = new Digester();
- digester.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
- digester.setFeature("http://xml.org/sax/features/external-general-entities", false);
- digester.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
- digester.parse(listString.toString());
- }
-
- @PostMapping(value = "good2")
- public void good2(HttpServletRequest request, HttpServletResponse response) throws Exception {
- BufferedReader br = request.getReader();
- String str = "";
- StringBuilder listString = new StringBuilder();
- while ((str = br.readLine()) != null) {
- listString.append(str).append("\n");
- }
- SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
- Schema schema = factory.newSchema();
- Validator validator = schema.newValidator();
- validator.setProperty("http://javax.xml.XMLConstants/property/accessExternalDTD", "");
- validator.setProperty("http://javax.xml.XMLConstants/property/accessExternalSchema", "");
- StreamSource source = new StreamSource(listString.toString());
- validator.validate(source);
- }
-}
diff --git a/java/ql/src/experimental/Security/CWE/CWE-611/XXE.qhelp b/java/ql/src/experimental/Security/CWE/CWE-611/XXE.qhelp
deleted file mode 100644
index c3cc04fdacb..00000000000
--- a/java/ql/src/experimental/Security/CWE/CWE-611/XXE.qhelp
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
-
-
-
-Parsing untrusted XML files with a weakly configured XML parser may lead to an XML External Entity (XXE) attack. This type of attack
-uses external entity references to access arbitrary files on a system, carry out denial of service, or server side
-request forgery. Even when the result of parsing is not returned to the user, out-of-band
-data retrieval techniques may allow attackers to steal sensitive data. Denial of services can also be
-carried out in this situation.
-
-
-There are many XML parsers for Java, and most of them are vulnerable to XXE because their default settings enable parsing of
-external entities. This query currently identifies vulnerable XML parsing from the following parsers: javax.xml.validation.Validator,
-org.dom4j.DocumentHelper, org.rundeck.api.parser.ParserHelper, org.apache.commons.digester3.Digester,
-org.apache.commons.digester.Digester, org.apache.tomcat.util.digester.Digester, java.beans.XMLDecoder.
-
-
-
-
-
-The best way to prevent XXE attacks is to disable the parsing of any Document Type Declarations (DTDs) in untrusted data.
-If this is not possible you should disable the parsing of external general entities and external parameter entities.
-This improves security but the code will still be at risk of denial of service and server side request forgery attacks.
-Protection against denial of service attacks may also be implemented by setting entity expansion limits, which is done
-by default in recent JDK and JRE implementations.
-
-
-
-
-
-The following bad examples parses the xml data entered by the user under an unsafe configuration, which is inherently insecure and may cause xml entity injection.
-In good examples, the security configuration is carried out, for example: Disable DTD to protect the program from XXE attacks.
-
-
-
-
-
-
-
-OWASP vulnerability description:
-XML External Entity (XXE) Processing.
-
-
-OWASP guidance on parsing xml files:
-XXE Prevention Cheat Sheet.
-
-
-Paper by Timothy Morgen:
-XML Schema, DTD, and Entity Attacks
-
-
-Out-of-band data retrieval: Timur Yunusov & Alexey Osipov, Black hat EU 2013:
-XML Out-Of-Band Data Retrieval.
-
-
-Denial of service attack (Billion laughs):
-Billion Laughs.
-
-
-The Java Tutorials:
-Processing Limit Definitions.
-
-
-
-
-
diff --git a/java/ql/src/experimental/Security/CWE/CWE-611/XXE.ql b/java/ql/src/experimental/Security/CWE/CWE-611/XXE.ql
deleted file mode 100644
index 118fbd5dcaa..00000000000
--- a/java/ql/src/experimental/Security/CWE/CWE-611/XXE.ql
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * @name Resolving XML external entity in user-controlled data (experimental sinks)
- * @description Parsing user-controlled XML documents and allowing expansion of external entity
- * references may lead to disclosure of confidential data or denial of service.
- * (note this version differs from query `java/xxe` by including support for additional possibly-vulnerable XML parsers)
- * @kind path-problem
- * @problem.severity error
- * @precision high
- * @id java/xxe-with-experimental-sinks
- * @tags security
- * experimental
- * external/cwe/cwe-611
- */
-
-import java
-import XXELib
-import semmle.code.java.dataflow.TaintTracking
-import semmle.code.java.dataflow.FlowSources
-import XxeFlow::PathGraph
-
-module XxeConfig implements DataFlow::ConfigSig {
- predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
-
- predicate isSink(DataFlow::Node sink) { sink instanceof UnsafeXxeSink }
-}
-
-module XxeFlow = TaintTracking::Global;
-
-from XxeFlow::PathNode source, XxeFlow::PathNode sink
-where XxeFlow::flowPath(source, sink)
-select sink.getNode(), source, sink, "Unsafe parsing of XML file from $@.", source.getNode(),
- "user input"
diff --git a/java/ql/src/experimental/Security/CWE/CWE-611/XXELib.qll b/java/ql/src/experimental/Security/CWE/CWE-611/XXELib.qll
deleted file mode 100644
index eb3cb3d269b..00000000000
--- a/java/ql/src/experimental/Security/CWE/CWE-611/XXELib.qll
+++ /dev/null
@@ -1,246 +0,0 @@
-import java
-import semmle.code.java.dataflow.DataFlow3
-import semmle.code.java.dataflow.DataFlow4
-import semmle.code.java.dataflow.DataFlow5
-import semmle.code.java.security.XmlParsers
-private import semmle.code.java.dataflow.SSA
-
-/** A data flow sink for untrusted user input used to insecure xml parse. */
-class UnsafeXxeSink extends DataFlow::ExprNode {
- UnsafeXxeSink() {
- exists(XmlParserCall parse |
- parse.getSink() = this.getExpr() and
- not parse.isSafe()
- )
- }
-}
-
-/** The class `org.rundeck.api.parser.ParserHelper`. */
-class ParserHelper extends RefType {
- ParserHelper() { this.hasQualifiedName("org.rundeck.api.parser", "ParserHelper") }
-}
-
-/** A call to `ParserHelper.loadDocument`. */
-class ParserHelperLoadDocument extends XmlParserCall {
- ParserHelperLoadDocument() {
- exists(Method m |
- this.getMethod() = m and
- m.getDeclaringType() instanceof ParserHelper and
- m.hasName("loadDocument")
- )
- }
-
- override Expr getSink() { result = this.getArgument(0) }
-
- override predicate isSafe() { none() }
-}
-
-/** The class `javax.xml.validation.Validator`. */
-class Validator extends RefType {
- Validator() { this.hasQualifiedName("javax.xml.validation", "Validator") }
-}
-
-/** A call to `Validator.validate`. */
-class ValidatorValidate extends XmlParserCall {
- ValidatorValidate() {
- exists(Method m |
- this.getMethod() = m and
- m.getDeclaringType() instanceof Validator and
- m.hasName("validate")
- )
- }
-
- override Expr getSink() { result = this.getArgument(0) }
-
- override predicate isSafe() { SafeValidatorFlow::flowToExpr(this.getQualifier()) }
-}
-
-/** A `ParserConfig` specific to `Validator`. */
-class ValidatorConfig extends TransformerConfig {
- ValidatorConfig() {
- exists(Method m |
- this.getMethod() = m and
- m.getDeclaringType() instanceof Validator and
- m.hasName("setProperty")
- )
- }
-}
-
-/** A safely configured `Validator`. */
-class SafeValidator extends VarAccess {
- SafeValidator() {
- exists(Variable v | v = this.getVariable() |
- exists(ValidatorConfig config | config.getQualifier() = v.getAnAccess() |
- config.disables(configAccessExternalDtd())
- ) and
- exists(ValidatorConfig config | config.getQualifier() = v.getAnAccess() |
- config.disables(configAccessExternalSchema())
- )
- )
- }
-}
-
-private module SafeValidatorFlowConfig implements DataFlow::ConfigSig {
- predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SafeValidator }
-
- predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma |
- sink.asExpr() = ma.getQualifier() and
- ma.getMethod().getDeclaringType() instanceof Validator
- )
- }
-
- int fieldFlowBranchLimit() { result = 0 }
-}
-
-private module SafeValidatorFlow = DataFlow::Global;
-
-/**
- * The classes `org.apache.commons.digester3.Digester`, `org.apache.commons.digester.Digester` or `org.apache.tomcat.util.digester.Digester`.
- */
-class Digester extends RefType {
- Digester() {
- this.hasQualifiedName([
- "org.apache.commons.digester3", "org.apache.commons.digester",
- "org.apache.tomcat.util.digester"
- ], "Digester")
- }
-}
-
-/** A call to `Digester.parse`. */
-class DigesterParse extends XmlParserCall {
- DigesterParse() {
- exists(Method m |
- this.getMethod() = m and
- m.getDeclaringType() instanceof Digester and
- m.hasName("parse")
- )
- }
-
- override Expr getSink() { result = this.getArgument(0) }
-
- override predicate isSafe() { SafeDigesterFlow::flowToExpr(this.getQualifier()) }
-}
-
-/** A `ParserConfig` that is specific to `Digester`. */
-class DigesterConfig extends ParserConfig {
- DigesterConfig() {
- exists(Method m |
- m = this.getMethod() and
- m.getDeclaringType() instanceof Digester and
- m.hasName("setFeature")
- )
- }
-}
-
-/**
- * A safely configured `Digester`.
- */
-class SafeDigester extends VarAccess {
- SafeDigester() {
- exists(Variable v | v = this.getVariable() |
- exists(DigesterConfig config | config.getQualifier() = v.getAnAccess() |
- config.enables(singleSafeConfig())
- )
- or
- exists(DigesterConfig config | config.getQualifier() = v.getAnAccess() |
- config
- .disables(any(ConstantStringExpr s |
- s.getStringValue() = "http://xml.org/sax/features/external-general-entities"
- ))
- ) and
- exists(DigesterConfig config | config.getQualifier() = v.getAnAccess() |
- config
- .disables(any(ConstantStringExpr s |
- s.getStringValue() = "http://xml.org/sax/features/external-parameter-entities"
- ))
- ) and
- exists(DigesterConfig config | config.getQualifier() = v.getAnAccess() |
- config
- .disables(any(ConstantStringExpr s |
- s.getStringValue() =
- "http://apache.org/xml/features/nonvalidating/load-external-dtd"
- ))
- )
- )
- }
-}
-
-private module SafeDigesterFlowConfig implements DataFlow::ConfigSig {
- predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SafeDigester }
-
- predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma |
- sink.asExpr() = ma.getQualifier() and ma.getMethod().getDeclaringType() instanceof Digester
- )
- }
-
- int fieldFlowBranchLimit() { result = 0 }
-}
-
-private module SafeDigesterFlow = DataFlow::Global;
-
-/** The class `java.beans.XMLDecoder`. */
-class XmlDecoder extends RefType {
- XmlDecoder() { this.hasQualifiedName("java.beans", "XMLDecoder") }
-}
-
-/** DEPRECATED: Alias for XmlDecoder */
-deprecated class XMLDecoder = XmlDecoder;
-
-/** A call to `XMLDecoder.readObject`. */
-class XmlDecoderReadObject extends XmlParserCall {
- XmlDecoderReadObject() {
- exists(Method m |
- this.getMethod() = m and
- m.getDeclaringType() instanceof XmlDecoder and
- m.hasName("readObject")
- )
- }
-
- override Expr getSink() { result = this.getQualifier() }
-
- override predicate isSafe() { none() }
-}
-
-/** DEPRECATED: Alias for XmlDecoderReadObject */
-deprecated class XMLDecoderReadObject = XmlDecoderReadObject;
-
-private predicate constantStringExpr(Expr e, string val) {
- e.(CompileTimeConstantExpr).getStringValue() = val
- or
- exists(SsaExplicitUpdate v, Expr src |
- e = v.getAUse() and
- src = v.getDefiningExpr().(VariableAssign).getSource() and
- constantStringExpr(src, val)
- )
-}
-
-/** A call to `SAXTransformerFactory.newTransformerHandler`. */
-class SaxTransformerFactoryNewTransformerHandler extends XmlParserCall {
- SaxTransformerFactoryNewTransformerHandler() {
- exists(Method m |
- this.getMethod() = m and
- m.getDeclaringType().hasQualifiedName("javax.xml.transform.sax", "SAXTransformerFactory") and
- m.hasName("newTransformerHandler")
- )
- }
-
- override Expr getSink() { result = this.getArgument(0) }
-
- override predicate isSafe() { SafeTransformerFactoryFlow::flowToExpr(this.getQualifier()) }
-}
-
-/** DEPRECATED: Alias for SaxTransformerFactoryNewTransformerHandler */
-deprecated class SAXTransformerFactoryNewTransformerHandler =
- SaxTransformerFactoryNewTransformerHandler;
-
-/** An expression that always has the same string value. */
-private class ConstantStringExpr extends Expr {
- string value;
-
- ConstantStringExpr() { constantStringExpr(this, value) }
-
- /** Get the string value of this expression. */
- string getStringValue() { result = value }
-}
diff --git a/java/ql/src/experimental/Security/CWE/CWE-611/XXELocal.qhelp b/java/ql/src/experimental/Security/CWE/CWE-611/XXELocal.qhelp
deleted file mode 100644
index 4dc505dec6a..00000000000
--- a/java/ql/src/experimental/Security/CWE/CWE-611/XXELocal.qhelp
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/java/ql/src/experimental/Security/CWE/CWE-611/XXELocal.ql b/java/ql/src/experimental/Security/CWE/CWE-611/XXELocal.ql
deleted file mode 100644
index 99e65fa99e8..00000000000
--- a/java/ql/src/experimental/Security/CWE/CWE-611/XXELocal.ql
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * @name Resolving XML external entity from a local source (experimental sinks)
- * @description Parsing user-controlled XML documents and allowing expansion of external entity
- * references may lead to disclosure of confidential data or denial of service.
- * (note this version differs from query `java/xxe` by including support for additional possibly-vulnerable XML parsers,
- * and by considering local information sources dangerous (e.g. environment variables) in addition to the remote sources
- * considered by the normal `java/xxe` query)
- * @kind path-problem
- * @problem.severity recommendation
- * @precision medium
- * @id java/xxe-local-experimental-sinks
- * @tags security
- * experimental
- * external/cwe/cwe-611
- */
-
-import java
-import XXELib
-import semmle.code.java.dataflow.TaintTracking
-import semmle.code.java.dataflow.FlowSources
-import XxeLocalFlow::PathGraph
-
-module XxeLocalConfig implements DataFlow::ConfigSig {
- predicate isSource(DataFlow::Node src) { src instanceof LocalUserInput }
-
- predicate isSink(DataFlow::Node sink) { sink instanceof UnsafeXxeSink }
-}
-
-module XxeLocalFlow = TaintTracking::Global;
-
-from XxeLocalFlow::PathNode source, XxeLocalFlow::PathNode sink
-where XxeLocalFlow::flowPath(source, sink)
-select sink.getNode(), source, sink, "Unsafe parsing of XML file from $@.", source.getNode(),
- "user input"
diff --git a/java/ql/test/experimental/query-tests/security/CWE-611/XXE.expected b/java/ql/test/experimental/query-tests/security/CWE-611/XXE.expected
deleted file mode 100644
index b99edb2122d..00000000000
--- a/java/ql/test/experimental/query-tests/security/CWE-611/XXE.expected
+++ /dev/null
@@ -1,26 +0,0 @@
-edges
-| XXE.java:22:43:22:66 | getInputStream(...) : ServletInputStream | XXE.java:24:18:24:35 | servletInputStream |
-| XXE.java:29:43:29:66 | getInputStream(...) : ServletInputStream | XXE.java:33:42:33:59 | servletInputStream : ServletInputStream |
-| XXE.java:33:25:33:60 | new StreamSource(...) : StreamSource | XXE.java:34:22:34:27 | source |
-| XXE.java:33:42:33:59 | servletInputStream : ServletInputStream | XXE.java:33:25:33:60 | new StreamSource(...) : StreamSource |
-| XXE.java:39:43:39:66 | getInputStream(...) : ServletInputStream | XXE.java:40:42:40:59 | servletInputStream : ServletInputStream |
-| XXE.java:40:27:40:60 | new XMLDecoder(...) : XMLDecoder | XXE.java:41:3:41:12 | xmlDecoder |
-| XXE.java:40:42:40:59 | servletInputStream : ServletInputStream | XXE.java:40:27:40:60 | new XMLDecoder(...) : XMLDecoder |
-nodes
-| XXE.java:22:43:22:66 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
-| XXE.java:24:18:24:35 | servletInputStream | semmle.label | servletInputStream |
-| XXE.java:29:43:29:66 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
-| XXE.java:33:25:33:60 | new StreamSource(...) : StreamSource | semmle.label | new StreamSource(...) : StreamSource |
-| XXE.java:33:42:33:59 | servletInputStream : ServletInputStream | semmle.label | servletInputStream : ServletInputStream |
-| XXE.java:34:22:34:27 | source | semmle.label | source |
-| XXE.java:39:43:39:66 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
-| XXE.java:40:27:40:60 | new XMLDecoder(...) : XMLDecoder | semmle.label | new XMLDecoder(...) : XMLDecoder |
-| XXE.java:40:42:40:59 | servletInputStream : ServletInputStream | semmle.label | servletInputStream : ServletInputStream |
-| XXE.java:41:3:41:12 | xmlDecoder | semmle.label | xmlDecoder |
-| XXE.java:46:49:46:72 | getInputStream(...) | semmle.label | getInputStream(...) |
-subpaths
-#select
-| XXE.java:24:18:24:35 | servletInputStream | XXE.java:22:43:22:66 | getInputStream(...) : ServletInputStream | XXE.java:24:18:24:35 | servletInputStream | Unsafe parsing of XML file from $@. | XXE.java:22:43:22:66 | getInputStream(...) | user input |
-| XXE.java:34:22:34:27 | source | XXE.java:29:43:29:66 | getInputStream(...) : ServletInputStream | XXE.java:34:22:34:27 | source | Unsafe parsing of XML file from $@. | XXE.java:29:43:29:66 | getInputStream(...) | user input |
-| XXE.java:41:3:41:12 | xmlDecoder | XXE.java:39:43:39:66 | getInputStream(...) : ServletInputStream | XXE.java:41:3:41:12 | xmlDecoder | Unsafe parsing of XML file from $@. | XXE.java:39:43:39:66 | getInputStream(...) | user input |
-| XXE.java:46:49:46:72 | getInputStream(...) | XXE.java:46:49:46:72 | getInputStream(...) | XXE.java:46:49:46:72 | getInputStream(...) | Unsafe parsing of XML file from $@. | XXE.java:46:49:46:72 | getInputStream(...) | user input |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-611/XXE.java b/java/ql/test/experimental/query-tests/security/CWE-611/XXE.java
deleted file mode 100644
index 92a669acdc0..00000000000
--- a/java/ql/test/experimental/query-tests/security/CWE-611/XXE.java
+++ /dev/null
@@ -1,92 +0,0 @@
-import java.beans.XMLDecoder;
-import java.io.BufferedReader;
-import javax.servlet.ServletInputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.xml.transform.stream.StreamSource;
-import javax.xml.validation.Schema;
-import javax.xml.validation.SchemaFactory;
-import javax.xml.validation.Validator;
-import org.rundeck.api.parser.ParserHelper;
-import org.apache.commons.digester3.Digester;
-import org.dom4j.Document;
-import org.dom4j.DocumentHelper;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.PostMapping;
-
-@Controller
-public class XXE {
-
- @PostMapping(value = "bad1")
- public void bad1(HttpServletRequest request, HttpServletResponse response) throws Exception {
- ServletInputStream servletInputStream = request.getInputStream();
- Digester digester = new Digester();
- digester.parse(servletInputStream); // bad
- }
-
- @PostMapping(value = "bad2")
- public void bad2(HttpServletRequest request) throws Exception {
- ServletInputStream servletInputStream = request.getInputStream();
- SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
- Schema schema = factory.newSchema();
- Validator validator = schema.newValidator();
- StreamSource source = new StreamSource(servletInputStream);
- validator.validate(source); // bad
- }
-
- @PostMapping(value = "bad3")
- public void bad3(HttpServletRequest request) throws Exception {
- ServletInputStream servletInputStream = request.getInputStream();
- XMLDecoder xmlDecoder = new XMLDecoder(servletInputStream);
- xmlDecoder.readObject(); // bad
- }
-
- @PostMapping(value = "bad4")
- public void bad4(HttpServletRequest request) throws Exception {
- Document document = ParserHelper.loadDocument(request.getInputStream()); // bad
- }
-
- @PostMapping(value = "good1")
- public void good1(HttpServletRequest request, HttpServletResponse response) throws Exception {
- BufferedReader br = request.getReader();
- String str = "";
- StringBuilder listString = new StringBuilder();
- while ((str = br.readLine()) != null) {
- listString.append(str);
- }
- Digester digester = new Digester();
- digester.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
- digester.setFeature("http://xml.org/sax/features/external-general-entities", false);
- digester.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
- digester.parse(listString.toString());
- }
-
- @PostMapping(value = "good2")
- public void good2(HttpServletRequest request, HttpServletResponse response) throws Exception {
- BufferedReader br = request.getReader();
- String str = "";
- StringBuilder listString = new StringBuilder();
- while ((str = br.readLine()) != null) {
- listString.append(str).append("\n");
- }
- SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
- Schema schema = factory.newSchema();
- Validator validator = schema.newValidator();
- validator.setProperty("http://javax.xml.XMLConstants/property/accessExternalDTD", "");
- validator.setProperty("http://javax.xml.XMLConstants/property/accessExternalSchema", "");
- StreamSource source = new StreamSource(listString.toString());
- validator.validate(source);
- }
-
- @PostMapping(value = "good3")
- public void good3(HttpServletRequest request) throws Exception {
- BufferedReader br = request.getReader();
- String str = "";
- StringBuilder listString = new StringBuilder();
- while ((str = br.readLine()) != null) {
- listString.append(str).append("\n");
- }
- // parseText falls back to a default SAXReader, which is safe
- Document document = DocumentHelper.parseText(listString.toString()); // Safe
- }
-}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-611/XXE.qlref b/java/ql/test/experimental/query-tests/security/CWE-611/XXE.qlref
deleted file mode 100644
index 0675e245daa..00000000000
--- a/java/ql/test/experimental/query-tests/security/CWE-611/XXE.qlref
+++ /dev/null
@@ -1 +0,0 @@
-experimental/Security/CWE/CWE-611/XXE.ql
diff --git a/java/ql/test/experimental/query-tests/security/CWE-611/options b/java/ql/test/experimental/query-tests/security/CWE-611/options
deleted file mode 100644
index 9aea8cdbe50..00000000000
--- a/java/ql/test/experimental/query-tests/security/CWE-611/options
+++ /dev/null
@@ -1 +0,0 @@
-//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/servlet-api-2.4/:${testdir}/../../../../stubs/springframework-5.3.8/:${testdir}/../../../../stubs/dom4j-2.1.1:${testdir}/../../../../stubs/apache-commons-digester3-3.2:${testdir}/../../../../stubs/jaxen-1.2.0/:${testdir}/../../../../stubs/rundeck-api-java-client-13.2
\ No newline at end of file
From 4606df5cb69d0f2731fd047b3a9f7c618b58023a Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Wed, 26 Apr 2023 12:24:43 +0200
Subject: [PATCH 055/870] Add change note
---
java/ql/src/change-notes/2023-04-26-xxe-sinks-promotion.md | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 java/ql/src/change-notes/2023-04-26-xxe-sinks-promotion.md
diff --git a/java/ql/src/change-notes/2023-04-26-xxe-sinks-promotion.md b/java/ql/src/change-notes/2023-04-26-xxe-sinks-promotion.md
new file mode 100644
index 00000000000..01bbfe267bd
--- /dev/null
+++ b/java/ql/src/change-notes/2023-04-26-xxe-sinks-promotion.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* Experimental sinks for the query "Resolving XML external entity in user-controlled data" (`java/xxe`) have been promoted to the main query pack. These sinks were originally [submitted as part of an experimental query by @haby0](https://github.com/github/codeql/pull/6564).
From 1e3d81842eeea92f8306a8cdf46021dfee8dec2b Mon Sep 17 00:00:00 2001
From: Owen Mansel-Chan
Date: Tue, 10 Jan 2023 15:38:17 +0000
Subject: [PATCH 056/870] Update CallNode.getArgument for implicit varargs
It now has one only result corresponding to a variadic parameter. If the
argument is followed by an ellipsis then it is just the argument itself.
Otherwise it is a ImplicitVarargsSlice node.
---
.../go/dataflow/internal/DataFlowNodes.qll | 63 ++++++++++++++++---
1 file changed, 54 insertions(+), 9 deletions(-)
diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll
index 70e9e00116a..5ab830d7272 100644
--- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll
+++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll
@@ -534,16 +534,11 @@ module Public {
CallExpr getCall() { result = this.getExpr() }
/**
- * Gets the data flow node corresponding to the `i`th argument of this call.
- *
- * Note that the first argument in calls to the built-in function `make` is a type, which is
- * not a data-flow node. It is skipped for the purposes of this predicate, so the (syntactically)
- * second argument becomes the first argument in terms of data flow.
- *
- * For calls of the form `f(g())` where `g` has multiple results, the arguments of the call to
- * `i` are the (implicit) element extraction nodes for the call to `g`.
+ * Gets the `i`th argument of this call, where tuple extraction has been
+ * done but arguments corresponding to a variadic parameter are still
+ * considered separate.
*/
- Node getArgument(int i) {
+ Node getSyntacticArgument(int i) {
if expr.getArgument(0).getType() instanceof TupleType
then result = DataFlow::extractTupleElement(DataFlow::exprNode(expr.getArgument(0)), i)
else
@@ -555,12 +550,62 @@ module Public {
)
}
+ /**
+ * Gets the data flow node corresponding to an argument of this call, where
+ * tuple extraction has been done but arguments corresponding to a variadic
+ * parameter are still considered separate.
+ */
+ Node getASyntacticArgument() { result = this.getSyntacticArgument(_) }
+
+ /**
+ * Gets the data flow node corresponding to the `i`th argument of this call.
+ *
+ * Note that the first argument in calls to the built-in function `make` is a type, which is
+ * not a data-flow node. It is skipped for the purposes of this predicate, so the (syntactically)
+ * second argument becomes the first argument in terms of data flow.
+ *
+ * For calls of the form `f(g())` where `g` has multiple results, the arguments of the call to
+ * `i` are the (implicit) element extraction nodes for the call to `g`.
+ *
+ * For calls to variadic functions without an ellipsis (`...`), there is a single argument of type
+ * `ImplicitVarargsSlice` corresponding to the variadic parameter. This is in contrast to the member
+ * predicate `getArgument` on `CallExpr`, which gets the syntactic arguments.
+ */
+ Node getArgument(int i) {
+ exists(SignatureType t, int lastParamIndex |
+ t = this.getACalleeIncludingExternals().getType() and
+ lastParamIndex = t.getNumParameter() - 1
+ |
+ if
+ not this.hasEllipsis() and
+ t.isVariadic() and
+ i >= lastParamIndex
+ then
+ result.(ImplicitVarargsSlice).getCallNode() = this and
+ i = lastParamIndex
+ else result = this.getSyntacticArgument(i)
+ )
+ }
+
/** Gets the data flow node corresponding to an argument of this call. */
Node getAnArgument() { result = this.getArgument(_) }
/** Gets the number of arguments of this call, if it can be determined. */
int getNumArgument() { result = count(this.getAnArgument()) }
+ /**
+ * Gets the 'i'th argument without an ellipsis after it which is passed to
+ * the varargs parameter of the target of this call (if there is one).
+ */
+ Node getImplicitVarargsArgument(int i) {
+ not this.hasEllipsis() and
+ i >= 0 and
+ exists(Function f | f = this.getTarget() |
+ f.isVariadic() and
+ result = this.getSyntacticArgument(f.getNumParameter() - 1 + i)
+ )
+ }
+
/** Gets a function passed as the `i`th argument of this call. */
FunctionNode getCallback(int i) { result.getASuccessor*() = this.getArgument(i) }
From 39da26e9b5c063b6079da29f2d69de8c2a0cee6b Mon Sep 17 00:00:00 2001
From: Owen Mansel-Chan
Date: Tue, 25 Apr 2023 07:20:16 +0100
Subject: [PATCH 057/870] Update ParameterInput.getEntryNode for implicit
varargs slices
---
go/ql/lib/semmle/go/dataflow/FunctionInputsAndOutputs.qll | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/go/ql/lib/semmle/go/dataflow/FunctionInputsAndOutputs.qll b/go/ql/lib/semmle/go/dataflow/FunctionInputsAndOutputs.qll
index c1653f5b3ad..2939f955a6f 100644
--- a/go/ql/lib/semmle/go/dataflow/FunctionInputsAndOutputs.qll
+++ b/go/ql/lib/semmle/go/dataflow/FunctionInputsAndOutputs.qll
@@ -74,7 +74,9 @@ private class ParameterInput extends FunctionInput, TInParameter {
override predicate isParameter(int i) { i = index }
- override DataFlow::Node getEntryNode(DataFlow::CallNode c) { result = c.getArgument(index) }
+ override DataFlow::Node getEntryNode(DataFlow::CallNode c) {
+ result = c.getSyntacticArgument(index)
+ }
override DataFlow::Node getExitNode(FuncDef f) {
result = DataFlow::parameterNode(f.getParameter(index))
From f2cb2b324ee85a2d23fe7316e4d8208c21509080 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Tue, 18 Apr 2023 13:24:14 +0100
Subject: [PATCH 058/870] Swift: Add analyzing-data-flow-in-swift.rst
---
.../analyzing-data-flow-in-swift.rst | 290 ++++++++++++++++++
.../codeql-for-swift.rst | 3 +
2 files changed, 293 insertions(+)
create mode 100644 docs/codeql/codeql-language-guides/analyzing-data-flow-in-swift.rst
diff --git a/docs/codeql/codeql-language-guides/analyzing-data-flow-in-swift.rst b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-swift.rst
new file mode 100644
index 00000000000..69b42f327d8
--- /dev/null
+++ b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-swift.rst
@@ -0,0 +1,290 @@
+.. _analyzing-data-flow-in-swift:
+
+Analyzing data flow in Swift
+============================
+
+You can use CodeQL to track the flow of data through a Swift program to places where the data is used.
+
+About this article
+------------------
+
+This article describes how data flow analysis is implemented in the CodeQL libraries for Swift and includes examples to help you write your own data flow queries.
+The following sections describe how to use the libraries for local data flow, global data flow, and taint tracking.
+For a more general introduction to modeling data flow, see ":ref:`About data flow analysis `."
+
+Local data flow
+---------------
+
+Local data flow tracks the flow of data within a single method or callable. Local data flow is easier, faster, and more precise than global data flow. Before looking at more complex tracking, you should always consider local tracking because it is sufficient for many queries.
+
+Using local data flow
+~~~~~~~~~~~~~~~~~~~~~
+
+You can use the local data flow library by importing the ``DataFlow`` module. The library uses the class ``Node`` to represent any element through which data can flow.
+The ``Node`` class has a number of useful subclasses, such as ``ExprNode`` for expressions and ``ParameterNode`` for parameters. You can map between data flow nodes and expressions/control-flow nodes using the member predicates ``asExpr`` and ``getCfgNode``:
+
+.. code-block:: ql
+
+ class Node {
+ /**
+ * Gets this node's underlying expression, if any.
+ */
+ Expr asExpr() { none() }
+
+ /**
+ * Gets this data flow node's corresponding control flow node.
+ */
+ ControlFlowNode getCfgNode() { none() }
+
+ ...
+ }
+
+You can use the predicates ``exprNode`` and ``parameterNode`` to map from expressions and parameters to their data-flow node:
+
+.. code-block:: ql
+
+ /** Gets a node corresponding to expression `e`. */
+ ExprNode exprNode(DataFlowExpr e) { result.asExpr() = e }
+
+ /**
+ * Gets the node corresponding to the value of parameter `p` at function entry.
+ */
+ ParameterNode parameterNode(DataFlowParameter p) { result.getParameter() = p }
+
+There can be multiple data-flow nodes associated with a single expression node in the AST.
+
+The predicate ``localFlowStep(Node nodeFrom, Node nodeTo)`` holds if there is an immediate data flow edge from the node ``nodeFrom`` to the node ``nodeTo``.
+You can apply the predicate recursively, by using the ``+`` and ``*`` operators, or you can use the predefined recursive predicate ``localFlow``.
+
+For example, you can find flow from an expression ``source`` to an expression ``sink`` in zero or more local steps:
+
+.. code-block:: ql
+
+ DataFlow::localFlow(DataFlow::exprNode(source), DataFlow::exprNode(sink))
+
+Using local taint tracking
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Local taint tracking extends local data flow to include flow steps where values are not preserved, for example, string manipulation.
+For example:
+
+.. code-block:: swift
+
+ temp = x
+ y = temp + ", " + temp
+
+If ``x`` is a tainted string then ``y`` is also tainted.
+
+The local taint tracking library is in the module ``TaintTracking``.
+Like local data flow, a predicate ``localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo)`` holds if there is an immediate taint propagation edge from the node ``nodeFrom`` to the node ``nodeTo``.
+You can apply the predicate recursively, by using the ``+`` and ``*`` operators, or you can use the predefined recursive predicate ``localTaint``.
+
+For example, you can find taint propagation from an expression ``source`` to an expression ``sink`` in zero or more local steps:
+
+.. code-block:: ql
+
+ TaintTracking::localTaint(DataFlow::exprNode(source), DataFlow::exprNode(sink))
+
+Examples of local data flow
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This query finds the ``format`` argument passed into each call to ``String.init(format:_:)``:
+
+.. code-block:: ql
+
+ import swift
+
+ from CallExpr call, MethodDecl method
+ where
+ call.getStaticTarget() = method and
+ method.hasQualifiedName("String", "init(format:_:)")
+ select call.getArgument(0).getExpr()
+
+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
+
+ import swift
+ import codeql.swift.dataflow.DataFlow
+
+ from CallExpr call, MethodDecl method, Expr sourceExpr, Expr sinkExpr
+ where
+ call.getStaticTarget() = method and
+ method.hasQualifiedName("String", "init(format:_:)") and
+ sinkExpr = call.getArgument(0).getExpr() and
+ DataFlow::localFlow(DataFlow::exprNode(sourceExpr), DataFlow::exprNode(sinkExpr))
+ select sourceExpr, sinkExpr
+
+We can vary the source, for example, making the source the parameter of a function rather than an expression. The following query finds where a parameter is used for the format:
+
+.. code-block:: ql
+
+ import swift
+ import codeql.swift.dataflow.DataFlow
+
+ from CallExpr call, MethodDecl method, ParamDecl sourceParam, Expr sinkExpr
+ where
+ call.getStaticTarget() = method and
+ method.hasQualifiedName("String", "init(format:_:)") and
+ sinkExpr = call.getArgument(0).getExpr() and
+ DataFlow::localFlow(DataFlow::parameterNode(sourceParam), DataFlow::exprNode(sinkExpr))
+ select sourceParam, sinkExpr
+
+The following example finds calls to ``String.init(format:_:)`` where the format string is not a hard-coded string literal:
+
+.. code-block:: ql
+
+ import swift
+ import codeql.swift.dataflow.DataFlow
+
+ from CallExpr call, MethodDecl method, Expr sinkExpr
+ where
+ call.getStaticTarget() = method and
+ method.hasQualifiedName("String", "init(format:_:)") and
+ sinkExpr = call.getArgument(0).getExpr() and
+ not exists(StringLiteralExpr sourceLiteral |
+ DataFlow::localFlow(DataFlow::exprNode(sourceLiteral), DataFlow::exprNode(sinkExpr))
+ )
+ select call, "Format argument to " + method.getName() + " isn't hard-coded."
+
+Global data flow
+----------------
+
+Global data flow tracks data flow throughout the entire program, and is therefore more powerful than local data flow.
+However, global data flow is less precise than local data flow, and the analysis typically requires significantly more time and memory to perform.
+
+.. pull-quote:: Note
+
+ .. include:: ../reusables/path-problem.rst
+
+Using global data flow
+~~~~~~~~~~~~~~~~~~~~~~
+
+You can use the global data flow library by implementing the module ``DataFlow::ConfigSig``:
+
+.. code-block:: ql
+
+ import codeql.swift.dataflow.DataFlow
+
+ module MyDataFlowConfiguration implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node source) {
+ ...
+ }
+
+ predicate isSink(DataFlow::Node sink) {
+ ...
+ }
+ }
+
+ module MyDataFlow = DataFlow::Global;
+
+These predicates are defined in the configuration:
+
+- ``isSource`` - defines where data may flow from.
+- ``isSink`` - defines where data may flow to.
+- ``isBarrier`` - optionally, restricts the data flow.
+- ``isAdditionalFlowStep`` - optionally, adds additional flow steps.
+
+The last line (``module MyDataFlow = ...``) performs data flow analysis using the configuration, and its results can be accessed with ``MyDataFlow::flow(DataFlow::Node source, DataFlow::Node sink)``:
+
+.. code-block:: ql
+
+ from DataFlow::Node source, DataFlow::Node sink
+ where MyDataFlow::flow(source, sink)
+ select source, "Dataflow to $@.", sink, sink.toString()
+
+Using global taint tracking
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Global taint tracking is to global data flow what local taint tracking is to local data flow.
+That is, global taint tracking extends global data flow with additional non-value-preserving steps.
+The global taint tracking library uses the same configuration module as the global data flow library but taint flow analysis is performed with ``TaintTracking::Global``:
+
+.. code-block:: ql
+
+ module MyTaintFlow = TaintTracking::Global;
+
+ from DataFlow::Node source, DataFlow::Node sink
+ where MyTaintFlow::flow(source, sink)
+ select source, "Taint flow to $@.", sink, sink.toString()
+
+Predefined sources and sinks
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The data flow library module ``codeql.swift.dataflow.FlowSources`` contains a number of predefined sources and sinks, providing a good starting point for defining data flow and taint flow based security queries.
+
+- The class ``RemoteFlowSource`` represents data flow from remote network inputs and from other applications.
+- The class ``LocalFlowSource`` represents data flow from local user input.
+- The class ``FlowSource`` includes both of the above.
+
+Examples of global data flow
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The following global taint-tracking query finds places where a string literal is used in a function call argument called "password".
+ - Since this is a taint-tracking query, the ``TaintTracking::Global`` module is used.
+ - The ``isSource`` predicate defines sources as any ``StringLiteralExpr``.
+ - The ``isSink`` predicate defines sinks as arguments to a ``CallExpr`` called "password".
+ - The sources and sinks may need tuning to a particular use case, for example if passwords are represented by a type other than ``String`` or passed in arguments of a different name than "password".
+
+.. code-block:: ql
+
+ import swift
+ import codeql.swift.dataflow.DataFlow
+ import codeql.swift.dataflow.TaintTracking
+
+ module ConstantPasswordConfig implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node node) { node.asExpr() instanceof StringLiteralExpr }
+
+ predicate isSink(DataFlow::Node node) {
+ // any argument called `password`
+ exists(CallExpr call | call.getArgumentWithLabel("password").getExpr() = node.asExpr())
+ }
+
+ module ConstantPasswordFlow = TaintTracking::Global;
+
+ from DataFlow::Node sourceNode, DataFlow::Node sinkNode
+ where ConstantPasswordFlow::flow(sourceNode, sinkNode)
+ select sinkNode, sourceNode, sinkNode,
+ "The value '" + sourceNode.toString() + "' is used as a constant password."
+
+
+The following global taint-tracking query finds places where a value from a remote or local user input is used as an argument to the SQLite ``Connection.execute(_:)`` function.
+ - Since this is a taint-tracking query, the ``TaintTracking::Global`` module is used.
+ - The ``isSource`` predicate defines sources as a ``FlowSource`` (remote or local user input).
+ - The ``isSink`` predicate defines sinks as the first argument in any call to ``Connection.execute(_:)``.
+
+.. code-block:: ql
+
+
+ import swift
+ import codeql.swift.dataflow.DataFlow
+ import codeql.swift.dataflow.TaintTracking
+ import codeql.swift.dataflow.FlowSources
+
+ module SqlInjectionConfig implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node node) { node instanceof FlowSource }
+
+ predicate isSink(DataFlow::Node node) {
+ exists(CallExpr call |
+ call.getStaticTarget().(MethodDecl).hasQualifiedName("Connection", ["execute(_:)"]) and
+ call.getArgument(0).getExpr() = node.asExpr()
+ )
+ }
+ }
+
+ module SqlInjectionFlow = TaintTracking::Global;
+
+ from DataFlow::Node sourceNode, DataFlow::Node sinkNode
+ where SqlInjectionFlow::flow(sourceNode, sinkNode)
+ select sinkNode, sourceNode, sinkNode, "This query depends on a $@.", sourceNode,
+ "user-provided value"
+
+Further reading
+---------------
+
+- ":ref:`Exploring data flow with path queries `"
+
+
+.. include:: ../reusables/swift-further-reading.rst
+.. include:: ../reusables/codeql-ref-tools-further-reading.rst
diff --git a/docs/codeql/codeql-language-guides/codeql-for-swift.rst b/docs/codeql/codeql-language-guides/codeql-for-swift.rst
index ccb3499b727..d43688921cf 100644
--- a/docs/codeql/codeql-language-guides/codeql-for-swift.rst
+++ b/docs/codeql/codeql-language-guides/codeql-for-swift.rst
@@ -9,5 +9,8 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
:hidden:
basic-query-for-swift-code
+ analyzing-data-flow-in-swift
- :doc:`Basic query for Swift code `: Learn to write and run a simple CodeQL query.
+
+- :doc:`Analyzing data flow in Swift `: You can use CodeQL to track the flow of data through a Swift program to places where the data is used.
From 5e7159f80045b44351562230db923043f2e8be02 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Wed, 26 Apr 2023 18:49:24 +0100
Subject: [PATCH 059/870] Swift: Minor edits.
---
.../analyzing-data-flow-in-swift.rst | 21 ++++++++-----------
1 file changed, 9 insertions(+), 12 deletions(-)
diff --git a/docs/codeql/codeql-language-guides/analyzing-data-flow-in-swift.rst b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-swift.rst
index 69b42f327d8..90576aac9f1 100644
--- a/docs/codeql/codeql-language-guides/analyzing-data-flow-in-swift.rst
+++ b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-swift.rst
@@ -15,7 +15,7 @@ For a more general introduction to modeling data flow, see ":ref:`About data flo
Local data flow
---------------
-Local data flow tracks the flow of data within a single method or callable. Local data flow is easier, faster, and more precise than global data flow. Before looking at more complex tracking, you should always consider local tracking because it is sufficient for many queries.
+Local data flow tracks the flow of data within a single function. Local data flow is easier, faster, and more precise than global data flow. Before looking at more complex tracking, you should always consider local tracking because it is sufficient for many queries.
Using local data flow
~~~~~~~~~~~~~~~~~~~~~
@@ -36,7 +36,7 @@ The ``Node`` class has a number of useful subclasses, such as ``ExprNode`` for e
*/
ControlFlowNode getCfgNode() { none() }
- ...
+ ...
}
You can use the predicates ``exprNode`` and ``parameterNode`` to map from expressions and parameters to their data-flow node:
@@ -65,7 +65,7 @@ For example, you can find flow from an expression ``source`` to an expression ``
Using local taint tracking
~~~~~~~~~~~~~~~~~~~~~~~~~~
-Local taint tracking extends local data flow to include flow steps where values are not preserved, for example, string manipulation.
+Local taint tracking extends local data flow to include flow steps where values are not preserved, such as string manipulation.
For example:
.. code-block:: swift
@@ -209,10 +209,10 @@ The global taint tracking library uses the same configuration module as the glob
where MyTaintFlow::flow(source, sink)
select source, "Taint flow to $@.", sink, sink.toString()
-Predefined sources and sinks
+Predefined sources
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The data flow library module ``codeql.swift.dataflow.FlowSources`` contains a number of predefined sources and sinks, providing a good starting point for defining data flow and taint flow based security queries.
+The data flow library module ``codeql.swift.dataflow.FlowSources`` contains a number of predefined sources, providing a good starting point for defining data flow and taint flow based security queries.
- The class ``RemoteFlowSource`` represents data flow from remote network inputs and from other applications.
- The class ``LocalFlowSource`` represents data flow from local user input.
@@ -221,11 +221,11 @@ The data flow library module ``codeql.swift.dataflow.FlowSources`` contains a nu
Examples of global data flow
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The following global taint-tracking query finds places where a string literal is used in a function call argument called "password".
+The following global taint-tracking query finds places where a string literal is used in a function call argument named "password".
- Since this is a taint-tracking query, the ``TaintTracking::Global`` module is used.
- The ``isSource`` predicate defines sources as any ``StringLiteralExpr``.
- The ``isSink`` predicate defines sinks as arguments to a ``CallExpr`` called "password".
- - The sources and sinks may need tuning to a particular use case, for example if passwords are represented by a type other than ``String`` or passed in arguments of a different name than "password".
+ - The sources and sinks may need tuning to a particular use, for example if passwords are represented by a type other than ``String`` or passed in arguments of a different name than "password".
.. code-block:: ql
@@ -245,8 +245,7 @@ The following global taint-tracking query finds places where a string literal is
from DataFlow::Node sourceNode, DataFlow::Node sinkNode
where ConstantPasswordFlow::flow(sourceNode, sinkNode)
- select sinkNode, sourceNode, sinkNode,
- "The value '" + sourceNode.toString() + "' is used as a constant password."
+ select sinkNode, "The value '" + sourceNode.toString() + "' is used as a constant password."
The following global taint-tracking query finds places where a value from a remote or local user input is used as an argument to the SQLite ``Connection.execute(_:)`` function.
@@ -256,7 +255,6 @@ The following global taint-tracking query finds places where a value from a remo
.. code-block:: ql
-
import swift
import codeql.swift.dataflow.DataFlow
import codeql.swift.dataflow.TaintTracking
@@ -277,8 +275,7 @@ The following global taint-tracking query finds places where a value from a remo
from DataFlow::Node sourceNode, DataFlow::Node sinkNode
where SqlInjectionFlow::flow(sourceNode, sinkNode)
- select sinkNode, sourceNode, sinkNode, "This query depends on a $@.", sourceNode,
- "user-provided value"
+ select sinkNode, "This query depends on a $@.", sourceNode, "user-provided value"
Further reading
---------------
From 6eefb268ddbc10c5178900a59077c5ba56f3c0c0 Mon Sep 17 00:00:00 2001
From: Stephan Brandauer
Date: Fri, 14 Apr 2023 12:35:59 +0200
Subject: [PATCH 060/870] Automodel extraction queries in java telemetry query
directory
---
.../AutomodelEndpointCharacteristics.qll | 320 ++++++++++++++++++
.../src/Telemetry/AutomodelEndpointTypes.qll | 60 ++++
.../Telemetry/AutomodelExtractCandidates.ql | 39 +++
.../AutomodelExtractNegativeExamples.ql | 36 ++
.../AutomodelExtractPositiveExamples.ql | 43 +++
.../AutomodelSharedCharacteristics.qll | 259 ++++++++++++++
6 files changed, 757 insertions(+)
create mode 100644 java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll
create mode 100644 java/ql/src/Telemetry/AutomodelEndpointTypes.qll
create mode 100644 java/ql/src/Telemetry/AutomodelExtractCandidates.ql
create mode 100644 java/ql/src/Telemetry/AutomodelExtractNegativeExamples.ql
create mode 100644 java/ql/src/Telemetry/AutomodelExtractPositiveExamples.ql
create mode 100644 java/ql/src/Telemetry/AutomodelSharedCharacteristics.qll
diff --git a/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll b/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll
new file mode 100644
index 00000000000..01c18ad2706
--- /dev/null
+++ b/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll
@@ -0,0 +1,320 @@
+/**
+ * For internal use only.
+ */
+
+private import java
+private import semmle.code.java.dataflow.DataFlow
+private import semmle.code.java.dataflow.TaintTracking
+private import semmle.code.java.security.PathCreation
+private import semmle.code.java.dataflow.ExternalFlow as ExternalFlow
+private import semmle.code.java.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
+private import semmle.code.java.security.ExternalAPIs as ExternalAPIs
+private import semmle.code.java.Expr as Expr
+private import semmle.code.java.security.QueryInjection
+private import semmle.code.java.security.RequestForgery
+import AutomodelSharedCharacteristics as SharedCharacteristics
+import AutomodelEndpointTypes as AutomodelEndpointTypes
+
+module CandidatesImpl implements SharedCharacteristics::CandidateSig {
+ class Endpoint = DataFlow::ParameterNode;
+
+ class EndpointType = AutomodelEndpointTypes::EndpointType;
+
+ predicate isNegative(AutomodelEndpointTypes::EndpointType t) {
+ t instanceof AutomodelEndpointTypes::NegativeSinkType
+ }
+
+ string getLocationString(Endpoint e) { result = e.getLocation().toString() }
+
+ predicate isKnownLabel(string label, string humanReadableLabel, EndpointType type) {
+ label = "read-file" and
+ humanReadableLabel = "read file" and
+ type instanceof AutomodelEndpointTypes::TaintedPathSinkType
+ or
+ label = "create-file" and
+ humanReadableLabel = "create file" and
+ type instanceof AutomodelEndpointTypes::TaintedPathSinkType
+ or
+ label = "sql" and
+ humanReadableLabel = "mad modeled sql" and
+ type instanceof AutomodelEndpointTypes::SqlSinkType
+ or
+ label = "open-url" and
+ humanReadableLabel = "open url" and
+ type instanceof AutomodelEndpointTypes::RequestForgerySinkType
+ or
+ label = "jdbc-url" and
+ humanReadableLabel = "jdbc url" and
+ type instanceof AutomodelEndpointTypes::RequestForgerySinkType
+ or
+ label = "command-injection" and
+ humanReadableLabel = "command injection" and
+ type instanceof AutomodelEndpointTypes::CommandInjectionSinkType
+ }
+
+ predicate isSink(Endpoint e, string label) {
+ exists(
+ string package, string type, boolean subtypes, string name, string signature, string ext,
+ string input
+ |
+ sinkSpec(e, package, type, subtypes, name, signature, ext, input) and
+ ExternalFlow::sinkModel(package, type, subtypes, name, [signature, ""], ext, input, label, _)
+ )
+ }
+
+ predicate isNeutral(Endpoint e) {
+ exists(string package, string type, string name, string signature |
+ sinkSpec(e, package, type, _, name, signature, _, _) and
+ ExternalFlow::neutralModel(package, type, name, [signature, ""], _)
+ )
+ }
+
+ additional predicate sinkSpec(
+ Endpoint e, string package, string type, boolean subtypes, string name, string signature,
+ string ext, string input
+ ) {
+ package = e.getEnclosingCallable().getDeclaringType().getPackage().toString() and
+ type = e.getEnclosingCallable().getDeclaringType().getName() and
+ subtypes = false and
+ name = e.getEnclosingCallable().getName() and
+ signature = ExternalFlow::paramsString(e.getEnclosingCallable()) and
+ ext = "" and
+ exists(int paramIdx | e.isParameterOf(_, paramIdx) | input = "Argument[" + paramIdx + "]")
+ }
+
+ predicate hasMetadata(Endpoint n, string metadata) {
+ exists(
+ string package, string type, boolean subtypes, string name, string signature, string ext,
+ int input, string provenance, boolean isPublic, boolean isFinal, string calleeJavaDoc
+ |
+ hasMetadata(n, package, type, name, signature, input, isFinal, isPublic, calleeJavaDoc) and
+ (if isFinal = true then subtypes = false else subtypes = true) and
+ ext = "" and // see https://github.slack.com/archives/CP9127VUK/p1673979477496069
+ provenance = "ai-generated" and
+ metadata =
+ "{" //
+ + "'Package': '" + package //
+ + "', 'Type': '" + type //
+ + "', 'Subtypes': " + subtypes //
+ + ", 'Name': '" + name //
+ + "', 'Signature': '" + signature //
+ + "', 'Ext': '" + ext //
+ + "', 'Argument index': " + input //
+ + ", 'Provenance': '" + provenance //
+ + "', 'Is public': " + isPublic //
+ + "', 'Callee JavaDoc': '" + calleeJavaDoc.replaceAll("'", "\"") //
+ + "'}" // TODO: Why are the curly braces added twice?
+ )
+ }
+}
+
+module CharacteristicsImpl = SharedCharacteristics::SharedCharacteristics;
+
+class EndpointCharacteristic = CharacteristicsImpl::EndpointCharacteristic;
+
+class Endpoint = CandidatesImpl::Endpoint;
+
+/*
+ * Predicates that are used to surface prompt examples and candidates for classification with an ML model.
+ */
+
+/**
+ * Holds if `n` has the given metadata.
+ *
+ * This is a helper function to extract and export needed information about each endpoint.
+ */
+predicate hasMetadata(
+ Endpoint n, string package, string type, string name, string signature, int input,
+ boolean isFinal, boolean isPublic, string calleeJavaDoc
+) {
+ exists(Callable callee |
+ n.asParameter() = callee.getParameter(input) and
+ package = callee.getDeclaringType().getPackage().getName() and
+ type = callee.getDeclaringType().getErasure().(RefType).nestedName() and
+ (
+ if callee.isFinal() or callee.getDeclaringType().isFinal()
+ then isFinal = true
+ else isFinal = false
+ ) and
+ name = callee.getSourceDeclaration().getName() and
+ signature = ExternalFlow::paramsString(callee) and // TODO: Why are brackets being escaped (`\[\]` vs `[]`)?
+ (if callee.isPublic() then isPublic = true else isPublic = false) and
+ if exists(callee.(Documentable).getJavadoc())
+ then calleeJavaDoc = callee.(Documentable).getJavadoc().toString()
+ else calleeJavaDoc = ""
+ )
+}
+
+/*
+ * EndpointCharacteristic classes that are specific to Automodel for Java.
+ */
+
+/**
+ * A negative characteristic that indicates that an is-style boolean method is unexploitable even if it is a sink.
+ *
+ * A sink is highly unlikely to be exploitable if its callee's name starts with `is` and the callee has a boolean return
+ * type (e.g. `isDirectory`). These kinds of calls normally do only checks, and appear before the proper call that does
+ * the dangerous/interesting thing, so we want the latter to be modeled as the sink.
+ *
+ * TODO: this might filter too much, it's possible that methods with more than one parameter contain interesting sinks
+ */
+private class UnexploitableIsCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic {
+ UnexploitableIsCharacteristic() { this = "unexploitable (is-style boolean method)" }
+
+ override predicate appliesToEndpoint(Endpoint e) {
+ not CandidatesImpl::isSink(e, _) and
+ e.getEnclosingCallable().getName().matches("is%") and
+ e.getEnclosingCallable().getReturnType() instanceof BooleanType
+ }
+}
+
+/**
+ * A negative characteristic that indicates that an existence-checking boolean method is unexploitable even if it is a
+ * sink.
+ *
+ * A sink is highly unlikely to be exploitable if its callee's name is `exists` or `notExists` and the callee has a
+ * boolean return type. These kinds of calls normally do only checks, and appear before the proper call that does the
+ * dangerous/interesting thing, so we want the latter to be modeled as the sink.
+ */
+private class UnexploitableExistsCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic {
+ UnexploitableExistsCharacteristic() { this = "unexploitable (existence-checking boolean method)" }
+
+ override predicate appliesToEndpoint(Endpoint e) {
+ not CandidatesImpl::isSink(e, _) and
+ exists(Callable callee |
+ callee = e.getEnclosingCallable() and
+ (
+ callee.getName().toLowerCase() = "exists" or
+ callee.getName().toLowerCase() = "notexists"
+ ) and
+ callee.getReturnType() instanceof BooleanType
+ )
+ }
+}
+
+/**
+ * A negative characteristic that indicates that an endpoint is an argument to an exception, which is not a sink.
+ */
+private class ExceptionCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic {
+ ExceptionCharacteristic() { this = "exception" }
+
+ override predicate appliesToEndpoint(Endpoint e) {
+ e.getEnclosingCallable().getDeclaringType().getASupertype*() instanceof TypeThrowable
+ }
+}
+
+/**
+ * A negative characteristic that indicates that an endpoint sits in a test file.
+ *
+ * WARNING: These endpoints should not be used as negative samples for training, because there can in fact be sinks in
+ * test files -- we just don't care to model them because they aren't exploitable.
+ */
+private class TestFileCharacteristic extends CharacteristicsImpl::LikelyNotASinkCharacteristic {
+ TestFileCharacteristic() { this = "test file" }
+
+ override predicate appliesToEndpoint(Endpoint e) {
+ exists(File f | f = e.getLocation().getFile() and isInTestFile(f))
+ }
+
+ private predicate isInTestFile(File file) {
+ file.getAbsolutePath().matches("%src/test/%") or
+ file.getAbsolutePath().matches("%/guava-tests/%") or
+ file.getAbsolutePath().matches("%/guava-testlib/%")
+ }
+}
+
+/**
+ * A negative characteristic that filters out calls to undocumented methods. The assumption is that methods that are
+ * intended / likely to be called from outside the package are documented.
+ *
+ * Note that in practice we have seen some interesting sinks in methods that are external-facing but undocumented (and
+ * appear in empty Javadoc pages), so this filter can be expected to lead to the loss of some interesting sinks.
+ */
+private class UndocumentedMethodCharacteristic extends CharacteristicsImpl::UninterestingToModelCharacteristic
+{
+ UndocumentedMethodCharacteristic() { this = "undocumented method" }
+
+ override predicate appliesToEndpoint(Endpoint e) {
+ not exists(e.getEnclosingCallable().(Documentable).getJavadoc())
+ }
+}
+
+/**
+ * A negative characteristic that filters out non-public methods. Non-public methods are not interesting to include in
+ * the standard Java modeling, because they cannot be called from outside the package.
+ */
+private class NonPublicMethodCharacteristic extends CharacteristicsImpl::UninterestingToModelCharacteristic
+{
+ NonPublicMethodCharacteristic() { this = "non-public method" }
+
+ override predicate appliesToEndpoint(Endpoint e) { not e.getEnclosingCallable().isPublic() }
+}
+
+/**
+ * Holds if the given endpoint has a self-contradictory combination of characteristics. Detects errors in our endpoint
+ * characteristics. Lists the problematic characteristics and their implications for all such endpoints, together with
+ * an error message indicating why this combination is problematic.
+ *
+ * Copied from
+ * javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_large_scale/ContradictoryEndpointCharacteristics.ql
+ */
+predicate erroneousEndpoints(
+ Endpoint endpoint, EndpointCharacteristic characteristic,
+ AutomodelEndpointTypes::EndpointType endpointType, float confidence, string errorMessage,
+ boolean ignoreKnownModelingErrors
+) {
+ // An endpoint's characteristics should not include positive indicators with medium/high confidence for more than one
+ // sink/source type (including the negative type).
+ exists(
+ EndpointCharacteristic characteristic2, AutomodelEndpointTypes::EndpointType endpointClass2,
+ float confidence2
+ |
+ endpointType != endpointClass2 and
+ (
+ endpointType instanceof AutomodelEndpointTypes::SinkType and
+ endpointClass2 instanceof AutomodelEndpointTypes::SinkType
+ or
+ endpointType instanceof AutomodelEndpointTypes::SourceType and
+ endpointClass2 instanceof AutomodelEndpointTypes::SourceType
+ ) and
+ characteristic.appliesToEndpoint(endpoint) and
+ characteristic2.appliesToEndpoint(endpoint) and
+ characteristic.hasImplications(endpointType, true, confidence) and
+ characteristic2.hasImplications(endpointClass2, true, confidence2) and
+ confidence > SharedCharacteristics::mediumConfidence() and
+ confidence2 > SharedCharacteristics::mediumConfidence() and
+ (
+ ignoreKnownModelingErrors = true and
+ not knownOverlappingCharacteristics(characteristic, characteristic2)
+ or
+ ignoreKnownModelingErrors = false
+ )
+ ) and
+ errorMessage = "Endpoint has high-confidence positive indicators for multiple classes"
+ or
+ // An endpoint's characteristics should not include positive indicators with medium/high confidence for some class and
+ // also include negative indicators with medium/high confidence for this same class.
+ exists(EndpointCharacteristic characteristic2, float confidence2 |
+ characteristic.appliesToEndpoint(endpoint) and
+ characteristic2.appliesToEndpoint(endpoint) and
+ characteristic.hasImplications(endpointType, true, confidence) and
+ characteristic2.hasImplications(endpointType, false, confidence2) and
+ confidence > SharedCharacteristics::mediumConfidence() and
+ confidence2 > SharedCharacteristics::mediumConfidence()
+ ) and
+ ignoreKnownModelingErrors = false and
+ errorMessage = "Endpoint has high-confidence positive and negative indicators for the same class"
+}
+
+/**
+ * Holds if `characteristic1` and `characteristic2` are among the pairs of currently known positive characteristics that
+ * have some overlap in their results. This indicates a problem with the underlying Java modeling. Specifically,
+ * `PathCreation` is prone to FPs.
+ */
+private predicate knownOverlappingCharacteristics(
+ EndpointCharacteristic characteristic1, EndpointCharacteristic characteristic2
+) {
+ characteristic1 != characteristic2 and
+ characteristic1 = ["mad taint step", "create path", "read file", "known non-sink"] and
+ characteristic2 = ["mad taint step", "create path", "read file", "known non-sink"]
+}
diff --git a/java/ql/src/Telemetry/AutomodelEndpointTypes.qll b/java/ql/src/Telemetry/AutomodelEndpointTypes.qll
new file mode 100644
index 00000000000..7414837b605
--- /dev/null
+++ b/java/ql/src/Telemetry/AutomodelEndpointTypes.qll
@@ -0,0 +1,60 @@
+/**
+ * For internal use only.
+ *
+ * Defines the set of classes that endpoint scoring models can predict. Endpoint scoring models must
+ * only predict classes defined within this file. This file is the source of truth for the integer
+ * representation of each of these classes.
+ */
+
+/** A class that can be predicted by a classifier. */
+abstract class EndpointType extends string {
+ /**
+ * Holds when the string matches the name of the sink / source type.
+ */
+ bindingset[this]
+ EndpointType() { any() }
+
+ /**
+ * Gets the name of the sink/source kind for this endpoint type as used in models-as-data.
+ *
+ * See https://github.com/github/codeql/blob/44213f0144fdd54bb679ca48d68b28dcf820f7a8/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll#LL353C11-L357C31
+ */
+ final string getKind() { result = this }
+}
+
+/** A class for sink types that can be predicted by a classifier. */
+abstract class SinkType extends EndpointType {
+ bindingset[this]
+ SinkType() { any() }
+}
+
+/** A class for source types that can be predicted by a classifier. */
+abstract class SourceType extends EndpointType {
+ bindingset[this]
+ SourceType() { any() }
+}
+
+/** The `Negative` class for non-sinks. */
+class NegativeSinkType extends SinkType {
+ NegativeSinkType() { this = "non-sink" }
+}
+
+/** A sink relevant to the SQL injection query */
+class SqlSinkType extends SinkType {
+ SqlSinkType() { this = "sql" }
+}
+
+/** A sink relevant to the tainted path injection query. */
+class TaintedPathSinkType extends SinkType {
+ TaintedPathSinkType() { this = "tainted-path" }
+}
+
+/** A sink relevant to the SSRF query. */
+class RequestForgerySinkType extends SinkType {
+ RequestForgerySinkType() { this = "ssrf" }
+}
+
+/** A sink relevant to the command injection query. */
+class CommandInjectionSinkType extends SinkType {
+ CommandInjectionSinkType() { this = "command-injection" }
+}
diff --git a/java/ql/src/Telemetry/AutomodelExtractCandidates.ql b/java/ql/src/Telemetry/AutomodelExtractCandidates.ql
new file mode 100644
index 00000000000..13a2988d505
--- /dev/null
+++ b/java/ql/src/Telemetry/AutomodelExtractCandidates.ql
@@ -0,0 +1,39 @@
+/**
+ * Surfaces the endpoints that pass the endpoint filters and are not already known to be sinks, and are therefore used
+ * as candidates for classification with an ML model.
+ *
+ * Note: This query does not actually classify the endpoints using the model.
+ *
+ * @name Automodel candidates
+ * @description A query to extract automodel candidates.
+ * @kind problem
+ * @severity info
+ * @id java/ml-powered/extract-automodel-candidates
+ * @tags automodel extract candidates
+ */
+
+import AutomodelEndpointCharacteristics
+
+from Endpoint sinkCandidate, string message
+where
+ not exists(CharacteristicsImpl::UninterestingToModelCharacteristic u |
+ u.appliesToEndpoint(sinkCandidate)
+ ) and
+ // If a node is already a known sink for any of our existing ATM queries and is already modeled as a MaD sink, we
+ // don't include it as a candidate. Otherwise, we might include it as a candidate for query A, but the model will
+ // label it as a sink for one of the sink types of query B, for which it's already a known sink. This would result in
+ // overlap between our detected sinks and the pre-existing modeling. We assume that, if a sink has already been
+ // modeled in a MaD model, then it doesn't belong to any additional sink types, and we don't need to reexamine it.
+ not CharacteristicsImpl::isSink(sinkCandidate, _) and
+ // The message is the concatenation of all sink types for which this endpoint is known neither to be a sink nor to be
+ // a non-sink, and we surface only endpoints that have at least one such sink type.
+ message =
+ strictconcat(AutomodelEndpointTypes::SinkType sinkType |
+ not CharacteristicsImpl::isKnownSink(sinkCandidate, sinkType) and
+ CharacteristicsImpl::isSinkCandidate(sinkCandidate, sinkType)
+ |
+ sinkType + ", "
+ ) + "\n" +
+ // Extract the needed metadata for this endpoint.
+ any(string metadata | CharacteristicsImpl::hasMetadata(sinkCandidate, metadata))
+select sinkCandidate, message
diff --git a/java/ql/src/Telemetry/AutomodelExtractNegativeExamples.ql b/java/ql/src/Telemetry/AutomodelExtractNegativeExamples.ql
new file mode 100644
index 00000000000..6612f422824
--- /dev/null
+++ b/java/ql/src/Telemetry/AutomodelExtractNegativeExamples.ql
@@ -0,0 +1,36 @@
+/**
+ * Surfaces endpoints are non-sinks with high confidence, for use as negative examples in the prompt.
+ *
+ * @name Negative examples (experimental)
+ * @kind problem
+ * @severity info
+ * @id java/ml-powered/non-sink
+ * @tags automodel extract negative-examples
+ */
+
+import AutomodelEndpointCharacteristics
+import AutomodelEndpointTypes
+
+from Endpoint endpoint, EndpointCharacteristic characteristic, float confidence, string message
+where
+ characteristic.appliesToEndpoint(endpoint) and
+ confidence >= SharedCharacteristics::highConfidence() and
+ characteristic.hasImplications(any(NegativeSinkType negative), true, confidence) and
+ // Exclude endpoints that have contradictory endpoint characteristics, because we only want examples we're highly
+ // certain about in the prompt.
+ not erroneousEndpoints(endpoint, _, _, _, _, false) and
+ // It's valid for a node to satisfy the logic for both `isSink` and `isSanitizer`, but in that case it will be
+ // treated by the actual query as a sanitizer, since the final logic is something like
+ // `isSink(n) and not isSanitizer(n)`. We don't want to include such nodes as negative examples in the prompt, because
+ // they're ambiguous and might confuse the model, so we explicitly exclude all known sinks from the negative examples.
+ not exists(EndpointCharacteristic characteristic2, float confidence2, SinkType positiveType |
+ not positiveType instanceof NegativeSinkType and
+ characteristic2.appliesToEndpoint(endpoint) and
+ confidence2 >= SharedCharacteristics::maximalConfidence() and
+ characteristic2.hasImplications(positiveType, true, confidence2)
+ ) and
+ message =
+ characteristic + "\n" +
+ // Extract the needed metadata for this endpoint.
+ any(string metadata | CharacteristicsImpl::hasMetadata(endpoint, metadata))
+select endpoint, message
diff --git a/java/ql/src/Telemetry/AutomodelExtractPositiveExamples.ql b/java/ql/src/Telemetry/AutomodelExtractPositiveExamples.ql
new file mode 100644
index 00000000000..3db636439fa
--- /dev/null
+++ b/java/ql/src/Telemetry/AutomodelExtractPositiveExamples.ql
@@ -0,0 +1,43 @@
+/**
+ * Surfaces endpoints are sinks with high confidence, for use as positive examples in the prompt.
+ *
+ * @name Positive examples (experimental)
+ * @kind problem
+ * @severity info
+ * @id java/ml-powered/known-sink
+ * @tags automodel extract positive-examples
+ */
+
+private import java
+private import semmle.code.java.security.ExternalAPIs as ExternalAPIs
+private import AutomodelEndpointCharacteristics
+private import AutomodelEndpointTypes
+
+// private import experimental.adaptivethreatmodeling.ATMConfigs // To import the configurations of all supported Java queries
+/*
+ * ****** WARNING: ******
+ * Before calling this query, make sure there's no codex-generated data extension file in `java/ql/lib/ext`. Otherwise,
+ * the ML-generated, noisy sinks will end up polluting the positive examples used in the prompt!
+ */
+
+from Endpoint sink, SinkType sinkType, string message
+where
+ // Exclude endpoints that have contradictory endpoint characteristics, because we only want examples we're highly
+ // certain about in the prompt.
+ not erroneousEndpoints(sink, _, _, _, _, false) and
+ // Extract positive examples of sinks belonging to the existing ATM query configurations.
+ (
+ CharacteristicsImpl::isKnownSink(sink, sinkType) and
+ // If there are _any_ erroneous endpoints, return an error message for all rows. This will prevent us from
+ // accidentally running this query when there's a codex-generated data extension file in `java/ql/lib/ext`.
+ if not erroneousEndpoints(_, _, _, _, _, true)
+ then
+ message =
+ sinkType + "\n" +
+ // Extract the needed metadata for this endpoint.
+ any(string metadata | CharacteristicsImpl::hasMetadata(sink, metadata))
+ else
+ message =
+ "Error: There are erroneous endpoints! Please check whether there's a codex-generated data extension file in `java/ql/lib/ext`."
+ )
+select sink, message
diff --git a/java/ql/src/Telemetry/AutomodelSharedCharacteristics.qll b/java/ql/src/Telemetry/AutomodelSharedCharacteristics.qll
new file mode 100644
index 00000000000..0d8bb679c86
--- /dev/null
+++ b/java/ql/src/Telemetry/AutomodelSharedCharacteristics.qll
@@ -0,0 +1,259 @@
+float maximalConfidence() { result = 1.0 }
+
+float highConfidence() { result = 0.9 }
+
+float mediumConfidence() { result = 0.6 }
+
+signature module CandidateSig {
+ class Endpoint;
+
+ class EndpointType;
+
+ string getLocationString(Endpoint e);
+
+ /**
+ * Defines what labels are known, and what endpoint type they correspond to.
+ */
+ predicate isKnownLabel(string label, string humanReadableLabel, EndpointType type);
+
+ /**
+ * EndpointType must have a 'negative' type that denotes the absence of any sink.
+ * This predicate should hold for that type, and that type only.
+ */
+ predicate isNegative(EndpointType t);
+
+ /**
+ * Should hold for any endpoint that is a sink of the given (known or unknown) label.
+ */
+ predicate isSink(Endpoint e, string label);
+
+ /**
+ * Should hold for any endpoint that is known to not be any sink.
+ */
+ predicate isNeutral(Endpoint e);
+
+ /**
+ * Holds if `e` has the given metadata.
+ *
+ * This is a helper function to extract and export needed information about each endpoint in the sink candidate query
+ * as well as the queries that extract positive and negative examples for the prompt / training set. The metadata is
+ * extracted as a string in the format of a Python dictionary.
+ */
+ predicate hasMetadata(Endpoint e, string metadata);
+}
+
+module SharedCharacteristics {
+ predicate isNegative(Candidate::EndpointType e) { Candidate::isNegative(e) }
+
+ predicate isSink(Candidate::Endpoint e, string label) { Candidate::isSink(e, label) }
+
+ predicate isNeutral(Candidate::Endpoint e) { Candidate::isNeutral(e) }
+
+ /**
+ * Holds if `sink` is a known sink of type `endpointType`.
+ */
+ predicate isKnownSink(Candidate::Endpoint sink, Candidate::EndpointType endpointType) {
+ // If the list of characteristics includes positive indicators with maximal confidence for this class, then it's a
+ // known sink for the class.
+ not isNegative(endpointType) and
+ exists(EndpointCharacteristic characteristic |
+ characteristic.appliesToEndpoint(sink) and
+ characteristic.hasImplications(endpointType, true, maximalConfidence())
+ )
+ }
+
+ /**
+ * Holds if the candidate sink `candidateSink` should be considered as a possible sink of type `sinkType`, and
+ * classified by the ML model. A candidate sink is a node that cannot be excluded from `sinkType` based on its
+ * characteristics.
+ */
+ predicate isSinkCandidate(Candidate::Endpoint candidateSink, Candidate::EndpointType sinkType) {
+ not isNegative(sinkType) and
+ not exists(getAReasonSinkExcluded(candidateSink, sinkType))
+ }
+
+ predicate hasMetadata(Candidate::Endpoint n, string metadata) {
+ Candidate::hasMetadata(n, metadata)
+ }
+
+ /**
+ * Gets the list of characteristics that cause `candidateSink` to be excluded as an effective sink for a given sink
+ * type.
+ */
+ EndpointCharacteristic getAReasonSinkExcluded(
+ Candidate::Endpoint candidateSink, Candidate::EndpointType sinkType
+ ) {
+ // An endpoint is a sink candidate if none of its characteristics give much indication whether or not it is a sink.
+ not isNegative(sinkType) and
+ result.appliesToEndpoint(candidateSink) and
+ // Exclude endpoints that have a characteristic that implies they're not sinks for _any_ sink type.
+ (
+ exists(float confidence |
+ confidence >= mediumConfidence() and
+ result.hasImplications(any(Candidate::EndpointType t | isNegative(t)), true, confidence)
+ )
+ or
+ // Exclude endpoints that have a characteristic that implies they're not sinks for _this particular_ sink type.
+ exists(float confidence |
+ confidence >= mediumConfidence() and
+ result.hasImplications(sinkType, false, confidence)
+ )
+ )
+ }
+
+ /**
+ * A set of characteristics that a particular endpoint might have. This set of characteristics is used to make decisions
+ * about whether to include the endpoint in the training set and with what label, as well as whether to score the
+ * endpoint at inference time.
+ */
+ abstract class EndpointCharacteristic extends string {
+ /**
+ * Holds when the string matches the name of the characteristic, which should describe some characteristic of the
+ * endpoint that is meaningful for determining whether it's a sink and if so of which type
+ */
+ bindingset[this]
+ EndpointCharacteristic() { any() }
+
+ /**
+ * Holds for parameters that have this characteristic. This predicate contains the logic that applies characteristics
+ * to the appropriate set of dataflow parameters.
+ */
+ abstract predicate appliesToEndpoint(Candidate::Endpoint n);
+
+ /**
+ * This predicate describes what the characteristic tells us about an endpoint.
+ *
+ * Params:
+ * endpointType: The sink/source type.
+ * isPositiveIndicator: If true, this characteristic indicates that this endpoint _is_ a member of the class; if
+ * false, it indicates that it _isn't_ a member of the class.
+ * confidence: A float in [0, 1], which tells us how strong an indicator this characteristic is for the endpoint
+ * belonging / not belonging to the given class. A confidence near zero means this characteristic is a very weak
+ * indicator of whether or not the endpoint belongs to the class. A confidence of 1 means that all endpoints with
+ * this characteristic definitively do/don't belong to the class.
+ */
+ abstract predicate hasImplications(
+ Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
+ );
+
+ /** Indicators with confidence at or above this threshold are considered to be high-confidence indicators. */
+ final float getHighConfidenceThreshold() { result = 0.8 }
+ }
+
+ /**
+ * A high-confidence characteristic that indicates that an endpoint is a sink of a specified type. These endpoints can
+ * be used as positive samples for training or for a few-shot prompt.
+ */
+ abstract class SinkCharacteristic extends EndpointCharacteristic {
+ bindingset[this]
+ SinkCharacteristic() { any() }
+
+ abstract Candidate::EndpointType getSinkType();
+
+ final override predicate hasImplications(
+ Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
+ ) {
+ endpointType = this.getSinkType() and
+ isPositiveIndicator = true and
+ confidence = maximalConfidence()
+ }
+ }
+
+ /**
+ * Endpoints identified as sinks by the MaD modeling are sinks with maximal confidence.
+ */
+ private class KnownSinkCharacteristic extends SinkCharacteristic {
+ string madLabel;
+ Candidate::EndpointType endpointType;
+
+ KnownSinkCharacteristic() { Candidate::isKnownLabel(madLabel, this, endpointType) }
+
+ override predicate appliesToEndpoint(Candidate::Endpoint e) { Candidate::isSink(e, madLabel) }
+
+ override Candidate::EndpointType getSinkType() { result = endpointType }
+ }
+
+ /**
+ * A high-confidence characteristic that indicates that an endpoint is not a sink of any type. These endpoints can be
+ * used as negative samples for training or for a few-shot prompt.
+ */
+ abstract class NotASinkCharacteristic extends EndpointCharacteristic {
+ bindingset[this]
+ NotASinkCharacteristic() { any() }
+
+ override predicate hasImplications(
+ Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
+ ) {
+ Candidate::isNegative(endpointType) and
+ isPositiveIndicator = true and
+ confidence = highConfidence()
+ }
+ }
+
+ /**
+ * A negative characteristic that indicates that an endpoint is not part of the source code for the project being
+ * analyzed.
+ *
+ * WARNING: These endpoints should not be used as negative samples for training, because they are not necessarily
+ * non-sinks. They are merely not interesting sinks to run through the ML model.
+ */
+ private class IsExternalCharacteristic extends LikelyNotASinkCharacteristic {
+ IsExternalCharacteristic() { this = "external" }
+
+ override predicate appliesToEndpoint(Candidate::Endpoint e) {
+ not exists(Candidate::getLocationString(e))
+ }
+ }
+
+ /**
+ * A negative characteristic that indicates that an endpoint was manually modeled as a neutral model.
+ *
+ * TODO: It may be necessary to turn this into a LikelyNotASinkCharacteristic, pending answers to the definition of a
+ * neutral model (https://github.com/github/codeql-java-team/issues/254#issuecomment-1435309148).
+ */
+ private class NeutralModelCharacteristic extends NotASinkCharacteristic {
+ NeutralModelCharacteristic() { this = "known non-sink" }
+
+ override predicate appliesToEndpoint(Candidate::Endpoint e) { Candidate::isNeutral(e) }
+ }
+
+ /**
+ * A medium-confidence characteristic that indicates that an endpoint is unlikely to be a sink of any type. These
+ * endpoints can be excluded from scoring at inference time, both to save time and to avoid false positives. They should
+ * not, however, be used as negative samples for training or for a few-shot prompt, because they may include a small
+ * number of sinks.
+ */
+ abstract class LikelyNotASinkCharacteristic extends EndpointCharacteristic {
+ bindingset[this]
+ LikelyNotASinkCharacteristic() { any() }
+
+ override predicate hasImplications(
+ Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
+ ) {
+ Candidate::isNegative(endpointType) and
+ isPositiveIndicator = true and
+ confidence = mediumConfidence()
+ }
+ }
+
+ /**
+ * A characteristic that indicates not necessarily that an endpoint is not a sink, but rather that it is not a sink
+ * that's interesting to model in the standard Java libraries. These filters should be removed when extracting sink
+ * candidates within a user's codebase for customized modeling.
+ *
+ * These endpoints should not be used as negative samples for training or for a few-shot prompt, because they are not
+ * necessarily non-sinks.
+ */
+ abstract class UninterestingToModelCharacteristic extends EndpointCharacteristic {
+ bindingset[this]
+ UninterestingToModelCharacteristic() { any() }
+
+ override predicate hasImplications(
+ Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
+ ) {
+ Candidate::isNegative(endpointType) and
+ isPositiveIndicator = true and
+ confidence = mediumConfidence()
+ }
+ }
+}
From 3868defb87db7980056c7b221c9b119736c6f02d Mon Sep 17 00:00:00 2001
From: Stephan Brandauer
Date: Wed, 26 Apr 2023 16:02:13 +0200
Subject: [PATCH 061/870] use ModelApi to define parameters worth modeling
---
.../Telemetry/AutomodelEndpointCharacteristics.qll | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll b/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll
index 01c18ad2706..185fb4f32e0 100644
--- a/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll
@@ -12,6 +12,7 @@ private import semmle.code.java.security.ExternalAPIs as ExternalAPIs
private import semmle.code.java.Expr as Expr
private import semmle.code.java.security.QueryInjection
private import semmle.code.java.security.RequestForgery
+private import semmle.code.java.dataflow.internal.ModelExclusions as ModelExclusions
import AutomodelSharedCharacteristics as SharedCharacteristics
import AutomodelEndpointTypes as AutomodelEndpointTypes
@@ -239,6 +240,18 @@ private class UndocumentedMethodCharacteristic extends CharacteristicsImpl::Unin
}
}
+/**
+ * A characteristic that limits candidates to parameters of methods that are recognized as `ModelApi`, iow., APIs that
+ * are considered worth modelling.
+ */
+private class NotAModelApiParameter extends CharacteristicsImpl::UninterestingToModelCharacteristic {
+ NotAModelApiParameter() { this = "not a model API parameter" }
+
+ override predicate appliesToEndpoint(Endpoint e) {
+ not exists(ModelExclusions::ModelApi api | api.getParameter(_) = e.asParameter())
+ }
+}
+
/**
* A negative characteristic that filters out non-public methods. Non-public methods are not interesting to include in
* the standard Java modeling, because they cannot be called from outside the package.
From a91b71c53bdcd25a9a7c7dff24bda13296464701 Mon Sep 17 00:00:00 2001
From: Stephan Brandauer
Date: Thu, 27 Apr 2023 09:53:37 +0200
Subject: [PATCH 062/870] add parameter names to metadata, set subtypes = false
for static method candidates; remove UndocumentedMethodCharacteristics, now
that we use ModelApi
---
.../AutomodelEndpointCharacteristics.qll | 34 +++++++------------
1 file changed, 13 insertions(+), 21 deletions(-)
diff --git a/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll b/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll
index 185fb4f32e0..b1fc38e31cf 100644
--- a/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll
@@ -83,13 +83,15 @@ module CandidatesImpl implements SharedCharacteristics::CandidateSig {
exists(int paramIdx | e.isParameterOf(_, paramIdx) | input = "Argument[" + paramIdx + "]")
}
- predicate hasMetadata(Endpoint n, string metadata) {
+ predicate hasMetadata(Endpoint e, string metadata) {
exists(
string package, string type, boolean subtypes, string name, string signature, string ext,
- int input, string provenance, boolean isPublic, boolean isFinal, string calleeJavaDoc
+ int input, string provenance, boolean isPublic, boolean isFinal, boolean isStatic,
+ string calleeJavaDoc
|
- hasMetadata(n, package, type, name, signature, input, isFinal, isPublic, calleeJavaDoc) and
- (if isFinal = true then subtypes = false else subtypes = true) and
+ hasMetadata(e, package, type, name, signature, input, isFinal, isStatic, isPublic,
+ calleeJavaDoc) and
+ (if isFinal = true or isStatic = true then subtypes = false else subtypes = true) and
ext = "" and // see https://github.slack.com/archives/CP9127VUK/p1673979477496069
provenance = "ai-generated" and
metadata =
@@ -98,6 +100,7 @@ module CandidatesImpl implements SharedCharacteristics::CandidateSig {
+ "', 'Type': '" + type //
+ "', 'Subtypes': " + subtypes //
+ ", 'Name': '" + name //
+ + ", 'ParamName': '" + e.toString() //
+ "', 'Signature': '" + signature //
+ "', 'Ext': '" + ext //
+ "', 'Argument index': " + input //
@@ -126,12 +129,17 @@ class Endpoint = CandidatesImpl::Endpoint;
*/
predicate hasMetadata(
Endpoint n, string package, string type, string name, string signature, int input,
- boolean isFinal, boolean isPublic, string calleeJavaDoc
+ boolean isFinal, boolean isStatic, boolean isPublic, string calleeJavaDoc
) {
exists(Callable callee |
n.asParameter() = callee.getParameter(input) and
package = callee.getDeclaringType().getPackage().getName() and
type = callee.getDeclaringType().getErasure().(RefType).nestedName() and
+ (
+ if callee.isStatic() or callee.getDeclaringType().isStatic()
+ then isStatic = true
+ else isStatic = false
+ ) and
(
if callee.isFinal() or callee.getDeclaringType().isFinal()
then isFinal = true
@@ -224,22 +232,6 @@ private class TestFileCharacteristic extends CharacteristicsImpl::LikelyNotASink
}
}
-/**
- * A negative characteristic that filters out calls to undocumented methods. The assumption is that methods that are
- * intended / likely to be called from outside the package are documented.
- *
- * Note that in practice we have seen some interesting sinks in methods that are external-facing but undocumented (and
- * appear in empty Javadoc pages), so this filter can be expected to lead to the loss of some interesting sinks.
- */
-private class UndocumentedMethodCharacteristic extends CharacteristicsImpl::UninterestingToModelCharacteristic
-{
- UndocumentedMethodCharacteristic() { this = "undocumented method" }
-
- override predicate appliesToEndpoint(Endpoint e) {
- not exists(e.getEnclosingCallable().(Documentable).getJavadoc())
- }
-}
-
/**
* A characteristic that limits candidates to parameters of methods that are recognized as `ModelApi`, iow., APIs that
* are considered worth modelling.
From ffe7c62766dd45a4e5d83de481cb5a659494cca1 Mon Sep 17 00:00:00 2001
From: Stephan Brandauer
Date: Thu, 27 Apr 2023 10:44:26 +0200
Subject: [PATCH 063/870] use US spelling
---
java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll b/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll
index b1fc38e31cf..eb4683b3cf4 100644
--- a/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll
@@ -234,7 +234,7 @@ private class TestFileCharacteristic extends CharacteristicsImpl::LikelyNotASink
/**
* A characteristic that limits candidates to parameters of methods that are recognized as `ModelApi`, iow., APIs that
- * are considered worth modelling.
+ * are considered worth modeling.
*/
private class NotAModelApiParameter extends CharacteristicsImpl::UninterestingToModelCharacteristic {
NotAModelApiParameter() { this = "not a model API parameter" }
From 52a8230ce357c88c91d68a5cb9f0ca2c82db6a44 Mon Sep 17 00:00:00 2001
From: Stephan Brandauer
Date: Thu, 27 Apr 2023 14:23:55 +0200
Subject: [PATCH 064/870] restructure shared characteristics module; add
framework support for sanitizers
---
.../AutomodelEndpointCharacteristics.qll | 3 +
.../AutomodelExtractNegativeExamples.ql | 2 +-
.../AutomodelExtractPositiveExamples.ql | 2 +-
.../AutomodelSharedCharacteristics.qll | 106 +++++++++++-------
4 files changed, 69 insertions(+), 44 deletions(-)
diff --git a/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll b/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll
index eb4683b3cf4..4d134945071 100644
--- a/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll
@@ -25,6 +25,9 @@ module CandidatesImpl implements SharedCharacteristics::CandidateSig {
t instanceof AutomodelEndpointTypes::NegativeSinkType
}
+ // Sanitizers are currently not modeled in MaD. TODO: check if this has large negative impact.
+ predicate isSanitizer(Endpoint e, EndpointType t) { none() }
+
string getLocationString(Endpoint e) { result = e.getLocation().toString() }
predicate isKnownLabel(string label, string humanReadableLabel, EndpointType type) {
diff --git a/java/ql/src/Telemetry/AutomodelExtractNegativeExamples.ql b/java/ql/src/Telemetry/AutomodelExtractNegativeExamples.ql
index 6612f422824..de07f29f1e8 100644
--- a/java/ql/src/Telemetry/AutomodelExtractNegativeExamples.ql
+++ b/java/ql/src/Telemetry/AutomodelExtractNegativeExamples.ql
@@ -5,7 +5,7 @@
* @kind problem
* @severity info
* @id java/ml-powered/non-sink
- * @tags automodel extract negative-examples
+ * @tags automodel extract examples negative
*/
import AutomodelEndpointCharacteristics
diff --git a/java/ql/src/Telemetry/AutomodelExtractPositiveExamples.ql b/java/ql/src/Telemetry/AutomodelExtractPositiveExamples.ql
index 3db636439fa..7c36a7a72b5 100644
--- a/java/ql/src/Telemetry/AutomodelExtractPositiveExamples.ql
+++ b/java/ql/src/Telemetry/AutomodelExtractPositiveExamples.ql
@@ -5,7 +5,7 @@
* @kind problem
* @severity info
* @id java/ml-powered/known-sink
- * @tags automodel extract positive-examples
+ * @tags automodel extract examples positive
*/
private import java
diff --git a/java/ql/src/Telemetry/AutomodelSharedCharacteristics.qll b/java/ql/src/Telemetry/AutomodelSharedCharacteristics.qll
index 0d8bb679c86..82c0ca5de54 100644
--- a/java/ql/src/Telemetry/AutomodelSharedCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelSharedCharacteristics.qll
@@ -4,11 +4,19 @@ float highConfidence() { result = 0.9 }
float mediumConfidence() { result = 0.6 }
+/**
+ * A specification of how to instantiate the shared characteristics for a given candidate class.
+ *
+ * The `CandidateSig` implementation specifies a type to use for Endpoints (eg., `ParameterNode`), as well as a type
+ * to label endpoint classes (the `EndpointType`). One of the endpoint classes needs to be a 'negative' class, meaning
+ * "not any of the other known endpoint types".
+ */
signature module CandidateSig {
class Endpoint;
class EndpointType;
+ /** The string representing the file+range of the endpoint. */
string getLocationString(Endpoint e);
/**
@@ -22,6 +30,11 @@ signature module CandidateSig {
*/
predicate isNegative(EndpointType t);
+ /**
+ * Should hold for any endpoint that is a flow sanitizer.
+ */
+ predicate isSanitizer(Endpoint e, EndpointType t);
+
/**
* Should hold for any endpoint that is a sink of the given (known or unknown) label.
*/
@@ -37,11 +50,23 @@ signature module CandidateSig {
*
* This is a helper function to extract and export needed information about each endpoint in the sink candidate query
* as well as the queries that extract positive and negative examples for the prompt / training set. The metadata is
- * extracted as a string in the format of a Python dictionary.
+ * extracted as a string in the format of a Python dictionary, eg.:
+ *
+ * `{'Package': 'com.foo.util', 'Type': 'HelperClass', ... }`.
+ *
+ * The meta data will be passed on to the machine learning code by the extraction queries.
*/
predicate hasMetadata(Endpoint e, string metadata);
}
+/**
+ * A set of shared characteristics for a given candidate class.
+ *
+ * This module is language-agnostic, although the `CandidateSig` module will be language-specific.
+ *
+ * The language specific implementation can also further extend the behaviour of this module by adding additional
+ * implementations of endpoint characteristics exported by this module.
+ */
module SharedCharacteristics {
predicate isNegative(Candidate::EndpointType e) { Candidate::isNegative(e) }
@@ -159,20 +184,6 @@ module SharedCharacteristics {
}
}
- /**
- * Endpoints identified as sinks by the MaD modeling are sinks with maximal confidence.
- */
- private class KnownSinkCharacteristic extends SinkCharacteristic {
- string madLabel;
- Candidate::EndpointType endpointType;
-
- KnownSinkCharacteristic() { Candidate::isKnownLabel(madLabel, this, endpointType) }
-
- override predicate appliesToEndpoint(Candidate::Endpoint e) { Candidate::isSink(e, madLabel) }
-
- override Candidate::EndpointType getSinkType() { result = endpointType }
- }
-
/**
* A high-confidence characteristic that indicates that an endpoint is not a sink of any type. These endpoints can be
* used as negative samples for training or for a few-shot prompt.
@@ -190,33 +201,6 @@ module SharedCharacteristics {
}
}
- /**
- * A negative characteristic that indicates that an endpoint is not part of the source code for the project being
- * analyzed.
- *
- * WARNING: These endpoints should not be used as negative samples for training, because they are not necessarily
- * non-sinks. They are merely not interesting sinks to run through the ML model.
- */
- private class IsExternalCharacteristic extends LikelyNotASinkCharacteristic {
- IsExternalCharacteristic() { this = "external" }
-
- override predicate appliesToEndpoint(Candidate::Endpoint e) {
- not exists(Candidate::getLocationString(e))
- }
- }
-
- /**
- * A negative characteristic that indicates that an endpoint was manually modeled as a neutral model.
- *
- * TODO: It may be necessary to turn this into a LikelyNotASinkCharacteristic, pending answers to the definition of a
- * neutral model (https://github.com/github/codeql-java-team/issues/254#issuecomment-1435309148).
- */
- private class NeutralModelCharacteristic extends NotASinkCharacteristic {
- NeutralModelCharacteristic() { this = "known non-sink" }
-
- override predicate appliesToEndpoint(Candidate::Endpoint e) { Candidate::isNeutral(e) }
- }
-
/**
* A medium-confidence characteristic that indicates that an endpoint is unlikely to be a sink of any type. These
* endpoints can be excluded from scoring at inference time, both to save time and to avoid false positives. They should
@@ -256,4 +240,42 @@ module SharedCharacteristics {
confidence = mediumConfidence()
}
}
+
+ /**
+ * Contains default implementations that are derived solely from the `CandidateSig` implementation.
+ */
+ private module DefaultCharacteristicImplementations {
+ /**
+ * Endpoints identified as sinks by the `CandidateSig` implementation are sinks with maximal confidence.
+ */
+ private class KnownSinkCharacteristic extends SinkCharacteristic {
+ string madLabel;
+ Candidate::EndpointType endpointType;
+
+ KnownSinkCharacteristic() { Candidate::isKnownLabel(madLabel, this, endpointType) }
+
+ override predicate appliesToEndpoint(Candidate::Endpoint e) { Candidate::isSink(e, madLabel) }
+
+ override Candidate::EndpointType getSinkType() { result = endpointType }
+ }
+
+ /**
+ * A negative characteristic that indicates that an endpoint was manually modeled as a neutral model.
+ */
+ private class NeutralModelCharacteristic extends NotASinkCharacteristic {
+ NeutralModelCharacteristic() { this = "known non-sink" }
+
+ override predicate appliesToEndpoint(Candidate::Endpoint e) { Candidate::isNeutral(e) }
+ }
+
+ /**
+ * A negative characteristic that indicates that an endpoint is not part of the source code for the project being
+ * analyzed.
+ */
+ private class IsSanitizerCharacteristic extends NotASinkCharacteristic {
+ IsSanitizerCharacteristic() { this = "external" }
+
+ override predicate appliesToEndpoint(Candidate::Endpoint e) { Candidate::isSanitizer(e, _) }
+ }
+ }
}
From adcf4a3dc2c0e08f6e93e137a87ab605db13b66f Mon Sep 17 00:00:00 2001
From: Stephan Brandauer
Date: Thu, 27 Apr 2023 14:41:43 +0200
Subject: [PATCH 065/870] documentation clean-up
---
java/ql/src/Telemetry/AutomodelExtractCandidates.ql | 4 ++--
.../ql/src/Telemetry/AutomodelExtractNegativeExamples.ql | 2 +-
.../ql/src/Telemetry/AutomodelExtractPositiveExamples.ql | 9 +--------
java/ql/src/Telemetry/AutomodelSharedCharacteristics.qll | 4 ++--
4 files changed, 6 insertions(+), 13 deletions(-)
diff --git a/java/ql/src/Telemetry/AutomodelExtractCandidates.ql b/java/ql/src/Telemetry/AutomodelExtractCandidates.ql
index 13a2988d505..0bf1c7e4c6c 100644
--- a/java/ql/src/Telemetry/AutomodelExtractCandidates.ql
+++ b/java/ql/src/Telemetry/AutomodelExtractCandidates.ql
@@ -1,6 +1,6 @@
/**
- * Surfaces the endpoints that pass the endpoint filters and are not already known to be sinks, and are therefore used
- * as candidates for classification with an ML model.
+ * Surfaces the endpoints that are not already known to be sinks, and are therefore used as candidates for
+ * classification with an ML model.
*
* Note: This query does not actually classify the endpoints using the model.
*
diff --git a/java/ql/src/Telemetry/AutomodelExtractNegativeExamples.ql b/java/ql/src/Telemetry/AutomodelExtractNegativeExamples.ql
index de07f29f1e8..08328e9e767 100644
--- a/java/ql/src/Telemetry/AutomodelExtractNegativeExamples.ql
+++ b/java/ql/src/Telemetry/AutomodelExtractNegativeExamples.ql
@@ -1,5 +1,5 @@
/**
- * Surfaces endpoints are non-sinks with high confidence, for use as negative examples in the prompt.
+ * Surfaces endpoints that are non-sinks with high confidence, for use as negative examples in the prompt.
*
* @name Negative examples (experimental)
* @kind problem
diff --git a/java/ql/src/Telemetry/AutomodelExtractPositiveExamples.ql b/java/ql/src/Telemetry/AutomodelExtractPositiveExamples.ql
index 7c36a7a72b5..95e9f682508 100644
--- a/java/ql/src/Telemetry/AutomodelExtractPositiveExamples.ql
+++ b/java/ql/src/Telemetry/AutomodelExtractPositiveExamples.ql
@@ -1,5 +1,5 @@
/**
- * Surfaces endpoints are sinks with high confidence, for use as positive examples in the prompt.
+ * Surfaces endpoints that are sinks with high confidence, for use as positive examples in the prompt.
*
* @name Positive examples (experimental)
* @kind problem
@@ -13,13 +13,6 @@ private import semmle.code.java.security.ExternalAPIs as ExternalAPIs
private import AutomodelEndpointCharacteristics
private import AutomodelEndpointTypes
-// private import experimental.adaptivethreatmodeling.ATMConfigs // To import the configurations of all supported Java queries
-/*
- * ****** WARNING: ******
- * Before calling this query, make sure there's no codex-generated data extension file in `java/ql/lib/ext`. Otherwise,
- * the ML-generated, noisy sinks will end up polluting the positive examples used in the prompt!
- */
-
from Endpoint sink, SinkType sinkType, string message
where
// Exclude endpoints that have contradictory endpoint characteristics, because we only want examples we're highly
diff --git a/java/ql/src/Telemetry/AutomodelSharedCharacteristics.qll b/java/ql/src/Telemetry/AutomodelSharedCharacteristics.qll
index 82c0ca5de54..58c46ceabd8 100644
--- a/java/ql/src/Telemetry/AutomodelSharedCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelSharedCharacteristics.qll
@@ -16,7 +16,7 @@ signature module CandidateSig {
class EndpointType;
- /** The string representing the file+range of the endpoint. */
+ /** Gets the string representing the file+range of the endpoint. */
string getLocationString(Endpoint e);
/**
@@ -64,7 +64,7 @@ signature module CandidateSig {
*
* This module is language-agnostic, although the `CandidateSig` module will be language-specific.
*
- * The language specific implementation can also further extend the behaviour of this module by adding additional
+ * The language specific implementation can also further extend the behavior of this module by adding additional
* implementations of endpoint characteristics exported by this module.
*/
module SharedCharacteristics {
From 08854136fe300df35c6dc69f3f22eb6927c1621b Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Thu, 27 Apr 2023 13:55:09 +0100
Subject: [PATCH 066/870] Swift: QLDoc consistency.
---
.../codeql-language-guides/analyzing-data-flow-in-swift.rst | 4 +++-
.../ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll | 4 +++-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/docs/codeql/codeql-language-guides/analyzing-data-flow-in-swift.rst b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-swift.rst
index 90576aac9f1..6ba39061232 100644
--- a/docs/codeql/codeql-language-guides/analyzing-data-flow-in-swift.rst
+++ b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-swift.rst
@@ -43,7 +43,9 @@ You can use the predicates ``exprNode`` and ``parameterNode`` to map from expres
.. code-block:: ql
- /** Gets a node corresponding to expression `e`. */
+ /**
+ * Gets a node corresponding to expression `e`.
+ */
ExprNode exprNode(DataFlowExpr e) { result.asExpr() = e }
/**
diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll
index 6d3db9c04fa..74779ea3b37 100644
--- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll
+++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll
@@ -129,7 +129,9 @@ class PostUpdateNode extends Node instanceof PostUpdateNodeImpl {
Node getPreUpdateNode() { result = super.getPreUpdateNode() }
}
-/** Gets a node corresponding to expression `e`. */
+/**
+ * Gets a node corresponding to expression `e`.
+ */
ExprNode exprNode(DataFlowExpr e) { result.asExpr() = e }
/**
From e2e8e5ddd35c274ac3fa5ae7db9ea01e71a63b09 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Thu, 27 Apr 2023 13:58:41 +0100
Subject: [PATCH 067/870] Swift: Add swift-further-reading.rst
---
docs/codeql/reusables/swift-further-reading.rst | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 docs/codeql/reusables/swift-further-reading.rst
diff --git a/docs/codeql/reusables/swift-further-reading.rst b/docs/codeql/reusables/swift-further-reading.rst
new file mode 100644
index 00000000000..306bc0fa0c0
--- /dev/null
+++ b/docs/codeql/reusables/swift-further-reading.rst
@@ -0,0 +1,4 @@
+- `CodeQL queries for Swift `__
+- `Example queries for Swift `__
+- `CodeQL library reference for Swift `__
+
From 74274e834ec584260737d3f5bc9d100931e9b9d9 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Thu, 27 Apr 2023 16:47:26 +0100
Subject: [PATCH 068/870] Swift: Add the four complete examples from the doc
pages to the examples directory.
---
swift/ql/examples/snippets/empty_if.ql | 15 ++++++++++
.../snippets/simple_constant_password.ql | 26 ++++++++++++++++
.../examples/snippets/simple_sql_injection.ql | 30 +++++++++++++++++++
.../simple_uncontrolled_string_format.ql | 20 +++++++++++++
4 files changed, 91 insertions(+)
create mode 100644 swift/ql/examples/snippets/empty_if.ql
create mode 100644 swift/ql/examples/snippets/simple_constant_password.ql
create mode 100644 swift/ql/examples/snippets/simple_sql_injection.ql
create mode 100644 swift/ql/examples/snippets/simple_uncontrolled_string_format.ql
diff --git a/swift/ql/examples/snippets/empty_if.ql b/swift/ql/examples/snippets/empty_if.ql
new file mode 100644
index 00000000000..6dd1c2ee1f0
--- /dev/null
+++ b/swift/ql/examples/snippets/empty_if.ql
@@ -0,0 +1,15 @@
+/**
+ * @name Empty 'if' statement
+ * @description Finds 'if' statements where the "then" branch is empty and no
+ * "else" branch exists.
+ * @id swift/examples/empty-if
+ * @tags example
+ */
+
+import swift
+
+from IfStmt ifStmt
+where
+ ifStmt.getThen().(BraceStmt).getNumberOfElements() = 0 and
+ not exists(ifStmt.getElse())
+select ifStmt, "This 'if' statement is redundant."
diff --git a/swift/ql/examples/snippets/simple_constant_password.ql b/swift/ql/examples/snippets/simple_constant_password.ql
new file mode 100644
index 00000000000..b101d700ec3
--- /dev/null
+++ b/swift/ql/examples/snippets/simple_constant_password.ql
@@ -0,0 +1,26 @@
+/**
+ * @name Constant password
+ * @description Finds places where a string literal is used in a function call
+ * argument named "password".
+ * @id swift/examples/simple-constant-password
+ * @tags example
+ */
+
+import swift
+import codeql.swift.dataflow.DataFlow
+import codeql.swift.dataflow.TaintTracking
+
+module ConstantPasswordConfig implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node node) { node.asExpr() instanceof StringLiteralExpr }
+
+ predicate isSink(DataFlow::Node node) {
+ // any argument called `password`
+ exists(CallExpr call | call.getArgumentWithLabel("password").getExpr() = node.asExpr())
+ }
+}
+
+module ConstantPasswordFlow = TaintTracking::Global;
+
+from DataFlow::Node sourceNode, DataFlow::Node sinkNode
+where ConstantPasswordFlow::flow(sourceNode, sinkNode)
+select sinkNode, "The value '" + sourceNode.toString() + "' is used as a constant password."
diff --git a/swift/ql/examples/snippets/simple_sql_injection.ql b/swift/ql/examples/snippets/simple_sql_injection.ql
new file mode 100644
index 00000000000..6aaa3a50701
--- /dev/null
+++ b/swift/ql/examples/snippets/simple_sql_injection.ql
@@ -0,0 +1,30 @@
+/**
+ * @name Database query built from user-controlled sources
+ * @description Finds places where a value from a remote or local user input
+ * is used as an argument to the SQLite ``Connection.execute(_:)``
+ * function.
+ * @id swift/examples/simple-sql-injection
+ * @tags example
+ */
+
+import swift
+import codeql.swift.dataflow.DataFlow
+import codeql.swift.dataflow.TaintTracking
+import codeql.swift.dataflow.FlowSources
+
+module SqlInjectionConfig implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node node) { node instanceof FlowSource }
+
+ predicate isSink(DataFlow::Node node) {
+ exists(CallExpr call |
+ call.getStaticTarget().(MethodDecl).hasQualifiedName("Connection", ["execute(_:)"]) and
+ call.getArgument(0).getExpr() = node.asExpr()
+ )
+ }
+}
+
+module SqlInjectionFlow = TaintTracking::Global;
+
+from DataFlow::Node sourceNode, DataFlow::Node sinkNode
+where SqlInjectionFlow::flow(sourceNode, sinkNode)
+select sinkNode, "This query depends on a $@.", sourceNode, "user-provided value"
diff --git a/swift/ql/examples/snippets/simple_uncontrolled_string_format.ql b/swift/ql/examples/snippets/simple_uncontrolled_string_format.ql
new file mode 100644
index 00000000000..ea4f34a1a9f
--- /dev/null
+++ b/swift/ql/examples/snippets/simple_uncontrolled_string_format.ql
@@ -0,0 +1,20 @@
+/**
+ * @name Uncontrolled format string
+ * @description Finds calls to ``String.init(format:_:)`` where the format
+ * string is not a hard-coded string literal.
+ * @id swift/examples/simple-uncontrolled-format-string
+ * @tags example
+ */
+
+import swift
+import codeql.swift.dataflow.DataFlow
+
+from CallExpr call, MethodDecl method, Expr sinkExpr
+where
+ call.getStaticTarget() = method and
+ method.hasQualifiedName("String", "init(format:_:)") and
+ sinkExpr = call.getArgument(0).getExpr() and
+ not exists(StringLiteralExpr sourceLiteral |
+ DataFlow::localFlow(DataFlow::exprNode(sourceLiteral), DataFlow::exprNode(sinkExpr))
+ )
+select call, "Format argument to " + method.getName() + " isn't hard-coded."
From 17077f3ec5989c6e7beba3a08612dc9ecc61b6d9 Mon Sep 17 00:00:00 2001
From: Owen Mansel-Chan
Date: Tue, 3 Jan 2023 15:47:34 +0000
Subject: [PATCH 069/870] Update OutParameter.getExitNode for implicit varargs
slices
---
.../lib/semmle/go/dataflow/FunctionInputsAndOutputs.qll | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/go/ql/lib/semmle/go/dataflow/FunctionInputsAndOutputs.qll b/go/ql/lib/semmle/go/dataflow/FunctionInputsAndOutputs.qll
index 2939f955a6f..fddf2c2ed23 100644
--- a/go/ql/lib/semmle/go/dataflow/FunctionInputsAndOutputs.qll
+++ b/go/ql/lib/semmle/go/dataflow/FunctionInputsAndOutputs.qll
@@ -282,7 +282,7 @@ private class OutReceiver extends FunctionOutput, TOutReceiver {
/**
* A parameter of a function, viewed as an output.
*
- * Note that slices passed to varargs parameters using `...` are not included, since in this
+ * Note that slices passed to variadic parameters using `...` are not included, since in this
* case it is ambiguous whether the output should be the slice itself or one of its elements.
*/
private class OutParameter extends FunctionOutput, TOutParameter {
@@ -300,9 +300,12 @@ private class OutParameter extends FunctionOutput, TOutParameter {
override DataFlow::Node getExitNode(DataFlow::CallNode c) {
exists(DataFlow::Node arg |
- arg = getArgument(c, index) and
- // exclude slices passed to varargs parameters using `...` calls
+ arg = c.getSyntacticArgument(index) and
+ // exclude slices followed by `...` passed to variadic parameters
not (c.hasEllipsis() and index = c.getNumArgument() - 1)
+ or
+ arg = c.(DataFlow::MethodCallNode).getReceiver() and
+ index = -1
|
result.(DataFlow::PostUpdateNode).getPreUpdateNode() = arg
)
From 2d3fed9c072d59b6a9a0a221c997a11069616ab8 Mon Sep 17 00:00:00 2001
From: Owen Mansel-Chan
Date: Wed, 21 Dec 2022 04:01:42 +0000
Subject: [PATCH 070/870] Accept intended test result changes
---
.../semmle/go/dataflow/ExternalFlowVarArgs/main.go | 4 ++--
.../go/dataflow/Nodes/CallNode_getArgument.expected | 8 +++-----
.../test/library-tests/semmle/go/dataflow/VarArgs/main.go | 4 ++--
3 files changed, 7 insertions(+), 9 deletions(-)
diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlowVarArgs/main.go b/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlowVarArgs/main.go
index 40c2d31149b..79043e3f7bb 100644
--- a/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlowVarArgs/main.go
+++ b/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlowVarArgs/main.go
@@ -24,7 +24,7 @@ func main() {
sink(test.FunctionWithParameter(sSlice[1])) // $ taintflow dataflow
sink(test.FunctionWithSliceParameter(sSlice)) // $ taintflow dataflow
sink(test.FunctionWithVarArgsParameter(sSlice...)) // $ taintflow dataflow
- sink(test.FunctionWithVarArgsParameter(s0, s1)) // $ MISSING: taintflow dataflow
+ sink(test.FunctionWithVarArgsParameter(s0, s1)) // $ taintflow dataflow
sliceOfStructs := []test.A{{Field: source()}}
sink(sliceOfStructs[0].Field) // $ taintflow dataflow
@@ -34,5 +34,5 @@ func main() {
aSlice := []test.A{a0, a1}
sink(test.FunctionWithSliceOfStructsParameter(aSlice)) // $ taintflow dataflow
sink(test.FunctionWithVarArgsOfStructsParameter(aSlice...)) // $ taintflow dataflow
- sink(test.FunctionWithVarArgsOfStructsParameter(a0, a1)) // $ MISSING: taintflow dataflow
+ sink(test.FunctionWithVarArgsOfStructsParameter(a0, a1)) // $ taintflow dataflow
}
diff --git a/go/ql/test/library-tests/semmle/go/dataflow/Nodes/CallNode_getArgument.expected b/go/ql/test/library-tests/semmle/go/dataflow/Nodes/CallNode_getArgument.expected
index fc391bafcff..2cc2fe8d3ee 100644
--- a/go/ql/test/library-tests/semmle/go/dataflow/Nodes/CallNode_getArgument.expected
+++ b/go/ql/test/library-tests/semmle/go/dataflow/Nodes/CallNode_getArgument.expected
@@ -1,6 +1,4 @@
-| main.go:7:2:7:25 | call to Println | 0 | main.go:7:14:7:24 | ...+... |
-| main.go:10:2:10:19 | call to Println | 0 | main.go:10:14:10:18 | ...+... |
+| main.go:7:2:7:25 | call to Println | 0 | main.go:7:2:7:25 | []type{args} |
+| main.go:10:2:10:19 | call to Println | 0 | main.go:10:2:10:19 | []type{args} |
| main.go:14:8:14:24 | call to make | 0 | main.go:14:23:14:23 | 1 |
-| main.go:16:2:16:26 | call to Println | 0 | main.go:16:14:16:15 | ss |
-| main.go:16:2:16:26 | call to Println | 1 | main.go:16:18:16:18 | 0 |
-| main.go:16:2:16:26 | call to Println | 2 | main.go:16:21:16:25 | index expression |
+| main.go:16:2:16:26 | call to Println | 0 | main.go:16:2:16:26 | []type{args} |
diff --git a/go/ql/test/library-tests/semmle/go/dataflow/VarArgs/main.go b/go/ql/test/library-tests/semmle/go/dataflow/VarArgs/main.go
index a2b60745eb5..3c3d80f7342 100644
--- a/go/ql/test/library-tests/semmle/go/dataflow/VarArgs/main.go
+++ b/go/ql/test/library-tests/semmle/go/dataflow/VarArgs/main.go
@@ -36,7 +36,7 @@ func main() {
sSlice := []string{s0, s1}
sink(functionWithSliceParameter(sSlice)) // $ taintflow dataflow
sink(functionWithVarArgsParameter(sSlice...)) // $ taintflow dataflow
- sink(functionWithVarArgsParameter(s0, s1)) // $ MISSING: taintflow dataflow
+ sink(functionWithVarArgsParameter(s0, s1)) // $ taintflow dataflow
sliceOfStructs := []A{{f: source()}}
sink(sliceOfStructs[0].f) // $ taintflow dataflow
@@ -46,5 +46,5 @@ func main() {
aSlice := []A{a0, a1}
sink(functionWithSliceOfStructsParameter(aSlice)) // $ taintflow dataflow
sink(functionWithVarArgsOfStructsParameter(aSlice...)) // $ taintflow dataflow
- sink(functionWithVarArgsOfStructsParameter(a0, a1)) // $ MISSING: taintflow dataflow
+ sink(functionWithVarArgsOfStructsParameter(a0, a1)) // $ taintflow dataflow
}
From bc0f9030e3124cb12e2dddeb8c6ea614150ee8cc Mon Sep 17 00:00:00 2001
From: Owen Mansel-Chan
Date: Thu, 13 Apr 2023 07:22:17 +0100
Subject: [PATCH 071/870] use CallNode.getSyntacticArgument
---
go/ql/lib/semmle/go/StringOps.qll | 2 +-
go/ql/lib/semmle/go/frameworks/Beego.qll | 6 +++---
go/ql/lib/semmle/go/frameworks/BeegoOrm.qll | 2 +-
go/ql/lib/semmle/go/frameworks/ElazarlGoproxy.qll | 4 ++--
go/ql/lib/semmle/go/frameworks/Email.qll | 4 ++--
go/ql/lib/semmle/go/frameworks/Glog.qll | 2 +-
go/ql/lib/semmle/go/frameworks/Logrus.qll | 2 +-
go/ql/lib/semmle/go/frameworks/Revel.qll | 4 ++--
go/ql/lib/semmle/go/frameworks/SQL.qll | 4 ++--
go/ql/lib/semmle/go/frameworks/Spew.qll | 2 +-
.../semmle/go/frameworks/SystemCommandExecutors.qll | 13 +++++++------
go/ql/lib/semmle/go/frameworks/Zap.qll | 2 +-
go/ql/lib/semmle/go/frameworks/stdlib/Fmt.qll | 2 +-
go/ql/lib/semmle/go/frameworks/stdlib/Log.qll | 2 +-
go/ql/lib/semmle/go/security/CommandInjection.qll | 6 +++---
go/ql/lib/semmle/go/security/Xss.qll | 4 ++--
go/ql/src/Security/CWE-352/ConstantOauth2State.ql | 2 +-
go/ql/src/Security/CWE-601/BadRedirectCheck.ql | 2 +-
go/ql/src/experimental/frameworks/CleverGo.qll | 2 +-
go/ql/src/experimental/frameworks/Fiber.qll | 6 +++---
.../semmle/go/frameworks/SQL/Gorm/gorm.ql | 2 +-
21 files changed, 38 insertions(+), 37 deletions(-)
diff --git a/go/ql/lib/semmle/go/StringOps.qll b/go/ql/lib/semmle/go/StringOps.qll
index db86f3864f7..4a01d49c1c3 100644
--- a/go/ql/lib/semmle/go/StringOps.qll
+++ b/go/ql/lib/semmle/go/StringOps.qll
@@ -219,7 +219,7 @@ module StringOps {
* replaced.
*/
DataFlow::Node getAReplacedArgument() {
- exists(int n | n % 2 = 0 and result = this.getArgument(n))
+ exists(int n | n % 2 = 0 and result = this.getSyntacticArgument(n))
}
}
diff --git a/go/ql/lib/semmle/go/frameworks/Beego.qll b/go/ql/lib/semmle/go/frameworks/Beego.qll
index edd622ab75a..0446cb2bbbf 100644
--- a/go/ql/lib/semmle/go/frameworks/Beego.qll
+++ b/go/ql/lib/semmle/go/frameworks/Beego.qll
@@ -253,7 +253,7 @@ module Beego {
this.getTarget().hasQualifiedName([packagePath(), logsPackagePath()], getALogFunctionName())
}
- override DataFlow::Node getAMessageComponent() { result = this.getAnArgument() }
+ override DataFlow::Node getAMessageComponent() { result = this.getASyntacticArgument() }
}
private class BeegoLoggerMethods extends LoggerCall::Range, DataFlow::MethodCallNode {
@@ -261,13 +261,13 @@ module Beego {
this.getTarget().hasQualifiedName(logsPackagePath(), "BeeLogger", getALogFunctionName())
}
- override DataFlow::Node getAMessageComponent() { result = this.getAnArgument() }
+ override DataFlow::Node getAMessageComponent() { result = this.getASyntacticArgument() }
}
private class UtilLoggers extends LoggerCall::Range, DataFlow::CallNode {
UtilLoggers() { this.getTarget().hasQualifiedName(utilsPackagePath(), "Display") }
- override DataFlow::Node getAMessageComponent() { result = this.getAnArgument() }
+ override DataFlow::Node getAMessageComponent() { result = this.getASyntacticArgument() }
}
private class HtmlQuoteSanitizer extends SharedXss::Sanitizer {
diff --git a/go/ql/lib/semmle/go/frameworks/BeegoOrm.qll b/go/ql/lib/semmle/go/frameworks/BeegoOrm.qll
index f83556c307f..ca5f7718082 100644
--- a/go/ql/lib/semmle/go/frameworks/BeegoOrm.qll
+++ b/go/ql/lib/semmle/go/frameworks/BeegoOrm.qll
@@ -33,7 +33,7 @@ module BeegoOrm {
// Note this class doesn't do any escaping, unlike the true ORM part of the package
QueryBuilderSink() {
exists(Method impl | impl.implements(packagePath(), "QueryBuilder", _) |
- this = impl.getACall().getAnArgument()
+ this = impl.getACall().getASyntacticArgument()
) and
this.getType().getUnderlyingType() instanceof StringType
}
diff --git a/go/ql/lib/semmle/go/frameworks/ElazarlGoproxy.qll b/go/ql/lib/semmle/go/frameworks/ElazarlGoproxy.qll
index fbbbccb4e05..30e421eb60a 100644
--- a/go/ql/lib/semmle/go/frameworks/ElazarlGoproxy.qll
+++ b/go/ql/lib/semmle/go/frameworks/ElazarlGoproxy.qll
@@ -90,7 +90,7 @@ module ElazarlGoproxy {
onreqcall.getTarget().hasQualifiedName(packagePath(), "ProxyHttpServer", "OnRequest")
|
handlerReg.getReceiver() = onreqcall.getASuccessor*() and
- check = onreqcall.getArgument(0)
+ check = onreqcall.getSyntacticArgument(0)
)
}
}
@@ -119,6 +119,6 @@ module ElazarlGoproxy {
private class ProxyLog extends LoggerCall::Range, DataFlow::MethodCallNode {
ProxyLog() { this.getTarget() instanceof ProxyLogFunction }
- override DataFlow::Node getAMessageComponent() { result = this.getAnArgument() }
+ override DataFlow::Node getAMessageComponent() { result = this.getASyntacticArgument() }
}
}
diff --git a/go/ql/lib/semmle/go/frameworks/Email.qll b/go/ql/lib/semmle/go/frameworks/Email.qll
index 3580aa8d7ae..a1d43d3c397 100644
--- a/go/ql/lib/semmle/go/frameworks/Email.qll
+++ b/go/ql/lib/semmle/go/frameworks/Email.qll
@@ -56,13 +56,13 @@ module EmailData {
// func NewV3MailInit(from *Email, subject string, to *Email, content ...*Content) *SGMailV3
exists(Function newv3MailInit |
newv3MailInit.hasQualifiedName(sendgridMail(), "NewV3MailInit") and
- this = newv3MailInit.getACall().getArgument(any(int i | i = 1 or i >= 3))
+ this = newv3MailInit.getACall().getSyntacticArgument(any(int i | i = 1 or i >= 3))
)
or
// func (s *SGMailV3) AddContent(c ...*Content) *SGMailV3
exists(Method addContent |
addContent.hasQualifiedName(sendgridMail(), "SGMailV3", "AddContent") and
- this = addContent.getACall().getAnArgument()
+ this = addContent.getACall().getASyntacticArgument()
)
}
}
diff --git a/go/ql/lib/semmle/go/frameworks/Glog.qll b/go/ql/lib/semmle/go/frameworks/Glog.qll
index 48558a73f7e..bd874973c52 100644
--- a/go/ql/lib/semmle/go/frameworks/Glog.qll
+++ b/go/ql/lib/semmle/go/frameworks/Glog.qll
@@ -49,7 +49,7 @@ module Glog {
GlogCall() { this = callee.getACall() }
override DataFlow::Node getAMessageComponent() {
- result = this.getArgument(any(int i | i >= callee.getFirstPrintedArg()))
+ result = this.getSyntacticArgument(any(int i | i >= callee.getFirstPrintedArg()))
}
}
}
diff --git a/go/ql/lib/semmle/go/frameworks/Logrus.qll b/go/ql/lib/semmle/go/frameworks/Logrus.qll
index 40cdfe393ef..9d48f1fa7ff 100644
--- a/go/ql/lib/semmle/go/frameworks/Logrus.qll
+++ b/go/ql/lib/semmle/go/frameworks/Logrus.qll
@@ -31,7 +31,7 @@ module Logrus {
private class LogCall extends LoggerCall::Range, DataFlow::CallNode {
LogCall() { this = any(LogFunction f).getACall() }
- override DataFlow::Node getAMessageComponent() { result = this.getAnArgument() }
+ override DataFlow::Node getAMessageComponent() { result = this.getASyntacticArgument() }
}
private class StringFormatters extends StringOps::Formatting::Range instanceof LogFunction {
diff --git a/go/ql/lib/semmle/go/frameworks/Revel.qll b/go/ql/lib/semmle/go/frameworks/Revel.qll
index ea873a3972a..622edf108f0 100644
--- a/go/ql/lib/semmle/go/frameworks/Revel.qll
+++ b/go/ql/lib/semmle/go/frameworks/Revel.qll
@@ -124,7 +124,7 @@ module Revel {
or
methodName = "RenderText" and
contentType = "text/plain" and
- this = methodCall.getAnArgument()
+ this = methodCall.getSyntacticArgument(_)
)
}
@@ -201,7 +201,7 @@ module Revel {
)
or
// a revel controller.Render(arg) will set controller.ViewArgs["arg"] = arg
- exists(Variable arg | arg.getARead() = render.(ControllerRender).getAnArgument() |
+ exists(Variable arg | arg.getARead() = render.(ControllerRender).getASyntacticArgument() |
var.getBaseVariable() = arg and
var.getQualifiedName() = read.getFieldName()
)
diff --git a/go/ql/lib/semmle/go/frameworks/SQL.qll b/go/ql/lib/semmle/go/frameworks/SQL.qll
index 9e9e48550fc..185f0b3f2bf 100644
--- a/go/ql/lib/semmle/go/frameworks/SQL.qll
+++ b/go/ql/lib/semmle/go/frameworks/SQL.qll
@@ -225,7 +225,7 @@ module SQL {
GormSink() {
exists(Method meth, string package, string name |
meth.hasQualifiedName(package, "DB", name) and
- this = meth.getACall().getArgument(0) and
+ this = meth.getACall().getSyntacticArgument(0) and
package = Gorm::packagePath() and
name in [
"Where", "Raw", "Order", "Not", "Or", "Select", "Table", "Group", "Having", "Joins",
@@ -272,7 +272,7 @@ module Xorm {
XormSink() {
exists(Method meth, string type, string name, int n |
meth.hasQualifiedName(Xorm::packagePath(), type, name) and
- this = meth.getACall().getArgument(n) and
+ this = meth.getACall().getSyntacticArgument(n) and
type = ["Engine", "Session"]
|
name =
diff --git a/go/ql/lib/semmle/go/frameworks/Spew.qll b/go/ql/lib/semmle/go/frameworks/Spew.qll
index 7c4133dfd04..68ff4501ed9 100644
--- a/go/ql/lib/semmle/go/frameworks/Spew.qll
+++ b/go/ql/lib/semmle/go/frameworks/Spew.qll
@@ -41,7 +41,7 @@ module Spew {
SpewCall() { this = target.getACall() }
override DataFlow::Node getAMessageComponent() {
- result = this.getArgument(any(int i | i >= target.getFirstPrintedArg()))
+ result = this.getSyntacticArgument(any(int i | i >= target.getFirstPrintedArg()))
}
}
diff --git a/go/ql/lib/semmle/go/frameworks/SystemCommandExecutors.qll b/go/ql/lib/semmle/go/frameworks/SystemCommandExecutors.qll
index 1e4b7637581..3728a6bee3c 100644
--- a/go/ql/lib/semmle/go/frameworks/SystemCommandExecutors.qll
+++ b/go/ql/lib/semmle/go/frameworks/SystemCommandExecutors.qll
@@ -14,11 +14,12 @@ private class ShellOrSudoExecution extends SystemCommandExecution::Range, DataFl
ShellOrSudoExecution() {
this instanceof SystemCommandExecution and
- shellCommand = this.getAnArgument().getAPredecessor*() and
- not hasSafeSubcommand(shellCommand.getStringValue(), this.getAnArgument().getStringValue())
+ shellCommand = this.getASyntacticArgument().getAPredecessor*() and
+ not hasSafeSubcommand(shellCommand.getStringValue(),
+ this.getASyntacticArgument().getStringValue())
}
- override DataFlow::Node getCommandName() { result = this.getAnArgument() }
+ override DataFlow::Node getCommandName() { result = this.getASyntacticArgument() }
override predicate doubleDashIsSanitizing() {
shellCommand.getStringValue().matches("%" + ["git", "rsync"])
@@ -49,7 +50,7 @@ private class SystemCommandExecutors extends SystemCommandExecution::Range, Data
)
}
- override DataFlow::Node getCommandName() { result = this.getArgument(cmdArg) }
+ override DataFlow::Node getCommandName() { result = this.getSyntacticArgument(cmdArg) }
}
/**
@@ -76,7 +77,7 @@ private class GoShCommandExecution extends SystemCommandExecution::Range, DataFl
)
}
- override DataFlow::Node getCommandName() { result = this.getArgument(0) }
+ override DataFlow::Node getCommandName() { result = this.getSyntacticArgument(0) }
}
/**
@@ -102,7 +103,7 @@ module CryptoSsh {
)
}
- override DataFlow::Node getCommandName() { result = this.getArgument(0) }
+ override DataFlow::Node getCommandName() { result = this.getSyntacticArgument(0) }
}
}
diff --git a/go/ql/lib/semmle/go/frameworks/Zap.qll b/go/ql/lib/semmle/go/frameworks/Zap.qll
index 7041c45a3c6..84feb3fb006 100644
--- a/go/ql/lib/semmle/go/frameworks/Zap.qll
+++ b/go/ql/lib/semmle/go/frameworks/Zap.qll
@@ -45,7 +45,7 @@ module Zap {
private class ZapCall extends LoggerCall::Range, DataFlow::MethodCallNode {
ZapCall() { this = any(ZapFunction f).getACall() }
- override DataFlow::Node getAMessageComponent() { result = this.getAnArgument() }
+ override DataFlow::Node getAMessageComponent() { result = this.getASyntacticArgument() }
}
// These are expressed using TaintTracking::FunctionModel because varargs functions don't work with Models-as-Data sumamries yet.
diff --git a/go/ql/lib/semmle/go/frameworks/stdlib/Fmt.qll b/go/ql/lib/semmle/go/frameworks/stdlib/Fmt.qll
index dd65aee23a4..735c5657f78 100644
--- a/go/ql/lib/semmle/go/frameworks/stdlib/Fmt.qll
+++ b/go/ql/lib/semmle/go/frameworks/stdlib/Fmt.qll
@@ -30,7 +30,7 @@ module Fmt {
private class PrintCall extends LoggerCall::Range, DataFlow::CallNode {
PrintCall() { this.getTarget() instanceof Printer }
- override DataFlow::Node getAMessageComponent() { result = this.getAnArgument() }
+ override DataFlow::Node getAMessageComponent() { result = this.getASyntacticArgument() }
}
/** The `Fprint` function or one of its variants. */
diff --git a/go/ql/lib/semmle/go/frameworks/stdlib/Log.qll b/go/ql/lib/semmle/go/frameworks/stdlib/Log.qll
index f465009a255..b821058bda0 100644
--- a/go/ql/lib/semmle/go/frameworks/stdlib/Log.qll
+++ b/go/ql/lib/semmle/go/frameworks/stdlib/Log.qll
@@ -27,7 +27,7 @@ module Log {
private class LogCall extends LoggerCall::Range, DataFlow::CallNode {
LogCall() { this = any(LogFunction f).getACall() }
- override DataFlow::Node getAMessageComponent() { result = this.getAnArgument() }
+ override DataFlow::Node getAMessageComponent() { result = this.getASyntacticArgument() }
}
/** A fatal log function, which calls `os.Exit`. */
diff --git a/go/ql/lib/semmle/go/security/CommandInjection.qll b/go/ql/lib/semmle/go/security/CommandInjection.qll
index a3bc2747e7a..2b68b5563c6 100644
--- a/go/ql/lib/semmle/go/security/CommandInjection.qll
+++ b/go/ql/lib/semmle/go/security/CommandInjection.qll
@@ -47,7 +47,7 @@ module CommandInjection {
exists(DataFlow::CallNode c |
this = c and
(c = Builtin::append().getACall() or c = any(SystemCommandExecution sce)) and
- c.getArgument(doubleDashIndex).getStringValue() = "--"
+ c.getSyntacticArgument(doubleDashIndex).getStringValue() = "--"
)
or
// array/slice literal containing a "--"
@@ -63,7 +63,7 @@ module CommandInjection {
alreadyHasDoubleDash.getType() instanceof SliceType
) and
this = userCall and
- DataFlow::localFlow(alreadyHasDoubleDash, userCall.getArgument(doubleDashIndex))
+ DataFlow::localFlow(alreadyHasDoubleDash, userCall.getSyntacticArgument(doubleDashIndex))
)
}
@@ -71,7 +71,7 @@ module CommandInjection {
exists(int sanitizedIndex |
sanitizedIndex > doubleDashIndex and
(
- result = this.(DataFlow::CallNode).getArgument(sanitizedIndex) or
+ result = this.(DataFlow::CallNode).getSyntacticArgument(sanitizedIndex) or
result = DataFlow::exprNode(this.asExpr().(ArrayOrSliceLit).getElement(sanitizedIndex))
)
)
diff --git a/go/ql/lib/semmle/go/security/Xss.qll b/go/ql/lib/semmle/go/security/Xss.qll
index 98b6da02fe8..245c320fff8 100644
--- a/go/ql/lib/semmle/go/security/Xss.qll
+++ b/go/ql/lib/semmle/go/security/Xss.qll
@@ -73,12 +73,12 @@ module SharedXss {
exists(body.getAContentTypeNode())
or
exists(DataFlow::CallNode call | call.getTarget().hasQualifiedName("fmt", "Fprintf") |
- body = call.getAnArgument() and
+ body = call.getASyntacticArgument() and
// checks that the format value does not start with (ignoring whitespace as defined by
// https://mimesniff.spec.whatwg.org/#whitespace-byte):
// - '<', which could lead to an HTML content type being detected, or
// - '%', which could be a format string.
- call.getArgument(1).getStringValue().regexpMatch("(?s)[\\t\\n\\x0c\\r ]*+[^<%].*")
+ call.getSyntacticArgument(1).getStringValue().regexpMatch("(?s)[\\t\\n\\x0c\\r ]*+[^<%].*")
)
or
exists(DataFlow::Node pred | body = pred.getASuccessor*() |
diff --git a/go/ql/src/Security/CWE-352/ConstantOauth2State.ql b/go/ql/src/Security/CWE-352/ConstantOauth2State.ql
index 9a5cb10b84f..abe982f7fe5 100644
--- a/go/ql/src/Security/CWE-352/ConstantOauth2State.ql
+++ b/go/ql/src/Security/CWE-352/ConstantOauth2State.ql
@@ -109,7 +109,7 @@ class PrivateUrlFlowsToAuthCodeUrlCall extends DataFlow::Configuration {
exists(DataFlow::CallNode cn |
cn.getACalleeIncludingExternals().asFunction() instanceof Fmt::AppenderOrSprinter
|
- pred = cn.getAnArgument() and succ = cn.getResult()
+ pred = cn.getASyntacticArgument() and succ = cn.getResult()
)
}
diff --git a/go/ql/src/Security/CWE-601/BadRedirectCheck.ql b/go/ql/src/Security/CWE-601/BadRedirectCheck.ql
index 9beb2fe160b..a04f197abab 100644
--- a/go/ql/src/Security/CWE-601/BadRedirectCheck.ql
+++ b/go/ql/src/Security/CWE-601/BadRedirectCheck.ql
@@ -121,7 +121,7 @@ class Configuration extends TaintTracking::Configuration {
)
or
exists(DataFlow::CallNode call, int i | call.getTarget().hasQualifiedName("path", "Join") |
- i > 0 and node = call.getArgument(i)
+ i > 0 and node = call.getSyntacticArgument(i)
)
}
diff --git a/go/ql/src/experimental/frameworks/CleverGo.qll b/go/ql/src/experimental/frameworks/CleverGo.qll
index 4b39ea005fd..2433ba4997a 100644
--- a/go/ql/src/experimental/frameworks/CleverGo.qll
+++ b/go/ql/src/experimental/frameworks/CleverGo.qll
@@ -278,7 +278,7 @@ private module CleverGo {
or
// signature: func (*Context) Stringf(code int, format string, a ...interface{}) error
methodName = "Stringf" and
- bodyNode = bodySetterCall.getArgument([1, any(int i | i >= 2)]) and
+ bodyNode = bodySetterCall.getSyntacticArgument([1, any(int i | i >= 2)]) and
contentTypeString = "text/plain"
or
// signature: func (*Context) XML(code int, data interface{}) error
diff --git a/go/ql/src/experimental/frameworks/Fiber.qll b/go/ql/src/experimental/frameworks/Fiber.qll
index cfc65afdc1c..ed2182a5ce8 100644
--- a/go/ql/src/experimental/frameworks/Fiber.qll
+++ b/go/ql/src/experimental/frameworks/Fiber.qll
@@ -183,7 +183,7 @@ private module Fiber {
// signature: func (*Ctx) Append(field string, values ...string)
methodName = "Append" and
headerNameNode = headerSetterCall.getArgument(0) and
- headerValueNode = headerSetterCall.getArgument(any(int i | i >= 1))
+ headerValueNode = headerSetterCall.getSyntacticArgument(any(int i | i >= 1))
or
// signature: func (*Ctx) Set(key string, val string)
methodName = "Set" and
@@ -270,7 +270,7 @@ private module Fiber {
or
// signature: func (*Ctx) Send(bodies ...interface{})
methodName = "Send" and
- bodyNode = bodySetterCall.getArgument(_)
+ bodyNode = bodySetterCall.getSyntacticArgument(_)
or
// signature: func (*Ctx) SendBytes(body []byte)
methodName = "SendBytes" and
@@ -286,7 +286,7 @@ private module Fiber {
or
// signature: func (*Ctx) Write(bodies ...interface{})
methodName = "Write" and
- bodyNode = bodySetterCall.getArgument(_)
+ bodyNode = bodySetterCall.getSyntacticArgument(_)
)
)
)
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.ql b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.ql
index 47a9e0bbc8d..e08b506deaf 100644
--- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.ql
+++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.ql
@@ -1,5 +1,5 @@
import go
from SQL::QueryString qs, Method meth, string a, string b, string c
-where meth.hasQualifiedName(a, b, c) and qs = meth.getACall().getArgument(0)
+where meth.hasQualifiedName(a, b, c) and qs = meth.getACall().getSyntacticArgument(0)
select qs, a, b, c
From f2368a944182ed386ad077109b58413ad6004c9a Mon Sep 17 00:00:00 2001
From: Owen Mansel-Chan
Date: Thu, 13 Apr 2023 07:22:47 +0100
Subject: [PATCH 072/870] Do not use variadic sink fn in tests
---
.../frameworks/CleverGo/UntrustedSources.go | 12 ++++--------
go/ql/test/experimental/frameworks/CleverGo/stubs.go | 2 +-
.../frameworks/Fiber/UntrustedFlowSources.go | 12 +++++-------
go/ql/test/experimental/frameworks/Fiber/stubs.go | 2 +-
4 files changed, 11 insertions(+), 17 deletions(-)
diff --git a/go/ql/test/experimental/frameworks/CleverGo/UntrustedSources.go b/go/ql/test/experimental/frameworks/CleverGo/UntrustedSources.go
index 4df9ddabd89..53451c2a315 100644
--- a/go/ql/test/experimental/frameworks/CleverGo/UntrustedSources.go
+++ b/go/ql/test/experimental/frameworks/CleverGo/UntrustedSources.go
@@ -14,10 +14,8 @@ func UntrustedSources_ClevergoTechClevergoV052() {
{
var receiverContext656 clevergo.Context
resultUsername414, resultPassword518, _ := receiverContext656.BasicAuth()
- sink(
- resultUsername414, // $ untrustedFlowSource
- resultPassword518, // $ untrustedFlowSource
- )
+ sink(resultUsername414) // $ untrustedFlowSource
+ sink(resultPassword518) // $ untrustedFlowSource
}
// func (*Context).Decode(v interface{}) (err error)
{
@@ -102,10 +100,8 @@ func UntrustedSources_ClevergoTechClevergoV052() {
// Untrusted flow sources from clevergo.tech/clevergo.Param struct fields.
{
structParam246 := new(clevergo.Param)
- sink(
- structParam246.Key, // $ untrustedFlowSource
- structParam246.Value, // $ untrustedFlowSource
- )
+ sink(structParam246.Key) // $ untrustedFlowSource
+ sink(structParam246.Value) // $ untrustedFlowSource
}
}
// Untrusted flow sources from types.
diff --git a/go/ql/test/experimental/frameworks/CleverGo/stubs.go b/go/ql/test/experimental/frameworks/CleverGo/stubs.go
index d435852dedb..27806846860 100644
--- a/go/ql/test/experimental/frameworks/CleverGo/stubs.go
+++ b/go/ql/test/experimental/frameworks/CleverGo/stubs.go
@@ -7,6 +7,6 @@ func source() interface{} {
return nil
}
-func sink(v ...interface{}) {}
+func sink(v interface{}) {}
func link(from interface{}, into interface{}) {}
diff --git a/go/ql/test/experimental/frameworks/Fiber/UntrustedFlowSources.go b/go/ql/test/experimental/frameworks/Fiber/UntrustedFlowSources.go
index 3e09a633694..f3178dbaca4 100644
--- a/go/ql/test/experimental/frameworks/Fiber/UntrustedFlowSources.go
+++ b/go/ql/test/experimental/frameworks/Fiber/UntrustedFlowSources.go
@@ -121,13 +121,11 @@ func UntrustedFlowSources_GithubComGofiberFiberV1146() {
// Untrusted flow sources from github.com/gofiber/fiber.Cookie struct fields.
{
structCookie322 := new(fiber.Cookie)
- sink(
- structCookie322.Domain, // $ untrustedFlowSource
- structCookie322.Name, // $ untrustedFlowSource
- structCookie322.Path, // $ untrustedFlowSource
- structCookie322.SameSite, // $ untrustedFlowSource
- structCookie322.Value, // $ untrustedFlowSource
- )
+ sink(structCookie322.Domain) // $ untrustedFlowSource
+ sink(structCookie322.Name) // $ untrustedFlowSource
+ sink(structCookie322.Path) // $ untrustedFlowSource
+ sink(structCookie322.SameSite) // $ untrustedFlowSource
+ sink(structCookie322.Value) // $ untrustedFlowSource
}
// Untrusted flow sources from github.com/gofiber/fiber.Error struct fields.
{
diff --git a/go/ql/test/experimental/frameworks/Fiber/stubs.go b/go/ql/test/experimental/frameworks/Fiber/stubs.go
index d435852dedb..27806846860 100644
--- a/go/ql/test/experimental/frameworks/Fiber/stubs.go
+++ b/go/ql/test/experimental/frameworks/Fiber/stubs.go
@@ -7,6 +7,6 @@ func source() interface{} {
return nil
}
-func sink(v ...interface{}) {}
+func sink(v interface{}) {}
func link(from interface{}, into interface{}) {}
From 3f095db853d75a3d74eb826660b3771500c18cf1 Mon Sep 17 00:00:00 2001
From: Owen Mansel-Chan
Date: Thu, 13 Apr 2023 07:41:32 +0100
Subject: [PATCH 073/870] Formatted parameters always a variadic parameter
---
go/ql/lib/semmle/go/StringOps.qll | 7 +------
go/ql/lib/semmle/go/frameworks/ElazarlGoproxy.qll | 2 --
go/ql/lib/semmle/go/frameworks/Glog.qll | 2 --
go/ql/lib/semmle/go/frameworks/Logrus.qll | 2 --
go/ql/lib/semmle/go/frameworks/Spew.qll | 2 --
go/ql/lib/semmle/go/frameworks/Zap.qll | 2 --
go/ql/lib/semmle/go/frameworks/stdlib/Fmt.qll | 2 --
go/ql/lib/semmle/go/frameworks/stdlib/Log.qll | 2 --
8 files changed, 1 insertion(+), 20 deletions(-)
diff --git a/go/ql/lib/semmle/go/StringOps.qll b/go/ql/lib/semmle/go/StringOps.qll
index 4a01d49c1c3..20e4d387af8 100644
--- a/go/ql/lib/semmle/go/StringOps.qll
+++ b/go/ql/lib/semmle/go/StringOps.qll
@@ -304,11 +304,6 @@ module StringOps {
* Gets the parameter index of the format string.
*/
abstract int getFormatStringIndex();
-
- /**
- * Gets the parameter index of the first parameter to be formatted.
- */
- abstract int getFirstFormattedParameterIndex();
}
/**
@@ -336,7 +331,7 @@ module StringOps {
formatDirective = this.getComponent(n) and
formatDirective.charAt(0) = "%" and
formatDirective.charAt(1) != "%" and
- result = this.getArgument((n / 2) + f.getFirstFormattedParameterIndex())
+ result = this.getImplicitVarargsArgument((n / 2))
}
}
}
diff --git a/go/ql/lib/semmle/go/frameworks/ElazarlGoproxy.qll b/go/ql/lib/semmle/go/frameworks/ElazarlGoproxy.qll
index 30e421eb60a..0cc5fe9505a 100644
--- a/go/ql/lib/semmle/go/frameworks/ElazarlGoproxy.qll
+++ b/go/ql/lib/semmle/go/frameworks/ElazarlGoproxy.qll
@@ -112,8 +112,6 @@ module ElazarlGoproxy {
ProxyLogFunction() { this.hasQualifiedName(packagePath(), "ProxyCtx", ["Logf", "Warnf"]) }
override int getFormatStringIndex() { result = 0 }
-
- override int getFirstFormattedParameterIndex() { result = 1 }
}
private class ProxyLog extends LoggerCall::Range, DataFlow::MethodCallNode {
diff --git a/go/ql/lib/semmle/go/frameworks/Glog.qll b/go/ql/lib/semmle/go/frameworks/Glog.qll
index bd874973c52..f9f5c9e3f11 100644
--- a/go/ql/lib/semmle/go/frameworks/Glog.qll
+++ b/go/ql/lib/semmle/go/frameworks/Glog.qll
@@ -39,8 +39,6 @@ module Glog {
StringFormatter() { this.getName().matches("%f") }
override int getFormatStringIndex() { result = super.getFirstPrintedArg() }
-
- override int getFirstFormattedParameterIndex() { result = super.getFirstPrintedArg() + 1 }
}
private class GlogCall extends LoggerCall::Range, DataFlow::CallNode {
diff --git a/go/ql/lib/semmle/go/frameworks/Logrus.qll b/go/ql/lib/semmle/go/frameworks/Logrus.qll
index 9d48f1fa7ff..9b93049acfb 100644
--- a/go/ql/lib/semmle/go/frameworks/Logrus.qll
+++ b/go/ql/lib/semmle/go/frameworks/Logrus.qll
@@ -43,7 +43,5 @@ module Logrus {
}
override int getFormatStringIndex() { result = argOffset }
-
- override int getFirstFormattedParameterIndex() { result = argOffset + 1 }
}
}
diff --git a/go/ql/lib/semmle/go/frameworks/Spew.qll b/go/ql/lib/semmle/go/frameworks/Spew.qll
index 68ff4501ed9..b12bd0fed81 100644
--- a/go/ql/lib/semmle/go/frameworks/Spew.qll
+++ b/go/ql/lib/semmle/go/frameworks/Spew.qll
@@ -31,8 +31,6 @@ module Spew {
StringFormatter() { this.getName().matches("%f") }
override int getFormatStringIndex() { result = super.getFirstPrintedArg() }
-
- override int getFirstFormattedParameterIndex() { result = super.getFirstPrintedArg() + 1 }
}
private class SpewCall extends LoggerCall::Range, DataFlow::CallNode {
diff --git a/go/ql/lib/semmle/go/frameworks/Zap.qll b/go/ql/lib/semmle/go/frameworks/Zap.qll
index 84feb3fb006..359f9aba410 100644
--- a/go/ql/lib/semmle/go/frameworks/Zap.qll
+++ b/go/ql/lib/semmle/go/frameworks/Zap.qll
@@ -32,8 +32,6 @@ module Zap {
ZapFormatter() { this.getName().matches("%f") }
override int getFormatStringIndex() { result = 0 }
-
- override int getFirstFormattedParameterIndex() { result = 1 }
}
/**
diff --git a/go/ql/lib/semmle/go/frameworks/stdlib/Fmt.qll b/go/ql/lib/semmle/go/frameworks/stdlib/Fmt.qll
index 735c5657f78..725692754a9 100644
--- a/go/ql/lib/semmle/go/frameworks/stdlib/Fmt.qll
+++ b/go/ql/lib/semmle/go/frameworks/stdlib/Fmt.qll
@@ -66,8 +66,6 @@ module Fmt {
}
override int getFormatStringIndex() { result = argOffset }
-
- override int getFirstFormattedParameterIndex() { result = argOffset + 1 }
}
/** The `Sscan` function or one of its variants. */
diff --git a/go/ql/lib/semmle/go/frameworks/stdlib/Log.qll b/go/ql/lib/semmle/go/frameworks/stdlib/Log.qll
index b821058bda0..90db1067ece 100644
--- a/go/ql/lib/semmle/go/frameworks/stdlib/Log.qll
+++ b/go/ql/lib/semmle/go/frameworks/stdlib/Log.qll
@@ -20,8 +20,6 @@ module Log {
LogFormatter() { this.getName().matches("%f") }
override int getFormatStringIndex() { result = 0 }
-
- override int getFirstFormattedParameterIndex() { result = 1 }
}
private class LogCall extends LoggerCall::Range, DataFlow::CallNode {
From bd3aaf0306d4e0382ae1cda051a63926f0a6dee3 Mon Sep 17 00:00:00 2001
From: Stephan Brandauer
Date: Fri, 28 Apr 2023 10:16:18 +0200
Subject: [PATCH 074/870] remove comment that no longer applies
---
java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll | 3 ---
1 file changed, 3 deletions(-)
diff --git a/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll b/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll
index 4d134945071..77425071792 100644
--- a/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll
@@ -217,9 +217,6 @@ private class ExceptionCharacteristic extends CharacteristicsImpl::NotASinkChara
/**
* A negative characteristic that indicates that an endpoint sits in a test file.
- *
- * WARNING: These endpoints should not be used as negative samples for training, because there can in fact be sinks in
- * test files -- we just don't care to model them because they aren't exploitable.
*/
private class TestFileCharacteristic extends CharacteristicsImpl::LikelyNotASinkCharacteristic {
TestFileCharacteristic() { this = "test file" }
From f3c1c53b54e97300efd2ae17495a16e46ae1da90 Mon Sep 17 00:00:00 2001
From: Owen Mansel-Chan
Date: Wed, 26 Apr 2023 16:29:31 +0100
Subject: [PATCH 075/870] Add CallExpr.getCalleeType()
This avoids using `getTarget()`, so it works even when that doesn't
exist (for example when calling a variable with function type).
---
go/ql/lib/semmle/go/Expr.qll | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/go/ql/lib/semmle/go/Expr.qll b/go/ql/lib/semmle/go/Expr.qll
index ad84b50ff0e..127ba95a9dd 100644
--- a/go/ql/lib/semmle/go/Expr.qll
+++ b/go/ql/lib/semmle/go/Expr.qll
@@ -885,6 +885,15 @@ class CallExpr extends CallOrConversionExpr {
)
}
+ /**
+ * Gets the signature type of the invoked function.
+ *
+ * Note that it avoids calling `getTarget()` so that it works even when that
+ * predicate isn't defined, for example when calling a variable with function
+ * type.
+ */
+ SignatureType getCalleeType() { result = this.getCalleeExpr().getType() }
+
/** Gets the declared target of this call. */
Function getTarget() { this.getCalleeExpr() = result.getAReference() }
From b928f13d9460673e6598027a062e8184882fc124 Mon Sep 17 00:00:00 2001
From: Owen Mansel-Chan
Date: Wed, 26 Apr 2023 16:30:51 +0100
Subject: [PATCH 076/870] Add `CallExpr.hasImplicitArgs()`
---
go/ql/lib/semmle/go/Expr.qll | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/go/ql/lib/semmle/go/Expr.qll b/go/ql/lib/semmle/go/Expr.qll
index 127ba95a9dd..90f838c2174 100644
--- a/go/ql/lib/semmle/go/Expr.qll
+++ b/go/ql/lib/semmle/go/Expr.qll
@@ -857,6 +857,12 @@ class CallExpr extends CallOrConversionExpr {
/** Gets the number of argument expressions of this call. */
int getNumArgument() { result = count(this.getAnArgument()) }
+ /** Holds if this call has implicit variadic arguments. */
+ predicate hasImplicitVarargs() {
+ this.getCalleeType().isVariadic() and
+ not this.hasEllipsis()
+ }
+
/**
* Gets an argument with an ellipsis after it which is passed to a varargs
* parameter, as in `f(x...)`.
From 52cc61198d88824f8087312c81d26e6cc8a43934 Mon Sep 17 00:00:00 2001
From: Owen Mansel-Chan
Date: Wed, 26 Apr 2023 16:33:34 +0100
Subject: [PATCH 077/870] Use `CallExpr.hasImplicitArgs()`
---
.../go/dataflow/internal/DataFlowNodes.qll | 17 ++++++-----------
1 file changed, 6 insertions(+), 11 deletions(-)
diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll
index 5ab830d7272..b3dce743f5c 100644
--- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll
+++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll
@@ -10,7 +10,7 @@ private newtype TNode =
MkInstructionNode(IR::Instruction insn) or
MkSsaNode(SsaDefinition ssa) or
MkGlobalFunctionNode(Function f) or
- MkImplicitVarargsSlice(CallExpr c) { c.getTarget().isVariadic() and not c.hasEllipsis() } or
+ MkImplicitVarargsSlice(CallExpr c) { c.hasImplicitVarargs() } or
MkSummarizedParameterNode(SummarizedCallable c, int i) {
FlowSummaryImpl::Private::summaryParameterNodeRange(c, i)
} or
@@ -572,13 +572,9 @@ module Public {
* predicate `getArgument` on `CallExpr`, which gets the syntactic arguments.
*/
Node getArgument(int i) {
- exists(SignatureType t, int lastParamIndex |
- t = this.getACalleeIncludingExternals().getType() and
- lastParamIndex = t.getNumParameter() - 1
- |
+ exists(int lastParamIndex | lastParamIndex = expr.getCalleeType().getNumParameter() - 1 |
if
- not this.hasEllipsis() and
- t.isVariadic() and
+ expr.hasImplicitVarargs() and
i >= lastParamIndex
then
result.(ImplicitVarargsSlice).getCallNode() = this and
@@ -598,11 +594,10 @@ module Public {
* the varargs parameter of the target of this call (if there is one).
*/
Node getImplicitVarargsArgument(int i) {
- not this.hasEllipsis() and
i >= 0 and
- exists(Function f | f = this.getTarget() |
- f.isVariadic() and
- result = this.getSyntacticArgument(f.getNumParameter() - 1 + i)
+ expr.hasImplicitVarargs() and
+ exists(int lastParamIndex | lastParamIndex = expr.getCalleeType().getNumParameter() - 1 |
+ result = this.getSyntacticArgument(lastParamIndex + i)
)
}
From c7c0a73b90dc947a4329b5c829e9344662c89122 Mon Sep 17 00:00:00 2001
From: Owen Mansel-Chan
Date: Thu, 27 Apr 2023 10:31:55 +0100
Subject: [PATCH 078/870] Accept review suggestions
---
.../go/dataflow/internal/DataFlowNodes.qll | 23 ++++++++-----------
1 file changed, 10 insertions(+), 13 deletions(-)
diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll
index b3dce743f5c..6bee34aa585 100644
--- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll
+++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll
@@ -551,7 +551,7 @@ module Public {
}
/**
- * Gets the data flow node corresponding to an argument of this call, where
+ * Gets a data flow node corresponding to an argument of this call, where
* tuple extraction has been done but arguments corresponding to a variadic
* parameter are still considered separate.
*/
@@ -567,20 +567,17 @@ module Public {
* For calls of the form `f(g())` where `g` has multiple results, the arguments of the call to
* `i` are the (implicit) element extraction nodes for the call to `g`.
*
- * For calls to variadic functions without an ellipsis (`...`), there is a single argument of type
- * `ImplicitVarargsSlice` corresponding to the variadic parameter. This is in contrast to the member
- * predicate `getArgument` on `CallExpr`, which gets the syntactic arguments.
+ * Returns a single `Node` corresponding to a variadic parameter. If there is no corresponding
+ * argument with an ellipsis (`...`), then it is a `ImplicitVarargsSlice`. This is in contrast
+ * to `getArgument` on `CallExpr`, which gets the syntactic arguments. Use
+ * `getSyntacticArgument` to get that behavior.
*/
Node getArgument(int i) {
- exists(int lastParamIndex | lastParamIndex = expr.getCalleeType().getNumParameter() - 1 |
- if
- expr.hasImplicitVarargs() and
- i >= lastParamIndex
- then
- result.(ImplicitVarargsSlice).getCallNode() = this and
- i = lastParamIndex
- else result = this.getSyntacticArgument(i)
- )
+ result = this.getSyntacticArgument(i) and
+ not (expr.hasImplicitVarargs() and i >= expr.getCalleeType().getNumParameter() - 1)
+ or
+ i = expr.getCalleeType().getNumParameter() - 1 and
+ result.(ImplicitVarargsSlice).getCallNode() = this
}
/** Gets the data flow node corresponding to an argument of this call. */
From 8415c4a4eb8f291c1e3e29f8fb1d365424f0e57a Mon Sep 17 00:00:00 2001
From: Owen Mansel-Chan
Date: Fri, 28 Apr 2023 05:57:09 +0100
Subject: [PATCH 079/870] Remove ArgumentNode assumption
---
.../semmle/go/frameworks/stdlib/NetHttp.qll | 66 ++++++++++---------
1 file changed, 35 insertions(+), 31 deletions(-)
diff --git a/go/ql/lib/semmle/go/frameworks/stdlib/NetHttp.qll b/go/ql/lib/semmle/go/frameworks/stdlib/NetHttp.qll
index 51db02a5cbe..5a4d76f763d 100644
--- a/go/ql/lib/semmle/go/frameworks/stdlib/NetHttp.qll
+++ b/go/ql/lib/semmle/go/frameworks/stdlib/NetHttp.qll
@@ -134,40 +134,44 @@ module NetHttp {
result = call.getReceiver()
}
- private class ResponseBody extends Http::ResponseBody::Range, DataFlow::ArgumentNode {
+ private class ResponseBody extends Http::ResponseBody::Range, DataFlow::Node {
DataFlow::Node responseWriter;
ResponseBody() {
- exists(DataFlow::CallNode call |
- // A direct call to ResponseWriter.Write, conveying taint from the argument to the receiver
- call.getTarget().(Method).implements("net/http", "ResponseWriter", "Write") and
- this = call.getArgument(0) and
- responseWriter = call.(DataFlow::MethodCallNode).getReceiver()
- )
- or
- exists(TaintTracking::FunctionModel model |
- // A modeled function conveying taint from some input to the response writer,
- // e.g. `io.Copy(responseWriter, someTaintedReader)`
- model.taintStep(this, responseWriter) and
- responseWriter.getType().implements("net/http", "ResponseWriter")
- )
- or
- exists(
- SummarizedCallable callable, DataFlow::CallNode call, SummaryComponentStack input,
- SummaryComponentStack output
- |
- callable = call.getACalleeIncludingExternals() and callable.propagatesFlow(input, output, _)
- |
- // A modeled function conveying taint from some input to the response writer,
- // e.g. `io.Copy(responseWriter, someTaintedReader)`
- // NB. SummarizedCallables do not implement a direct call-site-crossing flow step; instead
- // they are implemented by a function body with internal dataflow nodes, so we mimic the
- // one-step style for the particular case of taint propagation direct from an argument or receiver
- // to another argument, receiver or return value, matching the behavior for a `TaintTracking::FunctionModel`.
- this = getSummaryInputOrOutputNode(call, input) and
- responseWriter.(DataFlow::PostUpdateNode).getPreUpdateNode() =
- getSummaryInputOrOutputNode(call, output) and
- responseWriter.getType().implements("net/http", "ResponseWriter")
+ this = any(DataFlow::CallNode call).getASyntacticArgument() and
+ (
+ exists(DataFlow::CallNode call |
+ // A direct call to ResponseWriter.Write, conveying taint from the argument to the receiver
+ call.getTarget().(Method).implements("net/http", "ResponseWriter", "Write") and
+ this = call.getArgument(0) and
+ responseWriter = call.(DataFlow::MethodCallNode).getReceiver()
+ )
+ or
+ exists(TaintTracking::FunctionModel model |
+ // A modeled function conveying taint from some input to the response writer,
+ // e.g. `io.Copy(responseWriter, someTaintedReader)`
+ model.taintStep(this, responseWriter) and
+ responseWriter.getType().implements("net/http", "ResponseWriter")
+ )
+ or
+ exists(
+ SummarizedCallable callable, DataFlow::CallNode call, SummaryComponentStack input,
+ SummaryComponentStack output
+ |
+ callable = call.getACalleeIncludingExternals() and
+ callable.propagatesFlow(input, output, _)
+ |
+ // A modeled function conveying taint from some input to the response writer,
+ // e.g. `io.Copy(responseWriter, someTaintedReader)`
+ // NB. SummarizedCallables do not implement a direct call-site-crossing flow step; instead
+ // they are implemented by a function body with internal dataflow nodes, so we mimic the
+ // one-step style for the particular case of taint propagation direct from an argument or receiver
+ // to another argument, receiver or return value, matching the behavior for a `TaintTracking::FunctionModel`.
+ this = getSummaryInputOrOutputNode(call, input) and
+ responseWriter.(DataFlow::PostUpdateNode).getPreUpdateNode() =
+ getSummaryInputOrOutputNode(call, output) and
+ responseWriter.getType().implements("net/http", "ResponseWriter")
+ )
)
}
From faf846bd586c1a8deabab62758e4154625bd459e Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Thu, 27 Apr 2023 21:40:01 +0100
Subject: [PATCH 080/870] C++: Disable flow through nodes that are sources of
phi edges' back edges.
---
.../CWE/CWE-193/InvalidPointerDeref.ql | 2 +
.../InvalidPointerDeref.expected | 161 ------------------
.../CWE/CWE-193/pointer-deref/test.cpp | 14 +-
3 files changed, 9 insertions(+), 168 deletions(-)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql b/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql
index 2f77fff2ebf..46506cdff5d 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql
@@ -229,6 +229,8 @@ module InvalidPointerToDerefConfig implements DataFlow::ConfigSig {
pragma[inline]
predicate isSink(DataFlow::Node sink) { isInvalidPointerDerefSink(sink, _, _) }
+
+ predicate isBarrier(DataFlow::Node node) { node = any(DataFlow::SsaPhiNode phi).getAnInput(true) }
}
module InvalidPointerToDerefFlow = DataFlow::Global;
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected
index 338def9dfe0..8f863b7c50c 100644
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected
@@ -359,99 +359,50 @@ edges
| test.cpp:48:16:48:16 | q | test.cpp:42:14:42:15 | Load: * ... |
| test.cpp:48:16:48:16 | q | test.cpp:44:14:44:21 | Load: * ... |
| test.cpp:51:7:51:14 | mk_array indirection | test.cpp:60:19:60:26 | call to mk_array |
-| test.cpp:51:33:51:35 | end | test.cpp:60:34:60:37 | mk_array output argument |
| test.cpp:52:19:52:24 | call to malloc | test.cpp:51:7:51:14 | mk_array indirection |
| test.cpp:52:19:52:24 | call to malloc | test.cpp:53:12:53:16 | begin |
-| test.cpp:53:5:53:23 | ... = ... | test.cpp:51:33:51:35 | end |
-| test.cpp:53:12:53:16 | begin | test.cpp:53:5:53:23 | ... = ... |
-| test.cpp:53:12:53:16 | begin | test.cpp:53:12:53:23 | ... + ... |
-| test.cpp:53:12:53:23 | ... + ... | test.cpp:51:33:51:35 | end |
| test.cpp:60:19:60:26 | call to mk_array | test.cpp:62:39:62:39 | p |
| test.cpp:60:19:60:26 | call to mk_array | test.cpp:66:39:66:39 | p |
| test.cpp:60:19:60:26 | call to mk_array | test.cpp:70:38:70:38 | p |
-| test.cpp:60:34:60:37 | mk_array output argument | test.cpp:62:32:62:34 | end |
-| test.cpp:60:34:60:37 | mk_array output argument | test.cpp:66:32:66:34 | end |
-| test.cpp:60:34:60:37 | mk_array output argument | test.cpp:70:31:70:33 | end |
-| test.cpp:62:32:62:34 | end | test.cpp:67:9:67:14 | Store: ... = ... |
-| test.cpp:66:32:66:34 | end | test.cpp:67:9:67:14 | Store: ... = ... |
-| test.cpp:70:31:70:33 | end | test.cpp:67:9:67:14 | Store: ... = ... |
| test.cpp:80:9:80:16 | mk_array indirection [begin] | test.cpp:89:19:89:26 | call to mk_array [begin] |
| test.cpp:80:9:80:16 | mk_array indirection [begin] | test.cpp:119:18:119:25 | call to mk_array [begin] |
-| test.cpp:80:9:80:16 | mk_array indirection [end] | test.cpp:89:19:89:26 | call to mk_array [end] |
-| test.cpp:80:9:80:16 | mk_array indirection [end] | test.cpp:119:18:119:25 | call to mk_array [end] |
| test.cpp:82:5:82:28 | ... = ... | test.cpp:82:9:82:13 | arr indirection [post update] [begin] |
| test.cpp:82:9:82:13 | arr indirection [post update] [begin] | test.cpp:80:9:80:16 | mk_array indirection [begin] |
| test.cpp:82:9:82:13 | arr indirection [post update] [begin] | test.cpp:83:15:83:17 | arr indirection [begin] |
| test.cpp:82:17:82:22 | call to malloc | test.cpp:82:5:82:28 | ... = ... |
-| test.cpp:83:5:83:30 | ... = ... | test.cpp:83:9:83:11 | arr indirection [post update] [end] |
-| test.cpp:83:9:83:11 | arr indirection [post update] [end] | test.cpp:80:9:80:16 | mk_array indirection [end] |
| test.cpp:83:15:83:17 | arr indirection [begin] | test.cpp:83:19:83:23 | begin indirection |
-| test.cpp:83:15:83:30 | ... + ... | test.cpp:83:5:83:30 | ... = ... |
-| test.cpp:83:19:83:23 | begin | test.cpp:83:5:83:30 | ... = ... |
-| test.cpp:83:19:83:23 | begin | test.cpp:83:15:83:30 | ... + ... |
| test.cpp:83:19:83:23 | begin indirection | test.cpp:83:19:83:23 | begin |
| test.cpp:89:19:89:26 | call to mk_array [begin] | test.cpp:91:20:91:22 | arr indirection [begin] |
| test.cpp:89:19:89:26 | call to mk_array [begin] | test.cpp:95:20:95:22 | arr indirection [begin] |
| test.cpp:89:19:89:26 | call to mk_array [begin] | test.cpp:99:20:99:22 | arr indirection [begin] |
-| test.cpp:89:19:89:26 | call to mk_array [end] | test.cpp:91:36:91:38 | arr indirection [end] |
-| test.cpp:89:19:89:26 | call to mk_array [end] | test.cpp:95:36:95:38 | arr indirection [end] |
-| test.cpp:89:19:89:26 | call to mk_array [end] | test.cpp:99:35:99:37 | arr indirection [end] |
| test.cpp:91:20:91:22 | arr indirection [begin] | test.cpp:91:24:91:28 | begin |
| test.cpp:91:20:91:22 | arr indirection [begin] | test.cpp:91:24:91:28 | begin indirection |
| test.cpp:91:24:91:28 | begin | test.cpp:91:47:91:47 | p |
| test.cpp:91:24:91:28 | begin indirection | test.cpp:91:47:91:47 | p |
-| test.cpp:91:36:91:38 | arr indirection [end] | test.cpp:91:40:91:42 | end |
-| test.cpp:91:36:91:38 | arr indirection [end] | test.cpp:91:40:91:42 | end indirection |
-| test.cpp:91:40:91:42 | end | test.cpp:96:9:96:14 | Store: ... = ... |
-| test.cpp:91:40:91:42 | end indirection | test.cpp:91:40:91:42 | end |
| test.cpp:95:20:95:22 | arr indirection [begin] | test.cpp:95:24:95:28 | begin |
| test.cpp:95:20:95:22 | arr indirection [begin] | test.cpp:95:24:95:28 | begin indirection |
| test.cpp:95:24:95:28 | begin | test.cpp:95:47:95:47 | p |
| test.cpp:95:24:95:28 | begin indirection | test.cpp:95:47:95:47 | p |
-| test.cpp:95:36:95:38 | arr indirection [end] | test.cpp:95:40:95:42 | end |
-| test.cpp:95:36:95:38 | arr indirection [end] | test.cpp:95:40:95:42 | end indirection |
-| test.cpp:95:40:95:42 | end | test.cpp:96:9:96:14 | Store: ... = ... |
-| test.cpp:95:40:95:42 | end indirection | test.cpp:95:40:95:42 | end |
| test.cpp:99:20:99:22 | arr indirection [begin] | test.cpp:99:24:99:28 | begin |
| test.cpp:99:20:99:22 | arr indirection [begin] | test.cpp:99:24:99:28 | begin indirection |
| test.cpp:99:24:99:28 | begin | test.cpp:99:46:99:46 | p |
| test.cpp:99:24:99:28 | begin indirection | test.cpp:99:46:99:46 | p |
-| test.cpp:99:35:99:37 | arr indirection [end] | test.cpp:99:39:99:41 | end |
-| test.cpp:99:35:99:37 | arr indirection [end] | test.cpp:99:39:99:41 | end indirection |
-| test.cpp:99:39:99:41 | end | test.cpp:96:9:96:14 | Store: ... = ... |
-| test.cpp:99:39:99:41 | end indirection | test.cpp:99:39:99:41 | end |
| test.cpp:104:27:104:29 | arr [begin] | test.cpp:105:20:105:22 | arr indirection [begin] |
| test.cpp:104:27:104:29 | arr [begin] | test.cpp:109:20:109:22 | arr indirection [begin] |
| test.cpp:104:27:104:29 | arr [begin] | test.cpp:113:20:113:22 | arr indirection [begin] |
-| test.cpp:104:27:104:29 | arr [end] | test.cpp:105:36:105:38 | arr indirection [end] |
-| test.cpp:104:27:104:29 | arr [end] | test.cpp:109:36:109:38 | arr indirection [end] |
-| test.cpp:104:27:104:29 | arr [end] | test.cpp:113:35:113:37 | arr indirection [end] |
| test.cpp:105:20:105:22 | arr indirection [begin] | test.cpp:105:24:105:28 | begin |
| test.cpp:105:20:105:22 | arr indirection [begin] | test.cpp:105:24:105:28 | begin indirection |
| test.cpp:105:24:105:28 | begin | test.cpp:105:47:105:47 | p |
| test.cpp:105:24:105:28 | begin indirection | test.cpp:105:47:105:47 | p |
-| test.cpp:105:36:105:38 | arr indirection [end] | test.cpp:105:40:105:42 | end |
-| test.cpp:105:36:105:38 | arr indirection [end] | test.cpp:105:40:105:42 | end indirection |
-| test.cpp:105:40:105:42 | end | test.cpp:110:9:110:14 | Store: ... = ... |
-| test.cpp:105:40:105:42 | end indirection | test.cpp:105:40:105:42 | end |
| test.cpp:109:20:109:22 | arr indirection [begin] | test.cpp:109:24:109:28 | begin |
| test.cpp:109:20:109:22 | arr indirection [begin] | test.cpp:109:24:109:28 | begin indirection |
| test.cpp:109:24:109:28 | begin | test.cpp:109:47:109:47 | p |
| test.cpp:109:24:109:28 | begin indirection | test.cpp:109:47:109:47 | p |
-| test.cpp:109:36:109:38 | arr indirection [end] | test.cpp:109:40:109:42 | end |
-| test.cpp:109:36:109:38 | arr indirection [end] | test.cpp:109:40:109:42 | end indirection |
-| test.cpp:109:40:109:42 | end | test.cpp:110:9:110:14 | Store: ... = ... |
-| test.cpp:109:40:109:42 | end indirection | test.cpp:109:40:109:42 | end |
| test.cpp:113:20:113:22 | arr indirection [begin] | test.cpp:113:24:113:28 | begin |
| test.cpp:113:20:113:22 | arr indirection [begin] | test.cpp:113:24:113:28 | begin indirection |
| test.cpp:113:24:113:28 | begin | test.cpp:113:46:113:46 | p |
| test.cpp:113:24:113:28 | begin indirection | test.cpp:113:46:113:46 | p |
-| test.cpp:113:35:113:37 | arr indirection [end] | test.cpp:113:39:113:41 | end |
-| test.cpp:113:35:113:37 | arr indirection [end] | test.cpp:113:39:113:41 | end indirection |
-| test.cpp:113:39:113:41 | end | test.cpp:110:9:110:14 | Store: ... = ... |
-| test.cpp:113:39:113:41 | end indirection | test.cpp:113:39:113:41 | end |
| test.cpp:119:18:119:25 | call to mk_array [begin] | test.cpp:104:27:104:29 | arr [begin] |
-| test.cpp:119:18:119:25 | call to mk_array [end] | test.cpp:104:27:104:29 | arr [end] |
| test.cpp:124:15:124:20 | call to malloc | test.cpp:125:5:125:17 | ... = ... |
| test.cpp:124:15:124:20 | call to malloc | test.cpp:126:15:126:15 | p |
| test.cpp:125:5:125:17 | ... = ... | test.cpp:125:9:125:13 | arr indirection [post update] [begin] |
@@ -466,23 +417,15 @@ edges
| test.cpp:137:15:137:19 | begin indirection | test.cpp:137:15:137:19 | begin |
| test.cpp:141:10:141:19 | mk_array_p indirection [begin] | test.cpp:150:20:150:29 | call to mk_array_p indirection [begin] |
| test.cpp:141:10:141:19 | mk_array_p indirection [begin] | test.cpp:180:19:180:28 | call to mk_array_p indirection [begin] |
-| test.cpp:141:10:141:19 | mk_array_p indirection [end] | test.cpp:150:20:150:29 | call to mk_array_p indirection [end] |
-| test.cpp:141:10:141:19 | mk_array_p indirection [end] | test.cpp:180:19:180:28 | call to mk_array_p indirection [end] |
| test.cpp:143:5:143:29 | ... = ... | test.cpp:143:10:143:14 | arr indirection [post update] [begin] |
| test.cpp:143:10:143:14 | arr indirection [post update] [begin] | test.cpp:141:10:141:19 | mk_array_p indirection [begin] |
| test.cpp:143:10:143:14 | arr indirection [post update] [begin] | test.cpp:144:16:144:18 | arr indirection [begin] |
| test.cpp:143:18:143:23 | call to malloc | test.cpp:143:5:143:29 | ... = ... |
-| test.cpp:144:5:144:32 | ... = ... | test.cpp:144:10:144:12 | arr indirection [post update] [end] |
-| test.cpp:144:10:144:12 | arr indirection [post update] [end] | test.cpp:141:10:141:19 | mk_array_p indirection [end] |
| test.cpp:144:16:144:18 | arr indirection [begin] | test.cpp:144:21:144:25 | begin indirection |
-| test.cpp:144:16:144:32 | ... + ... | test.cpp:144:5:144:32 | ... = ... |
-| test.cpp:144:21:144:25 | begin | test.cpp:144:5:144:32 | ... = ... |
-| test.cpp:144:21:144:25 | begin | test.cpp:144:16:144:32 | ... + ... |
| test.cpp:144:21:144:25 | begin indirection | test.cpp:144:21:144:25 | begin |
| test.cpp:150:20:150:29 | call to mk_array_p indirection [begin] | test.cpp:152:20:152:22 | arr indirection [begin] |
| test.cpp:150:20:150:29 | call to mk_array_p indirection [begin] | test.cpp:156:20:156:22 | arr indirection [begin] |
| test.cpp:150:20:150:29 | call to mk_array_p indirection [begin] | test.cpp:160:20:160:22 | arr indirection [begin] |
-| test.cpp:150:20:150:29 | call to mk_array_p indirection [end] | test.cpp:156:37:156:39 | arr indirection [end] |
| test.cpp:152:20:152:22 | arr indirection [begin] | test.cpp:152:25:152:29 | begin |
| test.cpp:152:20:152:22 | arr indirection [begin] | test.cpp:152:25:152:29 | begin indirection |
| test.cpp:152:25:152:29 | begin | test.cpp:152:49:152:49 | p |
@@ -491,10 +434,6 @@ edges
| test.cpp:156:20:156:22 | arr indirection [begin] | test.cpp:156:25:156:29 | begin indirection |
| test.cpp:156:25:156:29 | begin | test.cpp:156:49:156:49 | p |
| test.cpp:156:25:156:29 | begin indirection | test.cpp:156:49:156:49 | p |
-| test.cpp:156:37:156:39 | arr indirection [end] | test.cpp:156:42:156:44 | end |
-| test.cpp:156:37:156:39 | arr indirection [end] | test.cpp:156:42:156:44 | end indirection |
-| test.cpp:156:42:156:44 | end | test.cpp:157:9:157:14 | Store: ... = ... |
-| test.cpp:156:42:156:44 | end indirection | test.cpp:156:42:156:44 | end |
| test.cpp:160:20:160:22 | arr indirection [begin] | test.cpp:160:25:160:29 | begin |
| test.cpp:160:20:160:22 | arr indirection [begin] | test.cpp:160:25:160:29 | begin indirection |
| test.cpp:160:25:160:29 | begin | test.cpp:160:48:160:48 | p |
@@ -502,35 +441,19 @@ edges
| test.cpp:165:29:165:31 | arr indirection [begin] | test.cpp:166:20:166:22 | arr indirection [begin] |
| test.cpp:165:29:165:31 | arr indirection [begin] | test.cpp:170:20:170:22 | arr indirection [begin] |
| test.cpp:165:29:165:31 | arr indirection [begin] | test.cpp:174:20:174:22 | arr indirection [begin] |
-| test.cpp:165:29:165:31 | arr indirection [end] | test.cpp:166:37:166:39 | arr indirection [end] |
-| test.cpp:165:29:165:31 | arr indirection [end] | test.cpp:170:37:170:39 | arr indirection [end] |
-| test.cpp:165:29:165:31 | arr indirection [end] | test.cpp:174:36:174:38 | arr indirection [end] |
| test.cpp:166:20:166:22 | arr indirection [begin] | test.cpp:166:25:166:29 | begin |
| test.cpp:166:20:166:22 | arr indirection [begin] | test.cpp:166:25:166:29 | begin indirection |
| test.cpp:166:25:166:29 | begin | test.cpp:166:49:166:49 | p |
| test.cpp:166:25:166:29 | begin indirection | test.cpp:166:49:166:49 | p |
-| test.cpp:166:37:166:39 | arr indirection [end] | test.cpp:166:42:166:44 | end |
-| test.cpp:166:37:166:39 | arr indirection [end] | test.cpp:166:42:166:44 | end indirection |
-| test.cpp:166:42:166:44 | end | test.cpp:171:9:171:14 | Store: ... = ... |
-| test.cpp:166:42:166:44 | end indirection | test.cpp:166:42:166:44 | end |
| test.cpp:170:20:170:22 | arr indirection [begin] | test.cpp:170:25:170:29 | begin |
| test.cpp:170:20:170:22 | arr indirection [begin] | test.cpp:170:25:170:29 | begin indirection |
| test.cpp:170:25:170:29 | begin | test.cpp:170:49:170:49 | p |
| test.cpp:170:25:170:29 | begin indirection | test.cpp:170:49:170:49 | p |
-| test.cpp:170:37:170:39 | arr indirection [end] | test.cpp:170:42:170:44 | end |
-| test.cpp:170:37:170:39 | arr indirection [end] | test.cpp:170:42:170:44 | end indirection |
-| test.cpp:170:42:170:44 | end | test.cpp:171:9:171:14 | Store: ... = ... |
-| test.cpp:170:42:170:44 | end indirection | test.cpp:170:42:170:44 | end |
| test.cpp:174:20:174:22 | arr indirection [begin] | test.cpp:174:25:174:29 | begin |
| test.cpp:174:20:174:22 | arr indirection [begin] | test.cpp:174:25:174:29 | begin indirection |
| test.cpp:174:25:174:29 | begin | test.cpp:174:48:174:48 | p |
| test.cpp:174:25:174:29 | begin indirection | test.cpp:174:48:174:48 | p |
-| test.cpp:174:36:174:38 | arr indirection [end] | test.cpp:174:41:174:43 | end |
-| test.cpp:174:36:174:38 | arr indirection [end] | test.cpp:174:41:174:43 | end indirection |
-| test.cpp:174:41:174:43 | end | test.cpp:171:9:171:14 | Store: ... = ... |
-| test.cpp:174:41:174:43 | end indirection | test.cpp:174:41:174:43 | end |
| test.cpp:180:19:180:28 | call to mk_array_p indirection [begin] | test.cpp:165:29:165:31 | arr indirection [begin] |
-| test.cpp:180:19:180:28 | call to mk_array_p indirection [end] | test.cpp:165:29:165:31 | arr indirection [end] |
| test.cpp:188:15:188:20 | call to malloc | test.cpp:189:15:189:15 | p |
| test.cpp:194:23:194:28 | call to malloc | test.cpp:195:17:195:17 | p |
| test.cpp:194:23:194:28 | call to malloc | test.cpp:197:8:197:8 | p |
@@ -590,38 +513,14 @@ edges
| test.cpp:261:14:261:15 | xs | test.cpp:261:14:261:21 | ... + ... |
| test.cpp:261:14:261:15 | xs | test.cpp:261:14:261:21 | ... + ... |
| test.cpp:261:14:261:15 | xs | test.cpp:261:14:261:21 | ... + ... |
-| test.cpp:261:14:261:15 | xs | test.cpp:261:14:261:21 | ... + ... |
-| test.cpp:261:14:261:15 | xs | test.cpp:262:26:262:28 | end |
-| test.cpp:261:14:261:15 | xs | test.cpp:262:26:262:28 | end |
| test.cpp:261:14:261:15 | xs | test.cpp:262:31:262:31 | x |
-| test.cpp:261:14:261:15 | xs | test.cpp:262:31:262:33 | ... ++ |
-| test.cpp:261:14:261:15 | xs | test.cpp:262:31:262:33 | ... ++ |
| test.cpp:261:14:261:15 | xs | test.cpp:264:14:264:14 | x |
| test.cpp:261:14:261:15 | xs | test.cpp:264:14:264:14 | x |
| test.cpp:261:14:261:21 | ... + ... | test.cpp:261:14:261:21 | ... + ... |
-| test.cpp:261:14:261:21 | ... + ... | test.cpp:261:14:261:21 | ... + ... |
-| test.cpp:261:14:261:21 | ... + ... | test.cpp:262:26:262:28 | end |
-| test.cpp:261:14:261:21 | ... + ... | test.cpp:262:26:262:28 | end |
-| test.cpp:261:14:261:21 | ... + ... | test.cpp:262:26:262:28 | end |
-| test.cpp:261:14:261:21 | ... + ... | test.cpp:262:26:262:28 | end |
| test.cpp:261:14:261:21 | ... + ... | test.cpp:264:13:264:14 | Load: * ... |
| test.cpp:261:14:261:21 | ... + ... | test.cpp:264:13:264:14 | Load: * ... |
| test.cpp:261:14:261:21 | ... + ... | test.cpp:264:13:264:14 | Load: * ... |
-| test.cpp:261:14:261:21 | ... + ... | test.cpp:264:13:264:14 | Load: * ... |
-| test.cpp:262:21:262:21 | x | test.cpp:264:13:264:14 | Load: * ... |
-| test.cpp:262:26:262:28 | end | test.cpp:262:26:262:28 | end |
-| test.cpp:262:26:262:28 | end | test.cpp:262:26:262:28 | end |
-| test.cpp:262:26:262:28 | end | test.cpp:264:13:264:14 | Load: * ... |
-| test.cpp:262:26:262:28 | end | test.cpp:264:13:264:14 | Load: * ... |
| test.cpp:262:31:262:31 | x | test.cpp:264:13:264:14 | Load: * ... |
-| test.cpp:262:31:262:33 | ... ++ | test.cpp:262:21:262:21 | x |
-| test.cpp:262:31:262:33 | ... ++ | test.cpp:262:21:262:21 | x |
-| test.cpp:262:31:262:33 | ... ++ | test.cpp:262:31:262:31 | x |
-| test.cpp:262:31:262:33 | ... ++ | test.cpp:262:31:262:31 | x |
-| test.cpp:262:31:262:33 | ... ++ | test.cpp:264:14:264:14 | x |
-| test.cpp:262:31:262:33 | ... ++ | test.cpp:264:14:264:14 | x |
-| test.cpp:262:31:262:33 | ... ++ | test.cpp:264:14:264:14 | x |
-| test.cpp:262:31:262:33 | ... ++ | test.cpp:264:14:264:14 | x |
| test.cpp:264:14:264:14 | x | test.cpp:262:31:262:31 | x |
| test.cpp:264:14:264:14 | x | test.cpp:264:13:264:14 | Load: * ... |
| test.cpp:264:14:264:14 | x | test.cpp:264:13:264:14 | Load: * ... |
@@ -630,74 +529,23 @@ edges
| test.cpp:271:14:271:15 | xs | test.cpp:271:14:271:21 | ... + ... |
| test.cpp:271:14:271:15 | xs | test.cpp:271:14:271:21 | ... + ... |
| test.cpp:271:14:271:15 | xs | test.cpp:271:14:271:21 | ... + ... |
-| test.cpp:271:14:271:15 | xs | test.cpp:271:14:271:21 | ... + ... |
-| test.cpp:271:14:271:15 | xs | test.cpp:272:26:272:28 | end |
-| test.cpp:271:14:271:15 | xs | test.cpp:272:26:272:28 | end |
| test.cpp:271:14:271:15 | xs | test.cpp:272:31:272:31 | x |
-| test.cpp:271:14:271:15 | xs | test.cpp:272:31:272:33 | ... ++ |
-| test.cpp:271:14:271:15 | xs | test.cpp:272:31:272:33 | ... ++ |
| test.cpp:271:14:271:15 | xs | test.cpp:274:5:274:6 | * ... |
| test.cpp:271:14:271:15 | xs | test.cpp:274:6:274:6 | x |
| test.cpp:271:14:271:15 | xs | test.cpp:274:6:274:6 | x |
| test.cpp:271:14:271:21 | ... + ... | test.cpp:271:14:271:21 | ... + ... |
-| test.cpp:271:14:271:21 | ... + ... | test.cpp:271:14:271:21 | ... + ... |
-| test.cpp:271:14:271:21 | ... + ... | test.cpp:272:26:272:28 | end |
-| test.cpp:271:14:271:21 | ... + ... | test.cpp:272:26:272:28 | end |
-| test.cpp:271:14:271:21 | ... + ... | test.cpp:272:26:272:28 | end |
-| test.cpp:271:14:271:21 | ... + ... | test.cpp:272:26:272:28 | end |
| test.cpp:271:14:271:21 | ... + ... | test.cpp:274:5:274:10 | Store: ... = ... |
| test.cpp:271:14:271:21 | ... + ... | test.cpp:274:5:274:10 | Store: ... = ... |
| test.cpp:271:14:271:21 | ... + ... | test.cpp:274:5:274:10 | Store: ... = ... |
-| test.cpp:271:14:271:21 | ... + ... | test.cpp:274:5:274:10 | Store: ... = ... |
-| test.cpp:272:21:272:21 | x | test.cpp:274:5:274:10 | Store: ... = ... |
-| test.cpp:272:26:272:28 | end | test.cpp:272:26:272:28 | end |
-| test.cpp:272:26:272:28 | end | test.cpp:272:26:272:28 | end |
-| test.cpp:272:26:272:28 | end | test.cpp:274:5:274:10 | Store: ... = ... |
-| test.cpp:272:26:272:28 | end | test.cpp:274:5:274:10 | Store: ... = ... |
| test.cpp:272:31:272:31 | x | test.cpp:274:5:274:10 | Store: ... = ... |
-| test.cpp:272:31:272:33 | ... ++ | test.cpp:272:21:272:21 | x |
-| test.cpp:272:31:272:33 | ... ++ | test.cpp:272:21:272:21 | x |
-| test.cpp:272:31:272:33 | ... ++ | test.cpp:272:31:272:31 | x |
-| test.cpp:272:31:272:33 | ... ++ | test.cpp:272:31:272:31 | x |
-| test.cpp:272:31:272:33 | ... ++ | test.cpp:274:5:274:6 | * ... |
-| test.cpp:272:31:272:33 | ... ++ | test.cpp:274:5:274:6 | * ... |
-| test.cpp:272:31:272:33 | ... ++ | test.cpp:274:6:274:6 | x |
-| test.cpp:272:31:272:33 | ... ++ | test.cpp:274:6:274:6 | x |
-| test.cpp:272:31:272:33 | ... ++ | test.cpp:274:6:274:6 | x |
-| test.cpp:272:31:272:33 | ... ++ | test.cpp:274:6:274:6 | x |
| test.cpp:274:5:274:6 | * ... | test.cpp:274:5:274:10 | Store: ... = ... |
| test.cpp:274:6:274:6 | x | test.cpp:272:31:272:31 | x |
| test.cpp:274:6:274:6 | x | test.cpp:274:5:274:6 | * ... |
| test.cpp:274:6:274:6 | x | test.cpp:274:5:274:10 | Store: ... = ... |
| test.cpp:274:6:274:6 | x | test.cpp:274:5:274:10 | Store: ... = ... |
| test.cpp:280:13:280:24 | new[] | test.cpp:281:14:281:15 | xs |
-| test.cpp:281:14:281:15 | xs | test.cpp:282:30:282:32 | ... ++ |
-| test.cpp:281:14:281:15 | xs | test.cpp:282:30:282:32 | ... ++ |
-| test.cpp:282:21:282:21 | x | test.cpp:284:13:284:14 | Load: * ... |
-| test.cpp:282:30:282:30 | x | test.cpp:284:13:284:14 | Load: * ... |
-| test.cpp:282:30:282:32 | ... ++ | test.cpp:282:21:282:21 | x |
-| test.cpp:282:30:282:32 | ... ++ | test.cpp:282:21:282:21 | x |
-| test.cpp:282:30:282:32 | ... ++ | test.cpp:282:30:282:30 | x |
-| test.cpp:282:30:282:32 | ... ++ | test.cpp:282:30:282:30 | x |
-| test.cpp:282:30:282:32 | ... ++ | test.cpp:284:14:284:14 | x |
-| test.cpp:282:30:282:32 | ... ++ | test.cpp:284:14:284:14 | x |
-| test.cpp:284:14:284:14 | x | test.cpp:284:13:284:14 | Load: * ... |
| test.cpp:290:13:290:24 | new[] | test.cpp:291:14:291:15 | xs |
| test.cpp:290:13:290:24 | new[] | test.cpp:292:30:292:30 | x |
-| test.cpp:291:14:291:15 | xs | test.cpp:292:30:292:32 | ... ++ |
-| test.cpp:291:14:291:15 | xs | test.cpp:292:30:292:32 | ... ++ |
-| test.cpp:292:21:292:21 | x | test.cpp:294:5:294:10 | Store: ... = ... |
-| test.cpp:292:30:292:30 | x | test.cpp:294:5:294:10 | Store: ... = ... |
-| test.cpp:292:30:292:32 | ... ++ | test.cpp:292:21:292:21 | x |
-| test.cpp:292:30:292:32 | ... ++ | test.cpp:292:21:292:21 | x |
-| test.cpp:292:30:292:32 | ... ++ | test.cpp:292:30:292:30 | x |
-| test.cpp:292:30:292:32 | ... ++ | test.cpp:292:30:292:30 | x |
-| test.cpp:292:30:292:32 | ... ++ | test.cpp:294:5:294:6 | * ... |
-| test.cpp:292:30:292:32 | ... ++ | test.cpp:294:5:294:6 | * ... |
-| test.cpp:292:30:292:32 | ... ++ | test.cpp:294:6:294:6 | x |
-| test.cpp:292:30:292:32 | ... ++ | test.cpp:294:6:294:6 | x |
-| test.cpp:294:5:294:6 | * ... | test.cpp:294:5:294:10 | Store: ... = ... |
-| test.cpp:294:6:294:6 | x | test.cpp:294:5:294:10 | Store: ... = ... |
#select
| test.cpp:6:14:6:15 | Load: * ... | test.cpp:4:15:4:20 | call to malloc | test.cpp:6:14:6:15 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:4:15:4:20 | call to malloc | call to malloc | test.cpp:5:19:5:22 | size | size |
| test.cpp:8:14:8:21 | Load: * ... | test.cpp:4:15:4:20 | call to malloc | test.cpp:8:14:8:21 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:4:15:4:20 | call to malloc | call to malloc | test.cpp:5:19:5:22 | size | size |
@@ -709,19 +557,10 @@ edges
| test.cpp:42:14:42:15 | Load: * ... | test.cpp:40:15:40:20 | call to malloc | test.cpp:42:14:42:15 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:40:15:40:20 | call to malloc | call to malloc | test.cpp:41:20:41:27 | ... - ... | ... - ... |
| test.cpp:44:14:44:21 | Load: * ... | test.cpp:40:15:40:20 | call to malloc | test.cpp:44:14:44:21 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:40:15:40:20 | call to malloc | call to malloc | test.cpp:41:20:41:27 | ... - ... | ... - ... |
| test.cpp:44:14:44:21 | Load: * ... | test.cpp:40:15:40:20 | call to malloc | test.cpp:44:14:44:21 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:40:15:40:20 | call to malloc | call to malloc | test.cpp:41:20:41:27 | ... - ... | ... - ... |
-| test.cpp:67:9:67:14 | Store: ... = ... | test.cpp:52:19:52:24 | call to malloc | test.cpp:67:9:67:14 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:52:19:52:24 | call to malloc | call to malloc | test.cpp:53:20:53:23 | size | size |
-| test.cpp:96:9:96:14 | Store: ... = ... | test.cpp:82:17:82:22 | call to malloc | test.cpp:96:9:96:14 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:82:17:82:22 | call to malloc | call to malloc | test.cpp:83:27:83:30 | size | size |
-| test.cpp:110:9:110:14 | Store: ... = ... | test.cpp:82:17:82:22 | call to malloc | test.cpp:110:9:110:14 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:82:17:82:22 | call to malloc | call to malloc | test.cpp:83:27:83:30 | size | size |
-| test.cpp:157:9:157:14 | Store: ... = ... | test.cpp:143:18:143:23 | call to malloc | test.cpp:157:9:157:14 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:143:18:143:23 | call to malloc | call to malloc | test.cpp:144:29:144:32 | size | size |
-| test.cpp:171:9:171:14 | Store: ... = ... | test.cpp:143:18:143:23 | call to malloc | test.cpp:171:9:171:14 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:143:18:143:23 | call to malloc | call to malloc | test.cpp:144:29:144:32 | size | size |
| test.cpp:201:5:201:19 | Store: ... = ... | test.cpp:194:23:194:28 | call to malloc | test.cpp:201:5:201:19 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:194:23:194:28 | call to malloc | call to malloc | test.cpp:195:21:195:23 | len | len |
| test.cpp:213:5:213:13 | Store: ... = ... | test.cpp:205:23:205:28 | call to malloc | test.cpp:213:5:213:13 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:205:23:205:28 | call to malloc | call to malloc | test.cpp:206:21:206:23 | len | len |
| test.cpp:232:3:232:20 | Store: ... = ... | test.cpp:231:18:231:30 | new[] | test.cpp:232:3:232:20 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:231:18:231:30 | new[] | new[] | test.cpp:232:11:232:15 | index | index |
| test.cpp:239:5:239:22 | Store: ... = ... | test.cpp:238:20:238:32 | new[] | test.cpp:239:5:239:22 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:238:20:238:32 | new[] | new[] | test.cpp:239:13:239:17 | index | index |
| test.cpp:254:9:254:16 | Store: ... = ... | test.cpp:248:24:248:30 | call to realloc | test.cpp:254:9:254:16 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:248:24:248:30 | call to realloc | call to realloc | test.cpp:254:11:254:11 | i | i |
-| test.cpp:264:13:264:14 | Load: * ... | test.cpp:260:13:260:24 | new[] | test.cpp:264:13:264:14 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:260:13:260:24 | new[] | new[] | test.cpp:261:19:261:21 | len | len |
| test.cpp:264:13:264:14 | Load: * ... | test.cpp:260:13:260:24 | new[] | test.cpp:264:13:264:14 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:260:13:260:24 | new[] | new[] | test.cpp:261:19:261:21 | len | len |
-| test.cpp:274:5:274:10 | Store: ... = ... | test.cpp:270:13:270:24 | new[] | test.cpp:274:5:274:10 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:270:13:270:24 | new[] | new[] | test.cpp:271:19:271:21 | len | len |
| test.cpp:274:5:274:10 | Store: ... = ... | test.cpp:270:13:270:24 | new[] | test.cpp:274:5:274:10 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:270:13:270:24 | new[] | new[] | test.cpp:271:19:271:21 | len | len |
-| test.cpp:284:13:284:14 | Load: * ... | test.cpp:280:13:280:24 | new[] | test.cpp:284:13:284:14 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:280:13:280:24 | new[] | new[] | test.cpp:281:19:281:21 | len | len |
-| test.cpp:294:5:294:10 | Store: ... = ... | test.cpp:290:13:290:24 | new[] | test.cpp:294:5:294:10 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:290:13:290:24 | new[] | new[] | test.cpp:291:19:291:21 | len | len |
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/test.cpp
index 3cd2cd9ad3d..109faa678be 100644
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/test.cpp
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/test.cpp
@@ -64,7 +64,7 @@ void test5(int size) {
}
for (char* p = begin; p <= end; ++p) {
- *p = 0; // BAD
+ *p = 0; // BAD [NOT DETECTED]
}
for (char* p = begin; p < end; ++p) {
@@ -93,7 +93,7 @@ void test6(int size) {
}
for (char* p = arr.begin; p <= arr.end; ++p) {
- *p = 0; // BAD
+ *p = 0; // BAD [NOT DETECTED]
}
for (char* p = arr.begin; p < arr.end; ++p) {
@@ -107,7 +107,7 @@ void test7_callee(array_t arr) {
}
for (char* p = arr.begin; p <= arr.end; ++p) {
- *p = 0; // BAD
+ *p = 0; // BAD [NOT DETECTED]
}
for (char* p = arr.begin; p < arr.end; ++p) {
@@ -154,7 +154,7 @@ void test9(int size) {
}
for (char* p = arr->begin; p <= arr->end; ++p) {
- *p = 0; // BAD
+ *p = 0; // BAD [NOT DETECTED]
}
for (char* p = arr->begin; p < arr->end; ++p) {
@@ -168,7 +168,7 @@ void test10_callee(array_t *arr) {
}
for (char* p = arr->begin; p <= arr->end; ++p) {
- *p = 0; // BAD
+ *p = 0; // BAD [NOT DETECTED]
}
for (char* p = arr->begin; p < arr->end; ++p) {
@@ -281,7 +281,7 @@ void test19(unsigned len)
int *end = xs + len;
for (int *x = xs; x < end; x++)
{
- int i = *x; // GOOD [FALSE POSITIVE]
+ int i = *x; // GOOD
}
}
@@ -291,6 +291,6 @@ void test20(unsigned len)
int *end = xs + len;
for (int *x = xs; x < end; x++)
{
- *x = 0; // GOOD [FALSE POSITIVE]
+ *x = 0; // GOOD
}
}
\ No newline at end of file
From 43527573d02be20a24588a2437cef2e24639dee0 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Thu, 27 Apr 2023 21:42:09 +0100
Subject: [PATCH 081/870] C++: Fix back edge detection for phi nodes.
---
.../cpp/ir/dataflow/internal/DataFlowUtil.qll | 2 +-
.../InvalidPointerDeref.expected | 108 ++++++++++++++++++
.../CWE/CWE-193/pointer-deref/test.cpp | 10 +-
3 files changed, 114 insertions(+), 6 deletions(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
index 8a3568497cc..ae4fbd2febe 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
@@ -552,7 +552,7 @@ class SsaPhiNode extends Node, TSsaPhiNode {
*/
final Node getAnInput(boolean fromBackEdge) {
localFlowStep(result, this) and
- if phi.getBasicBlock().dominates(result.getBasicBlock())
+ if phi.getBasicBlock().strictlyDominates(result.getBasicBlock())
then fromBackEdge = true
else fromBackEdge = false
}
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected
index 8f863b7c50c..76adf3dba50 100644
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected
@@ -359,50 +359,99 @@ edges
| test.cpp:48:16:48:16 | q | test.cpp:42:14:42:15 | Load: * ... |
| test.cpp:48:16:48:16 | q | test.cpp:44:14:44:21 | Load: * ... |
| test.cpp:51:7:51:14 | mk_array indirection | test.cpp:60:19:60:26 | call to mk_array |
+| test.cpp:51:33:51:35 | end | test.cpp:60:34:60:37 | mk_array output argument |
| test.cpp:52:19:52:24 | call to malloc | test.cpp:51:7:51:14 | mk_array indirection |
| test.cpp:52:19:52:24 | call to malloc | test.cpp:53:12:53:16 | begin |
+| test.cpp:53:5:53:23 | ... = ... | test.cpp:51:33:51:35 | end |
+| test.cpp:53:12:53:16 | begin | test.cpp:53:5:53:23 | ... = ... |
+| test.cpp:53:12:53:16 | begin | test.cpp:53:12:53:23 | ... + ... |
+| test.cpp:53:12:53:23 | ... + ... | test.cpp:51:33:51:35 | end |
| test.cpp:60:19:60:26 | call to mk_array | test.cpp:62:39:62:39 | p |
| test.cpp:60:19:60:26 | call to mk_array | test.cpp:66:39:66:39 | p |
| test.cpp:60:19:60:26 | call to mk_array | test.cpp:70:38:70:38 | p |
+| test.cpp:60:34:60:37 | mk_array output argument | test.cpp:62:32:62:34 | end |
+| test.cpp:60:34:60:37 | mk_array output argument | test.cpp:66:32:66:34 | end |
+| test.cpp:60:34:60:37 | mk_array output argument | test.cpp:70:31:70:33 | end |
+| test.cpp:62:32:62:34 | end | test.cpp:67:9:67:14 | Store: ... = ... |
+| test.cpp:66:32:66:34 | end | test.cpp:67:9:67:14 | Store: ... = ... |
+| test.cpp:70:31:70:33 | end | test.cpp:67:9:67:14 | Store: ... = ... |
| test.cpp:80:9:80:16 | mk_array indirection [begin] | test.cpp:89:19:89:26 | call to mk_array [begin] |
| test.cpp:80:9:80:16 | mk_array indirection [begin] | test.cpp:119:18:119:25 | call to mk_array [begin] |
+| test.cpp:80:9:80:16 | mk_array indirection [end] | test.cpp:89:19:89:26 | call to mk_array [end] |
+| test.cpp:80:9:80:16 | mk_array indirection [end] | test.cpp:119:18:119:25 | call to mk_array [end] |
| test.cpp:82:5:82:28 | ... = ... | test.cpp:82:9:82:13 | arr indirection [post update] [begin] |
| test.cpp:82:9:82:13 | arr indirection [post update] [begin] | test.cpp:80:9:80:16 | mk_array indirection [begin] |
| test.cpp:82:9:82:13 | arr indirection [post update] [begin] | test.cpp:83:15:83:17 | arr indirection [begin] |
| test.cpp:82:17:82:22 | call to malloc | test.cpp:82:5:82:28 | ... = ... |
+| test.cpp:83:5:83:30 | ... = ... | test.cpp:83:9:83:11 | arr indirection [post update] [end] |
+| test.cpp:83:9:83:11 | arr indirection [post update] [end] | test.cpp:80:9:80:16 | mk_array indirection [end] |
| test.cpp:83:15:83:17 | arr indirection [begin] | test.cpp:83:19:83:23 | begin indirection |
+| test.cpp:83:15:83:30 | ... + ... | test.cpp:83:5:83:30 | ... = ... |
+| test.cpp:83:19:83:23 | begin | test.cpp:83:5:83:30 | ... = ... |
+| test.cpp:83:19:83:23 | begin | test.cpp:83:15:83:30 | ... + ... |
| test.cpp:83:19:83:23 | begin indirection | test.cpp:83:19:83:23 | begin |
| test.cpp:89:19:89:26 | call to mk_array [begin] | test.cpp:91:20:91:22 | arr indirection [begin] |
| test.cpp:89:19:89:26 | call to mk_array [begin] | test.cpp:95:20:95:22 | arr indirection [begin] |
| test.cpp:89:19:89:26 | call to mk_array [begin] | test.cpp:99:20:99:22 | arr indirection [begin] |
+| test.cpp:89:19:89:26 | call to mk_array [end] | test.cpp:91:36:91:38 | arr indirection [end] |
+| test.cpp:89:19:89:26 | call to mk_array [end] | test.cpp:95:36:95:38 | arr indirection [end] |
+| test.cpp:89:19:89:26 | call to mk_array [end] | test.cpp:99:35:99:37 | arr indirection [end] |
| test.cpp:91:20:91:22 | arr indirection [begin] | test.cpp:91:24:91:28 | begin |
| test.cpp:91:20:91:22 | arr indirection [begin] | test.cpp:91:24:91:28 | begin indirection |
| test.cpp:91:24:91:28 | begin | test.cpp:91:47:91:47 | p |
| test.cpp:91:24:91:28 | begin indirection | test.cpp:91:47:91:47 | p |
+| test.cpp:91:36:91:38 | arr indirection [end] | test.cpp:91:40:91:42 | end |
+| test.cpp:91:36:91:38 | arr indirection [end] | test.cpp:91:40:91:42 | end indirection |
+| test.cpp:91:40:91:42 | end | test.cpp:96:9:96:14 | Store: ... = ... |
+| test.cpp:91:40:91:42 | end indirection | test.cpp:91:40:91:42 | end |
| test.cpp:95:20:95:22 | arr indirection [begin] | test.cpp:95:24:95:28 | begin |
| test.cpp:95:20:95:22 | arr indirection [begin] | test.cpp:95:24:95:28 | begin indirection |
| test.cpp:95:24:95:28 | begin | test.cpp:95:47:95:47 | p |
| test.cpp:95:24:95:28 | begin indirection | test.cpp:95:47:95:47 | p |
+| test.cpp:95:36:95:38 | arr indirection [end] | test.cpp:95:40:95:42 | end |
+| test.cpp:95:36:95:38 | arr indirection [end] | test.cpp:95:40:95:42 | end indirection |
+| test.cpp:95:40:95:42 | end | test.cpp:96:9:96:14 | Store: ... = ... |
+| test.cpp:95:40:95:42 | end indirection | test.cpp:95:40:95:42 | end |
| test.cpp:99:20:99:22 | arr indirection [begin] | test.cpp:99:24:99:28 | begin |
| test.cpp:99:20:99:22 | arr indirection [begin] | test.cpp:99:24:99:28 | begin indirection |
| test.cpp:99:24:99:28 | begin | test.cpp:99:46:99:46 | p |
| test.cpp:99:24:99:28 | begin indirection | test.cpp:99:46:99:46 | p |
+| test.cpp:99:35:99:37 | arr indirection [end] | test.cpp:99:39:99:41 | end |
+| test.cpp:99:35:99:37 | arr indirection [end] | test.cpp:99:39:99:41 | end indirection |
+| test.cpp:99:39:99:41 | end | test.cpp:96:9:96:14 | Store: ... = ... |
+| test.cpp:99:39:99:41 | end indirection | test.cpp:99:39:99:41 | end |
| test.cpp:104:27:104:29 | arr [begin] | test.cpp:105:20:105:22 | arr indirection [begin] |
| test.cpp:104:27:104:29 | arr [begin] | test.cpp:109:20:109:22 | arr indirection [begin] |
| test.cpp:104:27:104:29 | arr [begin] | test.cpp:113:20:113:22 | arr indirection [begin] |
+| test.cpp:104:27:104:29 | arr [end] | test.cpp:105:36:105:38 | arr indirection [end] |
+| test.cpp:104:27:104:29 | arr [end] | test.cpp:109:36:109:38 | arr indirection [end] |
+| test.cpp:104:27:104:29 | arr [end] | test.cpp:113:35:113:37 | arr indirection [end] |
| test.cpp:105:20:105:22 | arr indirection [begin] | test.cpp:105:24:105:28 | begin |
| test.cpp:105:20:105:22 | arr indirection [begin] | test.cpp:105:24:105:28 | begin indirection |
| test.cpp:105:24:105:28 | begin | test.cpp:105:47:105:47 | p |
| test.cpp:105:24:105:28 | begin indirection | test.cpp:105:47:105:47 | p |
+| test.cpp:105:36:105:38 | arr indirection [end] | test.cpp:105:40:105:42 | end |
+| test.cpp:105:36:105:38 | arr indirection [end] | test.cpp:105:40:105:42 | end indirection |
+| test.cpp:105:40:105:42 | end | test.cpp:110:9:110:14 | Store: ... = ... |
+| test.cpp:105:40:105:42 | end indirection | test.cpp:105:40:105:42 | end |
| test.cpp:109:20:109:22 | arr indirection [begin] | test.cpp:109:24:109:28 | begin |
| test.cpp:109:20:109:22 | arr indirection [begin] | test.cpp:109:24:109:28 | begin indirection |
| test.cpp:109:24:109:28 | begin | test.cpp:109:47:109:47 | p |
| test.cpp:109:24:109:28 | begin indirection | test.cpp:109:47:109:47 | p |
+| test.cpp:109:36:109:38 | arr indirection [end] | test.cpp:109:40:109:42 | end |
+| test.cpp:109:36:109:38 | arr indirection [end] | test.cpp:109:40:109:42 | end indirection |
+| test.cpp:109:40:109:42 | end | test.cpp:110:9:110:14 | Store: ... = ... |
+| test.cpp:109:40:109:42 | end indirection | test.cpp:109:40:109:42 | end |
| test.cpp:113:20:113:22 | arr indirection [begin] | test.cpp:113:24:113:28 | begin |
| test.cpp:113:20:113:22 | arr indirection [begin] | test.cpp:113:24:113:28 | begin indirection |
| test.cpp:113:24:113:28 | begin | test.cpp:113:46:113:46 | p |
| test.cpp:113:24:113:28 | begin indirection | test.cpp:113:46:113:46 | p |
+| test.cpp:113:35:113:37 | arr indirection [end] | test.cpp:113:39:113:41 | end |
+| test.cpp:113:35:113:37 | arr indirection [end] | test.cpp:113:39:113:41 | end indirection |
+| test.cpp:113:39:113:41 | end | test.cpp:110:9:110:14 | Store: ... = ... |
+| test.cpp:113:39:113:41 | end indirection | test.cpp:113:39:113:41 | end |
| test.cpp:119:18:119:25 | call to mk_array [begin] | test.cpp:104:27:104:29 | arr [begin] |
+| test.cpp:119:18:119:25 | call to mk_array [end] | test.cpp:104:27:104:29 | arr [end] |
| test.cpp:124:15:124:20 | call to malloc | test.cpp:125:5:125:17 | ... = ... |
| test.cpp:124:15:124:20 | call to malloc | test.cpp:126:15:126:15 | p |
| test.cpp:125:5:125:17 | ... = ... | test.cpp:125:9:125:13 | arr indirection [post update] [begin] |
@@ -417,15 +466,23 @@ edges
| test.cpp:137:15:137:19 | begin indirection | test.cpp:137:15:137:19 | begin |
| test.cpp:141:10:141:19 | mk_array_p indirection [begin] | test.cpp:150:20:150:29 | call to mk_array_p indirection [begin] |
| test.cpp:141:10:141:19 | mk_array_p indirection [begin] | test.cpp:180:19:180:28 | call to mk_array_p indirection [begin] |
+| test.cpp:141:10:141:19 | mk_array_p indirection [end] | test.cpp:150:20:150:29 | call to mk_array_p indirection [end] |
+| test.cpp:141:10:141:19 | mk_array_p indirection [end] | test.cpp:180:19:180:28 | call to mk_array_p indirection [end] |
| test.cpp:143:5:143:29 | ... = ... | test.cpp:143:10:143:14 | arr indirection [post update] [begin] |
| test.cpp:143:10:143:14 | arr indirection [post update] [begin] | test.cpp:141:10:141:19 | mk_array_p indirection [begin] |
| test.cpp:143:10:143:14 | arr indirection [post update] [begin] | test.cpp:144:16:144:18 | arr indirection [begin] |
| test.cpp:143:18:143:23 | call to malloc | test.cpp:143:5:143:29 | ... = ... |
+| test.cpp:144:5:144:32 | ... = ... | test.cpp:144:10:144:12 | arr indirection [post update] [end] |
+| test.cpp:144:10:144:12 | arr indirection [post update] [end] | test.cpp:141:10:141:19 | mk_array_p indirection [end] |
| test.cpp:144:16:144:18 | arr indirection [begin] | test.cpp:144:21:144:25 | begin indirection |
+| test.cpp:144:16:144:32 | ... + ... | test.cpp:144:5:144:32 | ... = ... |
+| test.cpp:144:21:144:25 | begin | test.cpp:144:5:144:32 | ... = ... |
+| test.cpp:144:21:144:25 | begin | test.cpp:144:16:144:32 | ... + ... |
| test.cpp:144:21:144:25 | begin indirection | test.cpp:144:21:144:25 | begin |
| test.cpp:150:20:150:29 | call to mk_array_p indirection [begin] | test.cpp:152:20:152:22 | arr indirection [begin] |
| test.cpp:150:20:150:29 | call to mk_array_p indirection [begin] | test.cpp:156:20:156:22 | arr indirection [begin] |
| test.cpp:150:20:150:29 | call to mk_array_p indirection [begin] | test.cpp:160:20:160:22 | arr indirection [begin] |
+| test.cpp:150:20:150:29 | call to mk_array_p indirection [end] | test.cpp:156:37:156:39 | arr indirection [end] |
| test.cpp:152:20:152:22 | arr indirection [begin] | test.cpp:152:25:152:29 | begin |
| test.cpp:152:20:152:22 | arr indirection [begin] | test.cpp:152:25:152:29 | begin indirection |
| test.cpp:152:25:152:29 | begin | test.cpp:152:49:152:49 | p |
@@ -434,6 +491,10 @@ edges
| test.cpp:156:20:156:22 | arr indirection [begin] | test.cpp:156:25:156:29 | begin indirection |
| test.cpp:156:25:156:29 | begin | test.cpp:156:49:156:49 | p |
| test.cpp:156:25:156:29 | begin indirection | test.cpp:156:49:156:49 | p |
+| test.cpp:156:37:156:39 | arr indirection [end] | test.cpp:156:42:156:44 | end |
+| test.cpp:156:37:156:39 | arr indirection [end] | test.cpp:156:42:156:44 | end indirection |
+| test.cpp:156:42:156:44 | end | test.cpp:157:9:157:14 | Store: ... = ... |
+| test.cpp:156:42:156:44 | end indirection | test.cpp:156:42:156:44 | end |
| test.cpp:160:20:160:22 | arr indirection [begin] | test.cpp:160:25:160:29 | begin |
| test.cpp:160:20:160:22 | arr indirection [begin] | test.cpp:160:25:160:29 | begin indirection |
| test.cpp:160:25:160:29 | begin | test.cpp:160:48:160:48 | p |
@@ -441,19 +502,35 @@ edges
| test.cpp:165:29:165:31 | arr indirection [begin] | test.cpp:166:20:166:22 | arr indirection [begin] |
| test.cpp:165:29:165:31 | arr indirection [begin] | test.cpp:170:20:170:22 | arr indirection [begin] |
| test.cpp:165:29:165:31 | arr indirection [begin] | test.cpp:174:20:174:22 | arr indirection [begin] |
+| test.cpp:165:29:165:31 | arr indirection [end] | test.cpp:166:37:166:39 | arr indirection [end] |
+| test.cpp:165:29:165:31 | arr indirection [end] | test.cpp:170:37:170:39 | arr indirection [end] |
+| test.cpp:165:29:165:31 | arr indirection [end] | test.cpp:174:36:174:38 | arr indirection [end] |
| test.cpp:166:20:166:22 | arr indirection [begin] | test.cpp:166:25:166:29 | begin |
| test.cpp:166:20:166:22 | arr indirection [begin] | test.cpp:166:25:166:29 | begin indirection |
| test.cpp:166:25:166:29 | begin | test.cpp:166:49:166:49 | p |
| test.cpp:166:25:166:29 | begin indirection | test.cpp:166:49:166:49 | p |
+| test.cpp:166:37:166:39 | arr indirection [end] | test.cpp:166:42:166:44 | end |
+| test.cpp:166:37:166:39 | arr indirection [end] | test.cpp:166:42:166:44 | end indirection |
+| test.cpp:166:42:166:44 | end | test.cpp:171:9:171:14 | Store: ... = ... |
+| test.cpp:166:42:166:44 | end indirection | test.cpp:166:42:166:44 | end |
| test.cpp:170:20:170:22 | arr indirection [begin] | test.cpp:170:25:170:29 | begin |
| test.cpp:170:20:170:22 | arr indirection [begin] | test.cpp:170:25:170:29 | begin indirection |
| test.cpp:170:25:170:29 | begin | test.cpp:170:49:170:49 | p |
| test.cpp:170:25:170:29 | begin indirection | test.cpp:170:49:170:49 | p |
+| test.cpp:170:37:170:39 | arr indirection [end] | test.cpp:170:42:170:44 | end |
+| test.cpp:170:37:170:39 | arr indirection [end] | test.cpp:170:42:170:44 | end indirection |
+| test.cpp:170:42:170:44 | end | test.cpp:171:9:171:14 | Store: ... = ... |
+| test.cpp:170:42:170:44 | end indirection | test.cpp:170:42:170:44 | end |
| test.cpp:174:20:174:22 | arr indirection [begin] | test.cpp:174:25:174:29 | begin |
| test.cpp:174:20:174:22 | arr indirection [begin] | test.cpp:174:25:174:29 | begin indirection |
| test.cpp:174:25:174:29 | begin | test.cpp:174:48:174:48 | p |
| test.cpp:174:25:174:29 | begin indirection | test.cpp:174:48:174:48 | p |
+| test.cpp:174:36:174:38 | arr indirection [end] | test.cpp:174:41:174:43 | end |
+| test.cpp:174:36:174:38 | arr indirection [end] | test.cpp:174:41:174:43 | end indirection |
+| test.cpp:174:41:174:43 | end | test.cpp:171:9:171:14 | Store: ... = ... |
+| test.cpp:174:41:174:43 | end indirection | test.cpp:174:41:174:43 | end |
| test.cpp:180:19:180:28 | call to mk_array_p indirection [begin] | test.cpp:165:29:165:31 | arr indirection [begin] |
+| test.cpp:180:19:180:28 | call to mk_array_p indirection [end] | test.cpp:165:29:165:31 | arr indirection [end] |
| test.cpp:188:15:188:20 | call to malloc | test.cpp:189:15:189:15 | p |
| test.cpp:194:23:194:28 | call to malloc | test.cpp:195:17:195:17 | p |
| test.cpp:194:23:194:28 | call to malloc | test.cpp:197:8:197:8 | p |
@@ -513,13 +590,26 @@ edges
| test.cpp:261:14:261:15 | xs | test.cpp:261:14:261:21 | ... + ... |
| test.cpp:261:14:261:15 | xs | test.cpp:261:14:261:21 | ... + ... |
| test.cpp:261:14:261:15 | xs | test.cpp:261:14:261:21 | ... + ... |
+| test.cpp:261:14:261:15 | xs | test.cpp:261:14:261:21 | ... + ... |
+| test.cpp:261:14:261:15 | xs | test.cpp:262:26:262:28 | end |
+| test.cpp:261:14:261:15 | xs | test.cpp:262:26:262:28 | end |
| test.cpp:261:14:261:15 | xs | test.cpp:262:31:262:31 | x |
| test.cpp:261:14:261:15 | xs | test.cpp:264:14:264:14 | x |
| test.cpp:261:14:261:15 | xs | test.cpp:264:14:264:14 | x |
| test.cpp:261:14:261:21 | ... + ... | test.cpp:261:14:261:21 | ... + ... |
+| test.cpp:261:14:261:21 | ... + ... | test.cpp:261:14:261:21 | ... + ... |
+| test.cpp:261:14:261:21 | ... + ... | test.cpp:262:26:262:28 | end |
+| test.cpp:261:14:261:21 | ... + ... | test.cpp:262:26:262:28 | end |
+| test.cpp:261:14:261:21 | ... + ... | test.cpp:262:26:262:28 | end |
+| test.cpp:261:14:261:21 | ... + ... | test.cpp:262:26:262:28 | end |
| test.cpp:261:14:261:21 | ... + ... | test.cpp:264:13:264:14 | Load: * ... |
| test.cpp:261:14:261:21 | ... + ... | test.cpp:264:13:264:14 | Load: * ... |
| test.cpp:261:14:261:21 | ... + ... | test.cpp:264:13:264:14 | Load: * ... |
+| test.cpp:261:14:261:21 | ... + ... | test.cpp:264:13:264:14 | Load: * ... |
+| test.cpp:262:26:262:28 | end | test.cpp:262:26:262:28 | end |
+| test.cpp:262:26:262:28 | end | test.cpp:262:26:262:28 | end |
+| test.cpp:262:26:262:28 | end | test.cpp:264:13:264:14 | Load: * ... |
+| test.cpp:262:26:262:28 | end | test.cpp:264:13:264:14 | Load: * ... |
| test.cpp:262:31:262:31 | x | test.cpp:264:13:264:14 | Load: * ... |
| test.cpp:264:14:264:14 | x | test.cpp:262:31:262:31 | x |
| test.cpp:264:14:264:14 | x | test.cpp:264:13:264:14 | Load: * ... |
@@ -529,14 +619,27 @@ edges
| test.cpp:271:14:271:15 | xs | test.cpp:271:14:271:21 | ... + ... |
| test.cpp:271:14:271:15 | xs | test.cpp:271:14:271:21 | ... + ... |
| test.cpp:271:14:271:15 | xs | test.cpp:271:14:271:21 | ... + ... |
+| test.cpp:271:14:271:15 | xs | test.cpp:271:14:271:21 | ... + ... |
+| test.cpp:271:14:271:15 | xs | test.cpp:272:26:272:28 | end |
+| test.cpp:271:14:271:15 | xs | test.cpp:272:26:272:28 | end |
| test.cpp:271:14:271:15 | xs | test.cpp:272:31:272:31 | x |
| test.cpp:271:14:271:15 | xs | test.cpp:274:5:274:6 | * ... |
| test.cpp:271:14:271:15 | xs | test.cpp:274:6:274:6 | x |
| test.cpp:271:14:271:15 | xs | test.cpp:274:6:274:6 | x |
| test.cpp:271:14:271:21 | ... + ... | test.cpp:271:14:271:21 | ... + ... |
+| test.cpp:271:14:271:21 | ... + ... | test.cpp:271:14:271:21 | ... + ... |
+| test.cpp:271:14:271:21 | ... + ... | test.cpp:272:26:272:28 | end |
+| test.cpp:271:14:271:21 | ... + ... | test.cpp:272:26:272:28 | end |
+| test.cpp:271:14:271:21 | ... + ... | test.cpp:272:26:272:28 | end |
+| test.cpp:271:14:271:21 | ... + ... | test.cpp:272:26:272:28 | end |
| test.cpp:271:14:271:21 | ... + ... | test.cpp:274:5:274:10 | Store: ... = ... |
| test.cpp:271:14:271:21 | ... + ... | test.cpp:274:5:274:10 | Store: ... = ... |
| test.cpp:271:14:271:21 | ... + ... | test.cpp:274:5:274:10 | Store: ... = ... |
+| test.cpp:271:14:271:21 | ... + ... | test.cpp:274:5:274:10 | Store: ... = ... |
+| test.cpp:272:26:272:28 | end | test.cpp:272:26:272:28 | end |
+| test.cpp:272:26:272:28 | end | test.cpp:272:26:272:28 | end |
+| test.cpp:272:26:272:28 | end | test.cpp:274:5:274:10 | Store: ... = ... |
+| test.cpp:272:26:272:28 | end | test.cpp:274:5:274:10 | Store: ... = ... |
| test.cpp:272:31:272:31 | x | test.cpp:274:5:274:10 | Store: ... = ... |
| test.cpp:274:5:274:6 | * ... | test.cpp:274:5:274:10 | Store: ... = ... |
| test.cpp:274:6:274:6 | x | test.cpp:272:31:272:31 | x |
@@ -557,6 +660,11 @@ edges
| test.cpp:42:14:42:15 | Load: * ... | test.cpp:40:15:40:20 | call to malloc | test.cpp:42:14:42:15 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:40:15:40:20 | call to malloc | call to malloc | test.cpp:41:20:41:27 | ... - ... | ... - ... |
| test.cpp:44:14:44:21 | Load: * ... | test.cpp:40:15:40:20 | call to malloc | test.cpp:44:14:44:21 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:40:15:40:20 | call to malloc | call to malloc | test.cpp:41:20:41:27 | ... - ... | ... - ... |
| test.cpp:44:14:44:21 | Load: * ... | test.cpp:40:15:40:20 | call to malloc | test.cpp:44:14:44:21 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:40:15:40:20 | call to malloc | call to malloc | test.cpp:41:20:41:27 | ... - ... | ... - ... |
+| test.cpp:67:9:67:14 | Store: ... = ... | test.cpp:52:19:52:24 | call to malloc | test.cpp:67:9:67:14 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:52:19:52:24 | call to malloc | call to malloc | test.cpp:53:20:53:23 | size | size |
+| test.cpp:96:9:96:14 | Store: ... = ... | test.cpp:82:17:82:22 | call to malloc | test.cpp:96:9:96:14 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:82:17:82:22 | call to malloc | call to malloc | test.cpp:83:27:83:30 | size | size |
+| test.cpp:110:9:110:14 | Store: ... = ... | test.cpp:82:17:82:22 | call to malloc | test.cpp:110:9:110:14 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:82:17:82:22 | call to malloc | call to malloc | test.cpp:83:27:83:30 | size | size |
+| test.cpp:157:9:157:14 | Store: ... = ... | test.cpp:143:18:143:23 | call to malloc | test.cpp:157:9:157:14 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:143:18:143:23 | call to malloc | call to malloc | test.cpp:144:29:144:32 | size | size |
+| test.cpp:171:9:171:14 | Store: ... = ... | test.cpp:143:18:143:23 | call to malloc | test.cpp:171:9:171:14 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:143:18:143:23 | call to malloc | call to malloc | test.cpp:144:29:144:32 | size | size |
| test.cpp:201:5:201:19 | Store: ... = ... | test.cpp:194:23:194:28 | call to malloc | test.cpp:201:5:201:19 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:194:23:194:28 | call to malloc | call to malloc | test.cpp:195:21:195:23 | len | len |
| test.cpp:213:5:213:13 | Store: ... = ... | test.cpp:205:23:205:28 | call to malloc | test.cpp:213:5:213:13 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:205:23:205:28 | call to malloc | call to malloc | test.cpp:206:21:206:23 | len | len |
| test.cpp:232:3:232:20 | Store: ... = ... | test.cpp:231:18:231:30 | new[] | test.cpp:232:3:232:20 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:231:18:231:30 | new[] | new[] | test.cpp:232:11:232:15 | index | index |
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/test.cpp
index 109faa678be..fd971c786cb 100644
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/test.cpp
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/test.cpp
@@ -64,7 +64,7 @@ void test5(int size) {
}
for (char* p = begin; p <= end; ++p) {
- *p = 0; // BAD [NOT DETECTED]
+ *p = 0; // BAD
}
for (char* p = begin; p < end; ++p) {
@@ -93,7 +93,7 @@ void test6(int size) {
}
for (char* p = arr.begin; p <= arr.end; ++p) {
- *p = 0; // BAD [NOT DETECTED]
+ *p = 0; // BAD
}
for (char* p = arr.begin; p < arr.end; ++p) {
@@ -107,7 +107,7 @@ void test7_callee(array_t arr) {
}
for (char* p = arr.begin; p <= arr.end; ++p) {
- *p = 0; // BAD [NOT DETECTED]
+ *p = 0; // BAD
}
for (char* p = arr.begin; p < arr.end; ++p) {
@@ -154,7 +154,7 @@ void test9(int size) {
}
for (char* p = arr->begin; p <= arr->end; ++p) {
- *p = 0; // BAD [NOT DETECTED]
+ *p = 0; // BAD
}
for (char* p = arr->begin; p < arr->end; ++p) {
@@ -168,7 +168,7 @@ void test10_callee(array_t *arr) {
}
for (char* p = arr->begin; p <= arr->end; ++p) {
- *p = 0; // BAD [NOT DETECTED]
+ *p = 0; // BAD
}
for (char* p = arr->begin; p < arr->end; ++p) {
From 837f16c212bc405641d9007fc3e641003132b4ed Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Fri, 28 Apr 2023 12:15:51 +0100
Subject: [PATCH 082/870] Swift: Address singleton set literal warning
---
.../codeql-language-guides/analyzing-data-flow-in-swift.rst | 2 +-
swift/ql/examples/snippets/simple_sql_injection.ql | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/codeql/codeql-language-guides/analyzing-data-flow-in-swift.rst b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-swift.rst
index 6ba39061232..f117f233109 100644
--- a/docs/codeql/codeql-language-guides/analyzing-data-flow-in-swift.rst
+++ b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-swift.rst
@@ -267,7 +267,7 @@ The following global taint-tracking query finds places where a value from a remo
predicate isSink(DataFlow::Node node) {
exists(CallExpr call |
- call.getStaticTarget().(MethodDecl).hasQualifiedName("Connection", ["execute(_:)"]) and
+ call.getStaticTarget().(MethodDecl).hasQualifiedName("Connection", "execute(_:)") and
call.getArgument(0).getExpr() = node.asExpr()
)
}
diff --git a/swift/ql/examples/snippets/simple_sql_injection.ql b/swift/ql/examples/snippets/simple_sql_injection.ql
index 6aaa3a50701..7695e62e599 100644
--- a/swift/ql/examples/snippets/simple_sql_injection.ql
+++ b/swift/ql/examples/snippets/simple_sql_injection.ql
@@ -17,7 +17,7 @@ module SqlInjectionConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node node) {
exists(CallExpr call |
- call.getStaticTarget().(MethodDecl).hasQualifiedName("Connection", ["execute(_:)"]) and
+ call.getStaticTarget().(MethodDecl).hasQualifiedName("Connection", "execute(_:)") and
call.getArgument(0).getExpr() = node.asExpr()
)
}
From e0074d52ebe59c4a380d585d0a72f7ccaabd1090 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Fri, 28 Apr 2023 10:56:35 +0200
Subject: [PATCH 083/870] Add autogenerated models for org.apache.commons.net
---
.../org.apache.commons.net.model.yml | 1416 +++++++++++++++++
1 file changed, 1416 insertions(+)
create mode 100644 java/ql/lib/ext/generated/org.apache.commons.net.model.yml
diff --git a/java/ql/lib/ext/generated/org.apache.commons.net.model.yml b/java/ql/lib/ext/generated/org.apache.commons.net.model.yml
new file mode 100644
index 00000000000..49c61eb4328
--- /dev/null
+++ b/java/ql/lib/ext/generated/org.apache.commons.net.model.yml
@@ -0,0 +1,1416 @@
+# THIS FILE IS AN AUTO-GENERATED MODELS AS DATA FILE. DO NOT EDIT.
+# Definitions of models for the org.apache.commons.net framework.
+extensions:
+ - addsTo:
+ pack: codeql/java-all
+ extensible: sinkModel
+ data:
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "appendFile", "(String,InputStream)", "", "Argument[this]", "open-url", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "appendFileStream", "(String)", "", "Argument[this]", "open-url", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "initiateListParsing", "()", "", "Argument[this]", "open-url", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "initiateListParsing", "(String)", "", "Argument[this]", "open-url", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "initiateListParsing", "(String,String)", "", "Argument[this]", "open-url", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "initiateMListParsing", "()", "", "Argument[this]", "open-url", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "initiateMListParsing", "(String)", "", "Argument[this]", "open-url", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listDirectories", "()", "", "Argument[this]", "open-url", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listDirectories", "(String)", "", "Argument[this]", "open-url", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listFiles", "()", "", "Argument[this]", "open-url", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listFiles", "(String)", "", "Argument[this]", "open-url", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listFiles", "(String,FTPFileFilter)", "", "Argument[this]", "open-url", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listNames", "()", "", "Argument[this]", "open-url", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listNames", "(String)", "", "Argument[this]", "open-url", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "mlistDir", "()", "", "Argument[this]", "open-url", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "mlistDir", "(String)", "", "Argument[this]", "open-url", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "mlistDir", "(String,FTPFileFilter)", "", "Argument[this]", "open-url", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "retrieveFile", "(String,OutputStream)", "", "Argument[this]", "open-url", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "retrieveFileStream", "(String)", "", "Argument[this]", "open-url", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "storeFile", "(String,InputStream)", "", "Argument[this]", "open-url", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "storeFileStream", "(String)", "", "Argument[this]", "open-url", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "storeUniqueFile", "(InputStream)", "", "Argument[this]", "open-url", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "storeUniqueFile", "(String,InputStream)", "", "Argument[this]", "open-url", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "storeUniqueFileStream", "()", "", "Argument[this]", "open-url", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "storeUniqueFileStream", "(String)", "", "Argument[this]", "open-url", "df-generated"]
+ - ["org.apache.commons.net.util", "KeyManagerUtils", false, "createClientKeyManager", "(File,String)", "", "Argument[0]", "read-file", "df-generated"]
+ - ["org.apache.commons.net.util", "KeyManagerUtils", false, "createClientKeyManager", "(File,String,String)", "", "Argument[0]", "read-file", "df-generated"]
+ - ["org.apache.commons.net.util", "KeyManagerUtils", false, "createClientKeyManager", "(String,File,String,String,String)", "", "Argument[1]", "read-file", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", true, "connect", "(String,int)", "", "Argument[this]", "open-url", "df-generated"]
+ - addsTo:
+ pack: codeql/java-all
+ extensible: sourceModel
+ data:
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listNames", "()", "", "ReturnValue", "remote", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listNames", "(String)", "", "ReturnValue", "remote", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "retrieveFile", "(String,OutputStream)", "", "Argument[1]", "remote", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "retrieveFileStream", "(String)", "", "ReturnValue", "remote", "df-generated"]
+ - addsTo:
+ pack: codeql/java-all
+ extensible: summaryModel
+ data:
+ - ["org.apache.commons.net.bsd", "RCommandClient", true, "rcommand", "(String,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.bsd", "RCommandClient", true, "rcommand", "(String,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.bsd", "RCommandClient", true, "rcommand", "(String,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.bsd", "RCommandClient", true, "rcommand", "(String,String,String,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.bsd", "RCommandClient", true, "rcommand", "(String,String,String,boolean)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.bsd", "RCommandClient", true, "rcommand", "(String,String,String,boolean)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.bsd", "RExecClient", true, "getErrorStream", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.bsd", "RExecClient", true, "getInputStream", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.bsd", "RExecClient", true, "getOutputStream", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.bsd", "RExecClient", true, "rexec", "(String,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.bsd", "RExecClient", true, "rexec", "(String,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.bsd", "RExecClient", true, "rexec", "(String,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.bsd", "RExecClient", true, "rexec", "(String,String,String,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.bsd", "RExecClient", true, "rexec", "(String,String,String,boolean)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.bsd", "RExecClient", true, "rexec", "(String,String,String,boolean)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.bsd", "RLoginClient", true, "rlogin", "(String,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.bsd", "RLoginClient", true, "rlogin", "(String,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.bsd", "RLoginClient", true, "rlogin", "(String,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.bsd", "RLoginClient", true, "rlogin", "(String,String,String,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.bsd", "RLoginClient", true, "rlogin", "(String,String,String,int)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.bsd", "RLoginClient", true, "rlogin", "(String,String,String,int)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.chargen", "CharGenTCPClient", false, "getInputStream", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.chargen", "CharGenUDPClient", false, "receive", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.daytime", "DaytimeTCPClient", false, "getTime", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.discard", "DiscardTCPClient", true, "getOutputStream", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.echo", "EchoTCPClient", false, "getInputStream", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.finger", "FingerClient", true, "getInputStream", "(boolean)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.finger", "FingerClient", true, "getInputStream", "(boolean,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.finger", "FingerClient", true, "getInputStream", "(boolean,String,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.finger", "FingerClient", true, "query", "(boolean)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.finger", "FingerClient", true, "query", "(boolean,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "CompositeFileEntryParser", true, "CompositeFileEntryParser", "(FTPFileEntryParser[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "FTPTimestampParserImpl", true, "getDefaultDateFormat", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "FTPTimestampParserImpl", true, "getRecentDateFormat", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "FTPTimestampParserImpl", true, "getServerTimeZone", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "FTPTimestampParserImpl", true, "getShortMonths", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "FTPTimestampParserImpl", true, "parseTimestamp", "(String,Calendar)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "MLSxEntryParser", true, "parseEntry", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "ParserInitializationException", true, "ParserInitializationException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "ParserInitializationException", true, "ParserInitializationException", "(String,Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "ParserInitializationException", true, "ParserInitializationException", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "ParserInitializationException", true, "getRootCause", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "RegexFTPFileEntryParserImpl", true, "matches", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "VMSFTPEntryParser", true, "parseFileList", "(InputStream)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "VMSFTPEntryParser", true, "parseFileList", "(InputStream)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "Configurable", true, "configure", "(FTPClientConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "acct", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "appe", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "cwd", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "dele", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "eprt", "(InetAddress,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "getControlEncoding", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "getReplyString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "getReplyStrings", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "help", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "list", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "mdtm", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "mfmt", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "mfmt", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "mkd", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "mlsd", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "mlst", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "nlst", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "pass", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "port", "(InetAddress,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "rest", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "retr", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "rmd", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "rnfr", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "rnto", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "sendCommand", "(FTPCmd,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "sendCommand", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "sendCommand", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "sendCommand", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "sendCommand", "(int,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "setControlEncoding", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "site", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "size", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "smnt", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "stat", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "stor", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "stou", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", true, "user", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient$HostnameResolver", true, "resolve", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient$HostnameResolver", true, "resolve", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient$NatServerResolverImpl", true, "NatServerResolverImpl", "(FTPClient)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "appendFile", "(String,InputStream)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "appendFileStream", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "changeWorkingDirectory", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "deleteFile", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "doCommand", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "doCommand", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "doCommandAsStrings", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "doCommandAsStrings", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "doCommandAsStrings", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "doCommandAsStrings", "(String,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "doCommandAsStrings", "(String,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "enterRemoteActiveMode", "(InetAddress,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "featureValue", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "featureValues", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "getControlKeepAliveReplyTimeoutDuration", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "getControlKeepAliveTimeoutDuration", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "getCopyStreamListener", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "getDataTimeout", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "getModificationTime", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "getModificationTime", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "getModificationTime", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "getPassiveHost", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "getPassiveLocalIPAddress", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "getSize", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "getSize", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "getSize", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "getStatus", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "getStatus", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "getStatus", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "getStatus", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "getSystemName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "getSystemType", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "initiateListParsing", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "initiateListParsing", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "initiateListParsing", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "initiateListParsing", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "initiateListParsing", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "initiateListParsing", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "initiateListParsing", "(String,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "initiateMListParsing", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listDirectories", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listFiles", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listFiles", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listFiles", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listFiles", "(String,FTPFileFilter)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listHelp", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listHelp", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listHelp", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listHelp", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listNames", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "login", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "login", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "login", "(String,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "login", "(String,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "login", "(String,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "makeDirectory", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "mdtmCalendar", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "mdtmFile", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "mdtmFile", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "mdtmFile", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "mdtmInstant", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "mlistDir", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "mlistDir", "(String,FTPFileFilter)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "mlistFile", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "mlistFile", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "mlistFile", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "printWorkingDirectory", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "remoteAppend", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "remoteRetrieve", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "remoteStore", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "remoteStoreUnique", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "removeDirectory", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "rename", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "rename", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "retrieveFile", "(String,OutputStream)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "retrieveFileStream", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "sendSiteCommand", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "setActiveExternalIPAddress", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "setControlKeepAliveReplyTimeout", "(Duration)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "setControlKeepAliveTimeout", "(Duration)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "setCopyStreamListener", "(CopyStreamListener)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "setDataTimeout", "(Duration)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "setModificationTime", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "setModificationTime", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "setParserFactory", "(FTPFileEntryParserFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "setPassiveLocalIPAddress", "(InetAddress)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "setPassiveLocalIPAddress", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "setPassiveNatWorkaroundStrategy", "(HostnameResolver)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "setReportActiveExternalIPAddress", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "storeFile", "(String,InputStream)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "storeFileStream", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "storeUniqueFile", "(String,InputStream)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "storeUniqueFileStream", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "structureMount", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "FTPClientConfig", "(FTPClientConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "FTPClientConfig", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "FTPClientConfig", "(String,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "FTPClientConfig", "(String,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "FTPClientConfig", "(String,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "FTPClientConfig", "(String,String,String,String,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "FTPClientConfig", "(String,String,String,String,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "FTPClientConfig", "(String,String,String,String,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "FTPClientConfig", "(String,String,String,String,String,String)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "FTPClientConfig", "(String,String,String,String,String,String)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "FTPClientConfig", "(String,String,String,String,String,String)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "FTPClientConfig", "(String,String,String,String,String,String,boolean,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "FTPClientConfig", "(String,String,String,String,String,String,boolean,boolean)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "FTPClientConfig", "(String,String,String,String,String,String,boolean,boolean)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "FTPClientConfig", "(String,String,String,String,String,String,boolean,boolean)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "FTPClientConfig", "(String,String,String,String,String,String,boolean,boolean)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "FTPClientConfig", "(String,String,String,String,String,String,boolean,boolean)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "getDefaultDateFormatStr", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "getRecentDateFormatStr", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "getServerLanguageCode", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "getServerSystemKey", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "getServerTimeZoneId", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "getShortMonthNames", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "setDefaultDateFormatStr", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "setRecentDateFormatStr", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "setServerLanguageCode", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "setServerTimeZoneId", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", true, "setShortMonthNames", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPConnectionClosedException", true, "FTPConnectionClosedException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", true, "getGroup", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", true, "getLink", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", true, "getRawListing", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", true, "getTimestamp", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", true, "getUser", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", true, "setGroup", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", true, "setLink", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", true, "setRawListing", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", true, "setTimestamp", "(Calendar)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", true, "setUser", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", true, "toFormattedString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", true, "toFormattedString", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFileEntryParser", true, "parseFTPEntry", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFileEntryParser", true, "parseFTPEntry", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFileEntryParser", true, "preParse", "(List)", "", "Argument[0].Element", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFileEntryParser", true, "readNextEntry", "(BufferedReader)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPHTTPClient", true, "FTPHTTPClient", "(String,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPHTTPClient", true, "FTPHTTPClient", "(String,int,Charset)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPHTTPClient", true, "FTPHTTPClient", "(String,int,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPHTTPClient", true, "FTPHTTPClient", "(String,int,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPHTTPClient", true, "FTPHTTPClient", "(String,int,String,String)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPHTTPClient", true, "FTPHTTPClient", "(String,int,String,String,Charset)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPHTTPClient", true, "FTPHTTPClient", "(String,int,String,String,Charset)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPHTTPClient", true, "FTPHTTPClient", "(String,int,String,String,Charset)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPListParseEngine", true, "FTPListParseEngine", "(FTPFileEntryParser)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPListParseEngine", true, "getFileList", "(FTPFileFilter)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPListParseEngine", true, "getFiles", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPListParseEngine", true, "getFiles", "(FTPFileFilter)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPListParseEngine", true, "getNext", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPListParseEngine", true, "getPrevious", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPListParseEngine", true, "readServerList", "(InputStream)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPListParseEngine", true, "readServerList", "(InputStream,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", true, "FTPSClient", "(SSLContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", true, "FTPSClient", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", true, "FTPSClient", "(String,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", true, "FTPSClient", "(boolean,SSLContext)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", true, "execADAT", "(byte[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", true, "execAUTH", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", true, "execCONF", "(byte[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", true, "execENC", "(byte[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", true, "execMIC", "(byte[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", true, "execPROT", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", true, "getAuthValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", true, "getEnabledCipherSuites", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", true, "getEnabledProtocols", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", true, "getHostnameVerifier", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", true, "getTrustManager", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", true, "parseADATReply", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", true, "setAuthValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", true, "setEnabledCipherSuites", "(String[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", true, "setEnabledProtocols", "(String[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", true, "setHostnameVerifier", "(HostnameVerifier)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", true, "setKeyManager", "(KeyManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", true, "setTrustManager", "(TrustManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSServerSocketFactory", true, "FTPSServerSocketFactory", "(SSLContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSServerSocketFactory", true, "init", "(ServerSocket)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSSocketFactory", true, "FTPSSocketFactory", "(SSLContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSSocketFactory", true, "init", "(ServerSocket)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "AuthenticatingIMAPClient", true, "AuthenticatingIMAPClient", "(SSLContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "AuthenticatingIMAPClient", true, "AuthenticatingIMAPClient", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "AuthenticatingIMAPClient", true, "AuthenticatingIMAPClient", "(String,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "AuthenticatingIMAPClient", true, "AuthenticatingIMAPClient", "(String,boolean,SSLContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "AuthenticatingIMAPClient", true, "AuthenticatingIMAPClient", "(String,boolean,SSLContext)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "AuthenticatingIMAPClient", true, "AuthenticatingIMAPClient", "(boolean,SSLContext)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "AuthenticatingIMAPClient", true, "auth", "(AUTH_METHOD,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "AuthenticatingIMAPClient", true, "auth", "(AUTH_METHOD,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "AuthenticatingIMAPClient", true, "authenticate", "(AUTH_METHOD,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "AuthenticatingIMAPClient", true, "authenticate", "(AUTH_METHOD,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAP$IMAPChunkListener", true, "chunkReceived", "(IMAP)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAP", true, "doCommand", "(IMAPCommand,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAP", true, "getReplyString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAP", true, "getReplyStrings", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAP", true, "sendCommand", "(IMAPCommand,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAP", true, "sendCommand", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAP", true, "sendCommand", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAP", true, "sendCommand", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAP", true, "sendData", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAP", true, "setChunkListener", "(IMAPChunkListener)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "append", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "append", "(String,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "append", "(String,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "append", "(String,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "append", "(String,String,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "append", "(String,String,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "append", "(String,String,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "append", "(String,String,String,String)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "copy", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "copy", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "create", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "delete", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "examine", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "fetch", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "fetch", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "list", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "list", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "login", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "login", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "lsub", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "lsub", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "rename", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "rename", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "search", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "search", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "search", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "select", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "status", "(String,String[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "status", "(String,String[])", "", "Argument[1].ArrayElement", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "store", "(String,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "store", "(String,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "store", "(String,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "subscribe", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "uid", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "uid", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", true, "unsubscribe", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPSClient", true, "IMAPSClient", "(SSLContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPSClient", true, "IMAPSClient", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPSClient", true, "IMAPSClient", "(String,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPSClient", true, "IMAPSClient", "(String,boolean,SSLContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPSClient", true, "IMAPSClient", "(String,boolean,SSLContext)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPSClient", true, "IMAPSClient", "(boolean,SSLContext)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPSClient", true, "getEnabledCipherSuites", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPSClient", true, "getEnabledProtocols", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPSClient", true, "getHostnameVerifier", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPSClient", true, "getTrustManager", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPSClient", true, "setEnabledCipherSuites", "(String[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPSClient", true, "setEnabledProtocols", "(String[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPSClient", true, "setHostnameVerifier", "(HostnameVerifier)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPSClient", true, "setKeyManager", "(KeyManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPSClient", true, "setTrustManager", "(TrustManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.io", "CRLFLineReader", false, "CRLFLineReader", "(Reader)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.io", "CopyStreamEvent", true, "CopyStreamEvent", "(Object,long,int,long)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.io", "CopyStreamEvent", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.io", "CopyStreamException", true, "CopyStreamException", "(String,long,IOException)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.io", "CopyStreamException", true, "getIOException", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.io", "DotTerminatedMessageReader", false, "DotTerminatedMessageReader", "(Reader)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.io", "DotTerminatedMessageWriter", false, "DotTerminatedMessageWriter", "(Writer)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.io", "FromNetASCIIOutputStream", false, "FromNetASCIIOutputStream", "(OutputStream)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.io", "SocketInputStream", true, "SocketInputStream", "(Socket,InputStream)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.io", "SocketOutputStream", true, "SocketOutputStream", "(Socket,OutputStream)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.io", "SocketOutputStream", true, "SocketOutputStream", "(Socket,OutputStream)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.io", "ToNetASCIIOutputStream", false, "ToNetASCIIOutputStream", "(OutputStream)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.io", "Util", false, "copyReader", "(Reader,Writer)", "", "Argument[0]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.io", "Util", false, "copyReader", "(Reader,Writer,int)", "", "Argument[0]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.io", "Util", false, "copyReader", "(Reader,Writer,int,long,CopyStreamListener)", "", "Argument[0]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.io", "Util", false, "copyStream", "(InputStream,OutputStream)", "", "Argument[0]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.io", "Util", false, "copyStream", "(InputStream,OutputStream,int)", "", "Argument[0]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.io", "Util", false, "copyStream", "(InputStream,OutputStream,int,long,CopyStreamListener)", "", "Argument[0]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.io", "Util", false, "copyStream", "(InputStream,OutputStream,int,long,CopyStreamListener,boolean)", "", "Argument[0]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Article", true, "addReference", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Article", true, "getArticleId", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Article", true, "getDate", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Article", true, "getFrom", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Article", true, "getReferences", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Article", true, "getSubject", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Article", true, "setArticleId", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Article", true, "setDate", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Article", true, "setFrom", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Article", true, "setSubject", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Article", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", true, "article", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", true, "authinfoPass", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", true, "authinfoUser", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", true, "body", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", true, "getReplyString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", true, "group", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", true, "head", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", true, "ihave", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", true, "listActive", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", true, "newgroups", "(String,String,boolean,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", true, "newgroups", "(String,String,boolean,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", true, "newgroups", "(String,String,boolean,String)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", true, "newnews", "(String,String,String,boolean,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", true, "newnews", "(String,String,String,boolean,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", true, "newnews", "(String,String,String,boolean,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", true, "newnews", "(String,String,String,boolean,String)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", true, "sendCommand", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", true, "sendCommand", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", true, "sendCommand", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", true, "sendCommand", "(int,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", true, "stat", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", true, "xhdr", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", true, "xhdr", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", true, "xover", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "authenticate", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "authenticate", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "forwardArticle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "forwardArticle", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "forwardArticle", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "iterateNewNews", "(NewGroupsOrNewsQuery)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "iterateNewNews", "(NewGroupsOrNewsQuery)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "iterateNewNews", "(NewGroupsOrNewsQuery)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "iterateNewNewsgroupListing", "(NewGroupsOrNewsQuery)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "iterateNewNewsgroupListing", "(NewGroupsOrNewsQuery)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "iterateNewNewsgroupListing", "(NewGroupsOrNewsQuery)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "iterateNewNewsgroups", "(NewGroupsOrNewsQuery)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "iterateNewsgroupListing", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "iterateNewsgroupListing", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "iterateNewsgroupListing", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "iterateNewsgroupListing", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "iterateNewsgroups", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "listHelp", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "listNewNews", "(NewGroupsOrNewsQuery)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "listNewNews", "(NewGroupsOrNewsQuery)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "listNewNews", "(NewGroupsOrNewsQuery)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "listNewNewsgroups", "(NewGroupsOrNewsQuery)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "listNewNewsgroups", "(NewGroupsOrNewsQuery)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "listNewsgroups", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "listNewsgroups", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "listNewsgroups", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "listOverviewFmt", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "postArticle", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticle", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticle", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticle", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticle", "(String,ArticleInfo)", "", "Argument[0]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticle", "(String,ArticleInfo)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticle", "(String,ArticleInfo)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticle", "(String,ArticleInfo)", "", "Argument[this]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticle", "(String,ArticleInfo)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticle", "(String,ArticlePointer)", "", "Argument[0]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticle", "(String,ArticlePointer)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticle", "(String,ArticlePointer)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticle", "(String,ArticlePointer)", "", "Argument[this]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticle", "(String,ArticlePointer)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticle", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticle", "(int,ArticlePointer)", "", "Argument[this]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticle", "(int,ArticlePointer)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticle", "(long)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticle", "(long,ArticleInfo)", "", "Argument[this]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticle", "(long,ArticleInfo)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleBody", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleBody", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleBody", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleBody", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleBody", "(String,ArticleInfo)", "", "Argument[0]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleBody", "(String,ArticleInfo)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleBody", "(String,ArticleInfo)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleBody", "(String,ArticleInfo)", "", "Argument[this]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleBody", "(String,ArticleInfo)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleBody", "(String,ArticlePointer)", "", "Argument[0]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleBody", "(String,ArticlePointer)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleBody", "(String,ArticlePointer)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleBody", "(String,ArticlePointer)", "", "Argument[this]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleBody", "(String,ArticlePointer)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleBody", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleBody", "(int,ArticlePointer)", "", "Argument[this]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleBody", "(int,ArticlePointer)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleBody", "(long)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleBody", "(long,ArticleInfo)", "", "Argument[this]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleBody", "(long,ArticleInfo)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleHeader", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleHeader", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleHeader", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleHeader", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleHeader", "(String,ArticleInfo)", "", "Argument[0]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleHeader", "(String,ArticleInfo)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleHeader", "(String,ArticleInfo)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleHeader", "(String,ArticleInfo)", "", "Argument[this]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleHeader", "(String,ArticleInfo)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleHeader", "(String,ArticlePointer)", "", "Argument[0]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleHeader", "(String,ArticlePointer)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleHeader", "(String,ArticlePointer)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleHeader", "(String,ArticlePointer)", "", "Argument[this]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleHeader", "(String,ArticlePointer)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleHeader", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleHeader", "(int,ArticlePointer)", "", "Argument[this]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleHeader", "(int,ArticlePointer)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleHeader", "(long)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleHeader", "(long,ArticleInfo)", "", "Argument[this]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleHeader", "(long,ArticleInfo)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleInfo", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleInfo", "(int,int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleInfo", "(long)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveArticleInfo", "(long,long)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveHeader", "(String,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveHeader", "(String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveHeader", "(String,int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveHeader", "(String,int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveHeader", "(String,int,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveHeader", "(String,int,int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveHeader", "(String,long)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveHeader", "(String,long)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveHeader", "(String,long)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveHeader", "(String,long,long)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveHeader", "(String,long,long)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "retrieveHeader", "(String,long,long)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "selectArticle", "(ArticleInfo)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "selectArticle", "(ArticlePointer)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "selectArticle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "selectArticle", "(String,ArticleInfo)", "", "Argument[0]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "selectArticle", "(String,ArticleInfo)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "selectArticle", "(String,ArticleInfo)", "", "Argument[this]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "selectArticle", "(String,ArticlePointer)", "", "Argument[0]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "selectArticle", "(String,ArticlePointer)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "selectArticle", "(String,ArticlePointer)", "", "Argument[this]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "selectArticle", "(int,ArticlePointer)", "", "Argument[this]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "selectArticle", "(long,ArticleInfo)", "", "Argument[this]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "selectNewsgroup", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "selectNewsgroup", "(String,NewsgroupInfo)", "", "Argument[0]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "selectNewsgroup", "(String,NewsgroupInfo)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "selectNewsgroup", "(String,NewsgroupInfo)", "", "Argument[this]", "Argument[1]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "selectNextArticle", "(ArticleInfo)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "selectNextArticle", "(ArticlePointer)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "selectPreviousArticle", "(ArticleInfo)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", true, "selectPreviousArticle", "(ArticlePointer)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPConnectionClosedException", false, "NNTPConnectionClosedException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NewGroupsOrNewsQuery", false, "addDistribution", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NewGroupsOrNewsQuery", false, "addNewsgroup", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NewGroupsOrNewsQuery", false, "getDate", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NewGroupsOrNewsQuery", false, "getDistributions", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NewGroupsOrNewsQuery", false, "getNewsgroups", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NewGroupsOrNewsQuery", false, "getTime", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NewGroupsOrNewsQuery", false, "omitNewsgroup", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NewsgroupInfo", false, "getNewsgroup", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "SimpleNNTPHeader", true, "SimpleNNTPHeader", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "SimpleNNTPHeader", true, "SimpleNNTPHeader", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "SimpleNNTPHeader", true, "addHeaderField", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "SimpleNNTPHeader", true, "addHeaderField", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "SimpleNNTPHeader", true, "addNewsgroup", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "SimpleNNTPHeader", true, "getFromAddress", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "SimpleNNTPHeader", true, "getNewsgroups", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "SimpleNNTPHeader", true, "getSubject", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "SimpleNNTPHeader", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Threadable", true, "messageThreadId", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Threadable", true, "messageThreadReferences", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Threadable", true, "setChild", "(Threadable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Threadable", true, "setNext", "(Threadable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Threadable", true, "simplifiedSubject", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Threader", true, "thread", "(Iterable)", "", "Argument[0].Element", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Threader", true, "thread", "(List)", "", "Argument[0].Element", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Threader", true, "thread", "(Threadable[])", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", true, "getDatagramPacket", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", true, "setDatagramPacket", "(DatagramPacket)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeInfo", true, "TimeInfo", "(NtpV3Packet,long)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeInfo", true, "TimeInfo", "(NtpV3Packet,long,List)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeInfo", true, "TimeInfo", "(NtpV3Packet,long,List)", "", "Argument[2].Element", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeInfo", true, "TimeInfo", "(NtpV3Packet,long,List,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeInfo", true, "TimeInfo", "(NtpV3Packet,long,List,boolean)", "", "Argument[2].Element", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeInfo", true, "TimeInfo", "(NtpV3Packet,long,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeInfo", true, "addComment", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeInfo", true, "getAddress", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeInfo", true, "getComments", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeInfo", true, "getMessage", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "ExtendedPOP3Client", true, "auth", "(AUTH_METHOD,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "ExtendedPOP3Client", true, "auth", "(AUTH_METHOD,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3", true, "getReplyString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3", true, "getReplyStrings", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3", true, "sendCommand", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3", true, "sendCommand", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3", true, "sendCommand", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3", true, "sendCommand", "(int,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3Client", true, "listUniqueIdentifier", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3Client", true, "login", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3Client", true, "login", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3Client", true, "login", "(String,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3Client", true, "retrieveMessage", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3Client", true, "retrieveMessageTop", "(int,int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3MessageInfo", false, "POP3MessageInfo", "(int,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3MessageInfo", false, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3SClient", true, "POP3SClient", "(SSLContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3SClient", true, "POP3SClient", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3SClient", true, "POP3SClient", "(String,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3SClient", true, "POP3SClient", "(String,boolean,SSLContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3SClient", true, "POP3SClient", "(String,boolean,SSLContext)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3SClient", true, "POP3SClient", "(boolean,SSLContext)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3SClient", true, "getEnabledCipherSuites", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3SClient", true, "getEnabledProtocols", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3SClient", true, "getHostnameVerifier", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3SClient", true, "getTrustManager", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3SClient", true, "setEnabledCipherSuites", "(String[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3SClient", true, "setEnabledProtocols", "(String[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3SClient", true, "setHostnameVerifier", "(HostnameVerifier)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3SClient", true, "setKeyManager", "(KeyManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3SClient", true, "setTrustManager", "(TrustManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "AuthenticatingSMTPClient", true, "AuthenticatingSMTPClient", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "AuthenticatingSMTPClient", true, "AuthenticatingSMTPClient", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "AuthenticatingSMTPClient", true, "AuthenticatingSMTPClient", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "AuthenticatingSMTPClient", true, "AuthenticatingSMTPClient", "(String,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "AuthenticatingSMTPClient", true, "AuthenticatingSMTPClient", "(String,boolean,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "AuthenticatingSMTPClient", true, "AuthenticatingSMTPClient", "(String,boolean,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "AuthenticatingSMTPClient", true, "AuthenticatingSMTPClient", "(boolean,SSLContext)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "AuthenticatingSMTPClient", true, "auth", "(AUTH_METHOD,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "AuthenticatingSMTPClient", true, "auth", "(AUTH_METHOD,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "AuthenticatingSMTPClient", true, "ehlo", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "AuthenticatingSMTPClient", true, "elogin", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "RelayPath", false, "RelayPath", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "RelayPath", false, "addRelay", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "RelayPath", false, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTP", true, "SMTP", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTP", true, "expn", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTP", true, "getReplyString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTP", true, "getReplyStrings", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTP", true, "helo", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTP", true, "help", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTP", true, "mail", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTP", true, "rcpt", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTP", true, "saml", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTP", true, "send", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTP", true, "sendCommand", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTP", true, "sendCommand", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTP", true, "sendCommand", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTP", true, "sendCommand", "(int,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTP", true, "soml", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTP", true, "vrfy", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPClient", true, "SMTPClient", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPClient", true, "addRecipient", "(RelayPath)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPClient", true, "addRecipient", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPClient", true, "listHelp", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPClient", true, "listHelp", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPClient", true, "listHelp", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPClient", true, "listHelp", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPClient", true, "login", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPClient", true, "sendMessageData", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPClient", true, "sendSimpleMessage", "(String,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPClient", true, "sendSimpleMessage", "(String,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPClient", true, "sendSimpleMessage", "(String,String[],String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPClient", true, "sendSimpleMessage", "(String,String[],String)", "", "Argument[1].ArrayElement", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPClient", true, "setSender", "(RelayPath)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPClient", true, "setSender", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPClient", true, "verify", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPConnectionClosedException", false, "SMTPConnectionClosedException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPSClient", true, "SMTPSClient", "(SSLContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPSClient", true, "SMTPSClient", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPSClient", true, "SMTPSClient", "(String,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPSClient", true, "SMTPSClient", "(String,boolean,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPSClient", true, "SMTPSClient", "(String,boolean,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPSClient", true, "SMTPSClient", "(boolean,SSLContext)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPSClient", true, "getEnabledCipherSuites", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPSClient", true, "getEnabledProtocols", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPSClient", true, "getHostnameVerifier", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPSClient", true, "getKeyManager", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPSClient", true, "getTrustManager", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPSClient", true, "setEnabledCipherSuites", "(String[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPSClient", true, "setEnabledProtocols", "(String[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPSClient", true, "setHostnameVerifier", "(HostnameVerifier)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPSClient", true, "setKeyManager", "(KeyManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPSClient", true, "setTrustManager", "(TrustManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SimpleSMTPHeader", true, "SimpleSMTPHeader", "(String,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SimpleSMTPHeader", true, "SimpleSMTPHeader", "(String,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SimpleSMTPHeader", true, "SimpleSMTPHeader", "(String,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SimpleSMTPHeader", true, "addCC", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SimpleSMTPHeader", true, "addHeaderField", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SimpleSMTPHeader", true, "addHeaderField", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SimpleSMTPHeader", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.telnet", "InvalidTelnetOptionException", true, "InvalidTelnetOptionException", "(String,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.telnet", "Telnet", true, "addOptionHandler", "(TelnetOptionHandler)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.telnet", "Telnet", true, "registerNotifHandler", "(TelnetNotificationHandler)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetClient", true, "TelnetClient", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetClient", true, "TelnetClient", "(String,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetClient", true, "getInputStream", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetClient", true, "getOutputStream", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetClient", true, "registerInputListener", "(TelnetInputListener)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetClient", true, "registerSpyStream", "(OutputStream)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TerminalTypeOptionHandler", true, "TerminalTypeOptionHandler", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TerminalTypeOptionHandler", true, "TerminalTypeOptionHandler", "(String,boolean,boolean,boolean,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTP", true, "bufferedReceive", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTP", true, "bufferedSend", "(TFTPPacket)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPAckPacket", false, "TFTPAckPacket", "(InetAddress,int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPAckPacket", false, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "receiveFile", "(String,int,OutputStream,InetAddress)", "", "Argument[0]", "Argument[2]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "receiveFile", "(String,int,OutputStream,InetAddress)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "receiveFile", "(String,int,OutputStream,InetAddress)", "", "Argument[3]", "Argument[2]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "receiveFile", "(String,int,OutputStream,InetAddress)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "receiveFile", "(String,int,OutputStream,InetAddress)", "", "Argument[this]", "Argument[2]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "receiveFile", "(String,int,OutputStream,InetAddress,int)", "", "Argument[0]", "Argument[2]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "receiveFile", "(String,int,OutputStream,InetAddress,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "receiveFile", "(String,int,OutputStream,InetAddress,int)", "", "Argument[3]", "Argument[2]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "receiveFile", "(String,int,OutputStream,InetAddress,int)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "receiveFile", "(String,int,OutputStream,InetAddress,int)", "", "Argument[this]", "Argument[2]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "receiveFile", "(String,int,OutputStream,String)", "", "Argument[0]", "Argument[2]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "receiveFile", "(String,int,OutputStream,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "receiveFile", "(String,int,OutputStream,String)", "", "Argument[3]", "Argument[2]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "receiveFile", "(String,int,OutputStream,String)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "receiveFile", "(String,int,OutputStream,String)", "", "Argument[this]", "Argument[2]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "receiveFile", "(String,int,OutputStream,String,int)", "", "Argument[0]", "Argument[2]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "receiveFile", "(String,int,OutputStream,String,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "receiveFile", "(String,int,OutputStream,String,int)", "", "Argument[3]", "Argument[2]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "receiveFile", "(String,int,OutputStream,String,int)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "receiveFile", "(String,int,OutputStream,String,int)", "", "Argument[this]", "Argument[2]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "sendFile", "(String,int,InputStream,InetAddress)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "sendFile", "(String,int,InputStream,InetAddress)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "sendFile", "(String,int,InputStream,InetAddress)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "sendFile", "(String,int,InputStream,InetAddress,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "sendFile", "(String,int,InputStream,InetAddress,int)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "sendFile", "(String,int,InputStream,InetAddress,int)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "sendFile", "(String,int,InputStream,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "sendFile", "(String,int,InputStream,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "sendFile", "(String,int,InputStream,String)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "sendFile", "(String,int,InputStream,String,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "sendFile", "(String,int,InputStream,String,int)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", true, "sendFile", "(String,int,InputStream,String,int)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPDataPacket", false, "TFTPDataPacket", "(InetAddress,int,int,byte[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPDataPacket", false, "TFTPDataPacket", "(InetAddress,int,int,byte[])", "", "Argument[3]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPDataPacket", false, "TFTPDataPacket", "(InetAddress,int,int,byte[],int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPDataPacket", false, "TFTPDataPacket", "(InetAddress,int,int,byte[],int,int)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPDataPacket", false, "getData", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPDataPacket", false, "setData", "(byte[],int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPDataPacket", false, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPErrorPacket", false, "TFTPErrorPacket", "(InetAddress,int,int,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPErrorPacket", false, "TFTPErrorPacket", "(InetAddress,int,int,String)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPErrorPacket", false, "getMessage", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPErrorPacket", false, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPPacket", true, "getAddress", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPPacket", true, "newTFTPPacket", "(DatagramPacket)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPPacket", true, "setAddress", "(InetAddress)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPPacket", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPPacketException", true, "TFTPPacketException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPReadRequestPacket", false, "TFTPReadRequestPacket", "(InetAddress,int,String,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPReadRequestPacket", false, "TFTPReadRequestPacket", "(InetAddress,int,String,int)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPReadRequestPacket", false, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPRequestPacket", true, "getFilename", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPWriteRequestPacket", false, "TFTPWriteRequestPacket", "(InetAddress,int,String,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPWriteRequestPacket", false, "TFTPWriteRequestPacket", "(InetAddress,int,String,int)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPWriteRequestPacket", false, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", true, "Base64", "(int,byte[])", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", true, "Base64", "(int,byte[],boolean)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", true, "decode", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", true, "decode", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", true, "decode", "(byte[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", true, "decode", "(byte[])", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", true, "decodeBase64", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", true, "decodeBase64", "(byte[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", true, "encode", "(byte[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", true, "encode", "(byte[])", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", true, "encodeBase64", "(byte[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", true, "encodeBase64", "(byte[],boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", true, "encodeBase64", "(byte[],boolean,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", true, "encodeBase64", "(byte[],boolean,boolean,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", true, "encodeBase64Chunked", "(byte[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", true, "encodeBase64String", "(byte[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", true, "encodeBase64String", "(byte[],boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", true, "encodeBase64StringUnChunked", "(byte[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", true, "encodeBase64URLSafe", "(byte[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", true, "encodeBase64URLSafeString", "(byte[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", true, "encodeToString", "(byte[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", true, "encodeToString", "(byte[])", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.util", "KeyManagerUtils", false, "createClientKeyManager", "(File,String,String)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.util", "KeyManagerUtils", false, "createClientKeyManager", "(KeyStore,String,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.util", "KeyManagerUtils", false, "createClientKeyManager", "(String,File,String,String,String)", "", "Argument[3]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.util", "ListenerList", true, "addListener", "(EventListener)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net.whois", "WhoisClient", false, "getInputStream", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net.whois", "WhoisClient", false, "query", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net", "DatagramSocketClient", true, "getLocalAddress", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net", "DatagramSocketClient", true, "setDatagramSocketFactory", "(DatagramSocketFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net", "DefaultSocketFactory", true, "DefaultSocketFactory", "(Proxy)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net", "MalformedServerReplyException", true, "MalformedServerReplyException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net", "PrintCommandListener", true, "PrintCommandListener", "(PrintWriter)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net", "PrintCommandListener", true, "PrintCommandListener", "(PrintWriter,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net", "PrintCommandListener", true, "PrintCommandListener", "(PrintWriter,boolean,char)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net", "PrintCommandListener", true, "PrintCommandListener", "(PrintWriter,boolean,char,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net", "ProtocolCommandEvent", true, "ProtocolCommandEvent", "(Object,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net", "ProtocolCommandEvent", true, "ProtocolCommandEvent", "(Object,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net", "ProtocolCommandEvent", true, "ProtocolCommandEvent", "(Object,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net", "ProtocolCommandEvent", true, "ProtocolCommandEvent", "(Object,int,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net", "ProtocolCommandEvent", true, "ProtocolCommandEvent", "(Object,int,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net", "ProtocolCommandEvent", true, "getCommand", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net", "ProtocolCommandEvent", true, "getMessage", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net", "ProtocolCommandSupport", true, "ProtocolCommandSupport", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", true, "connect", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", true, "connect", "(String,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", true, "connect", "(String,int,InetAddress,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", true, "getLocalAddress", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", true, "getProxy", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", true, "getRemoteAddress", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", true, "getServerSocketFactory", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", true, "setProxy", "(Proxy)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", true, "setServerSocketFactory", "(ServerSocketFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", true, "setSocketFactory", "(SocketFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
+ - addsTo:
+ pack: codeql/java-all
+ extensible: neutralModel
+ data:
+ - ["org.apache.commons.net.bsd", "RCommandClient", "connect", "(InetAddress,int,InetAddress)", "df-generated"]
+ - ["org.apache.commons.net.bsd", "RCommandClient", "connect", "(String,int,InetAddress)", "df-generated"]
+ - ["org.apache.commons.net.bsd", "RExecClient", "isRemoteVerificationEnabled", "()", "df-generated"]
+ - ["org.apache.commons.net.bsd", "RExecClient", "setRemoteVerificationEnabled", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.chargen", "CharGenUDPClient", "send", "(InetAddress)", "df-generated"]
+ - ["org.apache.commons.net.chargen", "CharGenUDPClient", "send", "(InetAddress,int)", "df-generated"]
+ - ["org.apache.commons.net.daytime", "DaytimeUDPClient", "getTime", "(InetAddress)", "df-generated"]
+ - ["org.apache.commons.net.daytime", "DaytimeUDPClient", "getTime", "(InetAddress,int)", "df-generated"]
+ - ["org.apache.commons.net.discard", "DiscardUDPClient", "send", "(byte[],InetAddress)", "df-generated"]
+ - ["org.apache.commons.net.discard", "DiscardUDPClient", "send", "(byte[],int,InetAddress)", "df-generated"]
+ - ["org.apache.commons.net.discard", "DiscardUDPClient", "send", "(byte[],int,InetAddress,int)", "df-generated"]
+ - ["org.apache.commons.net.echo", "EchoUDPClient", "receive", "(byte[])", "df-generated"]
+ - ["org.apache.commons.net.echo", "EchoUDPClient", "receive", "(byte[],int)", "df-generated"]
+ - ["org.apache.commons.net.examples.mail", "POP3Mail", "printMessageInfo", "(BufferedReader,int)", "df-generated"]
+ - ["org.apache.commons.net.examples.nntp", "NNTPUtils", "getArticleInfo", "(NNTPClient,long,long)", "df-generated"]
+ - ["org.apache.commons.net.examples.ntp", "NTPClient", "processResponse", "(TimeInfo)", "df-generated"]
+ - ["org.apache.commons.net.examples.ntp", "SimpleNTPServer", "SimpleNTPServer", "(int)", "df-generated"]
+ - ["org.apache.commons.net.examples.ntp", "SimpleNTPServer", "connect", "()", "df-generated"]
+ - ["org.apache.commons.net.examples.ntp", "SimpleNTPServer", "getPort", "()", "df-generated"]
+ - ["org.apache.commons.net.examples.ntp", "SimpleNTPServer", "isRunning", "()", "df-generated"]
+ - ["org.apache.commons.net.examples.ntp", "SimpleNTPServer", "isStarted", "()", "df-generated"]
+ - ["org.apache.commons.net.examples.ntp", "SimpleNTPServer", "start", "()", "df-generated"]
+ - ["org.apache.commons.net.examples.ntp", "SimpleNTPServer", "stop", "()", "df-generated"]
+ - ["org.apache.commons.net.examples.ntp", "TimeClient", "timeTCP", "(String)", "df-generated"]
+ - ["org.apache.commons.net.examples.ntp", "TimeClient", "timeUDP", "(String)", "df-generated"]
+ - ["org.apache.commons.net.examples.unix", "chargen", "chargenTCP", "(String)", "df-generated"]
+ - ["org.apache.commons.net.examples.unix", "chargen", "chargenUDP", "(String)", "df-generated"]
+ - ["org.apache.commons.net.examples.unix", "daytime", "daytimeTCP", "(String)", "df-generated"]
+ - ["org.apache.commons.net.examples.unix", "daytime", "daytimeUDP", "(String)", "df-generated"]
+ - ["org.apache.commons.net.examples.unix", "echo", "echoTCP", "(String)", "df-generated"]
+ - ["org.apache.commons.net.examples.unix", "echo", "echoUDP", "(String)", "df-generated"]
+ - ["org.apache.commons.net.examples.unix", "rdate", "timeTCP", "(String)", "df-generated"]
+ - ["org.apache.commons.net.examples.unix", "rdate", "timeUDP", "(String)", "df-generated"]
+ - ["org.apache.commons.net.examples.util", "IOUtil", "readWrite", "(InputStream,OutputStream,InputStream,OutputStream)", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "ConfigurableFTPFileEntryParserImpl", "ConfigurableFTPFileEntryParserImpl", "(String)", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "ConfigurableFTPFileEntryParserImpl", "ConfigurableFTPFileEntryParserImpl", "(String,int)", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "ConfigurableFTPFileEntryParserImpl", "getDefaultConfiguration", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "ConfigurableFTPFileEntryParserImpl", "parseTimestamp", "(String)", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "DefaultFTPFileEntryParserFactory", "createMVSEntryParser", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "DefaultFTPFileEntryParserFactory", "createNTFTPEntryParser", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "DefaultFTPFileEntryParserFactory", "createNetwareFTPEntryParser", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "DefaultFTPFileEntryParserFactory", "createOS2FTPEntryParser", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "DefaultFTPFileEntryParserFactory", "createOS400FTPEntryParser", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "DefaultFTPFileEntryParserFactory", "createUnixFTPEntryParser", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "DefaultFTPFileEntryParserFactory", "createVMSVersioningFTPEntryParser", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "FTPFileEntryParserFactory", "createFileEntryParser", "(FTPClientConfig)", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "FTPFileEntryParserFactory", "createFileEntryParser", "(String)", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "FTPTimestampParser", "parseTimestamp", "(String)", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "FTPTimestampParserImpl", "getDefaultDateFormatString", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "FTPTimestampParserImpl", "getRecentDateFormatString", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "MLSxEntryParser", "getInstance", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "MLSxEntryParser", "parseGMTdateTime", "(String)", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "MLSxEntryParser", "parseGmtInstant", "(String)", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "MacOsPeterFTPEntryParser", "MacOsPeterFTPEntryParser", "(FTPClientConfig)", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "NTFTPEntryParser", "NTFTPEntryParser", "(FTPClientConfig)", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "NetwareFTPEntryParser", "NetwareFTPEntryParser", "(FTPClientConfig)", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "OS2FTPEntryParser", "OS2FTPEntryParser", "(FTPClientConfig)", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "OS400FTPEntryParser", "OS400FTPEntryParser", "(FTPClientConfig)", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "RegexFTPFileEntryParserImpl", "RegexFTPFileEntryParserImpl", "(String)", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "RegexFTPFileEntryParserImpl", "RegexFTPFileEntryParserImpl", "(String,int)", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "RegexFTPFileEntryParserImpl", "getGroupCnt", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "RegexFTPFileEntryParserImpl", "getGroupsAsString", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "RegexFTPFileEntryParserImpl", "group", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "RegexFTPFileEntryParserImpl", "setRegex", "(String)", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "RegexFTPFileEntryParserImpl", "setRegex", "(String,int)", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "UnixFTPEntryParser", "UnixFTPEntryParser", "(FTPClientConfig)", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "UnixFTPEntryParser", "UnixFTPEntryParser", "(FTPClientConfig,boolean)", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "VMSFTPEntryParser", "VMSFTPEntryParser", "(FTPClientConfig)", "df-generated"]
+ - ["org.apache.commons.net.ftp.parser", "VMSVersioningFTPEntryParser", "VMSVersioningFTPEntryParser", "(FTPClientConfig)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "Configurable", "configure", "(FTPClientConfig)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "abor", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "allo", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "allo", "(int,int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "allo", "(long)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "allo", "(long,int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "cdup", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "epsv", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "feat", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "getReply", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "getReplyCode", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "help", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "isStrictMultilineParsing", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "isStrictReplyParsing", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "list", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "mlsd", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "mlst", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "mode", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "nlst", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "noop", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "pasv", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "pwd", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "quit", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "rein", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "sendCommand", "(FTPCmd)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "sendCommand", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "setStrictMultilineParsing", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "setStrictReplyParsing", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "stat", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "stou", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "stru", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "syst", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "type", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTP", "type", "(int,int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "abort", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "allocate", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "allocate", "(int,int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "allocate", "(long)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "allocate", "(long,int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "changeToParentDirectory", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "completePendingCommand", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "enterLocalActiveMode", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "enterLocalPassiveMode", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "enterRemotePassiveMode", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "features", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "getAutodetectUTF8", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "getBufferSize", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "getControlKeepAliveReplyTimeout", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "getControlKeepAliveTimeout", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "getCslDebug", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "getDataConnectionMode", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "getListHiddenFiles", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "getPassivePort", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "getReceiveDataSocketBufferSize", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "getRestartOffset", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "getSendDataSocketBufferSize", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "hasFeature", "(FTPCmd)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "hasFeature", "(String)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "hasFeature", "(String,String)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "initiateMListParsing", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "isIpAddressFromPasvResponse", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "isRemoteVerificationEnabled", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "isUseEPSVwithIPv4", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "listDirectories", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "listNames", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "logout", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "mlistDir", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "reinitialize", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "remoteStoreUnique", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "sendNoOp", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "setActivePortRange", "(int,int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "setAutodetectUTF8", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "setBufferSize", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "setControlKeepAliveReplyTimeout", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "setControlKeepAliveTimeout", "(long)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "setDataTimeout", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "setFileStructure", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "setFileTransferMode", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "setFileType", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "setFileType", "(int,int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "setIpAddressFromPasvResponse", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "setListHiddenFiles", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "setPassiveNatWorkaround", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "setReceieveDataSocketBufferSize", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "setRemoteVerificationEnabled", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "setRestartOffset", "(long)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "setSendDataSocketBufferSize", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "setUseEPSVwithIPv4", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "storeUniqueFile", "(InputStream)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", "storeUniqueFileStream", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", "getDateFormatSymbols", "(String)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", "getSupportedLanguageCodes", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", "getUnparseableEntries", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", "isLenientFutureDates", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", "lookupDateFormatSymbols", "(String)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", "setLenientFutureDates", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClientConfig", "setUnparseableEntries", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPCmd", "getCommand", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPCommand", "getCommand", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", "getHardLinkCount", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", "getSize", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", "getTimestampInstant", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", "getType", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", "hasPermission", "(int,int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", "isDirectory", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", "isFile", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", "isSymbolicLink", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", "isUnknown", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", "isValid", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", "setHardLinkCount", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", "setPermission", "(int,int,boolean)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", "setSize", "(long)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPFile", "setType", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPListParseEngine", "hasNext", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPListParseEngine", "hasPrevious", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPListParseEngine", "resetIterator", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPReply", "isNegativePermanent", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPReply", "isNegativeTransient", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPReply", "isPositiveCompletion", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPReply", "isPositiveIntermediate", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPReply", "isPositivePreliminary", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPReply", "isProtectedReplyCode", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", "FTPSClient", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", "execCCC", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", "execPBSZ", "(long)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", "getEnableSessionCreation", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", "getNeedClientAuth", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", "getUseClientMode", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", "getWantClientAuth", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", "isEndpointCheckingEnabled", "()", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", "parsePBSZ", "(long)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", "setEnabledSessionCreation", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", "setEndpointCheckingEnabled", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", "setNeedClientAuth", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", "setUseClientMode", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSClient", "setWantClientAuth", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSCommand", "getCommand", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSSocketFactory", "createServerSocket", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSSocketFactory", "createServerSocket", "(int,int)", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPSSocketFactory", "createServerSocket", "(int,int,InetAddress)", "df-generated"]
+ - ["org.apache.commons.net.imap", "AuthenticatingIMAPClient$AUTH_METHOD", "getAuthName", "()", "df-generated"]
+ - ["org.apache.commons.net.imap", "AuthenticatingIMAPClient", "AuthenticatingIMAPClient", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAP", "doCommand", "(IMAPCommand)", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAP", "getState", "()", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAP", "sendCommand", "(IMAPCommand)", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", "capability", "()", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", "check", "()", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", "close", "()", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", "expunge", "()", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", "logout", "()", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPClient", "noop", "()", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPCommand", "getCommand", "(IMAPCommand)", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPCommand", "getIMAPCommand", "()", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPReply", "getReplyCode", "(String)", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPReply", "getUntaggedReplyCode", "(String)", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPReply", "isContinuation", "(String)", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPReply", "isContinuation", "(int)", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPReply", "isSuccess", "(int)", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPReply", "isUntagged", "(String)", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPReply", "literalCount", "(String)", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPSClient", "IMAPSClient", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPSClient", "execTLS", "()", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPSClient", "isEndpointCheckingEnabled", "()", "df-generated"]
+ - ["org.apache.commons.net.imap", "IMAPSClient", "setEndpointCheckingEnabled", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.io", "CopyStreamAdapter", "addCopyStreamListener", "(CopyStreamListener)", "df-generated"]
+ - ["org.apache.commons.net.io", "CopyStreamAdapter", "removeCopyStreamListener", "(CopyStreamListener)", "df-generated"]
+ - ["org.apache.commons.net.io", "CopyStreamEvent", "getBytesTransferred", "()", "df-generated"]
+ - ["org.apache.commons.net.io", "CopyStreamEvent", "getStreamSize", "()", "df-generated"]
+ - ["org.apache.commons.net.io", "CopyStreamEvent", "getTotalBytesTransferred", "()", "df-generated"]
+ - ["org.apache.commons.net.io", "CopyStreamException", "getTotalBytesTransferred", "()", "df-generated"]
+ - ["org.apache.commons.net.io", "CopyStreamListener", "bytesTransferred", "(CopyStreamEvent)", "df-generated"]
+ - ["org.apache.commons.net.io", "CopyStreamListener", "bytesTransferred", "(long,int,long)", "df-generated"]
+ - ["org.apache.commons.net.io", "FromNetASCIIInputStream", "FromNetASCIIInputStream", "(InputStream)", "df-generated"]
+ - ["org.apache.commons.net.io", "FromNetASCIIInputStream", "isConversionRequired", "()", "df-generated"]
+ - ["org.apache.commons.net.io", "ToNetASCIIInputStream", "ToNetASCIIInputStream", "(InputStream)", "df-generated"]
+ - ["org.apache.commons.net.io", "Util", "closeQuietly", "(Closeable)", "df-generated"]
+ - ["org.apache.commons.net.io", "Util", "closeQuietly", "(Socket)", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Article", "addHeaderField", "(String,String)", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Article", "getArticleNumber", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Article", "getArticleNumberLong", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Article", "printThread", "(Article)", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Article", "printThread", "(Article,PrintStream)", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Article", "printThread", "(Article,int)", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Article", "printThread", "(Article,int,PrintStream)", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Article", "setArticleNumber", "(int)", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Article", "setArticleNumber", "(long)", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", "article", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", "article", "(int)", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", "article", "(long)", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", "body", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", "body", "(int)", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", "body", "(long)", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", "getReply", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", "getReplyCode", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", "head", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", "head", "(int)", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", "head", "(long)", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", "help", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", "isAllowedToPost", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", "last", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", "list", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", "next", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", "post", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", "quit", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", "sendCommand", "(int)", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", "stat", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", "stat", "(int)", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTP", "stat", "(long)", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", "completePendingCommand", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", "iterateArticleInfo", "(long,long)", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", "iterateNewsgroups", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", "logout", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", "selectArticle", "(int)", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", "selectArticle", "(long)", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", "selectNextArticle", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPClient", "selectPreviousArticle", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPCommand", "getCommand", "(int)", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPReply", "isInformational", "(int)", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPReply", "isNegativePermanent", "(int)", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPReply", "isNegativeTransient", "(int)", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPReply", "isPositiveCompletion", "(int)", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NNTPReply", "isPositiveIntermediate", "(int)", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NewGroupsOrNewsQuery", "NewGroupsOrNewsQuery", "(Calendar,boolean)", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NewGroupsOrNewsQuery", "isGMT", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NewsgroupInfo", "getArticleCount", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NewsgroupInfo", "getArticleCountLong", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NewsgroupInfo", "getFirstArticle", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NewsgroupInfo", "getFirstArticleLong", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NewsgroupInfo", "getLastArticle", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NewsgroupInfo", "getLastArticleLong", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "NewsgroupInfo", "getPostingPermission", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Threadable", "isDummy", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Threadable", "makeDummy", "()", "df-generated"]
+ - ["org.apache.commons.net.nntp", "Threadable", "subjectIsReply", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NTPUDPClient", "getTime", "(InetAddress)", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NTPUDPClient", "getTime", "(InetAddress,int)", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NTPUDPClient", "getVersion", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NTPUDPClient", "setVersion", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpUtils", "getHostAddress", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpUtils", "getModeName", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpUtils", "getRefAddress", "(NtpV3Packet)", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpUtils", "getReferenceClock", "(NtpV3Packet)", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Impl", "toString", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "getLeapIndicator", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "getMode", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "getModeName", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "getOriginateTimeStamp", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "getPoll", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "getPrecision", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "getReceiveTimeStamp", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "getReferenceId", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "getReferenceIdString", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "getReferenceTimeStamp", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "getRootDelay", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "getRootDelayInMillisDouble", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "getRootDispersion", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "getRootDispersionInMillis", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "getRootDispersionInMillisDouble", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "getStratum", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "getTransmitTimeStamp", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "getType", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "getVersion", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "setLeapIndicator", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "setMode", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "setOriginateTimeStamp", "(TimeStamp)", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "setPoll", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "setPrecision", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "setReceiveTimeStamp", "(TimeStamp)", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "setReferenceId", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "setReferenceTime", "(TimeStamp)", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "setRootDelay", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "setRootDispersion", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "setStratum", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "setTransmitTime", "(TimeStamp)", "df-generated"]
+ - ["org.apache.commons.net.ntp", "NtpV3Packet", "setVersion", "(int)", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeInfo", "computeDetails", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeInfo", "getDelay", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeInfo", "getOffset", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeInfo", "getReturnTime", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeStamp", "TimeStamp", "(Date)", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeStamp", "TimeStamp", "(String)", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeStamp", "TimeStamp", "(long)", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeStamp", "getCurrentTime", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeStamp", "getDate", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeStamp", "getFraction", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeStamp", "getNtpTime", "(long)", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeStamp", "getSeconds", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeStamp", "getTime", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeStamp", "getTime", "(long)", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeStamp", "ntpValue", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeStamp", "parseNtpString", "(String)", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeStamp", "toDateString", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeStamp", "toString", "()", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeStamp", "toString", "(long)", "df-generated"]
+ - ["org.apache.commons.net.ntp", "TimeStamp", "toUTCString", "()", "df-generated"]
+ - ["org.apache.commons.net.pop3", "ExtendedPOP3Client$AUTH_METHOD", "getAuthName", "()", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3", "getAdditionalReply", "()", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3", "getState", "()", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3", "removeProtocolCommandistener", "(ProtocolCommandListener)", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3", "sendCommand", "(int)", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3", "setState", "(int)", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3Client", "capa", "()", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3Client", "deleteMessage", "(int)", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3Client", "listMessage", "(int)", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3Client", "listMessages", "()", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3Client", "listUniqueIdentifiers", "()", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3Client", "logout", "()", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3Client", "noop", "()", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3Client", "reset", "()", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3Client", "status", "()", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3Command", "getCommand", "(int)", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3MessageInfo", "POP3MessageInfo", "(int,int)", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3SClient", "POP3SClient", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3SClient", "execTLS", "()", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3SClient", "isEndpointCheckingEnabled", "()", "df-generated"]
+ - ["org.apache.commons.net.pop3", "POP3SClient", "setEndpointCheckingEnabled", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.smtp", "AuthenticatingSMTPClient$AUTH_METHOD", "getAuthName", "(AUTH_METHOD)", "df-generated"]
+ - ["org.apache.commons.net.smtp", "AuthenticatingSMTPClient", "elogin", "()", "df-generated"]
+ - ["org.apache.commons.net.smtp", "AuthenticatingSMTPClient", "getEnhancedReplyCode", "()", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTP", "data", "()", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTP", "getReply", "()", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTP", "getReplyCode", "()", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTP", "help", "()", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTP", "noop", "()", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTP", "quit", "()", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTP", "removeProtocolCommandistener", "(ProtocolCommandListener)", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTP", "rset", "()", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTP", "sendCommand", "(int)", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTP", "turn", "()", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPClient", "completePendingCommand", "()", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPClient", "login", "()", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPClient", "logout", "()", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPClient", "reset", "()", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPClient", "sendNoOp", "()", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPClient", "sendShortMessageData", "(String)", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPCommand", "getCommand", "(int)", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPReply", "isNegativePermanent", "(int)", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPReply", "isNegativeTransient", "(int)", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPReply", "isPositiveCompletion", "(int)", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPReply", "isPositiveIntermediate", "(int)", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPReply", "isPositivePreliminary", "(int)", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPSClient", "SMTPSClient", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPSClient", "execTLS", "()", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPSClient", "isEndpointCheckingEnabled", "()", "df-generated"]
+ - ["org.apache.commons.net.smtp", "SMTPSClient", "setEndpointCheckingEnabled", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.telnet", "EchoOptionHandler", "EchoOptionHandler", "(boolean,boolean,boolean,boolean)", "df-generated"]
+ - ["org.apache.commons.net.telnet", "SimpleOptionHandler", "SimpleOptionHandler", "(int)", "df-generated"]
+ - ["org.apache.commons.net.telnet", "SimpleOptionHandler", "SimpleOptionHandler", "(int,boolean,boolean,boolean,boolean)", "df-generated"]
+ - ["org.apache.commons.net.telnet", "SuppressGAOptionHandler", "SuppressGAOptionHandler", "(boolean,boolean,boolean,boolean)", "df-generated"]
+ - ["org.apache.commons.net.telnet", "Telnet", "deleteOptionHandler", "(int)", "df-generated"]
+ - ["org.apache.commons.net.telnet", "Telnet", "unregisterNotifHandler", "()", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetClient", "TelnetClient", "(int)", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetClient", "getLocalOptionState", "(int)", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetClient", "getReaderThread", "()", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetClient", "getRemoteOptionState", "(int)", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetClient", "sendAYT", "(long)", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetClient", "sendCommand", "(byte)", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetClient", "sendSubnegotiation", "(int[])", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetClient", "setReaderThread", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetClient", "stopSpyStream", "()", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetClient", "unregisterInputListener", "()", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetCommand", "getCommand", "(int)", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetCommand", "isValidCommand", "(int)", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetNotificationHandler", "receivedNegotiation", "(int,int)", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetOption", "getOption", "(int)", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetOption", "isValidOption", "(int)", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetOptionHandler", "TelnetOptionHandler", "(int,boolean,boolean,boolean,boolean)", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetOptionHandler", "answerSubnegotiation", "(int[],int)", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetOptionHandler", "getAcceptLocal", "()", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetOptionHandler", "getAcceptRemote", "()", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetOptionHandler", "getInitLocal", "()", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetOptionHandler", "getInitRemote", "()", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetOptionHandler", "getOptionCode", "()", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetOptionHandler", "setAcceptLocal", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetOptionHandler", "setAcceptRemote", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetOptionHandler", "setInitLocal", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetOptionHandler", "setInitRemote", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetOptionHandler", "startSubnegotiationLocal", "()", "df-generated"]
+ - ["org.apache.commons.net.telnet", "TelnetOptionHandler", "startSubnegotiationRemote", "()", "df-generated"]
+ - ["org.apache.commons.net.telnet", "WindowSizeOptionHandler", "WindowSizeOptionHandler", "(int,int)", "df-generated"]
+ - ["org.apache.commons.net.telnet", "WindowSizeOptionHandler", "WindowSizeOptionHandler", "(int,int,boolean,boolean,boolean,boolean)", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTP", "beginBufferedOps", "()", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTP", "discardPackets", "()", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTP", "endBufferedOps", "()", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTP", "getModeName", "(int)", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTP", "receive", "()", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTP", "send", "(TFTPPacket)", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPAckPacket", "getBlockNumber", "()", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPAckPacket", "setBlockNumber", "(int)", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", "getMaxTimeouts", "()", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", "getTotalBytesReceived", "()", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", "getTotalBytesSent", "()", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPClient", "setMaxTimeouts", "(int)", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPDataPacket", "getBlockNumber", "()", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPDataPacket", "getDataLength", "()", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPDataPacket", "getDataOffset", "()", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPDataPacket", "setBlockNumber", "(int)", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPErrorPacket", "getError", "()", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPPacket", "getPort", "()", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPPacket", "getType", "()", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPPacket", "newDatagram", "()", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPPacket", "setPort", "(int)", "df-generated"]
+ - ["org.apache.commons.net.tftp", "TFTPRequestPacket", "getMode", "()", "df-generated"]
+ - ["org.apache.commons.net.time", "TimeTCPClient", "getDate", "()", "df-generated"]
+ - ["org.apache.commons.net.time", "TimeTCPClient", "getTime", "()", "df-generated"]
+ - ["org.apache.commons.net.time", "TimeUDPClient", "getDate", "(InetAddress)", "df-generated"]
+ - ["org.apache.commons.net.time", "TimeUDPClient", "getDate", "(InetAddress,int)", "df-generated"]
+ - ["org.apache.commons.net.time", "TimeUDPClient", "getTime", "(InetAddress)", "df-generated"]
+ - ["org.apache.commons.net.time", "TimeUDPClient", "getTime", "(InetAddress,int)", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", "Base64", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", "Base64", "(int)", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", "decodeInteger", "(byte[])", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", "encodeInteger", "(BigInteger)", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", "isArrayByteBase64", "(byte[])", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", "isBase64", "(byte)", "df-generated"]
+ - ["org.apache.commons.net.util", "Base64", "isUrlSafe", "()", "df-generated"]
+ - ["org.apache.commons.net.util", "Charsets", "toCharset", "(String)", "df-generated"]
+ - ["org.apache.commons.net.util", "Charsets", "toCharset", "(String,String)", "df-generated"]
+ - ["org.apache.commons.net.util", "KeyManagerUtils", "createClientKeyManager", "(File,String)", "df-generated"]
+ - ["org.apache.commons.net.util", "ListenerList", "getListenerCount", "()", "df-generated"]
+ - ["org.apache.commons.net.util", "ListenerList", "removeListener", "(EventListener)", "df-generated"]
+ - ["org.apache.commons.net.util", "SSLContextUtils", "createSSLContext", "(String,KeyManager,TrustManager)", "df-generated"]
+ - ["org.apache.commons.net.util", "SSLContextUtils", "createSSLContext", "(String,KeyManager[],TrustManager[])", "df-generated"]
+ - ["org.apache.commons.net.util", "SSLSocketUtils", "enableEndpointNameVerification", "(SSLSocket)", "df-generated"]
+ - ["org.apache.commons.net.util", "SubnetUtils$SubnetInfo", "asInteger", "(String)", "df-generated"]
+ - ["org.apache.commons.net.util", "SubnetUtils$SubnetInfo", "getAddress", "()", "df-generated"]
+ - ["org.apache.commons.net.util", "SubnetUtils$SubnetInfo", "getAddressCount", "()", "df-generated"]
+ - ["org.apache.commons.net.util", "SubnetUtils$SubnetInfo", "getAddressCountLong", "()", "df-generated"]
+ - ["org.apache.commons.net.util", "SubnetUtils$SubnetInfo", "getAllAddresses", "()", "df-generated"]
+ - ["org.apache.commons.net.util", "SubnetUtils$SubnetInfo", "getBroadcastAddress", "()", "df-generated"]
+ - ["org.apache.commons.net.util", "SubnetUtils$SubnetInfo", "getCidrSignature", "()", "df-generated"]
+ - ["org.apache.commons.net.util", "SubnetUtils$SubnetInfo", "getHighAddress", "()", "df-generated"]
+ - ["org.apache.commons.net.util", "SubnetUtils$SubnetInfo", "getLowAddress", "()", "df-generated"]
+ - ["org.apache.commons.net.util", "SubnetUtils$SubnetInfo", "getNetmask", "()", "df-generated"]
+ - ["org.apache.commons.net.util", "SubnetUtils$SubnetInfo", "getNetworkAddress", "()", "df-generated"]
+ - ["org.apache.commons.net.util", "SubnetUtils$SubnetInfo", "getNextAddress", "()", "df-generated"]
+ - ["org.apache.commons.net.util", "SubnetUtils$SubnetInfo", "getPreviousAddress", "()", "df-generated"]
+ - ["org.apache.commons.net.util", "SubnetUtils$SubnetInfo", "isInRange", "(String)", "df-generated"]
+ - ["org.apache.commons.net.util", "SubnetUtils$SubnetInfo", "isInRange", "(int)", "df-generated"]
+ - ["org.apache.commons.net.util", "SubnetUtils$SubnetInfo", "toString", "()", "df-generated"]
+ - ["org.apache.commons.net.util", "SubnetUtils", "SubnetUtils", "(String)", "df-generated"]
+ - ["org.apache.commons.net.util", "SubnetUtils", "SubnetUtils", "(String,String)", "df-generated"]
+ - ["org.apache.commons.net.util", "SubnetUtils", "getInfo", "()", "df-generated"]
+ - ["org.apache.commons.net.util", "SubnetUtils", "getNext", "()", "df-generated"]
+ - ["org.apache.commons.net.util", "SubnetUtils", "getPrevious", "()", "df-generated"]
+ - ["org.apache.commons.net.util", "SubnetUtils", "isInclusiveHostCount", "()", "df-generated"]
+ - ["org.apache.commons.net.util", "SubnetUtils", "setInclusiveHostCount", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net.util", "TrustManagerUtils", "getAcceptAllTrustManager", "()", "df-generated"]
+ - ["org.apache.commons.net.util", "TrustManagerUtils", "getDefaultTrustManager", "(KeyStore)", "df-generated"]
+ - ["org.apache.commons.net.util", "TrustManagerUtils", "getValidateServerCertificateTrustManager", "()", "df-generated"]
+ - ["org.apache.commons.net", "DatagramSocketClient", "close", "()", "df-generated"]
+ - ["org.apache.commons.net", "DatagramSocketClient", "getCharset", "()", "df-generated"]
+ - ["org.apache.commons.net", "DatagramSocketClient", "getCharsetName", "()", "df-generated"]
+ - ["org.apache.commons.net", "DatagramSocketClient", "getDefaultTimeout", "()", "df-generated"]
+ - ["org.apache.commons.net", "DatagramSocketClient", "getLocalPort", "()", "df-generated"]
+ - ["org.apache.commons.net", "DatagramSocketClient", "getSoTimeout", "()", "df-generated"]
+ - ["org.apache.commons.net", "DatagramSocketClient", "isOpen", "()", "df-generated"]
+ - ["org.apache.commons.net", "DatagramSocketClient", "open", "()", "df-generated"]
+ - ["org.apache.commons.net", "DatagramSocketClient", "open", "(int)", "df-generated"]
+ - ["org.apache.commons.net", "DatagramSocketClient", "open", "(int,InetAddress)", "df-generated"]
+ - ["org.apache.commons.net", "DatagramSocketClient", "setCharset", "(Charset)", "df-generated"]
+ - ["org.apache.commons.net", "DatagramSocketClient", "setDefaultTimeout", "(int)", "df-generated"]
+ - ["org.apache.commons.net", "DatagramSocketClient", "setSoTimeout", "(int)", "df-generated"]
+ - ["org.apache.commons.net", "DatagramSocketFactory", "createDatagramSocket", "()", "df-generated"]
+ - ["org.apache.commons.net", "DatagramSocketFactory", "createDatagramSocket", "(int)", "df-generated"]
+ - ["org.apache.commons.net", "DatagramSocketFactory", "createDatagramSocket", "(int,InetAddress)", "df-generated"]
+ - ["org.apache.commons.net", "DefaultSocketFactory", "createServerSocket", "(int)", "df-generated"]
+ - ["org.apache.commons.net", "DefaultSocketFactory", "createServerSocket", "(int,int)", "df-generated"]
+ - ["org.apache.commons.net", "DefaultSocketFactory", "createServerSocket", "(int,int,InetAddress)", "df-generated"]
+ - ["org.apache.commons.net", "PrintCommandListener", "PrintCommandListener", "(PrintStream)", "df-generated"]
+ - ["org.apache.commons.net", "PrintCommandListener", "PrintCommandListener", "(PrintStream,boolean)", "df-generated"]
+ - ["org.apache.commons.net", "PrintCommandListener", "PrintCommandListener", "(PrintStream,boolean,char)", "df-generated"]
+ - ["org.apache.commons.net", "PrintCommandListener", "PrintCommandListener", "(PrintStream,boolean,char,boolean)", "df-generated"]
+ - ["org.apache.commons.net", "ProtocolCommandEvent", "getReplyCode", "()", "df-generated"]
+ - ["org.apache.commons.net", "ProtocolCommandEvent", "isCommand", "()", "df-generated"]
+ - ["org.apache.commons.net", "ProtocolCommandEvent", "isReply", "()", "df-generated"]
+ - ["org.apache.commons.net", "ProtocolCommandListener", "protocolCommandSent", "(ProtocolCommandEvent)", "df-generated"]
+ - ["org.apache.commons.net", "ProtocolCommandListener", "protocolReplyReceived", "(ProtocolCommandEvent)", "df-generated"]
+ - ["org.apache.commons.net", "ProtocolCommandSupport", "addProtocolCommandListener", "(ProtocolCommandListener)", "df-generated"]
+ - ["org.apache.commons.net", "ProtocolCommandSupport", "fireCommandSent", "(String,String)", "df-generated"]
+ - ["org.apache.commons.net", "ProtocolCommandSupport", "fireReplyReceived", "(int,String)", "df-generated"]
+ - ["org.apache.commons.net", "ProtocolCommandSupport", "getListenerCount", "()", "df-generated"]
+ - ["org.apache.commons.net", "ProtocolCommandSupport", "removeProtocolCommandListener", "(ProtocolCommandListener)", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "addProtocolCommandListener", "(ProtocolCommandListener)", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "connect", "(InetAddress)", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "connect", "(InetAddress,int)", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "connect", "(InetAddress,int,InetAddress,int)", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "connect", "(String,int)", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "disconnect", "()", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "getCharset", "()", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "getCharsetName", "()", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "getConnectTimeout", "()", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "getDefaultPort", "()", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "getDefaultTimeout", "()", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "getKeepAlive", "()", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "getLocalPort", "()", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "getRemotePort", "()", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "getSoLinger", "()", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "getSoTimeout", "()", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "getTcpNoDelay", "()", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "isAvailable", "()", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "isConnected", "()", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "removeProtocolCommandListener", "(ProtocolCommandListener)", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "setCharset", "(Charset)", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "setConnectTimeout", "(int)", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "setDefaultPort", "(int)", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "setDefaultTimeout", "(int)", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "setKeepAlive", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "setReceiveBufferSize", "(int)", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "setSendBufferSize", "(int)", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "setSoLinger", "(boolean,int)", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "setSoTimeout", "(int)", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "setTcpNoDelay", "(boolean)", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", "verifyRemote", "(Socket)", "df-generated"]
From c0cf0b430eaff8e6ee0d5b89a280980424e6b1ae Mon Sep 17 00:00:00 2001
From: tyage
Date: Sun, 30 Apr 2023 18:07:52 +0900
Subject: [PATCH 084/870] JS: support submodules
---
javascript/ql/lib/semmle/javascript/NPM.qll | 21 +++++++++++++++++++--
1 file changed, 19 insertions(+), 2 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/NPM.qll b/javascript/ql/lib/semmle/javascript/NPM.qll
index d05045784a6..4e64ef6ac09 100644
--- a/javascript/ql/lib/semmle/javascript/NPM.qll
+++ b/javascript/ql/lib/semmle/javascript/NPM.qll
@@ -12,8 +12,25 @@ class PackageJson extends JsonObject {
this.isTopLevel()
}
- /** Gets the name of this package. */
- string getPackageName() { result = this.getPropStringValue("name") }
+ /**
+ * Gets the name of this package.
+ * If the package is located under the package `foo` and its relative path is `bar`, it can be `foo/bar`
+ */
+ string getPackageName() {
+ result = this.getPropStringValue("name")
+ or
+ exists(
+ PackageJson parentPackage, string currentDir, string parentDir, string parentPackageName
+ |
+ currentDir = this.getJsonFile().getParentContainer().getAbsolutePath() and
+ parentDir = parentPackage.getJsonFile().getParentContainer().getAbsolutePath() and
+ parentPackageName = parentPackage.getPropStringValue("name") and
+ parentDir.indexOf("node_modules") != -1 and
+ currentDir != parentDir and
+ currentDir.indexOf(parentDir) = 0 and
+ result = parentPackageName + currentDir.suffix(parentDir.length())
+ )
+ }
/** Gets the version of this package. */
string getVersion() { result = this.getPropStringValue("version") }
From 71952fe5518ce823b7968059daf03e7b7e9a8335 Mon Sep 17 00:00:00 2001
From: tyage
Date: Sun, 30 Apr 2023 18:18:35 +0900
Subject: [PATCH 085/870] JS: Add test for sub module
---
.../NPM/src/node_modules/parent-modue/main.js | 1 +
.../NPM/src/node_modules/parent-modue/package.json | 4 ++++
.../src/node_modules/parent-modue/sub-module/main.js | 1 +
.../node_modules/parent-modue/sub-module/package.json | 3 +++
.../ql/test/library-tests/NPM/src/test-submodule.js | 2 ++
javascript/ql/test/library-tests/NPM/tests.expected | 11 +++++++++++
6 files changed, 22 insertions(+)
create mode 100644 javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/main.js
create mode 100644 javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/package.json
create mode 100644 javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/sub-module/main.js
create mode 100644 javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/sub-module/package.json
create mode 100644 javascript/ql/test/library-tests/NPM/src/test-submodule.js
diff --git a/javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/main.js b/javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/main.js
new file mode 100644
index 00000000000..8ba4196fbca
--- /dev/null
+++ b/javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/main.js
@@ -0,0 +1 @@
+export default "parent";
diff --git a/javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/package.json b/javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/package.json
new file mode 100644
index 00000000000..44481e79cba
--- /dev/null
+++ b/javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "parent-module",
+ "main": "main.js"
+}
\ No newline at end of file
diff --git a/javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/sub-module/main.js b/javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/sub-module/main.js
new file mode 100644
index 00000000000..cfc0568dea7
--- /dev/null
+++ b/javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/sub-module/main.js
@@ -0,0 +1 @@
+export default "sub";
diff --git a/javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/sub-module/package.json b/javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/sub-module/package.json
new file mode 100644
index 00000000000..a60376465b8
--- /dev/null
+++ b/javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/sub-module/package.json
@@ -0,0 +1,3 @@
+{
+ "main": "main.js"
+}
\ No newline at end of file
diff --git a/javascript/ql/test/library-tests/NPM/src/test-submodule.js b/javascript/ql/test/library-tests/NPM/src/test-submodule.js
new file mode 100644
index 00000000000..f40d70a3aa1
--- /dev/null
+++ b/javascript/ql/test/library-tests/NPM/src/test-submodule.js
@@ -0,0 +1,2 @@
+require("parent-module");
+require("parent-module/sub-module");
diff --git a/javascript/ql/test/library-tests/NPM/tests.expected b/javascript/ql/test/library-tests/NPM/tests.expected
index 48691fe0117..1262db527c9 100644
--- a/javascript/ql/test/library-tests/NPM/tests.expected
+++ b/javascript/ql/test/library-tests/NPM/tests.expected
@@ -16,18 +16,24 @@ importedModule
| src/node_modules/nested/tst3.js:1:1:1:29 | require ... odule') | src/node_modules/third-party-module/fancy.js:1:1:4:0 | |
| src/node_modules/nested/tst3.js:2:1:2:12 | require('a') | src/node_modules/nested/node_modules/a/index.js:1:1:1:25 | |
| src/node_modules/tst2.js:1:1:1:38 | require ... cy.js') | src/node_modules/third-party-module/fancy.js:1:1:4:0 | |
+| src/test-submodule.js:1:1:1:24 | require ... odule") | src/node_modules/parent-modue/main.js:1:1:2:0 | |
+| src/test-submodule.js:2:1:2:35 | require ... odule") | src/node_modules/parent-modue/sub-module/main.js:1:1:2:0 | |
| src/tst2.js:1:1:1:12 | require(".") | src/index.js:1:1:4:0 | |
| src/tst.js:1:1:1:38 | require ... cy.js') | src/node_modules/third-party-module/fancy.js:1:1:4:0 | |
modules
| src | test-package | src/index.js:1:1:4:0 | |
| src | test-package | src/lib/tst2.js:1:1:1:14 | |
| src | test-package | src/lib/tst.js:1:1:4:0 | |
+| src | test-package | src/test-submodule.js:1:1:3:0 | |
| src | test-package | src/tst2.js:1:1:1:13 | |
| src | test-package | src/tst.js:1:1:2:38 | |
| src/node_modules/b | b | src/node_modules/b/lib/index.js:1:1:2:0 | |
| src/node_modules/b | b | src/node_modules/b/lib/util.ts:1:1:2:0 | |
| src/node_modules/c | c | src/node_modules/c/src/index.js:1:1:2:0 | |
| src/node_modules/d | d | src/node_modules/d/main.js:1:1:2:0 | |
+| src/node_modules/parent-modue | parent-module | src/node_modules/parent-modue/main.js:1:1:2:0 | |
+| src/node_modules/parent-modue | parent-module | src/node_modules/parent-modue/sub-module/main.js:1:1:2:0 | |
+| src/node_modules/parent-modue/sub-module | parent-module/sub-module | src/node_modules/parent-modue/sub-module/main.js:1:1:2:0 | |
| src/node_modules/third-party-module | third-party-module | src/node_modules/third-party-module/fancy.js:1:1:4:0 | |
npm
| src/node_modules/third-party-module/package.json:1:1:5:1 | {\\n "na ... y.js"\\n} | third-party-module | 23.4.0 |
@@ -36,12 +42,16 @@ getMainModule
| src/node_modules/b/package.json:1:1:4:1 | {\\n "na ... "lib"\\n} | b | src/node_modules/b/lib/index.js:1:1:2:0 | |
| src/node_modules/c/package.json:1:1:4:1 | {\\n "na ... src/"\\n} | c | src/node_modules/c/src/index.js:1:1:2:0 | |
| src/node_modules/d/package.json:1:1:4:1 | {\\n "na ... main"\\n} | d | src/node_modules/d/main.js:1:1:2:0 | |
+| src/node_modules/parent-modue/package.json:1:1:4:1 | {\\n "na ... n.js"\\n} | parent-module | src/node_modules/parent-modue/main.js:1:1:2:0 | |
+| src/node_modules/parent-modue/sub-module/package.json:1:1:3:1 | {\\n "ma ... n.js"\\n} | parent-module/sub-module | src/node_modules/parent-modue/sub-module/main.js:1:1:2:0 | |
| src/node_modules/third-party-module/package.json:1:1:5:1 | {\\n "na ... y.js"\\n} | third-party-module | src/node_modules/third-party-module/fancy.js:1:1:4:0 | |
| src/package.json:1:1:20:1 | {\\n "na ... "\\n }\\n} | test-package | src/index.js:1:1:4:0 | |
packageJson
| src/node_modules/b/package.json:1:1:4:1 | {\\n "na ... "lib"\\n} |
| src/node_modules/c/package.json:1:1:4:1 | {\\n "na ... src/"\\n} |
| src/node_modules/d/package.json:1:1:4:1 | {\\n "na ... main"\\n} |
+| src/node_modules/parent-modue/package.json:1:1:4:1 | {\\n "na ... n.js"\\n} |
+| src/node_modules/parent-modue/sub-module/package.json:1:1:3:1 | {\\n "ma ... n.js"\\n} |
| src/node_modules/third-party-module/package.json:1:1:5:1 | {\\n "na ... y.js"\\n} |
| src/package.json:1:1:20:1 | {\\n "na ... "\\n }\\n} |
dependencyInfo
@@ -53,5 +63,6 @@ dependencyInfo
| src/package.json:11:20:11:37 | "1.2.3-alpha.beta" | something | unknown |
| src/package.json:12:14:12:57 | "! garb ... arse %" | foo | unknown |
| src/package.json:15:16:15:20 | "1.0" | mocha | 1.0 |
+| src/test-submodule.js:1:1:3:0 | | test-package | 0.1.0 |
| src/tst2.js:1:1:1:13 | | test-package | 0.1.0 |
| src/tst.js:1:1:2:38 | | test-package | 0.1.0 |
From 80d401fba8439c462bf1995d20a89291cff9f5df Mon Sep 17 00:00:00 2001
From: tyage
Date: Sun, 30 Apr 2023 18:26:46 +0900
Subject: [PATCH 086/870] JS: change note
---
javascript/ql/lib/change-notes/2023-04-30-npm-submodule.md | 5 +++++
javascript/ql/lib/semmle/javascript/NPM.qll | 2 +-
2 files changed, 6 insertions(+), 1 deletion(-)
create mode 100644 javascript/ql/lib/change-notes/2023-04-30-npm-submodule.md
diff --git a/javascript/ql/lib/change-notes/2023-04-30-npm-submodule.md b/javascript/ql/lib/change-notes/2023-04-30-npm-submodule.md
new file mode 100644
index 00000000000..5ef95cf7d58
--- /dev/null
+++ b/javascript/ql/lib/change-notes/2023-04-30-npm-submodule.md
@@ -0,0 +1,5 @@
+---
+category: minorAnalysis
+---
+
+- Added a support of sub modules in `node_modules`.
diff --git a/javascript/ql/lib/semmle/javascript/NPM.qll b/javascript/ql/lib/semmle/javascript/NPM.qll
index 4e64ef6ac09..394471b0b12 100644
--- a/javascript/ql/lib/semmle/javascript/NPM.qll
+++ b/javascript/ql/lib/semmle/javascript/NPM.qll
@@ -14,7 +14,7 @@ class PackageJson extends JsonObject {
/**
* Gets the name of this package.
- * If the package is located under the package `foo` and its relative path is `bar`, it can be `foo/bar`
+ * If the package is located under the package `pkg1` and its relative path is `foo/bar`, it can be `pkg1/foo/bar`
*/
string getPackageName() {
result = this.getPropStringValue("name")
From f52c845663858af566dc994c341eb3a77efce329 Mon Sep 17 00:00:00 2001
From: tyage
Date: Sun, 30 Apr 2023 19:52:11 +0900
Subject: [PATCH 087/870] Fix comment.
---
javascript/ql/lib/semmle/javascript/NPM.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/lib/semmle/javascript/NPM.qll b/javascript/ql/lib/semmle/javascript/NPM.qll
index 394471b0b12..53220a566b8 100644
--- a/javascript/ql/lib/semmle/javascript/NPM.qll
+++ b/javascript/ql/lib/semmle/javascript/NPM.qll
@@ -14,7 +14,7 @@ class PackageJson extends JsonObject {
/**
* Gets the name of this package.
- * If the package is located under the package `pkg1` and its relative path is `foo/bar`, it can be `pkg1/foo/bar`
+ * If the package is located under the package `pkg1` and its relative path is `foo/bar`, it can be `pkg1/foo/bar`.
*/
string getPackageName() {
result = this.getPropStringValue("name")
From f2def8433717d57c65bd731df2bd42ea3bcd2f74 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Sun, 30 Apr 2023 14:59:50 +0200
Subject: [PATCH 088/870] Misc: Add script to accept `.expected` changes from
CI
This script can be used to go over `codeql test run` expected/actual log
output from actions CI checks for a PR, and apply patches locally to
make the tests pass.
Designed for use by GitHub employees, since it needs access to internal
CI runs. Just run this tool while the branch for the PR is checked out!
You need the `gh` cli tool installed and authenticated.
Example can be seen in https://github.com/github/codeql/pull/12950
---
.vscode/tasks.json | 18 +-
CODEOWNERS | 3 +
.../accept-expected-changes-from-ci.py | 445 ++++++++++++++++++
3 files changed, 465 insertions(+), 1 deletion(-)
create mode 100755 misc/scripts/accept-expected-changes-from-ci.py
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 169b6bdd64d..68df2f6f498 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -22,6 +22,22 @@
"command": "${config:python.pythonPath}",
},
"problemMatcher": []
+ },
+ {
+ "label": "Accept .expected changes from CI",
+ "type": "process",
+ // Non-Windows OS will usually have Python 3 already installed at /usr/bin/python3.
+ "command": "python3",
+ "args": [
+ "misc/scripts/accept-expected-changes-from-ci.py"
+ ],
+ "group": "build",
+ "windows": {
+ // On Windows, use whatever Python interpreter is configured for this workspace. The default is
+ // just `python`, so if Python is already on the path, this will find it.
+ "command": "${config:python.pythonPath}",
+ },
+ "problemMatcher": []
}
]
-}
\ No newline at end of file
+}
diff --git a/CODEOWNERS b/CODEOWNERS
index 856d325b487..6e2dd9dc66b 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -40,3 +40,6 @@ WORKSPACE.bazel @github/codeql-ci-reviewers
/.github/workflows/ql-for-ql-* @github/codeql-ql-for-ql-reviewers
/.github/workflows/ruby-* @github/codeql-ruby
/.github/workflows/swift.yml @github/codeql-swift
+
+# Misc
+/misc/scripts/accept-expected-changes-from-ci.py @RasmusWL
diff --git a/misc/scripts/accept-expected-changes-from-ci.py b/misc/scripts/accept-expected-changes-from-ci.py
new file mode 100755
index 00000000000..7d85c1f7a1f
--- /dev/null
+++ b/misc/scripts/accept-expected-changes-from-ci.py
@@ -0,0 +1,445 @@
+#!/usr/bin/env python3
+"""
+This script can be used to go over `codeql test run` expected/actual log output from
+github actions, and apply patches locally to make the tests pass.
+
+It works by forming individual patches for each test-failure, and then applying them one
+by one. Since Python runs some tests twice (for both Python2 and Python3), we try to do
+some simple duplicate patch detection, to avoid failing to apply the same patch twice.
+
+Use it by checking out an up to date version of your branch, and just run the script (it
+will use `gh` cli to figure out what the PR number is). You must have the `gh` cli tool
+installed.
+
+The parsing of logs is somewhat tolerant of output being interleaved with other parallel
+execution, but it might fail in some cases ¯\_(ツ)_/¯
+
+Code written to hack things together until they work, so don't expect much :D
+"""
+
+
+import sys
+import re
+import tempfile
+from typing import List, Dict, Set, Optional, Any
+from pathlib import Path
+import subprocess
+import logging
+import json
+from dataclasses import dataclass, field
+from datetime import datetime
+import os.path
+import itertools
+
+LOGGER = logging.getLogger(Path(__file__).name)
+
+DEBUG_SAVE_PATCHES = False
+DEBUG_LOG_FILE = None
+
+
+def _get_codeql_repo_dir() -> Path:
+ return Path(__file__).parent.parent.parent
+
+
+CODEQL_REPO_DIR = _get_codeql_repo_dir()
+
+
+def _get_semmle_code_dir() -> Optional[Path]:
+ guess = CODEQL_REPO_DIR.parent
+ try:
+ out = subprocess.check_output(
+ ["git", "remote", "-v"],
+ cwd=guess,
+ ).decode("utf-8")
+ if "github/semmle-code" in out:
+ return guess
+ else:
+ return None
+ except subprocess.CalledProcessError:
+ return None
+
+
+SEMMLE_CODE_DIR = _get_semmle_code_dir()
+if SEMMLE_CODE_DIR is None:
+ LOGGER.warning("Could not find semmle-code repo, will not be able to apply patches for it")
+
+
+@dataclass(frozen=True, eq=True, order=True)
+class Patch:
+ filename: str
+ dir: Optional[str]
+ patch_first_line: str
+ patch: List[str] = field(hash=False)
+
+ def format_as_patch(self) -> str:
+ return (f"--- a/{self.filename}\n" +
+ f"+++ b/{self.filename}\n" +
+ "\n".join(self.patch) +
+ "\n"
+ )
+
+
+def parse_log_line(log_line: str) -> str:
+ if '\t' in log_line:
+ m = re.fullmatch(r"^(?:[^\t]+\t){2}\S+ ?(.*)$", log_line.strip())
+ else:
+ m = re.fullmatch(r"^(?:[^ ]+ )(.*)$", log_line.strip())
+
+ if not m:
+ return ""
+
+ return m.group(1)
+
+
+def make_patches_from_log_file(log_file_lines) -> List[Patch]:
+ # TODO: If the expected output contains `Password=123` this will be masked as `***`
+ # in the actions logs. We _could_ try to detect this and replace the line with the
+ # actual content from file (but it would require parsing the @@ lines, to understand
+ # what the old linenumber to read is)
+ patches = []
+
+ lines = iter(log_file_lines)
+
+ for raw_line in lines:
+ line = parse_log_line(raw_line)
+
+ if line == "--- expected":
+ while True:
+ next_line = parse_log_line(next(lines))
+ if next_line == "+++ actual":
+ break
+
+ lines_changed = []
+
+ while True:
+ next_line = parse_log_line(next(lines))
+ # it can be the case that
+ if next_line[0] in (" ", "-", "+", "@"):
+ lines_changed.append(next_line)
+ if "FAILED" in next_line:
+ break
+
+ # error line _should_ be next, but sometimes the output gets interleaved...
+ # so we just skip until we find the error line
+ error_line = next_line
+ while True:
+ # internal
+ filename_match = re.fullmatch(r"^##\[error\].*FAILED\(RESULT\) (.*)$", error_line)
+ if not filename_match:
+ # codeql action
+ filename_match = re.fullmatch(r"^.*FAILED\(RESULT\) (.*)$", error_line)
+ if filename_match:
+ break
+ error_line = parse_log_line(next(lines))
+
+ full_path = filename_match.group(1)
+
+ known_start_paths = {
+ # internal CI runs
+ "/home/runner/work/semmle-code/semmle-code/ql/": CODEQL_REPO_DIR,
+ "/home/runner/work/semmle-code/semmle-code/target/codeql-java-integration-tests/ql/": CODEQL_REPO_DIR,
+ "/home/runner/work/semmle-code/semmle-code/" : SEMMLE_CODE_DIR,
+ # github actions on codeql repo
+ "/home/runner/work/codeql/codeql/": CODEQL_REPO_DIR,
+ "/Users/runner/work/codeql/codeql/" : CODEQL_REPO_DIR, # MacOS
+ }
+ for known_start_path, dir in known_start_paths.items():
+ if full_path.startswith(known_start_path):
+ filename = Path(full_path[len(known_start_path):])
+ break
+ else:
+ raise Exception(f"Unknown path {full_path}, skipping")
+
+ expected_filename = filename.with_suffix(".expected")
+
+ # if the .expected file used to be empty, `codeql test run` outputs a diff
+ # that uses `@@ -1,1 ` but git wants it to be `@@ -0,0 ` (and not include
+ # the removal of the empty line)
+ if lines_changed[0].startswith("@@ -1,1 ") and lines_changed[1] == "-":
+ lines_changed[0] = lines_changed[0].replace("@@ -1,1 ", "@@ -0,0 ")
+ del lines_changed[1]
+
+ patch = Patch(
+ filename=expected_filename,
+ dir=dir,
+ patch_first_line=lines_changed[0],
+ patch=lines_changed,
+ )
+ patches.append(patch)
+
+ return patches
+
+def make_github_api_list_request(path: str) -> Any:
+ """Handles the way paginate currently works, which is to return multiple lists of
+ results, instead of merging them :(
+ """
+
+ s = subprocess.check_output(
+ ["gh", "api", "--paginate", path],
+ ).decode("utf-8")
+
+ try:
+ return json.loads(s)
+ except json.JSONDecodeError:
+ assert "][" in s
+ parts = s.split("][")
+ parts[0] = parts[0] + "]"
+ parts[-1] = "[" + parts[-1]
+ for i in range(1, len(parts) - 1):
+ parts[i] = "[" + parts[i] + "]"
+ return itertools.chain(*[json.loads(x) for x in parts])
+
+
+@dataclass
+class GithubStatus():
+ state: str
+ context: str
+ target_url: str
+ created_at: datetime
+ nwo: str
+ job_id: int = None
+
+
+def get_log_content(status: GithubStatus) -> str:
+ LOGGER.debug(f"'{status.context}': Getting logs")
+ if status.job_id:
+ content = subprocess.check_output(
+ ["gh", "api", f"/repos/{status.nwo}/actions/jobs/{status.job_id}/logs"],
+ ).decode("utf-8")
+ else:
+ m = re.fullmatch(r"^https://github\.com/([^/]+/[^/]+)/actions/runs/(\d+)(?:/jobs/(\d+))?$", status.target_url)
+ nwo = m.group(1)
+ run_id = m.group(2)
+ content = subprocess.check_output(
+ ["gh", "run", "view", "--repo", nwo, run_id, "--log-failed"],
+ ).decode("utf-8")
+
+ if DEBUG_SAVE_PATCHES:
+ tmp = tempfile.NamedTemporaryFile(delete=False)
+ tmp.write(content.encode(encoding="utf-8"))
+ print(tmp.name)
+ return content
+
+
+def main(pr_number: int):
+ LOGGER.info(f"Getting status URL for codeql PR #{pr_number}")
+ github_sha = subprocess.check_output(
+ ["gh", "api", f"repos/github/codeql/pulls/{pr_number}", "--jq", ".head.sha"]
+ ).decode("utf-8").strip()
+ local_sha = subprocess.check_output(
+ ["git", "rev-parse", "HEAD"]
+ ).decode("utf-8").strip()
+
+ if local_sha != github_sha:
+ LOGGER.error(f"GitHub SHA ({github_sha}) different from your local SHA ({local_sha}), sync your changes first!")
+ sys.exit(1)
+ sha = github_sha
+
+ status_url = f"https://api.github.com/repos/github/codeql/commits/{sha}/statuses"
+ LOGGER.info(f"Getting status details ({status_url})")
+ statuses = make_github_api_list_request(status_url)
+ newest_status: Dict[str, GithubStatus] = dict()
+
+ for status in statuses:
+ created_at = datetime.fromisoformat(status["created_at"][:-1])
+ key = status["context"]
+ if key not in newest_status or created_at > newest_status[key].created_at:
+ m = re.fullmatch(r"^https://github\.com/([^/]+/[^/]+)/actions/runs/(\d+)(?:/jobs/(\d+))?$", status["target_url"])
+
+ newest_status[key] = GithubStatus(
+ state=status["state"],
+ context=status["context"],
+ target_url=status["target_url"],
+ created_at=created_at,
+ nwo=m.group(1),
+ )
+
+ supported_internal_status_language_test_names = [
+ "Java Integration Tests Linux",
+ # "codeql-coding-standards Unit Tests Linux",
+ # TODO: Currently disabled, would just require support for figuring out where
+ # https://github.com/github/codeql-coding-standards/ is checked out locally
+ ]
+
+ lang_test_failures: List[GithubStatus] = list()
+ for status in newest_status.values():
+ if " Language Tests" in status.context or status.context in supported_internal_status_language_test_names:
+ if status.state == "failure":
+ lang_test_failures.append(status)
+ elif status.state == "pending":
+ LOGGER.error(f"Language tests ({status.context}) are still running, please wait for them to finish before running this script again")
+ sys.exit(1)
+
+ job_failure_urls = set()
+ for lang_test_failure in lang_test_failures:
+ job_failure_urls.add(lang_test_failure.target_url)
+
+ assert len(job_failure_urls) == 1, f"Multiple job failure URLs: {job_failure_urls}"
+ job_failure_url = job_failure_urls.pop()
+
+ # fixup URL. On the status, the target URL is the run, and it's really hard to
+ # change this to link to the full `/runs//jobs/` URL, since
+ # the `` is not available in a context: https://github.com/community/community/discussions/40291
+ m = re.fullmatch(r"^https://github\.com/([^/]+/[^/]+)/actions/runs/(\d+)$", job_failure_url)
+ nwo = m.group(1)
+ run_id = m.group(2)
+ jobs_url = f"https://api.github.com/repos/{nwo}/actions/runs/{run_id}/jobs"
+ LOGGER.info(f"Fixing up target url from looking at {jobs_url}")
+ jobs = json.loads(subprocess.check_output(["gh", "api", "--paginate", jobs_url]).decode("utf-8"))
+ for lang_test_failure in lang_test_failures:
+ workflow_translation = {
+ "codeql-coding-standards Unit Tests Linux": "Start codeql-coding-standards"
+ }
+ expected_workflow_name = workflow_translation.get(lang_test_failure.context, lang_test_failure.context)
+
+ for job in jobs["jobs"]:
+ api_name: str = job["name"]
+ if " / " not in api_name:
+ continue
+
+ workflow_name, job_name = api_name.split(" / ")
+ # The job names we're looking for looks like "Python2 Language Tests / Python2 Language Tests" or "Java Language Tests / Java Language Tests Linux"
+ # for "Java Integration Tests Linux / Java Integration tests Linux" we need to ignore case :|
+ if workflow_name == expected_workflow_name and job_name.lower().startswith(lang_test_failure.context.lower()):
+ lang_test_failure.job_id = job["id"]
+ break
+ else:
+ LOGGER.error(f"Could not find job for {lang_test_failure.context!r}")
+ sys.exit(1)
+
+ # Ruby/Swift/C#/Go use github actions, and not internal CI. These are not reported
+ # from the /statuses API, but from the /check-suites API
+ check_suites_url = f"https://api.github.com/repos/github/codeql/commits/{sha}/check-suites"
+ LOGGER.info(f"Getting check suites ({check_suites_url})")
+ check_suites = json.loads(subprocess.check_output(["gh", "api", "--paginate", check_suites_url]).decode("utf-8"))
+
+ check_failure_urls = []
+ for check in check_suites["check_suites"]:
+ if check["status"] != "completed":
+ print(check)
+ LOGGER.error("At least one check not completed yet!")
+ sys.exit(1)
+
+ if check["conclusion"] == "failure":
+ check_failure_urls.append(check["check_runs_url"])
+
+ for check_failure_url in check_failure_urls:
+ # run information: https://docs.github.com/en/rest/actions/workflow-jobs?apiVersion=2022-11-28#get-a-job-for-a-workflow-run
+ check_runs = json.loads(subprocess.check_output(["gh", "api", "--paginate", check_failure_url]).decode("utf-8"))
+ for check_run in check_runs["check_runs"]:
+ if check_run["conclusion"] == "failure":
+ m = re.fullmatch(r"^https://github\.com/([^/]+/[^/]+)/actions/runs/(\d+)(?:/jobs/(\d+))?$", check_run["details_url"])
+ nwo = m.group(1)
+ run_id = m.group(2)
+ jobs_url = f"https://api.github.com/repos/{nwo}/actions/runs/{run_id}/jobs"
+ LOGGER.debug(f"Looking into failure at {jobs_url}")
+ jobs = json.loads(subprocess.check_output(["gh", "api", "--paginate", jobs_url]).decode("utf-8"))
+
+ for job in jobs["jobs"]:
+ OK_WORKFLOW_NAMES = ["C#: Run QL Tests", "Go: Run Tests", "Ruby: Run QL Tests", "Swift"]
+ def ok_job_name(job_name: str) -> bool:
+ if job_name.startswith("qltest"):
+ return True
+ if job_name.startswith("Test Linux"):
+ return True
+ return False
+
+ if job["name"] == check_run['name'] and job["workflow_name"] in OK_WORKFLOW_NAMES and ok_job_name(job["name"]):
+ # print(check_run['name'], 'matched to', f"{job['workflow_name']} / {job['name']}")
+ lang_test_failures.append(GithubStatus(
+ state="failure",
+ context=f"{job['workflow_name']} / {job['name']}",
+ target_url=job["html_url"],
+ created_at=check_run["completed_at"],
+ nwo=nwo,
+ job_id=job["id"],
+ ))
+ break
+ else:
+ LOGGER.debug(f"Ignoring actions failure for '{check_run['name']}' ({check_run['details_url']})")
+
+ if not lang_test_failures:
+ LOGGER.info("No language test failures found")
+ return
+
+ patches_to_apply: Set[Patch] = set()
+
+ for failure in lang_test_failures:
+ log_content = get_log_content(failure)
+ LOGGER.info(f"'{failure.context}': Making patches")
+ patches = make_patches_from_log_file(log_content.splitlines())
+
+ if not patches:
+ LOGGER.warning(f"No patches generated for job {failure}")
+ continue
+
+ for patch in patches:
+ if patch in patches_to_apply:
+ LOGGER.debug(f"Skipping duplicate patch for {patch.filename}")
+ continue
+ patches_to_apply.add(patch)
+
+ if DEBUG_SAVE_PATCHES:
+ sys.exit("debug save patches")
+
+ if not patches_to_apply:
+ print("No patches to apply")
+ return
+
+ semmle_code_changed = False
+
+ for patch in patches_to_apply:
+ with tempfile.NamedTemporaryFile(prefix=f"patches-", suffix=".patch") as temp:
+ temp.write(patch.format_as_patch().encode(encoding="utf-8"))
+ temp.flush()
+ LOGGER.info(f"Applying patch for '{patch.filename}'")
+ try:
+ if patch.dir is None:
+ LOGGER.warning(f"Did not find local semmle-code directory, so skipping patch for '{patch.filename}'")
+ continue
+
+ subprocess.check_call(["git", "apply", temp.name], cwd=patch.dir)
+ if patch.dir == SEMMLE_CODE_DIR:
+ semmle_code_changed = True
+ except subprocess.CalledProcessError:
+ LOGGER.error(f"Could not apply patches for '{patch.filename}' '{patch.patch[0]}', skipping")
+ tmp_keep = tempfile.NamedTemporaryFile(delete=False)
+ tmp_keep.write(patch.format_as_patch().encode(encoding="utf-8"))
+ LOGGER.error(f"Patch saved to {tmp_keep.name}")
+
+ if semmle_code_changed:
+ print("Expected output in semmle-code changed!")
+
+def get_pr_number() -> int:
+ if len(sys.argv) < 2:
+ pr_number_response = subprocess.check_output([
+ "gh", "pr", "view", "--json", "number"
+ ]).decode("utf-8")
+
+ return json.loads(pr_number_response)["number"]
+ else:
+ return int(sys.argv[1])
+
+
+if __name__ == "__main__":
+
+ level = logging.INFO
+
+ try:
+ import coloredlogs
+ coloredlogs.install(level, fmt="%(levelname)s: %(message)s")
+ except ImportError:
+ logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
+
+
+ if DEBUG_LOG_FILE:
+ patches = make_patches_from_log_file(open(DEBUG_LOG_FILE, "r").readlines())
+ for patch in patches:
+ if True:
+ tmp_keep = tempfile.NamedTemporaryFile(delete=False)
+ tmp_keep.write(patch.format_as_patch().encode(encoding="utf-8"))
+ LOGGER.info(f"Patch for {patch.filename} saved to {tmp_keep.name}")
+ sys.exit(1)
+
+ os.chdir(CODEQL_REPO_DIR)
+ main(get_pr_number())
From e677b62241895f39403542855b7e8b2e5a2376a3 Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Fri, 17 Mar 2023 10:13:11 +0100
Subject: [PATCH 089/870] use type-tracking instead of global dataflow for
tracking regular expressions
---
config/identical-files.json | 1 -
.../lib/semmle/python/dataflow/new/Regexp.qll | 9 +-
.../new/internal/DataFlowImplForRegExp.qll | 398 ------------------
python/ql/lib/semmle/python/regex.qll | 43 +-
.../python/regexp/internal/RegExpTracking.qll | 79 ++++
5 files changed, 106 insertions(+), 424 deletions(-)
delete mode 100644 python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplForRegExp.qll
create mode 100644 python/ql/lib/semmle/python/regexp/internal/RegExpTracking.qll
diff --git a/config/identical-files.json b/config/identical-files.json
index d694c69f9bc..4edce66949c 100644
--- a/config/identical-files.json
+++ b/config/identical-files.json
@@ -48,7 +48,6 @@
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll",
- "python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplForRegExp.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl1.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll",
diff --git a/python/ql/lib/semmle/python/dataflow/new/Regexp.qll b/python/ql/lib/semmle/python/dataflow/new/Regexp.qll
index a518fc41f46..19beceec88b 100644
--- a/python/ql/lib/semmle/python/dataflow/new/Regexp.qll
+++ b/python/ql/lib/semmle/python/dataflow/new/Regexp.qll
@@ -5,6 +5,7 @@
private import semmle.python.RegexTreeView
private import semmle.python.regex
private import semmle.python.dataflow.new.DataFlow
+private import semmle.python.regexp.internal.RegExpTracking
/**
* Provides utility predicates related to regular expressions.
@@ -25,18 +26,18 @@ deprecated module RegExpPatterns {
* as a part of a regular expression.
*/
class RegExpPatternSource extends DataFlow::CfgNode {
- private Regex astNode;
+ private DataFlow::Node sink;
- RegExpPatternSource() { astNode = this.asExpr() }
+ RegExpPatternSource() { this = regExpSource(sink) }
/**
* Gets a node where the pattern of this node is parsed as a part of
* a regular expression.
*/
- DataFlow::Node getAParse() { result = this }
+ DataFlow::Node getAParse() { result = sink }
/**
* Gets the root term of the regular expression parsed from this pattern.
*/
- RegExpTerm getRegExpTerm() { result.getRegex() = astNode }
+ RegExpTerm getRegExpTerm() { result.getRegex() = this.asExpr() }
}
diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplForRegExp.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplForRegExp.qll
deleted file mode 100644
index be70086a93a..00000000000
--- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplForRegExp.qll
+++ /dev/null
@@ -1,398 +0,0 @@
-/**
- * DEPRECATED: Use `Global` and `GlobalWithState` instead.
- *
- * Provides a `Configuration` class backwards-compatible interface to the data
- * flow library.
- */
-
-private import DataFlowImplCommon
-private import DataFlowImplSpecific::Private
-import DataFlowImplSpecific::Public
-private import DataFlowImpl
-import DataFlowImplCommonPublic
-import FlowStateString
-private import codeql.util.Unit
-
-/**
- * A configuration of interprocedural data flow analysis. This defines
- * sources, sinks, and any other configurable aspect of the analysis. Each
- * use of the global data flow library must define its own unique extension
- * of this abstract class. 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 DataFlow::Configuration {
- * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
- * // Override `isSource` and `isSink`.
- * // Optionally override `isBarrier`.
- * // Optionally override `isAdditionalFlowStep`.
- * }
- * ```
- * Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and
- * the edges are those data-flow steps that preserve the value of the node
- * along with any additional edges defined by `isAdditionalFlowStep`.
- * Specifying nodes in `isBarrier` will remove those nodes from the graph, and
- * specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going
- * and/or out-going edges from those nodes, respectively.
- *
- * 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 two classes extending
- * `DataFlow::Configuration` should never depend on each other. One of them
- * should instead depend on a `DataFlow2::Configuration`, a
- * `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
- */
-abstract class Configuration extends string {
- bindingset[this]
- Configuration() { any() }
-
- /**
- * Holds if `source` is a relevant data flow source.
- */
- predicate isSource(Node source) { none() }
-
- /**
- * Holds if `source` is a relevant data flow source with the given initial
- * `state`.
- */
- predicate isSource(Node source, FlowState state) { none() }
-
- /**
- * Holds if `sink` is a relevant data flow sink.
- */
- predicate isSink(Node sink) { none() }
-
- /**
- * Holds if `sink` is a relevant data flow sink accepting `state`.
- */
- predicate isSink(Node sink, FlowState state) { none() }
-
- /**
- * Holds if data flow through `node` is prohibited. This completely removes
- * `node` from the data flow graph.
- */
- predicate isBarrier(Node node) { none() }
-
- /**
- * Holds if data flow through `node` is prohibited when the flow state is
- * `state`.
- */
- predicate isBarrier(Node node, FlowState state) { none() }
-
- /** Holds if data flow into `node` is prohibited. */
- predicate isBarrierIn(Node node) { none() }
-
- /** Holds if data flow out of `node` is prohibited. */
- predicate isBarrierOut(Node node) { none() }
-
- /**
- * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
- *
- * Holds if data flow through nodes guarded by `guard` is prohibited.
- */
- deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
-
- /**
- * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
- *
- * Holds if data flow through nodes guarded by `guard` is prohibited when
- * the flow state is `state`
- */
- deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
-
- /**
- * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
- */
- predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
-
- /**
- * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
- * This step is only applicable in `state1` and updates the flow state to `state2`.
- */
- predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
- none()
- }
-
- /**
- * Holds if an arbitrary number of implicit read steps of content `c` may be
- * taken at `node`.
- */
- predicate allowImplicitRead(Node node, ContentSet c) { none() }
-
- /**
- * Gets the virtual dispatch branching limit when calculating field flow.
- * This can be overridden to a smaller value to improve performance (a
- * value of 0 disables field flow), or a larger value to get more results.
- */
- int fieldFlowBranchLimit() { result = 2 }
-
- /**
- * Gets a data flow configuration feature to add restrictions to the set of
- * valid flow paths.
- *
- * - `FeatureHasSourceCallContext`:
- * Assume that sources have some existing call context to disallow
- * conflicting return-flow directly following the source.
- * - `FeatureHasSinkCallContext`:
- * Assume that sinks have some existing call context to disallow
- * conflicting argument-to-parameter flow directly preceding the sink.
- * - `FeatureEqualSourceSinkCallContext`:
- * Implies both of the above and additionally ensures that the entire flow
- * path preserves the call context.
- *
- * These features are generally not relevant for typical end-to-end data flow
- * queries, but should only be used for constructing paths that need to
- * somehow be pluggable in another path context.
- */
- FlowFeature getAFeature() { none() }
-
- /** Holds if sources should be grouped in the result of `hasFlowPath`. */
- predicate sourceGrouping(Node source, string sourceGroup) { none() }
-
- /** Holds if sinks should be grouped in the result of `hasFlowPath`. */
- predicate sinkGrouping(Node sink, string sinkGroup) { none() }
-
- /**
- * Holds if data may flow from `source` to `sink` for this configuration.
- */
- predicate hasFlow(Node source, Node sink) { hasFlow(source, sink, this) }
-
- /**
- * Holds if data may flow from `source` to `sink` for this configuration.
- *
- * The corresponding paths are generated from the end-points and the graph
- * included in the module `PathGraph`.
- */
- predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
-
- /**
- * Holds if data may flow from some source to `sink` for this configuration.
- */
- predicate hasFlowTo(Node sink) { hasFlowTo(sink, this) }
-
- /**
- * Holds if data may flow from some source to `sink` for this configuration.
- */
- predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
-
- /**
- * DEPRECATED: Use `FlowExploration` instead.
- *
- * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
- * measured in approximate number of interprocedural steps.
- */
- deprecated int explorationLimit() { none() }
-
- /**
- * Holds if hidden nodes should be included in the data flow graph.
- *
- * This feature should only be used for debugging or when the data flow graph
- * is not visualized (for example in a `path-problem` query).
- */
- predicate includeHiddenNodes() { none() }
-}
-
-/**
- * This class exists to prevent mutual recursion between the user-overridden
- * member predicates of `Configuration` and the rest of the data-flow library.
- * Good performance cannot be guaranteed in the presence of such recursion, so
- * it should be replaced by using more than one copy of the data flow library.
- */
-abstract private class ConfigurationRecursionPrevention extends Configuration {
- bindingset[this]
- ConfigurationRecursionPrevention() { any() }
-
- override predicate hasFlow(Node source, Node sink) {
- strictcount(Node n | this.isSource(n)) < 0
- or
- strictcount(Node n | this.isSource(n, _)) < 0
- or
- strictcount(Node n | this.isSink(n)) < 0
- or
- strictcount(Node n | this.isSink(n, _)) < 0
- or
- strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
- or
- strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, _, n2, _)) < 0
- or
- super.hasFlow(source, sink)
- }
-}
-
-/** A bridge class to access the deprecated `isBarrierGuard`. */
-private class BarrierGuardGuardedNodeBridge extends Unit {
- abstract predicate guardedNode(Node n, Configuration config);
-
- abstract predicate guardedNode(Node n, FlowState state, Configuration config);
-}
-
-private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
- deprecated override predicate guardedNode(Node n, Configuration config) {
- exists(BarrierGuard g |
- config.isBarrierGuard(g) and
- n = g.getAGuardedNode()
- )
- }
-
- deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
- exists(BarrierGuard g |
- config.isBarrierGuard(g, state) and
- n = g.getAGuardedNode()
- )
- }
-}
-
-private FlowState relevantState(Configuration config) {
- config.isSource(_, result) or
- config.isSink(_, result) or
- config.isBarrier(_, result) or
- config.isAdditionalFlowStep(_, result, _, _) or
- config.isAdditionalFlowStep(_, _, _, result)
-}
-
-private newtype TConfigState =
- TMkConfigState(Configuration config, FlowState state) {
- state = relevantState(config) or state instanceof FlowStateEmpty
- }
-
-private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
-
-private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
-
-private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
-
-private module Config implements FullStateConfigSig {
- class FlowState = TConfigState;
-
- predicate isSource(Node source, FlowState state) {
- getConfig(state).isSource(source, getState(state))
- or
- getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty
- }
-
- predicate isSink(Node sink, FlowState state) {
- getConfig(state).isSink(sink, getState(state))
- or
- getConfig(state).isSink(sink) and getState(state) instanceof FlowStateEmpty
- }
-
- predicate isBarrier(Node node) { none() }
-
- predicate isBarrier(Node node, FlowState state) {
- getConfig(state).isBarrier(node, getState(state)) or
- getConfig(state).isBarrier(node) or
- any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
- any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
- }
-
- predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
-
- predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
-
- predicate isAdditionalFlowStep(Node node1, Node node2) {
- singleConfiguration() and
- any(Configuration config).isAdditionalFlowStep(node1, node2)
- }
-
- predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
- getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and
- getConfig(state2) = getConfig(state1)
- or
- not singleConfiguration() and
- getConfig(state1).isAdditionalFlowStep(node1, node2) and
- state2 = state1
- }
-
- predicate allowImplicitRead(Node node, ContentSet c) {
- any(Configuration config).allowImplicitRead(node, c)
- }
-
- int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
-
- FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
-
- predicate sourceGrouping(Node source, string sourceGroup) {
- any(Configuration config).sourceGrouping(source, sourceGroup)
- }
-
- predicate sinkGrouping(Node sink, string sinkGroup) {
- any(Configuration config).sinkGrouping(sink, sinkGroup)
- }
-
- predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
-}
-
-private import Impl as I
-
-/**
- * A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
- * Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
- */
-class PathNode instanceof I::PathNode {
- /** Gets a textual representation of this element. */
- final string toString() { result = super.toString() }
-
- /**
- * Gets a textual representation of this element, including a textual
- * representation of the call context.
- */
- final string toStringWithContext() { result = super.toStringWithContext() }
-
- /**
- * Holds if this element is at the specified location.
- * The location spans column `startcolumn` of line `startline` to
- * column `endcolumn` of line `endline` in file `filepath`.
- * For more information, see
- * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
- */
- final predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-
- /** Gets the underlying `Node`. */
- final Node getNode() { result = super.getNode() }
-
- /** Gets the `FlowState` of this node. */
- final FlowState getState() { result = getState(super.getState()) }
-
- /** Gets the associated configuration. */
- final Configuration getConfiguration() { result = getConfig(super.getState()) }
-
- /** Gets a successor of this node, if any. */
- final PathNode getASuccessor() { result = super.getASuccessor() }
-
- /** Holds if this node is a source. */
- final predicate isSource() { super.isSource() }
-
- /** Holds if this node is a grouping of source nodes. */
- final predicate isSourceGroup(string group) { super.isSourceGroup(group) }
-
- /** Holds if this node is a grouping of sink nodes. */
- final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
-}
-
-module PathGraph = I::PathGraph;
-
-private predicate hasFlow(Node source, Node sink, Configuration config) {
- exists(PathNode source0, PathNode sink0 |
- hasFlowPath(source0, sink0, config) and
- source0.getNode() = source and
- sink0.getNode() = sink
- )
-}
-
-private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
- I::flowPath(source, sink) and source.getConfiguration() = config
-}
-
-private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
-
-predicate flowsTo = hasFlow/3;
diff --git a/python/ql/lib/semmle/python/regex.qll b/python/ql/lib/semmle/python/regex.qll
index d4d00da7aae..cc21ac104bf 100644
--- a/python/ql/lib/semmle/python/regex.qll
+++ b/python/ql/lib/semmle/python/regex.qll
@@ -2,6 +2,7 @@ import python
private import semmle.python.ApiGraphs
// Need to import since frameworks can extend the abstract `RegexString`
private import semmle.python.Frameworks
+private import semmle.python.Concepts as Concepts
/**
* Gets the positional argument index containing the regular expression flags for the member of the
@@ -38,38 +39,38 @@ private API::Node relevant_re_member(string name) {
name != "escape"
}
-private import semmle.python.dataflow.new.internal.DataFlowImplForRegExp as RegData
-
-/** A data-flow configuration for tracking string-constants that are used as regular expressions. */
-private class RegexTracking extends RegData::Configuration {
- RegexTracking() { this = "RegexTracking" }
-
- override predicate isSource(RegData::Node node) {
- node.asExpr() instanceof Bytes or
- node.asExpr() instanceof Unicode
- }
-
- override predicate isSink(RegData::Node node) { used_as_regex_internal(node.asExpr(), _) }
-}
-
/**
* Holds if the expression `e` is used as a regex with the `re` module, with the regex-mode `mode` (if known).
* If regex mode is not known, `mode` will be `"None"`.
*
* This predicate has not done any data-flow tracking.
*/
-private predicate used_as_regex_internal(Expr e, string mode) {
+// TODO: This thing should be refactored, along with removing RegexString.
+predicate used_as_regex_internal(Expr e, string mode) {
/* Call to re.xxx(regex, ... [mode]) */
- exists(DataFlow::CallCfgNode call, string name |
+ exists(DataFlow::CallCfgNode call |
+ call instanceof Concepts::RegexExecution and
+ e = call.(Concepts::RegexExecution).getRegex().asExpr()
+ or
call.getArg(0).asExpr() = e and
- call = relevant_re_member(name).getACall()
+ call = relevant_re_member(_).getACall()
|
mode = "None"
or
- mode = mode_from_node([call.getArg(re_member_flags_arg(name)), call.getArgByName("flags")])
+ exists(DataFlow::CallCfgNode callNode |
+ call = callNode and
+ mode =
+ mode_from_node([
+ callNode
+ .getArg(re_member_flags_arg(callNode.(DataFlow::MethodCallNode).getMethodName())),
+ callNode.getArgByName("flags")
+ ])
+ )
)
}
+private import regexp.internal.RegExpTracking as RegExpTracking
+
/**
* Holds if the string-constant `s` ends up being used as a regex with the `re` module, with the regex-mode `mode` (if known).
* If regex mode is not known, `mode` will be `"None"`.
@@ -78,8 +79,8 @@ private predicate used_as_regex_internal(Expr e, string mode) {
*/
predicate used_as_regex(Expr s, string mode) {
(s instanceof Bytes or s instanceof Unicode) and
- exists(RegexTracking t, RegData::Node source, RegData::Node sink |
- t.hasFlow(source, sink) and
+ exists(DataFlow::Node source, DataFlow::Node sink |
+ source = RegExpTracking::regExpSource(sink) and
used_as_regex_internal(sink.asExpr(), mode) and
s = source.asExpr()
)
@@ -90,7 +91,7 @@ private import semmle.python.RegexTreeView
/** Gets a parsed regular expression term that is executed at `exec`. */
RegExpTerm getTermForExecution(RegexExecution exec) {
- exists(RegexTracking t, DataFlow::Node source | t.hasFlow(source, exec.getRegex()) |
+ exists(DataFlow::Node source | source = RegExpTracking::regExpSource(exec.getRegex()) |
result.getRegex() = source.asExpr() and
result.isRootTerm()
)
diff --git a/python/ql/lib/semmle/python/regexp/internal/RegExpTracking.qll b/python/ql/lib/semmle/python/regexp/internal/RegExpTracking.qll
new file mode 100644
index 00000000000..4751f97b0a7
--- /dev/null
+++ b/python/ql/lib/semmle/python/regexp/internal/RegExpTracking.qll
@@ -0,0 +1,79 @@
+/**
+ * Provides predicates that track strings to where they are used as regular expressions.
+ * This is implemented using TypeTracking in two phases:
+ *
+ * 1: An exploratory backwards analysis that imprecisely tracks all nodes that may be used as regular expressions.
+ * The exploratory phase ends with a forwards analysis from string constants that were reached by the backwards analysis.
+ * This is similar to the exploratory phase of the JavaScript global DataFlow library.
+ *
+ * 2: A precise type tracking analysis that tracks constant strings to where they are used as regular expressions.
+ * This phase keeps track of which strings and regular expressions end up in which places.
+ */
+
+import python
+private import semmle.python.dataflow.new.DataFlow
+private import semmle.python.Concepts as Concepts
+
+/** Gets a constant string value that may be used as a regular expression. */
+DataFlow::LocalSourceNode strStart() {
+ result.asExpr() instanceof Bytes or
+ result.asExpr() instanceof Unicode
+}
+
+private import semmle.python.regex as Regex
+
+/** Gets a node where regular expressions that flow to the node are used. */
+DataFlow::Node regSink() {
+ result = any(Concepts::RegexExecution exec).getRegex()
+ or
+ // TODO: Refactor into something nicer, and remove the above import of `semmle.python.regex`
+ Regex::used_as_regex_internal(result.asExpr(), _)
+}
+
+/**
+ * Gets a dataflow node that may end up being in any regular expression execution.
+ * This is the backwards exploratory phase of the analysis.
+ */
+private DataFlow::TypeTrackingNode backwards(DataFlow::TypeBackTracker t) {
+ t.start() and
+ result = regSink().getALocalSource()
+ or
+ exists(DataFlow::TypeBackTracker t2 | result = backwards(t2).backtrack(t2, t))
+}
+
+/**
+ * Gets a reference to a string that reaches any regular expression execution.
+ * This is the forwards exploratory phase of the analysis.
+ */
+private DataFlow::TypeTrackingNode forwards(DataFlow::TypeTracker t) {
+ t.start() and
+ result = backwards(DataFlow::TypeBackTracker::end()) and
+ result.flowsTo(strStart())
+ or
+ exists(DataFlow::TypeTracker t2 | result = forwards(t2).track(t2, t)) and
+ result = backwards(_)
+}
+
+/**
+ * Gets a node that has been tracked from the string constant `start` to some node.
+ * This is used to figure out where `start` is evaluated as a regular expression.
+ *
+ * The result of the exploratory phase is used to limit the size of the search space in this precise analysis.
+ */
+private DataFlow::TypeTrackingNode regexTracking(DataFlow::Node start, DataFlow::TypeTracker t) {
+ result = forwards(_) and
+ (
+ t.start() and
+ start = strStart() and
+ result = start.getALocalSource()
+ or
+ exists(DataFlow::TypeTracker t2 | result = regexTracking(start, t2).track(t2, t))
+ )
+}
+
+/** Gets a node holding a value for the regular expression that is evaluated at `re`. */
+cached
+DataFlow::Node regExpSource(DataFlow::Node re) {
+ re = regSink() and
+ regexTracking(result, DataFlow::TypeTracker::end()).flowsTo(re)
+}
From f0254fc0895c4951479022201425b05870b6bf51 Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Fri, 17 Mar 2023 17:38:13 +0100
Subject: [PATCH 090/870] introduce RegExpInterpretation instead of
RegexString, and move RegexTreeView.qll into a regexp folder
---
python/ql/lib/semmle/python/PrintAst.qll | 2 +-
python/ql/lib/semmle/python/RegexTreeView.qll | 1094 +----------------
.../lib/semmle/python/dataflow/new/Regexp.qll | 2 +-
.../lib/semmle/python/frameworks/Django.qll | 13 +-
.../lib/semmle/python/frameworks/Tornado.qll | 11 +-
python/ql/lib/semmle/python/regex.qll | 102 +-
.../semmle/python/regexp/RegexTreeView.qll | 1090 ++++++++++++++++
.../python/regexp/internal/RegExpTracking.qll | 3 +-
.../PolynomialReDoSCustomizations.qll | 2 +-
.../python/security/regexp/HostnameRegex.qll | 2 +-
.../src/Security/CWE-020/OverlyLargeRange.ql | 2 +-
.../ql/src/Security/CWE-116/BadTagFilter.ql | 2 +-
python/ql/src/Security/CWE-730/ReDoS.ql | 2 +-
.../library-tests/regexparser/Consistency.ql | 2 +-
.../PolynomialBackTracking.ql | 2 +-
15 files changed, 1174 insertions(+), 1157 deletions(-)
create mode 100644 python/ql/lib/semmle/python/regexp/RegexTreeView.qll
diff --git a/python/ql/lib/semmle/python/PrintAst.qll b/python/ql/lib/semmle/python/PrintAst.qll
index 96e76de0b77..6189a47d4bb 100644
--- a/python/ql/lib/semmle/python/PrintAst.qll
+++ b/python/ql/lib/semmle/python/PrintAst.qll
@@ -7,7 +7,7 @@
*/
import python
-import semmle.python.RegexTreeView
+import semmle.python.regexp.RegexTreeView
import semmle.python.Yaml
private newtype TPrintAstConfiguration = MkPrintAstConfiguration()
diff --git a/python/ql/lib/semmle/python/RegexTreeView.qll b/python/ql/lib/semmle/python/RegexTreeView.qll
index a69e076d21a..84cfaa3a4c7 100644
--- a/python/ql/lib/semmle/python/RegexTreeView.qll
+++ b/python/ql/lib/semmle/python/RegexTreeView.qll
@@ -1,1094 +1,6 @@
-/** Provides a class hierarchy corresponding to a parse tree of regular expressions. */
-
-import python
-private import semmle.python.regex
-private import codeql.regex.nfa.NfaUtils as NfaUtils
-private import codeql.regex.RegexTreeView
-// exporting as RegexTreeView, and in the top-level scope.
-import Impl as RegexTreeView
-import Impl
-
-/** Gets the parse tree resulting from parsing `re`, if such has been constructed. */
-RegExpTerm getParsedRegExp(StrConst re) { result.getRegex() = re and result.isRootTerm() }
-
/**
- * An element containing a regular expression term, that is, either
- * a string literal (parsed as a regular expression)
- * or another regular expression term.
- *
- * For sequences and alternations, we require at least one child.
- * Otherwise, we wish to represent the term differently.
- * This avoids multiple representations of the same term.
+ * Deprecated. Use `semmle.python.regexp.RegexTreeView` instead.
*/
-private newtype TRegExpParent =
- /** A string literal used as a regular expression */
- TRegExpLiteral(Regex re) or
- /** A quantified term */
- TRegExpQuantifier(Regex re, int start, int end) { re.qualifiedItem(start, end, _, _) } or
- /** A sequence term */
- TRegExpSequence(Regex re, int start, int end) {
- re.sequence(start, end) and
- exists(seqChild(re, start, end, 1)) // if a sequence does not have more than one element, it should be treated as that element instead.
- } or
- /** An alternation term */
- TRegExpAlt(Regex re, int start, int end) {
- re.alternation(start, end) and
- exists(int part_end |
- re.alternationOption(start, end, start, part_end) and
- part_end < end
- ) // if an alternation does not have more than one element, it should be treated as that element instead.
- } or
- /** A character class term */
- TRegExpCharacterClass(Regex re, int start, int end) { re.charSet(start, end) } or
- /** A character range term */
- TRegExpCharacterRange(Regex re, int start, int end) { re.charRange(_, start, _, _, end) } or
- /** A group term */
- TRegExpGroup(Regex re, int start, int end) { re.group(start, end) } or
- /** A special character */
- TRegExpSpecialChar(Regex re, int start, int end) { re.specialCharacter(start, end, _) } or
- /** A normal character */
- TRegExpNormalChar(Regex re, int start, int end) {
- re.normalCharacterSequence(start, end)
- or
- re.escapedCharacter(start, end) and
- not re.specialCharacter(start, end, _)
- } or
- /** A back reference */
- TRegExpBackRef(Regex re, int start, int end) { re.backreference(start, end) }
-pragma[nomagic]
-private int seqChildEnd(Regex re, int start, int end, int i) {
- result = seqChild(re, start, end, i).getEnd()
-}
-
-// moved out so we can use it in the charpred
-private RegExpTerm seqChild(Regex re, int start, int end, int i) {
- re.sequence(start, end) and
- (
- i = 0 and
- result.getRegex() = re and
- result.getStart() = start and
- exists(int itemEnd |
- re.item(start, itemEnd) and
- result.getEnd() = itemEnd
- )
- or
- i > 0 and
- result.getRegex() = re and
- exists(int itemStart | itemStart = seqChildEnd(re, start, end, i - 1) |
- result.getStart() = itemStart and
- re.item(itemStart, result.getEnd())
- )
- )
-}
-
-/** An implementation that satisfies the RegexTreeView signature. */
-module Impl implements RegexTreeViewSig {
- /**
- * An element containing a regular expression term, that is, either
- * a string literal (parsed as a regular expression)
- * or another regular expression term.
- */
- class RegExpParent extends TRegExpParent {
- /** Gets a textual representation of this element. */
- string toString() { result = "RegExpParent" }
-
- /** Gets the `i`th child term. */
- abstract RegExpTerm getChild(int i);
-
- /** Gets a child term . */
- RegExpTerm getAChild() { result = this.getChild(_) }
-
- /** Gets the number of child terms. */
- int getNumChild() { result = count(this.getAChild()) }
-
- /** Gets the last child term of this element. */
- RegExpTerm getLastChild() { result = this.getChild(this.getNumChild() - 1) }
-
- /** Gets the associated regex. */
- abstract Regex getRegex();
- }
-
- /** A string literal used as a regular expression */
- class RegExpLiteral extends TRegExpLiteral, RegExpParent {
- Regex re;
-
- RegExpLiteral() { this = TRegExpLiteral(re) }
-
- override RegExpTerm getChild(int i) { i = 0 and result.getRegex() = re and result.isRootTerm() }
-
- /** Holds if dot, `.`, matches all characters, including newlines. */
- predicate isDotAll() { re.getAMode() = "DOTALL" }
-
- /** Holds if this regex matching is case-insensitive for this regex. */
- predicate isIgnoreCase() { re.getAMode() = "IGNORECASE" }
-
- /** Get a string representing all modes for this regex. */
- string getFlags() { result = concat(string mode | mode = re.getAMode() | mode, " | ") }
-
- override Regex getRegex() { result = re }
-
- /** Gets the primary QL class for this regex. */
- string getPrimaryQLClass() { result = "RegExpLiteral" }
- }
-
- /**
- * A regular expression term, that is, a syntactic part of a regular expression.
- */
- class RegExpTerm extends RegExpParent {
- Regex re;
- int start;
- int end;
-
- RegExpTerm() {
- this = TRegExpAlt(re, start, end)
- or
- this = TRegExpBackRef(re, start, end)
- or
- this = TRegExpCharacterClass(re, start, end)
- or
- this = TRegExpCharacterRange(re, start, end)
- or
- this = TRegExpNormalChar(re, start, end)
- or
- this = TRegExpGroup(re, start, end)
- or
- this = TRegExpQuantifier(re, start, end)
- or
- this = TRegExpSequence(re, start, end)
- or
- this = TRegExpSpecialChar(re, start, end)
- }
-
- /**
- * Gets the outermost term of this regular expression.
- */
- RegExpTerm getRootTerm() {
- this.isRootTerm() and result = this
- or
- result = this.getParent().(RegExpTerm).getRootTerm()
- }
-
- /**
- * Holds if this term is part of a string literal
- * that is interpreted as a regular expression.
- */
- predicate isUsedAsRegExp() { any() }
-
- /**
- * Holds if this is the root term of a regular expression.
- */
- predicate isRootTerm() { start = 0 and end = re.getText().length() }
-
- override RegExpTerm getChild(int i) {
- result = this.(RegExpAlt).getChild(i)
- or
- result = this.(RegExpBackRef).getChild(i)
- or
- result = this.(RegExpCharacterClass).getChild(i)
- or
- result = this.(RegExpCharacterRange).getChild(i)
- or
- result = this.(RegExpNormalChar).getChild(i)
- or
- result = this.(RegExpGroup).getChild(i)
- or
- result = this.(RegExpQuantifier).getChild(i)
- or
- result = this.(RegExpSequence).getChild(i)
- or
- result = this.(RegExpSpecialChar).getChild(i)
- }
-
- /**
- * Gets the parent term of this regular expression term, or the
- * regular expression literal if this is the root term.
- */
- RegExpParent getParent() { result.getAChild() = this }
-
- override Regex getRegex() { result = re }
-
- /** Gets the offset at which this term starts. */
- int getStart() { result = start }
-
- /** Gets the offset at which this term ends. */
- int getEnd() { result = end }
-
- override string toString() { result = re.getText().substring(start, end) }
-
- /**
- * Gets the location of the surrounding regex, as locations inside the regex do not exist.
- * To get location information corresponding to the term inside the regex,
- * use `hasLocationInfo`.
- */
- Location getLocation() { result = re.getLocation() }
-
- /** Holds if this term is found at the specified location offsets. */
- predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- exists(int re_start |
- re.getLocation().hasLocationInfo(filepath, startline, re_start, endline, _) and
- startcolumn = re_start + start + 4 and
- endcolumn = re_start + end + 3
- )
- }
-
- /** Gets the file in which this term is found. */
- File getFile() { result = this.getLocation().getFile() }
-
- /** Gets the raw source text of this term. */
- string getRawValue() { result = this.toString() }
-
- /** Gets the string literal in which this term is found. */
- RegExpLiteral getLiteral() { result = TRegExpLiteral(re) }
-
- /** Gets the regular expression term that is matched (textually) before this one, if any. */
- RegExpTerm getPredecessor() {
- exists(RegExpTerm parent | parent = this.getParent() |
- result = parent.(RegExpSequence).previousElement(this)
- or
- not exists(parent.(RegExpSequence).previousElement(this)) and
- not parent instanceof RegExpSubPattern and
- result = parent.getPredecessor()
- )
- }
-
- /** Gets the regular expression term that is matched (textually) after this one, if any. */
- RegExpTerm getSuccessor() {
- exists(RegExpTerm parent | parent = this.getParent() |
- result = parent.(RegExpSequence).nextElement(this)
- or
- not exists(parent.(RegExpSequence).nextElement(this)) and
- not parent instanceof RegExpSubPattern and
- result = parent.getSuccessor()
- )
- }
-
- /** Gets the primary QL class for this term. */
- string getPrimaryQLClass() { result = "RegExpTerm" }
- }
-
- /**
- * A quantified regular expression term.
- *
- * Example:
- *
- * ```
- * ((ECMA|Java)[sS]cript)*
- * ```
- */
- class RegExpQuantifier extends RegExpTerm, TRegExpQuantifier {
- int part_end;
- boolean may_repeat_forever;
-
- RegExpQuantifier() {
- this = TRegExpQuantifier(re, start, end) and
- re.qualifiedPart(start, part_end, end, _, may_repeat_forever)
- }
-
- override RegExpTerm getChild(int i) {
- i = 0 and
- result.getRegex() = re and
- result.getStart() = start and
- result.getEnd() = part_end
- }
-
- /** Hols if this term may match an unlimited number of times. */
- predicate mayRepeatForever() { may_repeat_forever = true }
-
- /** Gets the qualifier for this term. That is e.g "?" for "a?". */
- string getQualifier() { result = re.getText().substring(part_end, end) }
-
- override string getPrimaryQLClass() { result = "RegExpQuantifier" }
- }
-
- /**
- * A regular expression term that permits unlimited repetitions.
- */
- class InfiniteRepetitionQuantifier extends RegExpQuantifier {
- InfiniteRepetitionQuantifier() { this.mayRepeatForever() }
- }
-
- /**
- * A star-quantified term.
- *
- * Example:
- *
- * ```
- * \w*
- * ```
- */
- class RegExpStar extends InfiniteRepetitionQuantifier {
- RegExpStar() { this.getQualifier().charAt(0) = "*" }
-
- override string getPrimaryQLClass() { result = "RegExpStar" }
- }
-
- /**
- * A plus-quantified term.
- *
- * Example:
- *
- * ```
- * \w+
- * ```
- */
- class RegExpPlus extends InfiniteRepetitionQuantifier {
- RegExpPlus() { this.getQualifier().charAt(0) = "+" }
-
- override string getPrimaryQLClass() { result = "RegExpPlus" }
- }
-
- /**
- * An optional term.
- *
- * Example:
- *
- * ```
- * ;?
- * ```
- */
- class RegExpOpt extends RegExpQuantifier {
- RegExpOpt() { this.getQualifier().charAt(0) = "?" }
-
- override string getPrimaryQLClass() { result = "RegExpOpt" }
- }
-
- /**
- * A range-quantified term
- *
- * Examples:
- *
- * ```
- * \w{2,4}
- * \w{2,}
- * \w{2}
- * ```
- */
- class RegExpRange extends RegExpQuantifier {
- string upper;
- string lower;
-
- RegExpRange() { re.multiples(part_end, end, lower, upper) }
-
- /** Gets the string defining the upper bound of this range, if any. */
- string getUpper() { result = upper }
-
- /** Gets the string defining the lower bound of this range, if any. */
- string getLower() { result = lower }
-
- /**
- * Gets the upper bound of the range, if any.
- *
- * If there is no upper bound, any number of repetitions is allowed.
- * For a term of the form `r{lo}`, both the lower and the upper bound
- * are `lo`.
- */
- int getUpperBound() { result = this.getUpper().toInt() }
-
- /** Gets the lower bound of the range. */
- int getLowerBound() { result = this.getLower().toInt() }
-
- override string getPrimaryQLClass() { result = "RegExpRange" }
- }
-
- /**
- * A sequence term.
- *
- * Example:
- *
- * ```
- * (ECMA|Java)Script
- * ```
- *
- * This is a sequence with the elements `(ECMA|Java)` and `Script`.
- */
- class RegExpSequence extends RegExpTerm, TRegExpSequence {
- RegExpSequence() { this = TRegExpSequence(re, start, end) }
-
- override RegExpTerm getChild(int i) { result = seqChild(re, start, end, i) }
-
- /** Gets the element preceding `element` in this sequence. */
- RegExpTerm previousElement(RegExpTerm element) { element = this.nextElement(result) }
-
- /** Gets the element following `element` in this sequence. */
- RegExpTerm nextElement(RegExpTerm element) {
- exists(int i |
- element = this.getChild(i) and
- result = this.getChild(i + 1)
- )
- }
-
- override string getPrimaryQLClass() { result = "RegExpSequence" }
- }
-
- /**
- * An alternative term, that is, a term of the form `a|b`.
- *
- * Example:
- *
- * ```
- * ECMA|Java
- * ```
- */
- class RegExpAlt extends RegExpTerm, TRegExpAlt {
- RegExpAlt() { this = TRegExpAlt(re, start, end) }
-
- override RegExpTerm getChild(int i) {
- i = 0 and
- result.getRegex() = re and
- result.getStart() = start and
- exists(int part_end |
- re.alternationOption(start, end, start, part_end) and
- result.getEnd() = part_end
- )
- or
- i > 0 and
- result.getRegex() = re and
- exists(int part_start |
- part_start = this.getChild(i - 1).getEnd() + 1 // allow for the |
- |
- result.getStart() = part_start and
- re.alternationOption(start, end, part_start, result.getEnd())
- )
- }
-
- override string getPrimaryQLClass() { result = "RegExpAlt" }
- }
-
- /**
- * A character escape in a regular expression.
- *
- * Example:
- *
- * ```
- * \.
- * ```
- */
- class RegExpCharEscape = RegExpEscape;
-
- private import codeql.util.Numbers as Numbers
-
- /**
- * An escaped regular expression term, that is, a regular expression
- * term starting with a backslash, which is not a backreference.
- *
- * Example:
- *
- * ```
- * \.
- * \w
- * ```
- */
- class RegExpEscape extends RegExpNormalChar {
- RegExpEscape() { re.escapedCharacter(start, end) }
-
- /**
- * Gets the name of the escaped; for example, `w` for `\w`.
- * TODO: Handle named escapes.
- */
- override string getValue() {
- not this.isUnicode() and
- this.isIdentityEscape() and
- result = this.getUnescaped()
- or
- this.getUnescaped() = "n" and result = "\n"
- or
- this.getUnescaped() = "r" and result = "\r"
- or
- this.getUnescaped() = "t" and result = "\t"
- or
- this.getUnescaped() = "f" and result = 12.toUnicode()
- or
- this.getUnescaped() = "v" and result = 11.toUnicode()
- or
- this.isUnicode() and
- result = this.getUnicode()
- }
-
- /** Holds if this terms name is given by the part following the escape character. */
- predicate isIdentityEscape() { not this.getUnescaped() in ["n", "r", "t", "f"] }
-
- override string getPrimaryQLClass() { result = "RegExpEscape" }
-
- /** Gets the part of the term following the escape character. That is e.g. "w" if the term is "\w". */
- string getUnescaped() { result = this.getText().suffix(1) }
-
- /**
- * Gets the text for this escape. That is e.g. "\w".
- */
- private string getText() { result = re.getText().substring(start, end) }
-
- /**
- * Holds if this is a unicode escape.
- */
- private predicate isUnicode() { this.getText().prefix(2) = ["\\u", "\\U"] }
-
- /**
- * Gets the unicode char for this escape.
- * E.g. for `\u0061` this returns "a".
- */
- private string getUnicode() {
- result = Numbers::parseHexInt(this.getText().suffix(2)).toUnicode()
- }
- }
-
- /**
- * A word boundary, that is, a regular expression term of the form `\b`.
- */
- class RegExpWordBoundary extends RegExpSpecialChar {
- RegExpWordBoundary() { this.getChar() = "\\b" }
- }
-
- /**
- * A non-word boundary, that is, a regular expression term of the form `\B`.
- */
- class RegExpNonWordBoundary extends RegExpSpecialChar {
- RegExpNonWordBoundary() { this.getChar() = "\\B" }
- }
-
- /**
- * A character class escape in a regular expression.
- * That is, an escaped character that denotes multiple characters.
- *
- * Examples:
- *
- * ```
- * \w
- * \S
- * ```
- */
- class RegExpCharacterClassEscape extends RegExpEscape {
- RegExpCharacterClassEscape() { this.getValue() in ["d", "D", "s", "S", "w", "W"] }
-
- override RegExpTerm getChild(int i) { none() }
-
- override string getPrimaryQLClass() { result = "RegExpCharacterClassEscape" }
- }
-
- /**
- * A character class in a regular expression.
- *
- * Examples:
- *
- * ```
- * [a-z_]
- * [^<>&]
- * ```
- */
- class RegExpCharacterClass extends RegExpTerm, TRegExpCharacterClass {
- RegExpCharacterClass() { this = TRegExpCharacterClass(re, start, end) }
-
- /** Holds if this character class is inverted, matching the opposite of its content. */
- predicate isInverted() { re.getChar(start + 1) = "^" }
-
- /** Gets the `i`th char inside this charater class. */
- string getCharThing(int i) { result = re.getChar(i + start) }
-
- /** Holds if this character class can match anything. */
- predicate isUniversalClass() {
- // [^]
- this.isInverted() and not exists(this.getAChild())
- or
- // [\w\W] and similar
- not this.isInverted() and
- exists(string cce1, string cce2 |
- cce1 = this.getAChild().(RegExpCharacterClassEscape).getValue() and
- cce2 = this.getAChild().(RegExpCharacterClassEscape).getValue()
- |
- cce1 != cce2 and cce1.toLowerCase() = cce2.toLowerCase()
- )
- }
-
- override RegExpTerm getChild(int i) {
- i = 0 and
- result.getRegex() = re and
- exists(int itemStart, int itemEnd |
- result.getStart() = itemStart and
- re.char_set_start(start, itemStart) and
- re.char_set_child(start, itemStart, itemEnd) and
- result.getEnd() = itemEnd
- )
- or
- i > 0 and
- result.getRegex() = re and
- exists(int itemStart | itemStart = this.getChild(i - 1).getEnd() |
- result.getStart() = itemStart and
- re.char_set_child(start, itemStart, result.getEnd())
- )
- }
-
- override string getPrimaryQLClass() { result = "RegExpCharacterClass" }
- }
-
- /**
- * A character range in a character class in a regular expression.
- *
- * Example:
- *
- * ```
- * a-z
- * ```
- */
- class RegExpCharacterRange extends RegExpTerm, TRegExpCharacterRange {
- int lower_end;
- int upper_start;
-
- RegExpCharacterRange() {
- this = TRegExpCharacterRange(re, start, end) and
- re.charRange(_, start, lower_end, upper_start, end)
- }
-
- /** Holds if this range goes from `lo` to `hi`, in effect is `lo-hi`. */
- predicate isRange(string lo, string hi) {
- lo = re.getText().substring(start, lower_end) and
- hi = re.getText().substring(upper_start, end)
- }
-
- override RegExpTerm getChild(int i) {
- i = 0 and
- result.getRegex() = re and
- result.getStart() = start and
- result.getEnd() = lower_end
- or
- i = 1 and
- result.getRegex() = re and
- result.getStart() = upper_start and
- result.getEnd() = end
- }
-
- override string getPrimaryQLClass() { result = "RegExpCharacterRange" }
- }
-
- /**
- * A normal character in a regular expression, that is, a character
- * without special meaning. This includes escaped characters.
- *
- * Examples:
- * ```
- * t
- * \t
- * ```
- */
- additional class RegExpNormalChar extends RegExpTerm, TRegExpNormalChar {
- RegExpNormalChar() { this = TRegExpNormalChar(re, start, end) }
-
- /**
- * Holds if this constant represents a valid Unicode character (as opposed
- * to a surrogate code point that does not correspond to a character by itself.)
- */
- predicate isCharacter() { any() }
-
- /** Gets the string representation of the char matched by this term. */
- string getValue() { result = re.getText().substring(start, end) }
-
- override RegExpTerm getChild(int i) { none() }
-
- override string getPrimaryQLClass() { result = "RegExpNormalChar" }
- }
-
- /**
- * A constant regular expression term, that is, a regular expression
- * term matching a single string. Currently, this will always be a single character.
- *
- * Example:
- *
- * ```
- * a
- * ```
- */
- class RegExpConstant extends RegExpTerm {
- string value;
-
- RegExpConstant() {
- this = TRegExpNormalChar(re, start, end) and
- not this instanceof RegExpCharacterClassEscape and
- // exclude chars in qualifiers
- // TODO: push this into regex library
- not exists(int qstart, int qend | re.qualifiedPart(_, qstart, qend, _, _) |
- qstart <= start and end <= qend
- ) and
- value = this.(RegExpNormalChar).getValue()
- }
-
- /**
- * Holds if this constant represents a valid Unicode character (as opposed
- * to a surrogate code point that does not correspond to a character by itself.)
- */
- predicate isCharacter() { any() }
-
- /** Gets the string matched by this constant term. */
- string getValue() { result = value }
-
- override RegExpTerm getChild(int i) { none() }
-
- override string getPrimaryQLClass() { result = "RegExpConstant" }
- }
-
- /**
- * A grouped regular expression.
- *
- * Examples:
- *
- * ```
- * (ECMA|Java)
- * (?:ECMA|Java)
- * (?['"])
- * ```
- */
- class RegExpGroup extends RegExpTerm, TRegExpGroup {
- RegExpGroup() { this = TRegExpGroup(re, start, end) }
-
- /**
- * Gets the index of this capture group within the enclosing regular
- * expression literal.
- *
- * For example, in the regular expression `/((a?).)(?:b)/`, the
- * group `((a?).)` has index 1, the group `(a?)` nested inside it
- * has index 2, and the group `(?:b)` has no index, since it is
- * not a capture group.
- */
- int getNumber() { result = re.getGroupNumber(start, end) }
-
- /** Holds if this is a capture group. */
- predicate isCapture() { exists(this.getNumber()) }
-
- /** Holds if this is a named capture group. */
- predicate isNamed() { exists(this.getName()) }
-
- /** Gets the name of this capture group, if any. */
- string getName() { result = re.getGroupName(start, end) }
-
- override RegExpTerm getChild(int i) {
- result.getRegex() = re and
- i = 0 and
- re.groupContents(start, end, result.getStart(), result.getEnd())
- }
-
- override string getPrimaryQLClass() { result = "RegExpGroup" }
- }
-
- /**
- * A special character in a regular expression.
- *
- * Examples:
- * ```
- * ^
- * $
- * .
- * ```
- */
- additional class RegExpSpecialChar extends RegExpTerm, TRegExpSpecialChar {
- string char;
-
- RegExpSpecialChar() {
- this = TRegExpSpecialChar(re, start, end) and
- re.specialCharacter(start, end, char)
- }
-
- /**
- * Holds if this constant represents a valid Unicode character (as opposed
- * to a surrogate code point that does not correspond to a character by itself.)
- */
- predicate isCharacter() { any() }
-
- /** Gets the char for this term. */
- string getChar() { result = char }
-
- override RegExpTerm getChild(int i) { none() }
-
- override string getPrimaryQLClass() { result = "RegExpSpecialChar" }
- }
-
- /**
- * A dot regular expression.
- *
- * Example:
- *
- * ```
- * .
- * ```
- */
- class RegExpDot extends RegExpSpecialChar {
- RegExpDot() { this.getChar() = "." }
-
- override string getPrimaryQLClass() { result = "RegExpDot" }
- }
-
- /**
- * A term that matches a specific position between characters in the string.
- *
- * Example:
- *
- * ```
- * \A
- * ```
- */
- class RegExpAnchor extends RegExpSpecialChar {
- RegExpAnchor() { this.getChar() = ["\\A", "^", "$", "\\Z"] }
- }
-
- /**
- * A dollar assertion `$` or `\Z` matching the end of a line.
- *
- * Example:
- *
- * ```
- * $
- * ```
- */
- class RegExpDollar extends RegExpAnchor {
- RegExpDollar() { this.getChar() = ["$", "\\Z"] }
-
- override string getPrimaryQLClass() { result = "RegExpDollar" }
- }
-
- /**
- * A caret assertion `^` or `\A` matching the beginning of a line.
- *
- * Example:
- *
- * ```
- * ^
- * ```
- */
- class RegExpCaret extends RegExpAnchor {
- RegExpCaret() { this.getChar() = ["^", "\\A"] }
-
- override string getPrimaryQLClass() { result = "RegExpCaret" }
- }
-
- /**
- * A zero-width match, that is, either an empty group or an assertion.
- *
- * Examples:
- * ```
- * ()
- * (?=\w)
- * ```
- */
- additional class RegExpZeroWidthMatch extends RegExpGroup {
- RegExpZeroWidthMatch() { re.zeroWidthMatch(start, end) }
-
- override RegExpTerm getChild(int i) { none() }
-
- override string getPrimaryQLClass() { result = "RegExpZeroWidthMatch" }
- }
-
- /**
- * A zero-width lookahead or lookbehind assertion.
- *
- * Examples:
- *
- * ```
- * (?=\w)
- * (?!\n)
- * (?<=\.)
- * (?`
- * in a regular expression.
- *
- * Examples:
- *
- * ```
- * \1
- * (?P=quote)
- * ```
- */
- class RegExpBackRef extends RegExpTerm, TRegExpBackRef {
- RegExpBackRef() { this = TRegExpBackRef(re, start, end) }
-
- /**
- * Gets the number of the capture group this back reference refers to, if any.
- */
- int getNumber() { result = re.getBackrefNumber(start, end) }
-
- /**
- * Gets the name of the capture group this back reference refers to, if any.
- */
- string getName() { result = re.getBackrefName(start, end) }
-
- /** Gets the capture group this back reference refers to. */
- RegExpGroup getGroup() {
- this.hasLiteralAndNumber(result.getLiteral(), result.getNumber()) or
- this.hasLiteralAndName(result.getLiteral(), result.getName())
- }
-
- /** Join-order helper for `getGroup`. */
- pragma[nomagic]
- private predicate hasLiteralAndNumber(RegExpLiteral literal, int number) {
- literal = this.getLiteral() and
- number = this.getNumber()
- }
-
- /** Join-order helper for `getGroup`. */
- pragma[nomagic]
- private predicate hasLiteralAndName(RegExpLiteral literal, string name) {
- literal = this.getLiteral() and
- name = this.getName()
- }
-
- override RegExpTerm getChild(int i) { none() }
-
- override string getPrimaryQLClass() { result = "RegExpBackRef" }
- }
-
- class Top = RegExpParent;
-
- /**
- * Holds if `term` is an escape class representing e.g. `\d`.
- * `clazz` is which character class it represents, e.g. "d" for `\d`.
- */
- predicate isEscapeClass(RegExpTerm term, string clazz) {
- exists(RegExpCharacterClassEscape escape | term = escape | escape.getValue() = clazz)
- }
-
- /**
- * Holds if `term` is a possessive quantifier.
- * As python's regexes do not support possessive quantifiers, this never holds, but is used by the shared library.
- */
- predicate isPossessive(RegExpQuantifier term) { none() }
-
- /**
- * Holds if the regex that `term` is part of is used in a way that ignores any leading prefix of the input it's matched against.
- * Not yet implemented for Python.
- */
- predicate matchesAnyPrefix(RegExpTerm term) { any() }
-
- /**
- * Holds if the regex that `term` is part of is used in a way that ignores any trailing suffix of the input it's matched against.
- * Not yet implemented for Python.
- */
- predicate matchesAnySuffix(RegExpTerm term) { any() }
-
- /**
- * Holds if the regular expression should not be considered.
- *
- * We make the pragmatic performance optimization to ignore regular expressions in files
- * that does not belong to the project code (such as installed dependencies).
- */
- predicate isExcluded(RegExpParent parent) {
- not exists(parent.getRegex().getLocation().getFile().getRelativePath())
- or
- // Regexes with many occurrences of ".*" may cause the polynomial ReDoS computation to explode, so
- // we explicitly exclude these.
- count(int i | exists(parent.getRegex().getText().regexpFind("\\.\\*", i, _)) | i) > 10
- }
-
- /**
- * Holds if `root` has the `i` flag for case-insensitive matching.
- */
- predicate isIgnoreCase(RegExpTerm root) {
- root.isRootTerm() and
- root.getLiteral().isIgnoreCase()
- }
-
- /**
- * Holds if `root` has the `s` flag for multi-line matching.
- */
- predicate isDotAll(RegExpTerm root) {
- root.isRootTerm() and
- root.getLiteral().isDotAll()
- }
-}
+deprecated import regexp.RegexTreeView as Dep
+import Dep
diff --git a/python/ql/lib/semmle/python/dataflow/new/Regexp.qll b/python/ql/lib/semmle/python/dataflow/new/Regexp.qll
index 19beceec88b..8fa3427256c 100644
--- a/python/ql/lib/semmle/python/dataflow/new/Regexp.qll
+++ b/python/ql/lib/semmle/python/dataflow/new/Regexp.qll
@@ -2,7 +2,7 @@
* Provides classes for working with regular expressions.
*/
-private import semmle.python.RegexTreeView
+private import semmle.python.regexp.RegexTreeView
private import semmle.python.regex
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.regexp.internal.RegExpTracking
diff --git a/python/ql/lib/semmle/python/frameworks/Django.qll b/python/ql/lib/semmle/python/frameworks/Django.qll
index c656ee85fda..886357594a1 100644
--- a/python/ql/lib/semmle/python/frameworks/Django.qll
+++ b/python/ql/lib/semmle/python/frameworks/Django.qll
@@ -2512,9 +2512,10 @@ module PrivateDjango {
any(int i | i < routeHandler.getFirstPossibleRoutedParamIndex() | routeHandler.getArg(i))
)
or
- exists(DjangoRouteHandler routeHandler, DjangoRouteRegex regex |
+ exists(DjangoRouteHandler routeHandler, DjangoRouteRegex regexUse, Regex regex |
+ regex.getAUse() = regexUse and
routeHandler = this.getARequestHandler() and
- regex.getRouteSetup() = this
+ regexUse.getRouteSetup() = this
|
// either using named capture groups (passed as keyword arguments) or using
// unnamed capture groups (passed as positional arguments)
@@ -2533,14 +2534,12 @@ module PrivateDjango {
/**
* A regex that is used to set up a route.
*
- * Needs this subclass to be considered a RegexString.
+ * Needs this subclass to be considered a RegExpInterpretation.
*/
- private class DjangoRouteRegex extends RegexString instanceof StrConst {
+ private class DjangoRouteRegex extends RegExpInterpretation::Range {
DjangoRegexRouteSetup rePathCall;
- DjangoRouteRegex() {
- rePathCall.getUrlPatternArg().getALocalSource() = DataFlow::exprNode(this)
- }
+ DjangoRouteRegex() { this = rePathCall.getUrlPatternArg() }
DjangoRegexRouteSetup getRouteSetup() { result = rePathCall }
}
diff --git a/python/ql/lib/semmle/python/frameworks/Tornado.qll b/python/ql/lib/semmle/python/frameworks/Tornado.qll
index 29bd4fa2279..f54ba64e780 100644
--- a/python/ql/lib/semmle/python/frameworks/Tornado.qll
+++ b/python/ql/lib/semmle/python/frameworks/Tornado.qll
@@ -384,12 +384,12 @@ module Tornado {
/**
* A regex that is used to set up a route.
*
- * Needs this subclass to be considered a RegexString.
+ * Needs this subclass to be considered a RegExpInterpretation.
*/
- private class TornadoRouteRegex extends RegexString instanceof StrConst {
+ private class TornadoRouteRegex extends RegExpInterpretation::Range {
TornadoRouteSetup setup;
- TornadoRouteRegex() { setup.getUrlPatternArg().getALocalSource() = DataFlow::exprNode(this) }
+ TornadoRouteRegex() { this = setup.getUrlPatternArg() }
TornadoRouteSetup getRouteSetup() { result = setup }
}
@@ -423,9 +423,10 @@ module Tornado {
not result = requestHandler.getArg(0)
)
or
- exists(Function requestHandler, TornadoRouteRegex regex |
+ exists(Function requestHandler, TornadoRouteRegex regexUse, Regex regex |
+ regex.getAUse() = regexUse and
requestHandler = this.getARequestHandler() and
- regex.getRouteSetup() = this
+ regexUse.getRouteSetup() = this
|
// first group will have group number 1
result = requestHandler.getArg(regex.getGroupNumber(_, _))
diff --git a/python/ql/lib/semmle/python/regex.qll b/python/ql/lib/semmle/python/regex.qll
index cc21ac104bf..8bb8c8b3ba4 100644
--- a/python/ql/lib/semmle/python/regex.qll
+++ b/python/ql/lib/semmle/python/regex.qll
@@ -1,6 +1,6 @@
import python
private import semmle.python.ApiGraphs
-// Need to import since frameworks can extend the abstract `RegexString`
+// Need to import since frameworks can extend the abstract `RegExpInterpretation::Range`
private import semmle.python.Frameworks
private import semmle.python.Concepts as Concepts
@@ -45,7 +45,7 @@ private API::Node relevant_re_member(string name) {
*
* This predicate has not done any data-flow tracking.
*/
-// TODO: This thing should be refactored, along with removing RegexString.
+// TODO: This should only be used to get the `mode`, and nowhere else.
predicate used_as_regex_internal(Expr e, string mode) {
/* Call to re.xxx(regex, ... [mode]) */
exists(DataFlow::CallCfgNode call |
@@ -70,24 +70,8 @@ predicate used_as_regex_internal(Expr e, string mode) {
}
private import regexp.internal.RegExpTracking as RegExpTracking
-
-/**
- * Holds if the string-constant `s` ends up being used as a regex with the `re` module, with the regex-mode `mode` (if known).
- * If regex mode is not known, `mode` will be `"None"`.
- *
- * This predicate has done data-flow tracking to find the string-constant that is used as a regex.
- */
-predicate used_as_regex(Expr s, string mode) {
- (s instanceof Bytes or s instanceof Unicode) and
- exists(DataFlow::Node source, DataFlow::Node sink |
- source = RegExpTracking::regExpSource(sink) and
- used_as_regex_internal(sink.asExpr(), mode) and
- s = source.asExpr()
- )
-}
-
private import semmle.python.Concepts
-private import semmle.python.RegexTreeView
+private import semmle.python.regexp.RegexTreeView
/** Gets a parsed regular expression term that is executed at `exec`. */
RegExpTerm getTermForExecution(RegexExecution exec) {
@@ -137,16 +121,70 @@ private DataFlow::Node re_flag_tracker(string flag_name) {
}
/** Gets a regular expression mode flag associated with the given data flow node. */
+// TODO: Move this into a RegexFlag module, along with related code?
string mode_from_node(DataFlow::Node node) { node = re_flag_tracker(result) }
+/** Provides a class for modeling regular expression interpretations. */
+module RegExpInterpretation {
+ /**
+ * A node that is not a regular expression literal, but is used in places that
+ * may interpret it as one. Instances of this class are typically strings that
+ * flow to method calls like `re.compile`.
+ */
+ abstract class Range extends DataFlow::Node { }
+}
+
+/**
+ * A node interpreted as a regular expression.
+ * Speficically nodes where string values are interpreted as regular expressions.
+ */
+class StdLibRegExpInterpretation extends RegExpInterpretation::Range {
+ StdLibRegExpInterpretation() {
+ this =
+ API::moduleImport("re").getMember(any(string name | name != "escape")).getACall().getArg(0)
+ }
+}
+
/** A StrConst used as a regular expression */
-abstract class RegexString extends Expr {
- RegexString() {
+deprecated class RegexString extends Regex {
+ RegexString() { this = RegExpTracking::regExpSource(_).asExpr() }
+}
+
+/** A StrConst used as a regular expression */
+class Regex extends Expr {
+ DataFlow::Node sink;
+
+ Regex() {
(this instanceof Bytes or this instanceof Unicode) and
+ this = RegExpTracking::regExpSource(sink).asExpr() and
// is part of the user code
exists(this.getLocation().getFile().getRelativePath())
}
+ /** Gets a data-flow node where this string value is used as a regular expression. */
+ DataFlow::Node getAUse() { result = sink }
+
+ /**
+ * Gets a mode (if any) of this regular expression. Can be any of:
+ * DEBUG
+ * IGNORECASE
+ * LOCALE
+ * MULTILINE
+ * DOTALL
+ * UNICODE
+ * VERBOSE
+ */
+ string getAMode() {
+ exists(string mode |
+ used_as_regex_internal(sink.asExpr(), mode) and
+ result != "None" and
+ result = mode
+ )
+ or
+ result = this.getModeFromPrefix()
+ }
+
+ // TODO: Refactor all of the below into a regex parsing file, similar to Ruby.
/**
* Helper predicate for `char_set_start(int start, int end)`.
*
@@ -1082,25 +1120,3 @@ abstract class RegexString extends Expr {
this.lastPart(start, end)
}
}
-
-/** A StrConst used as a regular expression */
-class Regex extends RegexString {
- Regex() { used_as_regex(this, _) }
-
- /**
- * Gets a mode (if any) of this regular expression. Can be any of:
- * DEBUG
- * IGNORECASE
- * LOCALE
- * MULTILINE
- * DOTALL
- * UNICODE
- * VERBOSE
- */
- string getAMode() {
- result != "None" and
- used_as_regex(this, result)
- or
- result = this.getModeFromPrefix()
- }
-}
diff --git a/python/ql/lib/semmle/python/regexp/RegexTreeView.qll b/python/ql/lib/semmle/python/regexp/RegexTreeView.qll
new file mode 100644
index 00000000000..568ed73c12f
--- /dev/null
+++ b/python/ql/lib/semmle/python/regexp/RegexTreeView.qll
@@ -0,0 +1,1090 @@
+/** Provides a class hierarchy corresponding to a parse tree of regular expressions. */
+
+import python
+private import semmle.python.regex
+private import codeql.regex.nfa.NfaUtils as NfaUtils
+private import codeql.regex.RegexTreeView
+// exporting as RegexTreeView, and in the top-level scope.
+import Impl as RegexTreeView
+import Impl
+
+/** Gets the parse tree resulting from parsing `re`, if such has been constructed. */
+RegExpTerm getParsedRegExp(StrConst re) { result.getRegex() = re and result.isRootTerm() }
+
+/**
+ * An element containing a regular expression term, that is, either
+ * a string literal (parsed as a regular expression)
+ * or another regular expression term.
+ *
+ * For sequences and alternations, we require at least one child.
+ * Otherwise, we wish to represent the term differently.
+ * This avoids multiple representations of the same term.
+ */
+private newtype TRegExpParent =
+ /** A string literal used as a regular expression */
+ TRegExpLiteral(Regex re) or
+ /** A quantified term */
+ TRegExpQuantifier(Regex re, int start, int end) { re.qualifiedItem(start, end, _, _) } or
+ /** A sequence term */
+ TRegExpSequence(Regex re, int start, int end) {
+ re.sequence(start, end) and
+ exists(seqChild(re, start, end, 1)) // if a sequence does not have more than one element, it should be treated as that element instead.
+ } or
+ /** An alternation term */
+ TRegExpAlt(Regex re, int start, int end) {
+ re.alternation(start, end) and
+ exists(int part_end |
+ re.alternationOption(start, end, start, part_end) and
+ part_end < end
+ ) // if an alternation does not have more than one element, it should be treated as that element instead.
+ } or
+ /** A character class term */
+ TRegExpCharacterClass(Regex re, int start, int end) { re.charSet(start, end) } or
+ /** A character range term */
+ TRegExpCharacterRange(Regex re, int start, int end) { re.charRange(_, start, _, _, end) } or
+ /** A group term */
+ TRegExpGroup(Regex re, int start, int end) { re.group(start, end) } or
+ /** A special character */
+ TRegExpSpecialChar(Regex re, int start, int end) { re.specialCharacter(start, end, _) } or
+ /** A normal character */
+ TRegExpNormalChar(Regex re, int start, int end) {
+ re.normalCharacterSequence(start, end)
+ or
+ re.escapedCharacter(start, end) and
+ not re.specialCharacter(start, end, _)
+ } or
+ /** A back reference */
+ TRegExpBackRef(Regex re, int start, int end) { re.backreference(start, end) }
+
+pragma[nomagic]
+private int seqChildEnd(Regex re, int start, int end, int i) {
+ result = seqChild(re, start, end, i).getEnd()
+}
+
+// moved out so we can use it in the charpred
+private RegExpTerm seqChild(Regex re, int start, int end, int i) {
+ re.sequence(start, end) and
+ (
+ i = 0 and
+ result.getRegex() = re and
+ result.getStart() = start and
+ exists(int itemEnd |
+ re.item(start, itemEnd) and
+ result.getEnd() = itemEnd
+ )
+ or
+ i > 0 and
+ result.getRegex() = re and
+ exists(int itemStart | itemStart = seqChildEnd(re, start, end, i - 1) |
+ result.getStart() = itemStart and
+ re.item(itemStart, result.getEnd())
+ )
+ )
+}
+
+/** An implementation that satisfies the RegexTreeView signature. */
+module Impl implements RegexTreeViewSig {
+ /**
+ * An element containing a regular expression term, that is, either
+ * a string literal (parsed as a regular expression)
+ * or another regular expression term.
+ */
+ class RegExpParent extends TRegExpParent {
+ /** Gets a textual representation of this element. */
+ string toString() { result = "RegExpParent" }
+
+ /** Gets the `i`th child term. */
+ abstract RegExpTerm getChild(int i);
+
+ /** Gets a child term . */
+ RegExpTerm getAChild() { result = this.getChild(_) }
+
+ /** Gets the number of child terms. */
+ int getNumChild() { result = count(this.getAChild()) }
+
+ /** Gets the last child term of this element. */
+ RegExpTerm getLastChild() { result = this.getChild(this.getNumChild() - 1) }
+
+ /** Gets the associated regex. */
+ abstract Regex getRegex();
+ }
+
+ /** A string literal used as a regular expression */
+ class RegExpLiteral extends TRegExpLiteral, RegExpParent {
+ Regex re;
+
+ RegExpLiteral() { this = TRegExpLiteral(re) }
+
+ override RegExpTerm getChild(int i) { i = 0 and result.getRegex() = re and result.isRootTerm() }
+
+ /** Holds if dot, `.`, matches all characters, including newlines. */
+ predicate isDotAll() { re.getAMode() = "DOTALL" }
+
+ /** Holds if this regex matching is case-insensitive for this regex. */
+ predicate isIgnoreCase() { re.getAMode() = "IGNORECASE" }
+
+ /** Get a string representing all modes for this regex. */
+ string getFlags() { result = concat(string mode | mode = re.getAMode() | mode, " | ") }
+
+ override Regex getRegex() { result = re }
+
+ /** Gets the primary QL class for this regex. */
+ string getPrimaryQLClass() { result = "RegExpLiteral" }
+ }
+
+ /**
+ * A regular expression term, that is, a syntactic part of a regular expression.
+ */
+ class RegExpTerm extends RegExpParent {
+ Regex re;
+ int start;
+ int end;
+
+ RegExpTerm() {
+ this = TRegExpAlt(re, start, end)
+ or
+ this = TRegExpBackRef(re, start, end)
+ or
+ this = TRegExpCharacterClass(re, start, end)
+ or
+ this = TRegExpCharacterRange(re, start, end)
+ or
+ this = TRegExpNormalChar(re, start, end)
+ or
+ this = TRegExpGroup(re, start, end)
+ or
+ this = TRegExpQuantifier(re, start, end)
+ or
+ this = TRegExpSequence(re, start, end)
+ or
+ this = TRegExpSpecialChar(re, start, end)
+ }
+
+ /**
+ * Gets the outermost term of this regular expression.
+ */
+ RegExpTerm getRootTerm() {
+ this.isRootTerm() and result = this
+ or
+ result = this.getParent().(RegExpTerm).getRootTerm()
+ }
+
+ /**
+ * Holds if this term is part of a string literal
+ * that is interpreted as a regular expression.
+ */
+ predicate isUsedAsRegExp() { any() }
+
+ /**
+ * Holds if this is the root term of a regular expression.
+ */
+ predicate isRootTerm() { start = 0 and end = re.getText().length() }
+
+ override RegExpTerm getChild(int i) {
+ result = this.(RegExpAlt).getChild(i)
+ or
+ result = this.(RegExpBackRef).getChild(i)
+ or
+ result = this.(RegExpCharacterClass).getChild(i)
+ or
+ result = this.(RegExpCharacterRange).getChild(i)
+ or
+ result = this.(RegExpNormalChar).getChild(i)
+ or
+ result = this.(RegExpGroup).getChild(i)
+ or
+ result = this.(RegExpQuantifier).getChild(i)
+ or
+ result = this.(RegExpSequence).getChild(i)
+ or
+ result = this.(RegExpSpecialChar).getChild(i)
+ }
+
+ /**
+ * Gets the parent term of this regular expression term, or the
+ * regular expression literal if this is the root term.
+ */
+ RegExpParent getParent() { result.getAChild() = this }
+
+ override Regex getRegex() { result = re }
+
+ /** Gets the offset at which this term starts. */
+ int getStart() { result = start }
+
+ /** Gets the offset at which this term ends. */
+ int getEnd() { result = end }
+
+ override string toString() { result = re.getText().substring(start, end) }
+
+ /**
+ * Gets the location of the surrounding regex, as locations inside the regex do not exist.
+ * To get location information corresponding to the term inside the regex,
+ * use `hasLocationInfo`.
+ */
+ Location getLocation() { result = re.getLocation() }
+
+ /** Holds if this term is found at the specified location offsets. */
+ predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ exists(int re_start |
+ re.getLocation().hasLocationInfo(filepath, startline, re_start, endline, _) and
+ startcolumn = re_start + start + 4 and
+ endcolumn = re_start + end + 3
+ )
+ }
+
+ /** Gets the file in which this term is found. */
+ File getFile() { result = this.getLocation().getFile() }
+
+ /** Gets the raw source text of this term. */
+ string getRawValue() { result = this.toString() }
+
+ /** Gets the string literal in which this term is found. */
+ RegExpLiteral getLiteral() { result = TRegExpLiteral(re) }
+
+ /** Gets the regular expression term that is matched (textually) before this one, if any. */
+ RegExpTerm getPredecessor() {
+ exists(RegExpTerm parent | parent = this.getParent() |
+ result = parent.(RegExpSequence).previousElement(this)
+ or
+ not exists(parent.(RegExpSequence).previousElement(this)) and
+ not parent instanceof RegExpSubPattern and
+ result = parent.getPredecessor()
+ )
+ }
+
+ /** Gets the regular expression term that is matched (textually) after this one, if any. */
+ RegExpTerm getSuccessor() {
+ exists(RegExpTerm parent | parent = this.getParent() |
+ result = parent.(RegExpSequence).nextElement(this)
+ or
+ not exists(parent.(RegExpSequence).nextElement(this)) and
+ not parent instanceof RegExpSubPattern and
+ result = parent.getSuccessor()
+ )
+ }
+
+ /** Gets the primary QL class for this term. */
+ string getPrimaryQLClass() { result = "RegExpTerm" }
+ }
+
+ /**
+ * A quantified regular expression term.
+ *
+ * Example:
+ *
+ * ```
+ * ((ECMA|Java)[sS]cript)*
+ * ```
+ */
+ class RegExpQuantifier extends RegExpTerm, TRegExpQuantifier {
+ int part_end;
+ boolean may_repeat_forever;
+
+ RegExpQuantifier() {
+ this = TRegExpQuantifier(re, start, end) and
+ re.qualifiedPart(start, part_end, end, _, may_repeat_forever)
+ }
+
+ override RegExpTerm getChild(int i) {
+ i = 0 and
+ result.getRegex() = re and
+ result.getStart() = start and
+ result.getEnd() = part_end
+ }
+
+ /** Hols if this term may match an unlimited number of times. */
+ predicate mayRepeatForever() { may_repeat_forever = true }
+
+ /** Gets the qualifier for this term. That is e.g "?" for "a?". */
+ string getQualifier() { result = re.getText().substring(part_end, end) }
+
+ override string getPrimaryQLClass() { result = "RegExpQuantifier" }
+ }
+
+ /**
+ * A regular expression term that permits unlimited repetitions.
+ */
+ class InfiniteRepetitionQuantifier extends RegExpQuantifier {
+ InfiniteRepetitionQuantifier() { this.mayRepeatForever() }
+ }
+
+ /**
+ * A star-quantified term.
+ *
+ * Example:
+ *
+ * ```
+ * \w*
+ * ```
+ */
+ class RegExpStar extends InfiniteRepetitionQuantifier {
+ RegExpStar() { this.getQualifier().charAt(0) = "*" }
+
+ override string getPrimaryQLClass() { result = "RegExpStar" }
+ }
+
+ /**
+ * A plus-quantified term.
+ *
+ * Example:
+ *
+ * ```
+ * \w+
+ * ```
+ */
+ class RegExpPlus extends InfiniteRepetitionQuantifier {
+ RegExpPlus() { this.getQualifier().charAt(0) = "+" }
+
+ override string getPrimaryQLClass() { result = "RegExpPlus" }
+ }
+
+ /**
+ * An optional term.
+ *
+ * Example:
+ *
+ * ```
+ * ;?
+ * ```
+ */
+ class RegExpOpt extends RegExpQuantifier {
+ RegExpOpt() { this.getQualifier().charAt(0) = "?" }
+
+ override string getPrimaryQLClass() { result = "RegExpOpt" }
+ }
+
+ /**
+ * A range-quantified term
+ *
+ * Examples:
+ *
+ * ```
+ * \w{2,4}
+ * \w{2,}
+ * \w{2}
+ * ```
+ */
+ class RegExpRange extends RegExpQuantifier {
+ string upper;
+ string lower;
+
+ RegExpRange() { re.multiples(part_end, end, lower, upper) }
+
+ /** Gets the string defining the upper bound of this range, if any. */
+ string getUpper() { result = upper }
+
+ /** Gets the string defining the lower bound of this range, if any. */
+ string getLower() { result = lower }
+
+ /**
+ * Gets the upper bound of the range, if any.
+ *
+ * If there is no upper bound, any number of repetitions is allowed.
+ * For a term of the form `r{lo}`, both the lower and the upper bound
+ * are `lo`.
+ */
+ int getUpperBound() { result = this.getUpper().toInt() }
+
+ /** Gets the lower bound of the range. */
+ int getLowerBound() { result = this.getLower().toInt() }
+
+ override string getPrimaryQLClass() { result = "RegExpRange" }
+ }
+
+ /**
+ * A sequence term.
+ *
+ * Example:
+ *
+ * ```
+ * (ECMA|Java)Script
+ * ```
+ *
+ * This is a sequence with the elements `(ECMA|Java)` and `Script`.
+ */
+ class RegExpSequence extends RegExpTerm, TRegExpSequence {
+ RegExpSequence() { this = TRegExpSequence(re, start, end) }
+
+ override RegExpTerm getChild(int i) { result = seqChild(re, start, end, i) }
+
+ /** Gets the element preceding `element` in this sequence. */
+ RegExpTerm previousElement(RegExpTerm element) { element = this.nextElement(result) }
+
+ /** Gets the element following `element` in this sequence. */
+ RegExpTerm nextElement(RegExpTerm element) {
+ exists(int i |
+ element = this.getChild(i) and
+ result = this.getChild(i + 1)
+ )
+ }
+
+ override string getPrimaryQLClass() { result = "RegExpSequence" }
+ }
+
+ /**
+ * An alternative term, that is, a term of the form `a|b`.
+ *
+ * Example:
+ *
+ * ```
+ * ECMA|Java
+ * ```
+ */
+ class RegExpAlt extends RegExpTerm, TRegExpAlt {
+ RegExpAlt() { this = TRegExpAlt(re, start, end) }
+
+ override RegExpTerm getChild(int i) {
+ i = 0 and
+ result.getRegex() = re and
+ result.getStart() = start and
+ exists(int part_end |
+ re.alternationOption(start, end, start, part_end) and
+ result.getEnd() = part_end
+ )
+ or
+ i > 0 and
+ result.getRegex() = re and
+ exists(int part_start |
+ part_start = this.getChild(i - 1).getEnd() + 1 // allow for the |
+ |
+ result.getStart() = part_start and
+ re.alternationOption(start, end, part_start, result.getEnd())
+ )
+ }
+
+ override string getPrimaryQLClass() { result = "RegExpAlt" }
+ }
+
+ /**
+ * A character escape in a regular expression.
+ *
+ * Example:
+ *
+ * ```
+ * \.
+ * ```
+ */
+ class RegExpCharEscape = RegExpEscape;
+
+ private import codeql.util.Numbers as Numbers
+
+ /**
+ * An escaped regular expression term, that is, a regular expression
+ * term starting with a backslash, which is not a backreference.
+ *
+ * Example:
+ *
+ * ```
+ * \.
+ * \w
+ * ```
+ */
+ class RegExpEscape extends RegExpNormalChar {
+ RegExpEscape() { re.escapedCharacter(start, end) }
+
+ /**
+ * Gets the name of the escaped; for example, `w` for `\w`.
+ * TODO: Handle named escapes.
+ */
+ override string getValue() {
+ not this.isUnicode() and
+ this.isIdentityEscape() and
+ result = this.getUnescaped()
+ or
+ this.getUnescaped() = "n" and result = "\n"
+ or
+ this.getUnescaped() = "r" and result = "\r"
+ or
+ this.getUnescaped() = "t" and result = "\t"
+ or
+ this.getUnescaped() = "f" and result = 12.toUnicode()
+ or
+ this.getUnescaped() = "v" and result = 11.toUnicode()
+ or
+ this.isUnicode() and
+ result = this.getUnicode()
+ }
+
+ /** Holds if this terms name is given by the part following the escape character. */
+ predicate isIdentityEscape() { not this.getUnescaped() in ["n", "r", "t", "f"] }
+
+ override string getPrimaryQLClass() { result = "RegExpEscape" }
+
+ /** Gets the part of the term following the escape character. That is e.g. "w" if the term is "\w". */
+ string getUnescaped() { result = this.getText().suffix(1) }
+
+ /**
+ * Gets the text for this escape. That is e.g. "\w".
+ */
+ private string getText() { result = re.getText().substring(start, end) }
+
+ /**
+ * Holds if this is a unicode escape.
+ */
+ private predicate isUnicode() { this.getText().prefix(2) = ["\\u", "\\U"] }
+
+ private string getUnicode() {
+ result = Numbers::parseHexInt(this.getText().suffix(2)).toUnicode()
+ }
+ }
+
+ /**
+ * A word boundary, that is, a regular expression term of the form `\b`.
+ */
+ class RegExpWordBoundary extends RegExpSpecialChar {
+ RegExpWordBoundary() { this.getChar() = "\\b" }
+ }
+
+ /**
+ * A non-word boundary, that is, a regular expression term of the form `\B`.
+ */
+ class RegExpNonWordBoundary extends RegExpSpecialChar {
+ RegExpNonWordBoundary() { this.getChar() = "\\B" }
+ }
+
+ /**
+ * A character class escape in a regular expression.
+ * That is, an escaped character that denotes multiple characters.
+ *
+ * Examples:
+ *
+ * ```
+ * \w
+ * \S
+ * ```
+ */
+ class RegExpCharacterClassEscape extends RegExpEscape {
+ RegExpCharacterClassEscape() { this.getValue() in ["d", "D", "s", "S", "w", "W"] }
+
+ override RegExpTerm getChild(int i) { none() }
+
+ override string getPrimaryQLClass() { result = "RegExpCharacterClassEscape" }
+ }
+
+ /**
+ * A character class in a regular expression.
+ *
+ * Examples:
+ *
+ * ```
+ * [a-z_]
+ * [^<>&]
+ * ```
+ */
+ class RegExpCharacterClass extends RegExpTerm, TRegExpCharacterClass {
+ RegExpCharacterClass() { this = TRegExpCharacterClass(re, start, end) }
+
+ /** Holds if this character class is inverted, matching the opposite of its content. */
+ predicate isInverted() { re.getChar(start + 1) = "^" }
+
+ /** Gets the `i`th char inside this charater class. */
+ string getCharThing(int i) { result = re.getChar(i + start) }
+
+ /** Holds if this character class can match anything. */
+ predicate isUniversalClass() {
+ // [^]
+ this.isInverted() and not exists(this.getAChild())
+ or
+ // [\w\W] and similar
+ not this.isInverted() and
+ exists(string cce1, string cce2 |
+ cce1 = this.getAChild().(RegExpCharacterClassEscape).getValue() and
+ cce2 = this.getAChild().(RegExpCharacterClassEscape).getValue()
+ |
+ cce1 != cce2 and cce1.toLowerCase() = cce2.toLowerCase()
+ )
+ }
+
+ override RegExpTerm getChild(int i) {
+ i = 0 and
+ result.getRegex() = re and
+ exists(int itemStart, int itemEnd |
+ result.getStart() = itemStart and
+ re.char_set_start(start, itemStart) and
+ re.char_set_child(start, itemStart, itemEnd) and
+ result.getEnd() = itemEnd
+ )
+ or
+ i > 0 and
+ result.getRegex() = re and
+ exists(int itemStart | itemStart = this.getChild(i - 1).getEnd() |
+ result.getStart() = itemStart and
+ re.char_set_child(start, itemStart, result.getEnd())
+ )
+ }
+
+ override string getPrimaryQLClass() { result = "RegExpCharacterClass" }
+ }
+
+ /**
+ * A character range in a character class in a regular expression.
+ *
+ * Example:
+ *
+ * ```
+ * a-z
+ * ```
+ */
+ class RegExpCharacterRange extends RegExpTerm, TRegExpCharacterRange {
+ int lower_end;
+ int upper_start;
+
+ RegExpCharacterRange() {
+ this = TRegExpCharacterRange(re, start, end) and
+ re.charRange(_, start, lower_end, upper_start, end)
+ }
+
+ /** Holds if this range goes from `lo` to `hi`, in effect is `lo-hi`. */
+ predicate isRange(string lo, string hi) {
+ lo = re.getText().substring(start, lower_end) and
+ hi = re.getText().substring(upper_start, end)
+ }
+
+ override RegExpTerm getChild(int i) {
+ i = 0 and
+ result.getRegex() = re and
+ result.getStart() = start and
+ result.getEnd() = lower_end
+ or
+ i = 1 and
+ result.getRegex() = re and
+ result.getStart() = upper_start and
+ result.getEnd() = end
+ }
+
+ override string getPrimaryQLClass() { result = "RegExpCharacterRange" }
+ }
+
+ /**
+ * A normal character in a regular expression, that is, a character
+ * without special meaning. This includes escaped characters.
+ *
+ * Examples:
+ * ```
+ * t
+ * \t
+ * ```
+ */
+ additional class RegExpNormalChar extends RegExpTerm, TRegExpNormalChar {
+ RegExpNormalChar() { this = TRegExpNormalChar(re, start, end) }
+
+ /**
+ * Holds if this constant represents a valid Unicode character (as opposed
+ * to a surrogate code point that does not correspond to a character by itself.)
+ */
+ predicate isCharacter() { any() }
+
+ /** Gets the string representation of the char matched by this term. */
+ string getValue() { result = re.getText().substring(start, end) }
+
+ override RegExpTerm getChild(int i) { none() }
+
+ override string getPrimaryQLClass() { result = "RegExpNormalChar" }
+ }
+
+ /**
+ * A constant regular expression term, that is, a regular expression
+ * term matching a single string. Currently, this will always be a single character.
+ *
+ * Example:
+ *
+ * ```
+ * a
+ * ```
+ */
+ class RegExpConstant extends RegExpTerm {
+ string value;
+
+ RegExpConstant() {
+ this = TRegExpNormalChar(re, start, end) and
+ not this instanceof RegExpCharacterClassEscape and
+ // exclude chars in qualifiers
+ // TODO: push this into regex library
+ not exists(int qstart, int qend | re.qualifiedPart(_, qstart, qend, _, _) |
+ qstart <= start and end <= qend
+ ) and
+ value = this.(RegExpNormalChar).getValue()
+ }
+
+ /**
+ * Holds if this constant represents a valid Unicode character (as opposed
+ * to a surrogate code point that does not correspond to a character by itself.)
+ */
+ predicate isCharacter() { any() }
+
+ /** Gets the string matched by this constant term. */
+ string getValue() { result = value }
+
+ override RegExpTerm getChild(int i) { none() }
+
+ override string getPrimaryQLClass() { result = "RegExpConstant" }
+ }
+
+ /**
+ * A grouped regular expression.
+ *
+ * Examples:
+ *
+ * ```
+ * (ECMA|Java)
+ * (?:ECMA|Java)
+ * (?['"])
+ * ```
+ */
+ class RegExpGroup extends RegExpTerm, TRegExpGroup {
+ RegExpGroup() { this = TRegExpGroup(re, start, end) }
+
+ /**
+ * Gets the index of this capture group within the enclosing regular
+ * expression literal.
+ *
+ * For example, in the regular expression `/((a?).)(?:b)/`, the
+ * group `((a?).)` has index 1, the group `(a?)` nested inside it
+ * has index 2, and the group `(?:b)` has no index, since it is
+ * not a capture group.
+ */
+ int getNumber() { result = re.getGroupNumber(start, end) }
+
+ /** Holds if this is a capture group. */
+ predicate isCapture() { exists(this.getNumber()) }
+
+ /** Holds if this is a named capture group. */
+ predicate isNamed() { exists(this.getName()) }
+
+ /** Gets the name of this capture group, if any. */
+ string getName() { result = re.getGroupName(start, end) }
+
+ override RegExpTerm getChild(int i) {
+ result.getRegex() = re and
+ i = 0 and
+ re.groupContents(start, end, result.getStart(), result.getEnd())
+ }
+
+ override string getPrimaryQLClass() { result = "RegExpGroup" }
+ }
+
+ /**
+ * A special character in a regular expression.
+ *
+ * Examples:
+ * ```
+ * ^
+ * $
+ * .
+ * ```
+ */
+ additional class RegExpSpecialChar extends RegExpTerm, TRegExpSpecialChar {
+ string char;
+
+ RegExpSpecialChar() {
+ this = TRegExpSpecialChar(re, start, end) and
+ re.specialCharacter(start, end, char)
+ }
+
+ /**
+ * Holds if this constant represents a valid Unicode character (as opposed
+ * to a surrogate code point that does not correspond to a character by itself.)
+ */
+ predicate isCharacter() { any() }
+
+ /** Gets the char for this term. */
+ string getChar() { result = char }
+
+ override RegExpTerm getChild(int i) { none() }
+
+ override string getPrimaryQLClass() { result = "RegExpSpecialChar" }
+ }
+
+ /**
+ * A dot regular expression.
+ *
+ * Example:
+ *
+ * ```
+ * .
+ * ```
+ */
+ class RegExpDot extends RegExpSpecialChar {
+ RegExpDot() { this.getChar() = "." }
+
+ override string getPrimaryQLClass() { result = "RegExpDot" }
+ }
+
+ /**
+ * A term that matches a specific position between characters in the string.
+ *
+ * Example:
+ *
+ * ```
+ * \A
+ * ```
+ */
+ class RegExpAnchor extends RegExpSpecialChar {
+ RegExpAnchor() { this.getChar() = ["\\A", "^", "$", "\\Z"] }
+ }
+
+ /**
+ * A dollar assertion `$` or `\Z` matching the end of a line.
+ *
+ * Example:
+ *
+ * ```
+ * $
+ * ```
+ */
+ class RegExpDollar extends RegExpAnchor {
+ RegExpDollar() { this.getChar() = ["$", "\\Z"] }
+
+ override string getPrimaryQLClass() { result = "RegExpDollar" }
+ }
+
+ /**
+ * A caret assertion `^` or `\A` matching the beginning of a line.
+ *
+ * Example:
+ *
+ * ```
+ * ^
+ * ```
+ */
+ class RegExpCaret extends RegExpAnchor {
+ RegExpCaret() { this.getChar() = ["^", "\\A"] }
+
+ override string getPrimaryQLClass() { result = "RegExpCaret" }
+ }
+
+ /**
+ * A zero-width match, that is, either an empty group or an assertion.
+ *
+ * Examples:
+ * ```
+ * ()
+ * (?=\w)
+ * ```
+ */
+ additional class RegExpZeroWidthMatch extends RegExpGroup {
+ RegExpZeroWidthMatch() { re.zeroWidthMatch(start, end) }
+
+ override RegExpTerm getChild(int i) { none() }
+
+ override string getPrimaryQLClass() { result = "RegExpZeroWidthMatch" }
+ }
+
+ /**
+ * A zero-width lookahead or lookbehind assertion.
+ *
+ * Examples:
+ *
+ * ```
+ * (?=\w)
+ * (?!\n)
+ * (?<=\.)
+ * (?`
+ * in a regular expression.
+ *
+ * Examples:
+ *
+ * ```
+ * \1
+ * (?P=quote)
+ * ```
+ */
+ class RegExpBackRef extends RegExpTerm, TRegExpBackRef {
+ RegExpBackRef() { this = TRegExpBackRef(re, start, end) }
+
+ /**
+ * Gets the number of the capture group this back reference refers to, if any.
+ */
+ int getNumber() { result = re.getBackrefNumber(start, end) }
+
+ /**
+ * Gets the name of the capture group this back reference refers to, if any.
+ */
+ string getName() { result = re.getBackrefName(start, end) }
+
+ /** Gets the capture group this back reference refers to. */
+ RegExpGroup getGroup() {
+ this.hasLiteralAndNumber(result.getLiteral(), result.getNumber()) or
+ this.hasLiteralAndName(result.getLiteral(), result.getName())
+ }
+
+ /** Join-order helper for `getGroup`. */
+ pragma[nomagic]
+ private predicate hasLiteralAndNumber(RegExpLiteral literal, int number) {
+ literal = this.getLiteral() and
+ number = this.getNumber()
+ }
+
+ /** Join-order helper for `getGroup`. */
+ pragma[nomagic]
+ private predicate hasLiteralAndName(RegExpLiteral literal, string name) {
+ literal = this.getLiteral() and
+ name = this.getName()
+ }
+
+ override RegExpTerm getChild(int i) { none() }
+
+ override string getPrimaryQLClass() { result = "RegExpBackRef" }
+ }
+
+ class Top = RegExpParent;
+
+ /**
+ * Holds if `term` is an escape class representing e.g. `\d`.
+ * `clazz` is which character class it represents, e.g. "d" for `\d`.
+ */
+ predicate isEscapeClass(RegExpTerm term, string clazz) {
+ exists(RegExpCharacterClassEscape escape | term = escape | escape.getValue() = clazz)
+ }
+
+ /**
+ * Holds if `term` is a possessive quantifier.
+ * As python's regexes do not support possessive quantifiers, this never holds, but is used by the shared library.
+ */
+ predicate isPossessive(RegExpQuantifier term) { none() }
+
+ /**
+ * Holds if the regex that `term` is part of is used in a way that ignores any leading prefix of the input it's matched against.
+ * Not yet implemented for Python.
+ */
+ predicate matchesAnyPrefix(RegExpTerm term) { any() }
+
+ /**
+ * Holds if the regex that `term` is part of is used in a way that ignores any trailing suffix of the input it's matched against.
+ * Not yet implemented for Python.
+ */
+ predicate matchesAnySuffix(RegExpTerm term) { any() }
+
+ /**
+ * Holds if the regular expression should not be considered.
+ *
+ * We make the pragmatic performance optimization to ignore regular expressions in files
+ * that does not belong to the project code (such as installed dependencies).
+ */
+ predicate isExcluded(RegExpParent parent) {
+ not exists(parent.getRegex().getLocation().getFile().getRelativePath())
+ or
+ // Regexes with many occurrences of ".*" may cause the polynomial ReDoS computation to explode, so
+ // we explicitly exclude these.
+ count(int i | exists(parent.getRegex().getText().regexpFind("\\.\\*", i, _)) | i) > 10
+ }
+
+ /**
+ * Holds if `root` has the `i` flag for case-insensitive matching.
+ */
+ predicate isIgnoreCase(RegExpTerm root) {
+ root.isRootTerm() and
+ root.getLiteral().isIgnoreCase()
+ }
+
+ /**
+ * Holds if `root` has the `s` flag for multi-line matching.
+ */
+ predicate isDotAll(RegExpTerm root) {
+ root.isRootTerm() and
+ root.getLiteral().isDotAll()
+ }
+}
diff --git a/python/ql/lib/semmle/python/regexp/internal/RegExpTracking.qll b/python/ql/lib/semmle/python/regexp/internal/RegExpTracking.qll
index 4751f97b0a7..fb67f0e8c2c 100644
--- a/python/ql/lib/semmle/python/regexp/internal/RegExpTracking.qll
+++ b/python/ql/lib/semmle/python/regexp/internal/RegExpTracking.qll
@@ -26,8 +26,7 @@ private import semmle.python.regex as Regex
DataFlow::Node regSink() {
result = any(Concepts::RegexExecution exec).getRegex()
or
- // TODO: Refactor into something nicer, and remove the above import of `semmle.python.regex`
- Regex::used_as_regex_internal(result.asExpr(), _)
+ result instanceof Regex::RegExpInterpretation::Range
}
/**
diff --git a/python/ql/lib/semmle/python/security/dataflow/PolynomialReDoSCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/PolynomialReDoSCustomizations.qll
index 27bec743a5e..09d787de57f 100644
--- a/python/ql/lib/semmle/python/security/dataflow/PolynomialReDoSCustomizations.qll
+++ b/python/ql/lib/semmle/python/security/dataflow/PolynomialReDoSCustomizations.qll
@@ -11,7 +11,7 @@ private import semmle.python.dataflow.new.TaintTracking
private import semmle.python.Concepts
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.dataflow.new.BarrierGuards
-private import semmle.python.RegexTreeView::RegexTreeView as TreeView
+private import semmle.python.regexp.RegexTreeView::RegexTreeView as TreeView
private import semmle.python.ApiGraphs
private import semmle.python.regex
diff --git a/python/ql/lib/semmle/python/security/regexp/HostnameRegex.qll b/python/ql/lib/semmle/python/security/regexp/HostnameRegex.qll
index 1feffcc1087..e7ec80ac804 100644
--- a/python/ql/lib/semmle/python/security/regexp/HostnameRegex.qll
+++ b/python/ql/lib/semmle/python/security/regexp/HostnameRegex.qll
@@ -5,7 +5,7 @@
private import python
private import semmle.python.dataflow.new.DataFlow
-private import semmle.python.RegexTreeView::RegexTreeView as TreeImpl
+private import semmle.python.regexp.RegexTreeView::RegexTreeView as TreeImpl
private import semmle.python.dataflow.new.Regexp as Regexp
private import codeql.regex.HostnameRegexp as Shared
diff --git a/python/ql/src/Security/CWE-020/OverlyLargeRange.ql b/python/ql/src/Security/CWE-020/OverlyLargeRange.ql
index 6bf7f41d8ed..25acc667430 100644
--- a/python/ql/src/Security/CWE-020/OverlyLargeRange.ql
+++ b/python/ql/src/Security/CWE-020/OverlyLargeRange.ql
@@ -12,7 +12,7 @@
* external/cwe/cwe-020
*/
-private import semmle.python.RegexTreeView::RegexTreeView as TreeView
+private import semmle.python.regexp.RegexTreeView::RegexTreeView as TreeView
import codeql.regex.OverlyLargeRangeQuery::Make
from TreeView::RegExpCharacterRange range, string reason
diff --git a/python/ql/src/Security/CWE-116/BadTagFilter.ql b/python/ql/src/Security/CWE-116/BadTagFilter.ql
index afcf73f357a..87620cd7ff2 100644
--- a/python/ql/src/Security/CWE-116/BadTagFilter.ql
+++ b/python/ql/src/Security/CWE-116/BadTagFilter.ql
@@ -14,7 +14,7 @@
* external/cwe/cwe-186
*/
-private import semmle.python.RegexTreeView::RegexTreeView as TreeView
+private import semmle.python.regexp.RegexTreeView::RegexTreeView as TreeView
import codeql.regex.nfa.BadTagFilterQuery::Make
from HtmlMatchingRegExp regexp, string msg
diff --git a/python/ql/src/Security/CWE-730/ReDoS.ql b/python/ql/src/Security/CWE-730/ReDoS.ql
index 4ba35c598da..e694aee6f3e 100644
--- a/python/ql/src/Security/CWE-730/ReDoS.ql
+++ b/python/ql/src/Security/CWE-730/ReDoS.ql
@@ -14,7 +14,7 @@
* external/cwe/cwe-400
*/
-private import semmle.python.RegexTreeView::RegexTreeView as TreeView
+private import semmle.python.regexp.RegexTreeView::RegexTreeView as TreeView
import codeql.regex.nfa.ExponentialBackTracking::Make
from TreeView::RegExpTerm t, string pump, State s, string prefixMsg
diff --git a/python/ql/test/library-tests/regexparser/Consistency.ql b/python/ql/test/library-tests/regexparser/Consistency.ql
index 54b2ca424fd..f5f0f860b58 100644
--- a/python/ql/test/library-tests/regexparser/Consistency.ql
+++ b/python/ql/test/library-tests/regexparser/Consistency.ql
@@ -3,7 +3,7 @@
*/
import python
-import semmle.python.RegexTreeView
+import semmle.python.regexp.RegexTreeView
from string str, int counter, Location loc
where
diff --git a/python/ql/test/query-tests/Security/CWE-730-PolynomialReDoS/PolynomialBackTracking.ql b/python/ql/test/query-tests/Security/CWE-730-PolynomialReDoS/PolynomialBackTracking.ql
index 19c905be1fe..6153b6e72ec 100644
--- a/python/ql/test/query-tests/Security/CWE-730-PolynomialReDoS/PolynomialBackTracking.ql
+++ b/python/ql/test/query-tests/Security/CWE-730-PolynomialReDoS/PolynomialBackTracking.ql
@@ -1,5 +1,5 @@
import python
-private import semmle.python.RegexTreeView::RegexTreeView as TreeView
+private import semmle.python.regexp.RegexTreeView::RegexTreeView as TreeView
import codeql.regex.nfa.SuperlinearBackTracking::Make
from PolynomialBackTrackingTerm t
From 556bb41999f40765277fd2dc7b344b0b329c7a3b Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Fri, 17 Mar 2023 17:58:24 +0100
Subject: [PATCH 091/870] move all code to find Regex flag into a module
---
python/ql/lib/semmle/python/regex.qll | 211 ++++++++++++--------------
1 file changed, 97 insertions(+), 114 deletions(-)
diff --git a/python/ql/lib/semmle/python/regex.qll b/python/ql/lib/semmle/python/regex.qll
index 8bb8c8b3ba4..6ef63a753dd 100644
--- a/python/ql/lib/semmle/python/regex.qll
+++ b/python/ql/lib/semmle/python/regex.qll
@@ -3,72 +3,6 @@ private import semmle.python.ApiGraphs
// Need to import since frameworks can extend the abstract `RegExpInterpretation::Range`
private import semmle.python.Frameworks
private import semmle.python.Concepts as Concepts
-
-/**
- * Gets the positional argument index containing the regular expression flags for the member of the
- * `re` module with the name `name`.
- */
-private int re_member_flags_arg(string name) {
- name = "compile" and result = 1
- or
- name = "search" and result = 2
- or
- name = "match" and result = 2
- or
- name = "split" and result = 3
- or
- name = "findall" and result = 2
- or
- name = "finditer" and result = 2
- or
- name = "sub" and result = 4
- or
- name = "subn" and result = 4
-}
-
-/**
- * Gets the names and corresponding API nodes of members of the `re` module that are likely to be
- * methods taking regular expressions as arguments.
- *
- * This is a helper predicate that fixes a bad join order, and should not be inlined without checking
- * that this is safe.
- */
-pragma[nomagic]
-private API::Node relevant_re_member(string name) {
- result = API::moduleImport("re").getMember(name) and
- name != "escape"
-}
-
-/**
- * Holds if the expression `e` is used as a regex with the `re` module, with the regex-mode `mode` (if known).
- * If regex mode is not known, `mode` will be `"None"`.
- *
- * This predicate has not done any data-flow tracking.
- */
-// TODO: This should only be used to get the `mode`, and nowhere else.
-predicate used_as_regex_internal(Expr e, string mode) {
- /* Call to re.xxx(regex, ... [mode]) */
- exists(DataFlow::CallCfgNode call |
- call instanceof Concepts::RegexExecution and
- e = call.(Concepts::RegexExecution).getRegex().asExpr()
- or
- call.getArg(0).asExpr() = e and
- call = relevant_re_member(_).getACall()
- |
- mode = "None"
- or
- exists(DataFlow::CallCfgNode callNode |
- call = callNode and
- mode =
- mode_from_node([
- callNode
- .getArg(re_member_flags_arg(callNode.(DataFlow::MethodCallNode).getMethodName())),
- callNode.getArgByName("flags")
- ])
- )
- )
-}
-
private import regexp.internal.RegExpTracking as RegExpTracking
private import semmle.python.Concepts
private import semmle.python.regexp.RegexTreeView
@@ -81,49 +15,6 @@ RegExpTerm getTermForExecution(RegexExecution exec) {
)
}
-/**
- * Gets the canonical name for the API graph node corresponding to the `re` flag `flag`. For flags
- * that have multiple names, we pick the long-form name as a canonical representative.
- */
-private string canonical_name(API::Node flag) {
- result in ["ASCII", "IGNORECASE", "LOCALE", "UNICODE", "MULTILINE", "TEMPLATE"] and
- flag = API::moduleImport("re").getMember([result, result.prefix(1)])
- or
- flag = API::moduleImport("re").getMember(["DOTALL", "S"]) and result = "DOTALL"
- or
- flag = API::moduleImport("re").getMember(["VERBOSE", "X"]) and result = "VERBOSE"
-}
-
-/**
- * A type tracker for regular expression flag names. Holds if the result is a node that may refer
- * to the `re` flag with the canonical name `flag_name`
- */
-private DataFlow::TypeTrackingNode re_flag_tracker(string flag_name, DataFlow::TypeTracker t) {
- t.start() and
- exists(API::Node flag | flag_name = canonical_name(flag) and result = flag.asSource())
- or
- exists(BinaryExprNode binop, DataFlow::Node operand |
- operand.getALocalSource() = re_flag_tracker(flag_name, t.continue()) and
- operand.asCfgNode() = binop.getAnOperand() and
- (binop.getOp() instanceof BitOr or binop.getOp() instanceof Add) and
- result.asCfgNode() = binop
- )
- or
- exists(DataFlow::TypeTracker t2 | result = re_flag_tracker(flag_name, t2).track(t2, t))
-}
-
-/**
- * A type tracker for regular expression flag names. Holds if the result is a node that may refer
- * to the `re` flag with the canonical name `flag_name`
- */
-private DataFlow::Node re_flag_tracker(string flag_name) {
- re_flag_tracker(flag_name, DataFlow::TypeTracker::end()).flowsTo(result)
-}
-
-/** Gets a regular expression mode flag associated with the given data flow node. */
-// TODO: Move this into a RegexFlag module, along with related code?
-string mode_from_node(DataFlow::Node node) { node = re_flag_tracker(result) }
-
/** Provides a class for modeling regular expression interpretations. */
module RegExpInterpretation {
/**
@@ -150,6 +41,102 @@ deprecated class RegexString extends Regex {
RegexString() { this = RegExpTracking::regExpSource(_).asExpr() }
}
+/** Utility predicates for finding the mode of a regex based on where it's used. */
+private module FindRegexMode {
+ // TODO: Movev this (and Regex) into a ParseRegExp file.
+ /**
+ * Gets the mode of the regex `regex` based on the context where it's used.
+ * Does not find the mode if it's in a prefix inside the regex itself (see `Regex::getAMode`).
+ */
+ string getAMode(Regex regex) {
+ exists(DataFlow::Node sink |
+ sink = regex.getAUse() and
+ /* Call to re.xxx(regex, ... [mode]) */
+ exists(DataFlow::CallCfgNode call |
+ call instanceof Concepts::RegexExecution and
+ sink = call.(Concepts::RegexExecution).getRegex()
+ or
+ call.getArg(_) = sink and
+ sink instanceof RegExpInterpretation::Range
+ |
+ exists(DataFlow::CallCfgNode callNode |
+ call = callNode and
+ result =
+ mode_from_node([
+ callNode
+ .getArg(re_member_flags_arg(callNode.(DataFlow::MethodCallNode).getMethodName())),
+ callNode.getArgByName("flags")
+ ])
+ )
+ )
+ )
+ }
+
+ /**
+ * Gets the positional argument index containing the regular expression flags for the member of the
+ * `re` module with the name `name`.
+ */
+ private int re_member_flags_arg(string name) {
+ name = "compile" and result = 1
+ or
+ name = "search" and result = 2
+ or
+ name = "match" and result = 2
+ or
+ name = "split" and result = 3
+ or
+ name = "findall" and result = 2
+ or
+ name = "finditer" and result = 2
+ or
+ name = "sub" and result = 4
+ or
+ name = "subn" and result = 4
+ }
+
+ /**
+ * Gets the canonical name for the API graph node corresponding to the `re` flag `flag`. For flags
+ * that have multiple names, we pick the long-form name as a canonical representative.
+ */
+ private string canonical_name(API::Node flag) {
+ result in ["ASCII", "IGNORECASE", "LOCALE", "UNICODE", "MULTILINE", "TEMPLATE"] and
+ flag = API::moduleImport("re").getMember([result, result.prefix(1)])
+ or
+ flag = API::moduleImport("re").getMember(["DOTALL", "S"]) and result = "DOTALL"
+ or
+ flag = API::moduleImport("re").getMember(["VERBOSE", "X"]) and result = "VERBOSE"
+ }
+
+ /**
+ * A type tracker for regular expression flag names. Holds if the result is a node that may refer
+ * to the `re` flag with the canonical name `flag_name`
+ */
+ private DataFlow::TypeTrackingNode re_flag_tracker(string flag_name, DataFlow::TypeTracker t) {
+ t.start() and
+ exists(API::Node flag | flag_name = canonical_name(flag) and result = flag.asSource())
+ or
+ exists(BinaryExprNode binop, DataFlow::Node operand |
+ operand.getALocalSource() = re_flag_tracker(flag_name, t.continue()) and
+ operand.asCfgNode() = binop.getAnOperand() and
+ (binop.getOp() instanceof BitOr or binop.getOp() instanceof Add) and
+ result.asCfgNode() = binop
+ )
+ or
+ exists(DataFlow::TypeTracker t2 | result = re_flag_tracker(flag_name, t2).track(t2, t))
+ }
+
+ /**
+ * A type tracker for regular expression flag names. Holds if the result is a node that may refer
+ * to the `re` flag with the canonical name `flag_name`
+ */
+ private DataFlow::Node re_flag_tracker(string flag_name) {
+ re_flag_tracker(flag_name, DataFlow::TypeTracker::end()).flowsTo(result)
+ }
+
+ /** Gets a regular expression mode flag associated with the given data flow node. */
+ private string mode_from_node(DataFlow::Node node) { node = re_flag_tracker(result) }
+}
+
/** A StrConst used as a regular expression */
class Regex extends Expr {
DataFlow::Node sink;
@@ -175,11 +162,7 @@ class Regex extends Expr {
* VERBOSE
*/
string getAMode() {
- exists(string mode |
- used_as_regex_internal(sink.asExpr(), mode) and
- result != "None" and
- result = mode
- )
+ result = FindRegexMode::getAMode(this)
or
result = this.getModeFromPrefix()
}
From 59cc90e547c545162daa1f9c8db6ab9f40664d2a Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Fri, 17 Mar 2023 18:20:12 +0100
Subject: [PATCH 092/870] move Regex into a ParseRegExp file, and rename the
class to RegExp
---
.../lib/semmle/python/frameworks/Django.qll | 2 +-
.../lib/semmle/python/frameworks/Tornado.qll | 2 +-
python/ql/lib/semmle/python/regex.qll | 1072 +----------------
.../semmle/python/regexp/RegexTreeView.qll | 34 +-
.../python/regexp/internal/ParseRegExp.qll | 1070 ++++++++++++++++
.../src/Expressions/Regex/BackspaceEscape.ql | 2 +-
.../Regex/DuplicateCharacterInSet.ql | 4 +-
.../Regex/MissingPartSpecialGroup.ql | 2 +-
.../src/Expressions/Regex/UnmatchableCaret.ql | 4 +-
.../Expressions/Regex/UnmatchableDollar.ql | 4 +-
10 files changed, 1102 insertions(+), 1094 deletions(-)
create mode 100644 python/ql/lib/semmle/python/regexp/internal/ParseRegExp.qll
diff --git a/python/ql/lib/semmle/python/frameworks/Django.qll b/python/ql/lib/semmle/python/frameworks/Django.qll
index 886357594a1..2240894a3f4 100644
--- a/python/ql/lib/semmle/python/frameworks/Django.qll
+++ b/python/ql/lib/semmle/python/frameworks/Django.qll
@@ -2512,7 +2512,7 @@ module PrivateDjango {
any(int i | i < routeHandler.getFirstPossibleRoutedParamIndex() | routeHandler.getArg(i))
)
or
- exists(DjangoRouteHandler routeHandler, DjangoRouteRegex regexUse, Regex regex |
+ exists(DjangoRouteHandler routeHandler, DjangoRouteRegex regexUse, RegExp regex |
regex.getAUse() = regexUse and
routeHandler = this.getARequestHandler() and
regexUse.getRouteSetup() = this
diff --git a/python/ql/lib/semmle/python/frameworks/Tornado.qll b/python/ql/lib/semmle/python/frameworks/Tornado.qll
index f54ba64e780..12a69908f80 100644
--- a/python/ql/lib/semmle/python/frameworks/Tornado.qll
+++ b/python/ql/lib/semmle/python/frameworks/Tornado.qll
@@ -423,7 +423,7 @@ module Tornado {
not result = requestHandler.getArg(0)
)
or
- exists(Function requestHandler, TornadoRouteRegex regexUse, Regex regex |
+ exists(Function requestHandler, TornadoRouteRegex regexUse, RegExp regex |
regex.getAUse() = regexUse and
requestHandler = this.getARequestHandler() and
regexUse.getRouteSetup() = this
diff --git a/python/ql/lib/semmle/python/regex.qll b/python/ql/lib/semmle/python/regex.qll
index 6ef63a753dd..0dfc74b5e6f 100644
--- a/python/ql/lib/semmle/python/regex.qll
+++ b/python/ql/lib/semmle/python/regex.qll
@@ -1,14 +1,13 @@
import python
-private import semmle.python.ApiGraphs
// Need to import since frameworks can extend the abstract `RegExpInterpretation::Range`
private import semmle.python.Frameworks
-private import semmle.python.Concepts as Concepts
private import regexp.internal.RegExpTracking as RegExpTracking
-private import semmle.python.Concepts
+private import semmle.python.Concepts as Concepts
private import semmle.python.regexp.RegexTreeView
+import regexp.internal.ParseRegExp
/** Gets a parsed regular expression term that is executed at `exec`. */
-RegExpTerm getTermForExecution(RegexExecution exec) {
+RegExpTerm getTermForExecution(Concepts::RegexExecution exec) {
exists(DataFlow::Node source | source = RegExpTracking::regExpSource(exec.getRegex()) |
result.getRegex() = source.asExpr() and
result.isRootTerm()
@@ -25,6 +24,8 @@ module RegExpInterpretation {
abstract class Range extends DataFlow::Node { }
}
+private import semmle.python.ApiGraphs
+
/**
* A node interpreted as a regular expression.
* Speficically nodes where string values are interpreted as regular expressions.
@@ -40,1066 +41,3 @@ class StdLibRegExpInterpretation extends RegExpInterpretation::Range {
deprecated class RegexString extends Regex {
RegexString() { this = RegExpTracking::regExpSource(_).asExpr() }
}
-
-/** Utility predicates for finding the mode of a regex based on where it's used. */
-private module FindRegexMode {
- // TODO: Movev this (and Regex) into a ParseRegExp file.
- /**
- * Gets the mode of the regex `regex` based on the context where it's used.
- * Does not find the mode if it's in a prefix inside the regex itself (see `Regex::getAMode`).
- */
- string getAMode(Regex regex) {
- exists(DataFlow::Node sink |
- sink = regex.getAUse() and
- /* Call to re.xxx(regex, ... [mode]) */
- exists(DataFlow::CallCfgNode call |
- call instanceof Concepts::RegexExecution and
- sink = call.(Concepts::RegexExecution).getRegex()
- or
- call.getArg(_) = sink and
- sink instanceof RegExpInterpretation::Range
- |
- exists(DataFlow::CallCfgNode callNode |
- call = callNode and
- result =
- mode_from_node([
- callNode
- .getArg(re_member_flags_arg(callNode.(DataFlow::MethodCallNode).getMethodName())),
- callNode.getArgByName("flags")
- ])
- )
- )
- )
- }
-
- /**
- * Gets the positional argument index containing the regular expression flags for the member of the
- * `re` module with the name `name`.
- */
- private int re_member_flags_arg(string name) {
- name = "compile" and result = 1
- or
- name = "search" and result = 2
- or
- name = "match" and result = 2
- or
- name = "split" and result = 3
- or
- name = "findall" and result = 2
- or
- name = "finditer" and result = 2
- or
- name = "sub" and result = 4
- or
- name = "subn" and result = 4
- }
-
- /**
- * Gets the canonical name for the API graph node corresponding to the `re` flag `flag`. For flags
- * that have multiple names, we pick the long-form name as a canonical representative.
- */
- private string canonical_name(API::Node flag) {
- result in ["ASCII", "IGNORECASE", "LOCALE", "UNICODE", "MULTILINE", "TEMPLATE"] and
- flag = API::moduleImport("re").getMember([result, result.prefix(1)])
- or
- flag = API::moduleImport("re").getMember(["DOTALL", "S"]) and result = "DOTALL"
- or
- flag = API::moduleImport("re").getMember(["VERBOSE", "X"]) and result = "VERBOSE"
- }
-
- /**
- * A type tracker for regular expression flag names. Holds if the result is a node that may refer
- * to the `re` flag with the canonical name `flag_name`
- */
- private DataFlow::TypeTrackingNode re_flag_tracker(string flag_name, DataFlow::TypeTracker t) {
- t.start() and
- exists(API::Node flag | flag_name = canonical_name(flag) and result = flag.asSource())
- or
- exists(BinaryExprNode binop, DataFlow::Node operand |
- operand.getALocalSource() = re_flag_tracker(flag_name, t.continue()) and
- operand.asCfgNode() = binop.getAnOperand() and
- (binop.getOp() instanceof BitOr or binop.getOp() instanceof Add) and
- result.asCfgNode() = binop
- )
- or
- exists(DataFlow::TypeTracker t2 | result = re_flag_tracker(flag_name, t2).track(t2, t))
- }
-
- /**
- * A type tracker for regular expression flag names. Holds if the result is a node that may refer
- * to the `re` flag with the canonical name `flag_name`
- */
- private DataFlow::Node re_flag_tracker(string flag_name) {
- re_flag_tracker(flag_name, DataFlow::TypeTracker::end()).flowsTo(result)
- }
-
- /** Gets a regular expression mode flag associated with the given data flow node. */
- private string mode_from_node(DataFlow::Node node) { node = re_flag_tracker(result) }
-}
-
-/** A StrConst used as a regular expression */
-class Regex extends Expr {
- DataFlow::Node sink;
-
- Regex() {
- (this instanceof Bytes or this instanceof Unicode) and
- this = RegExpTracking::regExpSource(sink).asExpr() and
- // is part of the user code
- exists(this.getLocation().getFile().getRelativePath())
- }
-
- /** Gets a data-flow node where this string value is used as a regular expression. */
- DataFlow::Node getAUse() { result = sink }
-
- /**
- * Gets a mode (if any) of this regular expression. Can be any of:
- * DEBUG
- * IGNORECASE
- * LOCALE
- * MULTILINE
- * DOTALL
- * UNICODE
- * VERBOSE
- */
- string getAMode() {
- result = FindRegexMode::getAMode(this)
- or
- result = this.getModeFromPrefix()
- }
-
- // TODO: Refactor all of the below into a regex parsing file, similar to Ruby.
- /**
- * Helper predicate for `char_set_start(int start, int end)`.
- *
- * In order to identify left brackets ('[') which actually start a character class,
- * we perform a left to right scan of the string.
- *
- * To avoid negative recursion we return a boolean. See `escaping`,
- * the helper for `escapingChar`, for a clean use of this pattern.
- *
- * result is true for those start chars that actually mark a start of a char set.
- */
- boolean char_set_start(int pos) {
- exists(int index |
- // is opening bracket
- this.char_set_delimiter(index, pos) = true and
- (
- // if this is the first bracket, `pos` starts a char set
- index = 1 and result = true
- or
- // if the previous char set delimiter was not a closing bracket, `pos` does
- // not start a char set. This is needed to handle cases such as `[[]` (a
- // char set that matches the `[` char)
- index > 1 and
- not this.char_set_delimiter(index - 1, _) = false and
- result = false
- or
- // special handling of cases such as `[][]` (the character-set of the characters `]` and `[`).
- exists(int prev_closing_bracket_pos |
- // previous bracket is a closing bracket
- this.char_set_delimiter(index - 1, prev_closing_bracket_pos) = false and
- if
- // check if the character that comes before the previous closing bracket
- // is an opening bracket (taking `^` into account)
- exists(int pos_before_prev_closing_bracket |
- if this.getChar(prev_closing_bracket_pos - 1) = "^"
- then pos_before_prev_closing_bracket = prev_closing_bracket_pos - 2
- else pos_before_prev_closing_bracket = prev_closing_bracket_pos - 1
- |
- this.char_set_delimiter(index - 2, pos_before_prev_closing_bracket) = true
- )
- then
- // brackets without anything in between is not valid character ranges, so
- // the first closing bracket in `[]]` and `[^]]` does not count,
- //
- // and we should _not_ mark the second opening bracket in `[][]` and `[^][]`
- // as starting a new char set. ^ ^
- exists(int pos_before_prev_closing_bracket |
- this.char_set_delimiter(index - 2, pos_before_prev_closing_bracket) = true
- |
- result = this.char_set_start(pos_before_prev_closing_bracket).booleanNot()
- )
- else
- // if not, `pos` does in fact mark a real start of a character range
- result = true
- )
- )
- )
- }
-
- /**
- * Helper predicate for chars that could be character-set delimiters.
- * Holds if the (non-escaped) char at `pos` in the string, is the (one-based) `index` occurrence of a bracket (`[` or `]`) in the string.
- * Result if `true` is the char is `[`, and `false` if the char is `]`.
- */
- boolean char_set_delimiter(int index, int pos) {
- pos = rank[index](int p | this.nonEscapedCharAt(p) = "[" or this.nonEscapedCharAt(p) = "]") and
- (
- this.nonEscapedCharAt(pos) = "[" and result = true
- or
- this.nonEscapedCharAt(pos) = "]" and result = false
- )
- }
-
- /** Holds if a character set starts between `start` and `end`. */
- predicate char_set_start(int start, int end) {
- this.char_set_start(start) = true and
- (
- this.getChar(start + 1) = "^" and end = start + 2
- or
- not this.getChar(start + 1) = "^" and end = start + 1
- )
- }
-
- /** Whether there is a character class, between start (inclusive) and end (exclusive) */
- predicate charSet(int start, int end) {
- exists(int inner_start |
- this.char_set_start(start, inner_start) and
- not this.char_set_start(_, start)
- |
- end - 1 = min(int i | this.nonEscapedCharAt(i) = "]" and inner_start < i)
- )
- }
-
- /** An indexed version of `char_set_token/3` */
- private predicate char_set_token(int charset_start, int index, int token_start, int token_end) {
- token_start =
- rank[index](int start, int end | this.char_set_token(charset_start, start, end) | start) and
- this.char_set_token(charset_start, token_start, token_end)
- }
-
- /** Either a char or a - */
- private predicate char_set_token(int charset_start, int start, int end) {
- this.char_set_start(charset_start, start) and
- (
- this.escapedCharacter(start, end)
- or
- exists(this.nonEscapedCharAt(start)) and end = start + 1
- )
- or
- this.char_set_token(charset_start, _, start) and
- (
- this.escapedCharacter(start, end)
- or
- exists(this.nonEscapedCharAt(start)) and
- end = start + 1 and
- not this.getChar(start) = "]"
- )
- }
-
- /**
- * Holds if the character set starting at `charset_start` contains either
- * a character or a range found between `start` and `end`.
- */
- predicate char_set_child(int charset_start, int start, int end) {
- this.char_set_token(charset_start, start, end) and
- not exists(int range_start, int range_end |
- this.charRange(charset_start, range_start, _, _, range_end) and
- range_start <= start and
- range_end >= end
- )
- or
- this.charRange(charset_start, start, _, _, end)
- }
-
- /**
- * Holds if the character set starting at `charset_start` contains a character range
- * with lower bound found between `start` and `lower_end`
- * and upper bound found between `upper_start` and `end`.
- */
- predicate charRange(int charset_start, int start, int lower_end, int upper_start, int end) {
- exists(int index |
- this.charRangeEnd(charset_start, index) = true and
- this.char_set_token(charset_start, index - 2, start, lower_end) and
- this.char_set_token(charset_start, index, upper_start, end)
- )
- }
-
- /**
- * Helper predicate for `charRange`.
- * We can determine where character ranges end by a left to right sweep.
- *
- * To avoid negative recursion we return a boolean. See `escaping`,
- * the helper for `escapingChar`, for a clean use of this pattern.
- */
- private boolean charRangeEnd(int charset_start, int index) {
- this.char_set_token(charset_start, index, _, _) and
- (
- index in [1, 2] and result = false
- or
- index > 2 and
- exists(int connector_start |
- this.char_set_token(charset_start, index - 1, connector_start, _) and
- this.nonEscapedCharAt(connector_start) = "-" and
- result =
- this.charRangeEnd(charset_start, index - 2)
- .booleanNot()
- .booleanAnd(this.charRangeEnd(charset_start, index - 1).booleanNot())
- )
- or
- not exists(int connector_start |
- this.char_set_token(charset_start, index - 1, connector_start, _) and
- this.nonEscapedCharAt(connector_start) = "-"
- ) and
- result = false
- )
- }
-
- /** Holds if the character at `pos` is a "\" that is actually escaping what comes after. */
- predicate escapingChar(int pos) { this.escaping(pos) = true }
-
- /**
- * Helper predicate for `escapingChar`.
- * In order to avoid negative recursion, we return a boolean.
- * This way, we can refer to `escaping(pos - 1).booleanNot()`
- * rather than to a negated version of `escaping(pos)`.
- */
- private boolean escaping(int pos) {
- pos = -1 and result = false
- or
- this.getChar(pos) = "\\" and result = this.escaping(pos - 1).booleanNot()
- or
- this.getChar(pos) != "\\" and result = false
- }
-
- /** Gets the text of this regex */
- string getText() {
- result = this.(Unicode).getS()
- or
- result = this.(Bytes).getS()
- }
-
- /** Gets the `i`th character of this regex */
- string getChar(int i) { result = this.getText().charAt(i) }
-
- /** Gets the `i`th character of this regex, unless it is part of a character escape sequence. */
- string nonEscapedCharAt(int i) {
- result = this.getText().charAt(i) and
- not exists(int x, int y | this.escapedCharacter(x, y) and i in [x .. y - 1])
- }
-
- private predicate isOptionDivider(int i) { this.nonEscapedCharAt(i) = "|" }
-
- private predicate isGroupEnd(int i) { this.nonEscapedCharAt(i) = ")" and not this.inCharSet(i) }
-
- private predicate isGroupStart(int i) { this.nonEscapedCharAt(i) = "(" and not this.inCharSet(i) }
-
- /**
- * Holds if the `i`th character could not be parsed.
- */
- predicate failedToParse(int i) {
- exists(this.getChar(i)) and
- not exists(int start, int end |
- this.top_level(start, end) and
- start <= i and
- end > i
- )
- }
-
- /** Named unicode characters, eg \N{degree sign} */
- private predicate escapedName(int start, int end) {
- this.escapingChar(start) and
- this.getChar(start + 1) = "N" and
- this.getChar(start + 2) = "{" and
- end - 1 = min(int i | start + 2 < i and this.getChar(i) = "}")
- }
-
- /**
- * Holds if an escaped character is found between `start` and `end`.
- * Escaped characters include hex values, octal values and named escapes,
- * but excludes backreferences.
- */
- predicate escapedCharacter(int start, int end) {
- this.escapingChar(start) and
- not this.numbered_backreference(start, _, _) and
- (
- // hex value \xhh
- this.getChar(start + 1) = "x" and end = start + 4
- or
- // octal value \o, \oo, or \ooo
- end in [start + 2 .. start + 4] and
- forall(int i | i in [start + 1 .. end - 1] | this.isOctal(i)) and
- not (
- end < start + 4 and
- this.isOctal(end)
- )
- or
- // 16-bit hex value \uhhhh
- this.getChar(start + 1) = "u" and end = start + 6
- or
- // 32-bit hex value \Uhhhhhhhh
- this.getChar(start + 1) = "U" and end = start + 10
- or
- this.escapedName(start, end)
- or
- // escape not handled above, update when adding a new case
- not this.getChar(start + 1) in ["x", "u", "U", "N"] and
- not exists(this.getChar(start + 1).toInt()) and
- end = start + 2
- )
- }
-
- pragma[inline]
- private predicate isOctal(int index) { this.getChar(index) = [0 .. 7].toString() }
-
- /** Holds if `index` is inside a character set. */
- predicate inCharSet(int index) {
- exists(int x, int y | this.charSet(x, y) and index in [x + 1 .. y - 2])
- }
-
- /**
- * 'simple' characters are any that don't alter the parsing of the regex.
- */
- private predicate simpleCharacter(int start, int end) {
- end = start + 1 and
- not this.charSet(start, _) and
- not this.charSet(_, start + 1) and
- exists(string c | c = this.getChar(start) |
- exists(int x, int y, int z |
- this.charSet(x, z) and
- this.char_set_start(x, y)
- |
- start = y
- or
- start = z - 2
- or
- start > y and start < z - 2 and not this.charRange(_, _, start, end, _)
- )
- or
- not this.inCharSet(start) and
- not c = "(" and
- not c = "[" and
- not c = ")" and
- not c = "|" and
- not this.qualifier(start, _, _, _)
- )
- }
-
- /**
- * Holds if a simple or escaped character is found between `start` and `end`.
- */
- predicate character(int start, int end) {
- (
- this.simpleCharacter(start, end) and
- not exists(int x, int y | this.escapedCharacter(x, y) and x <= start and y >= end)
- or
- this.escapedCharacter(start, end)
- ) and
- not exists(int x, int y | this.group_start(x, y) and x <= start and y >= end) and
- not exists(int x, int y | this.backreference(x, y) and x <= start and y >= end)
- }
-
- /**
- * Holds if a normal character is found between `start` and `end`.
- */
- predicate normalCharacter(int start, int end) {
- end = start + 1 and
- this.character(start, end) and
- not this.specialCharacter(start, end, _)
- }
-
- /**
- * Holds if a special character is found between `start` and `end`.
- */
- predicate specialCharacter(int start, int end, string char) {
- not this.inCharSet(start) and
- this.character(start, end) and
- (
- end = start + 1 and
- char = this.getChar(start) and
- (char = "$" or char = "^" or char = ".")
- or
- end = start + 2 and
- this.escapingChar(start) and
- char = this.getText().substring(start, end) and
- char = ["\\A", "\\Z", "\\b", "\\B"]
- )
- }
-
- /**
- * Holds if the range [start:end) consists of only 'normal' characters.
- */
- predicate normalCharacterSequence(int start, int end) {
- // a normal character inside a character set is interpreted on its own
- this.normalCharacter(start, end) and
- this.inCharSet(start)
- or
- // a maximal run of normal characters is considered as one constant
- exists(int s, int e |
- e = max(int i | this.normalCharacterRun(s, i)) and
- not this.inCharSet(s)
- |
- // 'abc' can be considered one constant, but
- // 'abc+' has to be broken up into 'ab' and 'c+',
- // as the qualifier only applies to 'c'.
- if this.qualifier(e, _, _, _)
- then
- end = e and start = e - 1
- or
- end = e - 1 and start = s and start < end
- else (
- end = e and
- start = s
- )
- )
- }
-
- private predicate normalCharacterRun(int start, int end) {
- (
- this.normalCharacterRun(start, end - 1)
- or
- start = end - 1 and not this.normalCharacter(start - 1, start)
- ) and
- this.normalCharacter(end - 1, end)
- }
-
- private predicate characterItem(int start, int end) {
- this.normalCharacterSequence(start, end) or
- this.escapedCharacter(start, end) or
- this.specialCharacter(start, end, _)
- }
-
- /** Whether the text in the range `start,end` is a group */
- predicate group(int start, int end) {
- this.groupContents(start, end, _, _)
- or
- this.emptyGroup(start, end)
- }
-
- /** Gets the number of the group in start,end */
- int getGroupNumber(int start, int end) {
- this.group(start, end) and
- not this.non_capturing_group_start(start, _) and
- result =
- count(int i | this.group(i, _) and i < start and not this.non_capturing_group_start(i, _)) + 1
- }
-
- /** Gets the name, if it has one, of the group in start,end */
- string getGroupName(int start, int end) {
- this.group(start, end) and
- exists(int name_end |
- this.named_group_start(start, name_end) and
- result = this.getText().substring(start + 4, name_end - 1)
- )
- }
-
- /** Whether the text in the range start, end is a group and can match the empty string. */
- predicate zeroWidthMatch(int start, int end) {
- this.emptyGroup(start, end)
- or
- this.negativeAssertionGroup(start, end)
- or
- this.positiveLookaheadAssertionGroup(start, end)
- or
- this.positiveLookbehindAssertionGroup(start, end)
- }
-
- /** Holds if an empty group is found between `start` and `end`. */
- predicate emptyGroup(int start, int end) {
- exists(int endm1 | end = endm1 + 1 |
- this.group_start(start, endm1) and
- this.isGroupEnd(endm1)
- )
- }
-
- private predicate emptyMatchAtStartGroup(int start, int end) {
- this.emptyGroup(start, end)
- or
- this.negativeAssertionGroup(start, end)
- or
- this.positiveLookaheadAssertionGroup(start, end)
- }
-
- private predicate emptyMatchAtEndGroup(int start, int end) {
- this.emptyGroup(start, end)
- or
- this.negativeAssertionGroup(start, end)
- or
- this.positiveLookbehindAssertionGroup(start, end)
- }
-
- private predicate negativeAssertionGroup(int start, int end) {
- exists(int in_start |
- this.negative_lookahead_assertion_start(start, in_start)
- or
- this.negative_lookbehind_assertion_start(start, in_start)
- |
- this.groupContents(start, end, in_start, _)
- )
- }
-
- /** Holds if a negative lookahead is found between `start` and `end` */
- predicate negativeLookaheadAssertionGroup(int start, int end) {
- exists(int in_start | this.negative_lookahead_assertion_start(start, in_start) |
- this.groupContents(start, end, in_start, _)
- )
- }
-
- /** Holds if a negative lookbehind is found between `start` and `end` */
- predicate negativeLookbehindAssertionGroup(int start, int end) {
- exists(int in_start | this.negative_lookbehind_assertion_start(start, in_start) |
- this.groupContents(start, end, in_start, _)
- )
- }
-
- /** Holds if a positive lookahead is found between `start` and `end` */
- predicate positiveLookaheadAssertionGroup(int start, int end) {
- exists(int in_start | this.lookahead_assertion_start(start, in_start) |
- this.groupContents(start, end, in_start, _)
- )
- }
-
- /** Holds if a positive lookbehind is found between `start` and `end` */
- predicate positiveLookbehindAssertionGroup(int start, int end) {
- exists(int in_start | this.lookbehind_assertion_start(start, in_start) |
- this.groupContents(start, end, in_start, _)
- )
- }
-
- private predicate group_start(int start, int end) {
- this.non_capturing_group_start(start, end)
- or
- this.flag_group_start(start, end, _)
- or
- this.named_group_start(start, end)
- or
- this.named_backreference_start(start, end)
- or
- this.lookahead_assertion_start(start, end)
- or
- this.negative_lookahead_assertion_start(start, end)
- or
- this.lookbehind_assertion_start(start, end)
- or
- this.negative_lookbehind_assertion_start(start, end)
- or
- this.comment_group_start(start, end)
- or
- this.simple_group_start(start, end)
- }
-
- /** Matches the start of a non-capturing group, e.g. `(?:` */
- private predicate non_capturing_group_start(int start, int end) {
- this.isGroupStart(start) and
- this.getChar(start + 1) = "?" and
- this.getChar(start + 2) = ":" and
- end = start + 3
- }
-
- /** Matches the start of a simple group, e.g. `(a+)`. */
- private predicate simple_group_start(int start, int end) {
- this.isGroupStart(start) and
- this.getChar(start + 1) != "?" and
- end = start + 1
- }
-
- /**
- * Matches the start of a named group, such as:
- * - `(?\w+)`
- * - `(?'name'\w+)`
- */
- private predicate named_group_start(int start, int end) {
- this.isGroupStart(start) and
- this.getChar(start + 1) = "?" and
- this.getChar(start + 2) = "P" and
- this.getChar(start + 3) = "<" and
- not this.getChar(start + 4) = "=" and
- not this.getChar(start + 4) = "!" and
- exists(int name_end |
- name_end = min(int i | i > start + 4 and this.getChar(i) = ">") and
- end = name_end + 1
- )
- }
-
- private predicate named_backreference_start(int start, int end) {
- this.isGroupStart(start) and
- this.getChar(start + 1) = "?" and
- this.getChar(start + 2) = "P" and
- this.getChar(start + 3) = "=" and
- // Should this be looking for unescaped ")"?
- // TODO: test this
- end = min(int i | i > start + 4 and this.getChar(i) = "?")
- }
-
- private predicate flag_group_start(int start, int end, string c) {
- this.isGroupStart(start) and
- this.getChar(start + 1) = "?" and
- end = start + 3 and
- c = this.getChar(start + 2) and
- c in ["i", "L", "m", "s", "u", "x"]
- }
-
- /**
- * Gets the mode of this regular expression string if
- * it is defined by a prefix.
- */
- string getModeFromPrefix() {
- exists(string c | this.flag_group_start(_, _, c) |
- c = "i" and result = "IGNORECASE"
- or
- c = "L" and result = "LOCALE"
- or
- c = "m" and result = "MULTILINE"
- or
- c = "s" and result = "DOTALL"
- or
- c = "u" and result = "UNICODE"
- or
- c = "x" and result = "VERBOSE"
- )
- }
-
- /** Matches the start of a positive lookahead assertion, i.e. `(?=`. */
- private predicate lookahead_assertion_start(int start, int end) {
- this.isGroupStart(start) and
- this.getChar(start + 1) = "?" and
- this.getChar(start + 2) = "=" and
- end = start + 3
- }
-
- /** Matches the start of a negative lookahead assertion, i.e. `(?!`. */
- private predicate negative_lookahead_assertion_start(int start, int end) {
- this.isGroupStart(start) and
- this.getChar(start + 1) = "?" and
- this.getChar(start + 2) = "!" and
- end = start + 3
- }
-
- /** Matches the start of a positive lookbehind assertion, i.e. `(?<=`. */
- private predicate lookbehind_assertion_start(int start, int end) {
- this.isGroupStart(start) and
- this.getChar(start + 1) = "?" and
- this.getChar(start + 2) = "<" and
- this.getChar(start + 3) = "=" and
- end = start + 4
- }
-
- /** Matches the start of a negative lookbehind assertion, i.e. `(?`. */
- private predicate named_backreference(int start, int end, string name) {
- this.named_backreference_start(start, start + 4) and
- end = min(int i | i > start + 4 and this.getChar(i) = ")") + 1 and
- name = this.getText().substring(start + 4, end - 2)
- }
-
- /** Matches a numbered backreference, e.g. `\1`. */
- private predicate numbered_backreference(int start, int end, int value) {
- this.escapingChar(start) and
- // starting with 0 makes it an octal escape
- not this.getChar(start + 1) = "0" and
- exists(string text, string svalue, int len |
- end = start + len and
- text = this.getText() and
- len in [2 .. 3]
- |
- svalue = text.substring(start + 1, start + len) and
- value = svalue.toInt() and
- // value is composed of digits
- forall(int i | i in [start + 1 .. start + len - 1] | this.getChar(i) = [0 .. 9].toString()) and
- // a longer reference is not possible
- not (
- len = 2 and
- exists(text.substring(start + 1, start + len + 1).toInt())
- ) and
- // 3 octal digits makes it an octal escape
- not forall(int i | i in [start + 1 .. start + 4] | this.isOctal(i))
- // TODO: Inside a character set, all numeric escapes are treated as characters.
- )
- }
-
- /** Whether the text in the range `start,end` is a back reference */
- predicate backreference(int start, int end) {
- this.numbered_backreference(start, end, _)
- or
- this.named_backreference(start, end, _)
- }
-
- /** Gets the number of the back reference in start,end */
- int getBackrefNumber(int start, int end) { this.numbered_backreference(start, end, result) }
-
- /** Gets the name, if it has one, of the back reference in start,end */
- string getBackrefName(int start, int end) { this.named_backreference(start, end, result) }
-
- private predicate baseItem(int start, int end) {
- this.characterItem(start, end) and
- not exists(int x, int y | this.charSet(x, y) and x <= start and y >= end)
- or
- this.group(start, end)
- or
- this.charSet(start, end)
- or
- this.backreference(start, end)
- }
-
- private predicate qualifier(int start, int end, boolean maybe_empty, boolean may_repeat_forever) {
- this.short_qualifier(start, end, maybe_empty, may_repeat_forever) and
- not this.getChar(end) = "?"
- or
- exists(int short_end | this.short_qualifier(start, short_end, maybe_empty, may_repeat_forever) |
- if this.getChar(short_end) = "?" then end = short_end + 1 else end = short_end
- )
- }
-
- private predicate short_qualifier(
- int start, int end, boolean maybe_empty, boolean may_repeat_forever
- ) {
- (
- this.getChar(start) = "+" and maybe_empty = false and may_repeat_forever = true
- or
- this.getChar(start) = "*" and maybe_empty = true and may_repeat_forever = true
- or
- this.getChar(start) = "?" and maybe_empty = true and may_repeat_forever = false
- ) and
- end = start + 1
- or
- exists(string lower, string upper |
- this.multiples(start, end, lower, upper) and
- (if lower = "" or lower.toInt() = 0 then maybe_empty = true else maybe_empty = false) and
- if upper = "" then may_repeat_forever = true else may_repeat_forever = false
- )
- }
-
- /**
- * Holds if a repetition quantifier is found between `start` and `end`,
- * with the given lower and upper bounds. If a bound is omitted, the corresponding
- * string is empty.
- */
- predicate multiples(int start, int end, string lower, string upper) {
- exists(string text, string match, string inner |
- text = this.getText() and
- end = start + match.length() and
- inner = match.substring(1, match.length() - 1)
- |
- match = text.regexpFind("\\{[0-9]+\\}", _, start) and
- lower = inner and
- upper = lower
- or
- match = text.regexpFind("\\{[0-9]*,[0-9]*\\}", _, start) and
- exists(int commaIndex |
- commaIndex = inner.indexOf(",") and
- lower = inner.prefix(commaIndex) and
- upper = inner.suffix(commaIndex + 1)
- )
- )
- }
-
- /**
- * Whether the text in the range start,end is a qualified item, where item is a character,
- * a character set or a group.
- */
- predicate qualifiedItem(int start, int end, boolean maybe_empty, boolean may_repeat_forever) {
- this.qualifiedPart(start, _, end, maybe_empty, may_repeat_forever)
- }
-
- /**
- * Holds if a qualified part is found between `start` and `part_end` and the qualifier is
- * found between `part_end` and `end`.
- *
- * `maybe_empty` is true if the part is optional.
- * `may_repeat_forever` is true if the part may be repeated unboundedly.
- */
- predicate qualifiedPart(
- int start, int part_end, int end, boolean maybe_empty, boolean may_repeat_forever
- ) {
- this.baseItem(start, part_end) and
- this.qualifier(part_end, end, maybe_empty, may_repeat_forever)
- }
-
- /** Holds if the range `start`, `end` contains a character, a quantifier, a character set or a group. */
- predicate item(int start, int end) {
- this.qualifiedItem(start, end, _, _)
- or
- this.baseItem(start, end) and not this.qualifier(end, _, _, _)
- }
-
- private predicate subsequence(int start, int end) {
- (
- start = 0 or
- this.group_start(_, start) or
- this.isOptionDivider(start - 1)
- ) and
- this.item(start, end)
- or
- exists(int mid |
- this.subsequence(start, mid) and
- this.item(mid, end)
- )
- }
-
- /**
- * Whether the text in the range start,end is a sequence of 1 or more items, where an item is a character,
- * a character set or a group.
- */
- predicate sequence(int start, int end) {
- this.sequenceOrQualified(start, end) and
- not this.qualifiedItem(start, end, _, _)
- }
-
- private predicate sequenceOrQualified(int start, int end) {
- this.subsequence(start, end) and
- not this.item_start(end)
- }
-
- private predicate item_start(int start) {
- this.characterItem(start, _) or
- this.isGroupStart(start) or
- this.charSet(start, _) or
- this.backreference(start, _)
- }
-
- private predicate item_end(int end) {
- this.characterItem(_, end)
- or
- exists(int endm1 | this.isGroupEnd(endm1) and end = endm1 + 1)
- or
- this.charSet(_, end)
- or
- this.qualifier(_, end, _, _)
- }
-
- private predicate top_level(int start, int end) {
- this.subalternation(start, end, _) and
- not this.isOptionDivider(end)
- }
-
- private predicate subalternation(int start, int end, int item_start) {
- this.sequenceOrQualified(start, end) and
- not this.isOptionDivider(start - 1) and
- item_start = start
- or
- start = end and
- not this.item_end(start) and
- this.isOptionDivider(end) and
- item_start = start
- or
- exists(int mid |
- this.subalternation(start, mid, _) and
- this.isOptionDivider(mid) and
- item_start = mid + 1
- |
- this.sequenceOrQualified(item_start, end)
- or
- not this.item_start(end) and end = item_start
- )
- }
-
- /**
- * Whether the text in the range start,end is an alternation
- */
- predicate alternation(int start, int end) {
- this.top_level(start, end) and
- exists(int less | this.subalternation(start, less, _) and less < end)
- }
-
- /**
- * Whether the text in the range start,end is an alternation and the text in part_start, part_end is one of the
- * options in that alternation.
- */
- predicate alternationOption(int start, int end, int part_start, int part_end) {
- this.alternation(start, end) and
- this.subalternation(start, part_end, part_start)
- }
-
- /** A part of the regex that may match the start of the string. */
- private predicate firstPart(int start, int end) {
- start = 0 and end = this.getText().length()
- or
- exists(int x | this.firstPart(x, end) |
- this.emptyMatchAtStartGroup(x, start) or
- this.qualifiedItem(x, start, true, _) or
- // ^ and \A match the start of the string
- this.specialCharacter(x, start, ["^", "\\A"])
- )
- or
- exists(int y | this.firstPart(start, y) |
- this.item(start, end)
- or
- this.qualifiedPart(start, end, y, _, _)
- )
- or
- exists(int x, int y | this.firstPart(x, y) |
- this.groupContents(x, y, start, end)
- or
- this.alternationOption(x, y, start, end)
- )
- }
-
- /** A part of the regex that may match the end of the string. */
- private predicate lastPart(int start, int end) {
- start = 0 and end = this.getText().length()
- or
- exists(int y | this.lastPart(start, y) |
- this.emptyMatchAtEndGroup(end, y)
- or
- this.qualifiedItem(end, y, true, _)
- or
- // $ and \Z match the end of the string.
- this.specialCharacter(end, y, ["$", "\\Z"])
- )
- or
- this.lastPart(_, end) and
- this.item(start, end)
- or
- exists(int y | this.lastPart(start, y) | this.qualifiedPart(start, end, y, _, _))
- or
- exists(int x, int y | this.lastPart(x, y) |
- this.groupContents(x, y, start, end)
- or
- this.alternationOption(x, y, start, end)
- )
- }
-
- /**
- * Whether the item at [start, end) is one of the first items
- * to be matched.
- */
- predicate firstItem(int start, int end) {
- (
- this.characterItem(start, end)
- or
- this.qualifiedItem(start, end, _, _)
- or
- this.charSet(start, end)
- ) and
- this.firstPart(start, end)
- }
-
- /**
- * Whether the item at [start, end) is one of the last items
- * to be matched.
- */
- predicate lastItem(int start, int end) {
- (
- this.characterItem(start, end)
- or
- this.qualifiedItem(start, end, _, _)
- or
- this.charSet(start, end)
- ) and
- this.lastPart(start, end)
- }
-}
diff --git a/python/ql/lib/semmle/python/regexp/RegexTreeView.qll b/python/ql/lib/semmle/python/regexp/RegexTreeView.qll
index 568ed73c12f..192476274a3 100644
--- a/python/ql/lib/semmle/python/regexp/RegexTreeView.qll
+++ b/python/ql/lib/semmle/python/regexp/RegexTreeView.qll
@@ -22,16 +22,16 @@ RegExpTerm getParsedRegExp(StrConst re) { result.getRegex() = re and result.isRo
*/
private newtype TRegExpParent =
/** A string literal used as a regular expression */
- TRegExpLiteral(Regex re) or
+ TRegExpLiteral(RegExp re) or
/** A quantified term */
- TRegExpQuantifier(Regex re, int start, int end) { re.qualifiedItem(start, end, _, _) } or
+ TRegExpQuantifier(RegExp re, int start, int end) { re.qualifiedItem(start, end, _, _) } or
/** A sequence term */
- TRegExpSequence(Regex re, int start, int end) {
+ TRegExpSequence(RegExp re, int start, int end) {
re.sequence(start, end) and
exists(seqChild(re, start, end, 1)) // if a sequence does not have more than one element, it should be treated as that element instead.
} or
/** An alternation term */
- TRegExpAlt(Regex re, int start, int end) {
+ TRegExpAlt(RegExp re, int start, int end) {
re.alternation(start, end) and
exists(int part_end |
re.alternationOption(start, end, start, part_end) and
@@ -39,30 +39,30 @@ private newtype TRegExpParent =
) // if an alternation does not have more than one element, it should be treated as that element instead.
} or
/** A character class term */
- TRegExpCharacterClass(Regex re, int start, int end) { re.charSet(start, end) } or
+ TRegExpCharacterClass(RegExp re, int start, int end) { re.charSet(start, end) } or
/** A character range term */
- TRegExpCharacterRange(Regex re, int start, int end) { re.charRange(_, start, _, _, end) } or
+ TRegExpCharacterRange(RegExp re, int start, int end) { re.charRange(_, start, _, _, end) } or
/** A group term */
- TRegExpGroup(Regex re, int start, int end) { re.group(start, end) } or
+ TRegExpGroup(RegExp re, int start, int end) { re.group(start, end) } or
/** A special character */
- TRegExpSpecialChar(Regex re, int start, int end) { re.specialCharacter(start, end, _) } or
+ TRegExpSpecialChar(RegExp re, int start, int end) { re.specialCharacter(start, end, _) } or
/** A normal character */
- TRegExpNormalChar(Regex re, int start, int end) {
+ TRegExpNormalChar(RegExp re, int start, int end) {
re.normalCharacterSequence(start, end)
or
re.escapedCharacter(start, end) and
not re.specialCharacter(start, end, _)
} or
/** A back reference */
- TRegExpBackRef(Regex re, int start, int end) { re.backreference(start, end) }
+ TRegExpBackRef(RegExp re, int start, int end) { re.backreference(start, end) }
pragma[nomagic]
-private int seqChildEnd(Regex re, int start, int end, int i) {
+private int seqChildEnd(RegExp re, int start, int end, int i) {
result = seqChild(re, start, end, i).getEnd()
}
// moved out so we can use it in the charpred
-private RegExpTerm seqChild(Regex re, int start, int end, int i) {
+private RegExpTerm seqChild(RegExp re, int start, int end, int i) {
re.sequence(start, end) and
(
i = 0 and
@@ -106,12 +106,12 @@ module Impl implements RegexTreeViewSig {
RegExpTerm getLastChild() { result = this.getChild(this.getNumChild() - 1) }
/** Gets the associated regex. */
- abstract Regex getRegex();
+ abstract RegExp getRegex();
}
/** A string literal used as a regular expression */
class RegExpLiteral extends TRegExpLiteral, RegExpParent {
- Regex re;
+ RegExp re;
RegExpLiteral() { this = TRegExpLiteral(re) }
@@ -126,7 +126,7 @@ module Impl implements RegexTreeViewSig {
/** Get a string representing all modes for this regex. */
string getFlags() { result = concat(string mode | mode = re.getAMode() | mode, " | ") }
- override Regex getRegex() { result = re }
+ override RegExp getRegex() { result = re }
/** Gets the primary QL class for this regex. */
string getPrimaryQLClass() { result = "RegExpLiteral" }
@@ -136,7 +136,7 @@ module Impl implements RegexTreeViewSig {
* A regular expression term, that is, a syntactic part of a regular expression.
*/
class RegExpTerm extends RegExpParent {
- Regex re;
+ RegExp re;
int start;
int end;
@@ -206,7 +206,7 @@ module Impl implements RegexTreeViewSig {
*/
RegExpParent getParent() { result.getAChild() = this }
- override Regex getRegex() { result = re }
+ override RegExp getRegex() { result = re }
/** Gets the offset at which this term starts. */
int getStart() { result = start }
diff --git a/python/ql/lib/semmle/python/regexp/internal/ParseRegExp.qll b/python/ql/lib/semmle/python/regexp/internal/ParseRegExp.qll
new file mode 100644
index 00000000000..7606743ea07
--- /dev/null
+++ b/python/ql/lib/semmle/python/regexp/internal/ParseRegExp.qll
@@ -0,0 +1,1070 @@
+import python
+private import semmle.python.dataflow.new.DataFlow
+private import semmle.python.Concepts as Concepts
+private import semmle.python.regex
+private import semmle.python.ApiGraphs
+private import semmle.python.regexp.internal.RegExpTracking as RegExpTracking
+
+/** Utility predicates for finding the mode of a regex based on where it's used. */
+private module FindRegexMode {
+ /**
+ * Gets the mode of the regex `regex` based on the context where it's used.
+ * Does not find the mode if it's in a prefix inside the regex itself (see `Regex::getAMode`).
+ */
+ string getAMode(RegExp regex) {
+ exists(DataFlow::Node sink |
+ sink = regex.getAUse() and
+ /* Call to re.xxx(regex, ... [mode]) */
+ exists(DataFlow::CallCfgNode call |
+ call instanceof Concepts::RegexExecution and
+ sink = call.(Concepts::RegexExecution).getRegex()
+ or
+ call.getArg(_) = sink and
+ sink instanceof RegExpInterpretation::Range
+ |
+ exists(DataFlow::CallCfgNode callNode |
+ call = callNode and
+ result =
+ mode_from_node([
+ callNode
+ .getArg(re_member_flags_arg(callNode.(DataFlow::MethodCallNode).getMethodName())),
+ callNode.getArgByName("flags")
+ ])
+ )
+ )
+ )
+ }
+
+ /**
+ * Gets the positional argument index containing the regular expression flags for the member of the
+ * `re` module with the name `name`.
+ */
+ private int re_member_flags_arg(string name) {
+ name = "compile" and result = 1
+ or
+ name = "search" and result = 2
+ or
+ name = "match" and result = 2
+ or
+ name = "split" and result = 3
+ or
+ name = "findall" and result = 2
+ or
+ name = "finditer" and result = 2
+ or
+ name = "sub" and result = 4
+ or
+ name = "subn" and result = 4
+ }
+
+ /**
+ * Gets the canonical name for the API graph node corresponding to the `re` flag `flag`. For flags
+ * that have multiple names, we pick the long-form name as a canonical representative.
+ */
+ private string canonical_name(API::Node flag) {
+ result in ["ASCII", "IGNORECASE", "LOCALE", "UNICODE", "MULTILINE", "TEMPLATE"] and
+ flag = API::moduleImport("re").getMember([result, result.prefix(1)])
+ or
+ flag = API::moduleImport("re").getMember(["DOTALL", "S"]) and result = "DOTALL"
+ or
+ flag = API::moduleImport("re").getMember(["VERBOSE", "X"]) and result = "VERBOSE"
+ }
+
+ /**
+ * A type tracker for regular expression flag names. Holds if the result is a node that may refer
+ * to the `re` flag with the canonical name `flag_name`
+ */
+ private DataFlow::TypeTrackingNode re_flag_tracker(string flag_name, DataFlow::TypeTracker t) {
+ t.start() and
+ exists(API::Node flag | flag_name = canonical_name(flag) and result = flag.asSource())
+ or
+ exists(BinaryExprNode binop, DataFlow::Node operand |
+ operand.getALocalSource() = re_flag_tracker(flag_name, t.continue()) and
+ operand.asCfgNode() = binop.getAnOperand() and
+ (binop.getOp() instanceof BitOr or binop.getOp() instanceof Add) and
+ result.asCfgNode() = binop
+ )
+ or
+ exists(DataFlow::TypeTracker t2 | result = re_flag_tracker(flag_name, t2).track(t2, t))
+ }
+
+ /**
+ * A type tracker for regular expression flag names. Holds if the result is a node that may refer
+ * to the `re` flag with the canonical name `flag_name`
+ */
+ private DataFlow::Node re_flag_tracker(string flag_name) {
+ re_flag_tracker(flag_name, DataFlow::TypeTracker::end()).flowsTo(result)
+ }
+
+ /** Gets a regular expression mode flag associated with the given data flow node. */
+ private string mode_from_node(DataFlow::Node node) { node = re_flag_tracker(result) }
+}
+
+/**
+ * DEPRECATED: Use `Regex` instead.
+ */
+deprecated class Regex = RegExp;
+
+/** A StrConst used as a regular expression */
+class RegExp extends Expr {
+ DataFlow::Node use;
+
+ RegExp() {
+ (this instanceof Bytes or this instanceof Unicode) and
+ this = RegExpTracking::regExpSource(use).asExpr()
+ }
+
+ /** Gets a data-flow node where this string value is used as a regular expression. */
+ DataFlow::Node getAUse() { result = use }
+
+ /**
+ * Gets a mode (if any) of this regular expression. Can be any of:
+ * DEBUG
+ * IGNORECASE
+ * LOCALE
+ * MULTILINE
+ * DOTALL
+ * UNICODE
+ * VERBOSE
+ */
+ string getAMode() {
+ result = FindRegexMode::getAMode(this)
+ or
+ result = this.getModeFromPrefix()
+ }
+
+ /**
+ * Helper predicate for `char_set_start(int start, int end)`.
+ *
+ * In order to identify left brackets ('[') which actually start a character class,
+ * we perform a left to right scan of the string.
+ *
+ * To avoid negative recursion we return a boolean. See `escaping`,
+ * the helper for `escapingChar`, for a clean use of this pattern.
+ *
+ * result is true for those start chars that actually mark a start of a char set.
+ */
+ boolean char_set_start(int pos) {
+ exists(int index |
+ // is opening bracket
+ this.char_set_delimiter(index, pos) = true and
+ (
+ // if this is the first bracket, `pos` starts a char set
+ index = 1 and result = true
+ or
+ // if the previous char set delimiter was not a closing bracket, `pos` does
+ // not start a char set. This is needed to handle cases such as `[[]` (a
+ // char set that matches the `[` char)
+ index > 1 and
+ not this.char_set_delimiter(index - 1, _) = false and
+ result = false
+ or
+ // special handling of cases such as `[][]` (the character-set of the characters `]` and `[`).
+ exists(int prev_closing_bracket_pos |
+ // previous bracket is a closing bracket
+ this.char_set_delimiter(index - 1, prev_closing_bracket_pos) = false and
+ if
+ // check if the character that comes before the previous closing bracket
+ // is an opening bracket (taking `^` into account)
+ exists(int pos_before_prev_closing_bracket |
+ if this.getChar(prev_closing_bracket_pos - 1) = "^"
+ then pos_before_prev_closing_bracket = prev_closing_bracket_pos - 2
+ else pos_before_prev_closing_bracket = prev_closing_bracket_pos - 1
+ |
+ this.char_set_delimiter(index - 2, pos_before_prev_closing_bracket) = true
+ )
+ then
+ // brackets without anything in between is not valid character ranges, so
+ // the first closing bracket in `[]]` and `[^]]` does not count,
+ //
+ // and we should _not_ mark the second opening bracket in `[][]` and `[^][]`
+ // as starting a new char set. ^ ^
+ exists(int pos_before_prev_closing_bracket |
+ this.char_set_delimiter(index - 2, pos_before_prev_closing_bracket) = true
+ |
+ result = this.char_set_start(pos_before_prev_closing_bracket).booleanNot()
+ )
+ else
+ // if not, `pos` does in fact mark a real start of a character range
+ result = true
+ )
+ )
+ )
+ }
+
+ /**
+ * Helper predicate for chars that could be character-set delimiters.
+ * Holds if the (non-escaped) char at `pos` in the string, is the (one-based) `index` occurrence of a bracket (`[` or `]`) in the string.
+ * Result if `true` is the char is `[`, and `false` if the char is `]`.
+ */
+ boolean char_set_delimiter(int index, int pos) {
+ pos = rank[index](int p | this.nonEscapedCharAt(p) = "[" or this.nonEscapedCharAt(p) = "]") and
+ (
+ this.nonEscapedCharAt(pos) = "[" and result = true
+ or
+ this.nonEscapedCharAt(pos) = "]" and result = false
+ )
+ }
+
+ /** Holds if a character set starts between `start` and `end`. */
+ predicate char_set_start(int start, int end) {
+ this.char_set_start(start) = true and
+ (
+ this.getChar(start + 1) = "^" and end = start + 2
+ or
+ not this.getChar(start + 1) = "^" and end = start + 1
+ )
+ }
+
+ /** Whether there is a character class, between start (inclusive) and end (exclusive) */
+ predicate charSet(int start, int end) {
+ exists(int inner_start |
+ this.char_set_start(start, inner_start) and
+ not this.char_set_start(_, start)
+ |
+ end - 1 = min(int i | this.nonEscapedCharAt(i) = "]" and inner_start < i)
+ )
+ }
+
+ /** An indexed version of `char_set_token/3` */
+ private predicate char_set_token(int charset_start, int index, int token_start, int token_end) {
+ token_start =
+ rank[index](int start, int end | this.char_set_token(charset_start, start, end) | start) and
+ this.char_set_token(charset_start, token_start, token_end)
+ }
+
+ /** Either a char or a - */
+ private predicate char_set_token(int charset_start, int start, int end) {
+ this.char_set_start(charset_start, start) and
+ (
+ this.escapedCharacter(start, end)
+ or
+ exists(this.nonEscapedCharAt(start)) and end = start + 1
+ )
+ or
+ this.char_set_token(charset_start, _, start) and
+ (
+ this.escapedCharacter(start, end)
+ or
+ exists(this.nonEscapedCharAt(start)) and
+ end = start + 1 and
+ not this.getChar(start) = "]"
+ )
+ }
+
+ /**
+ * Holds if the character set starting at `charset_start` contains either
+ * a character or a range found between `start` and `end`.
+ */
+ predicate char_set_child(int charset_start, int start, int end) {
+ this.char_set_token(charset_start, start, end) and
+ not exists(int range_start, int range_end |
+ this.charRange(charset_start, range_start, _, _, range_end) and
+ range_start <= start and
+ range_end >= end
+ )
+ or
+ this.charRange(charset_start, start, _, _, end)
+ }
+
+ /**
+ * Holds if the character set starting at `charset_start` contains a character range
+ * with lower bound found between `start` and `lower_end`
+ * and upper bound found between `upper_start` and `end`.
+ */
+ predicate charRange(int charset_start, int start, int lower_end, int upper_start, int end) {
+ exists(int index |
+ this.charRangeEnd(charset_start, index) = true and
+ this.char_set_token(charset_start, index - 2, start, lower_end) and
+ this.char_set_token(charset_start, index, upper_start, end)
+ )
+ }
+
+ /**
+ * Helper predicate for `charRange`.
+ * We can determine where character ranges end by a left to right sweep.
+ *
+ * To avoid negative recursion we return a boolean. See `escaping`,
+ * the helper for `escapingChar`, for a clean use of this pattern.
+ */
+ private boolean charRangeEnd(int charset_start, int index) {
+ this.char_set_token(charset_start, index, _, _) and
+ (
+ index in [1, 2] and result = false
+ or
+ index > 2 and
+ exists(int connector_start |
+ this.char_set_token(charset_start, index - 1, connector_start, _) and
+ this.nonEscapedCharAt(connector_start) = "-" and
+ result =
+ this.charRangeEnd(charset_start, index - 2)
+ .booleanNot()
+ .booleanAnd(this.charRangeEnd(charset_start, index - 1).booleanNot())
+ )
+ or
+ not exists(int connector_start |
+ this.char_set_token(charset_start, index - 1, connector_start, _) and
+ this.nonEscapedCharAt(connector_start) = "-"
+ ) and
+ result = false
+ )
+ }
+
+ /** Holds if the character at `pos` is a "\" that is actually escaping what comes after. */
+ predicate escapingChar(int pos) { this.escaping(pos) = true }
+
+ /**
+ * Helper predicate for `escapingChar`.
+ * In order to avoid negative recursion, we return a boolean.
+ * This way, we can refer to `escaping(pos - 1).booleanNot()`
+ * rather than to a negated version of `escaping(pos)`.
+ */
+ private boolean escaping(int pos) {
+ pos = -1 and result = false
+ or
+ this.getChar(pos) = "\\" and result = this.escaping(pos - 1).booleanNot()
+ or
+ this.getChar(pos) != "\\" and result = false
+ }
+
+ /** Gets the text of this regex */
+ string getText() {
+ result = this.(Unicode).getS()
+ or
+ result = this.(Bytes).getS()
+ }
+
+ /** Gets the `i`th character of this regex */
+ string getChar(int i) { result = this.getText().charAt(i) }
+
+ /** Gets the `i`th character of this regex, unless it is part of a character escape sequence. */
+ string nonEscapedCharAt(int i) {
+ result = this.getText().charAt(i) and
+ not exists(int x, int y | this.escapedCharacter(x, y) and i in [x .. y - 1])
+ }
+
+ private predicate isOptionDivider(int i) { this.nonEscapedCharAt(i) = "|" }
+
+ private predicate isGroupEnd(int i) { this.nonEscapedCharAt(i) = ")" and not this.inCharSet(i) }
+
+ private predicate isGroupStart(int i) { this.nonEscapedCharAt(i) = "(" and not this.inCharSet(i) }
+
+ /**
+ * Holds if the `i`th character could not be parsed.
+ */
+ predicate failedToParse(int i) {
+ exists(this.getChar(i)) and
+ not exists(int start, int end |
+ this.top_level(start, end) and
+ start <= i and
+ end > i
+ )
+ }
+
+ /** Named unicode characters, eg \N{degree sign} */
+ private predicate escapedName(int start, int end) {
+ this.escapingChar(start) and
+ this.getChar(start + 1) = "N" and
+ this.getChar(start + 2) = "{" and
+ end - 1 = min(int i | start + 2 < i and this.getChar(i) = "}")
+ }
+
+ /**
+ * Holds if an escaped character is found between `start` and `end`.
+ * Escaped characters include hex values, octal values and named escapes,
+ * but excludes backreferences.
+ */
+ predicate escapedCharacter(int start, int end) {
+ this.escapingChar(start) and
+ not this.numbered_backreference(start, _, _) and
+ (
+ // hex value \xhh
+ this.getChar(start + 1) = "x" and end = start + 4
+ or
+ // octal value \o, \oo, or \ooo
+ end in [start + 2 .. start + 4] and
+ forall(int i | i in [start + 1 .. end - 1] | this.isOctal(i)) and
+ not (
+ end < start + 4 and
+ this.isOctal(end)
+ )
+ or
+ // 16-bit hex value \uhhhh
+ this.getChar(start + 1) = "u" and end = start + 6
+ or
+ // 32-bit hex value \Uhhhhhhhh
+ this.getChar(start + 1) = "U" and end = start + 10
+ or
+ this.escapedName(start, end)
+ or
+ // escape not handled above, update when adding a new case
+ not this.getChar(start + 1) in ["x", "u", "U", "N"] and
+ not exists(this.getChar(start + 1).toInt()) and
+ end = start + 2
+ )
+ }
+
+ pragma[inline]
+ private predicate isOctal(int index) { this.getChar(index) = [0 .. 7].toString() }
+
+ /** Holds if `index` is inside a character set. */
+ predicate inCharSet(int index) {
+ exists(int x, int y | this.charSet(x, y) and index in [x + 1 .. y - 2])
+ }
+
+ /**
+ * 'simple' characters are any that don't alter the parsing of the regex.
+ */
+ private predicate simpleCharacter(int start, int end) {
+ end = start + 1 and
+ not this.charSet(start, _) and
+ not this.charSet(_, start + 1) and
+ exists(string c | c = this.getChar(start) |
+ exists(int x, int y, int z |
+ this.charSet(x, z) and
+ this.char_set_start(x, y)
+ |
+ start = y
+ or
+ start = z - 2
+ or
+ start > y and start < z - 2 and not this.charRange(_, _, start, end, _)
+ )
+ or
+ not this.inCharSet(start) and
+ not c = "(" and
+ not c = "[" and
+ not c = ")" and
+ not c = "|" and
+ not this.qualifier(start, _, _, _)
+ )
+ }
+
+ /**
+ * Holds if a simple or escaped character is found between `start` and `end`.
+ */
+ predicate character(int start, int end) {
+ (
+ this.simpleCharacter(start, end) and
+ not exists(int x, int y | this.escapedCharacter(x, y) and x <= start and y >= end)
+ or
+ this.escapedCharacter(start, end)
+ ) and
+ not exists(int x, int y | this.group_start(x, y) and x <= start and y >= end) and
+ not exists(int x, int y | this.backreference(x, y) and x <= start and y >= end)
+ }
+
+ /**
+ * Holds if a normal character is found between `start` and `end`.
+ */
+ predicate normalCharacter(int start, int end) {
+ end = start + 1 and
+ this.character(start, end) and
+ not this.specialCharacter(start, end, _)
+ }
+
+ /**
+ * Holds if a special character is found between `start` and `end`.
+ */
+ predicate specialCharacter(int start, int end, string char) {
+ not this.inCharSet(start) and
+ this.character(start, end) and
+ (
+ end = start + 1 and
+ char = this.getChar(start) and
+ (char = "$" or char = "^" or char = ".")
+ or
+ end = start + 2 and
+ this.escapingChar(start) and
+ char = this.getText().substring(start, end) and
+ char = ["\\A", "\\Z", "\\b", "\\B"]
+ )
+ }
+
+ /**
+ * Holds if the range [start:end) consists of only 'normal' characters.
+ */
+ predicate normalCharacterSequence(int start, int end) {
+ // a normal character inside a character set is interpreted on its own
+ this.normalCharacter(start, end) and
+ this.inCharSet(start)
+ or
+ // a maximal run of normal characters is considered as one constant
+ exists(int s, int e |
+ e = max(int i | this.normalCharacterRun(s, i)) and
+ not this.inCharSet(s)
+ |
+ // 'abc' can be considered one constant, but
+ // 'abc+' has to be broken up into 'ab' and 'c+',
+ // as the qualifier only applies to 'c'.
+ if this.qualifier(e, _, _, _)
+ then
+ end = e and start = e - 1
+ or
+ end = e - 1 and start = s and start < end
+ else (
+ end = e and
+ start = s
+ )
+ )
+ }
+
+ private predicate normalCharacterRun(int start, int end) {
+ (
+ this.normalCharacterRun(start, end - 1)
+ or
+ start = end - 1 and not this.normalCharacter(start - 1, start)
+ ) and
+ this.normalCharacter(end - 1, end)
+ }
+
+ private predicate characterItem(int start, int end) {
+ this.normalCharacterSequence(start, end) or
+ this.escapedCharacter(start, end) or
+ this.specialCharacter(start, end, _)
+ }
+
+ /** Whether the text in the range `start,end` is a group */
+ predicate group(int start, int end) {
+ this.groupContents(start, end, _, _)
+ or
+ this.emptyGroup(start, end)
+ }
+
+ /** Gets the number of the group in start,end */
+ int getGroupNumber(int start, int end) {
+ this.group(start, end) and
+ not this.non_capturing_group_start(start, _) and
+ result =
+ count(int i | this.group(i, _) and i < start and not this.non_capturing_group_start(i, _)) + 1
+ }
+
+ /** Gets the name, if it has one, of the group in start,end */
+ string getGroupName(int start, int end) {
+ this.group(start, end) and
+ exists(int name_end |
+ this.named_group_start(start, name_end) and
+ result = this.getText().substring(start + 4, name_end - 1)
+ )
+ }
+
+ /** Whether the text in the range start, end is a group and can match the empty string. */
+ predicate zeroWidthMatch(int start, int end) {
+ this.emptyGroup(start, end)
+ or
+ this.negativeAssertionGroup(start, end)
+ or
+ this.positiveLookaheadAssertionGroup(start, end)
+ or
+ this.positiveLookbehindAssertionGroup(start, end)
+ }
+
+ /** Holds if an empty group is found between `start` and `end`. */
+ predicate emptyGroup(int start, int end) {
+ exists(int endm1 | end = endm1 + 1 |
+ this.group_start(start, endm1) and
+ this.isGroupEnd(endm1)
+ )
+ }
+
+ private predicate emptyMatchAtStartGroup(int start, int end) {
+ this.emptyGroup(start, end)
+ or
+ this.negativeAssertionGroup(start, end)
+ or
+ this.positiveLookaheadAssertionGroup(start, end)
+ }
+
+ private predicate emptyMatchAtEndGroup(int start, int end) {
+ this.emptyGroup(start, end)
+ or
+ this.negativeAssertionGroup(start, end)
+ or
+ this.positiveLookbehindAssertionGroup(start, end)
+ }
+
+ private predicate negativeAssertionGroup(int start, int end) {
+ exists(int in_start |
+ this.negative_lookahead_assertion_start(start, in_start)
+ or
+ this.negative_lookbehind_assertion_start(start, in_start)
+ |
+ this.groupContents(start, end, in_start, _)
+ )
+ }
+
+ /** Holds if a negative lookahead is found between `start` and `end` */
+ predicate negativeLookaheadAssertionGroup(int start, int end) {
+ exists(int in_start | this.negative_lookahead_assertion_start(start, in_start) |
+ this.groupContents(start, end, in_start, _)
+ )
+ }
+
+ /** Holds if a negative lookbehind is found between `start` and `end` */
+ predicate negativeLookbehindAssertionGroup(int start, int end) {
+ exists(int in_start | this.negative_lookbehind_assertion_start(start, in_start) |
+ this.groupContents(start, end, in_start, _)
+ )
+ }
+
+ /** Holds if a positive lookahead is found between `start` and `end` */
+ predicate positiveLookaheadAssertionGroup(int start, int end) {
+ exists(int in_start | this.lookahead_assertion_start(start, in_start) |
+ this.groupContents(start, end, in_start, _)
+ )
+ }
+
+ /** Holds if a positive lookbehind is found between `start` and `end` */
+ predicate positiveLookbehindAssertionGroup(int start, int end) {
+ exists(int in_start | this.lookbehind_assertion_start(start, in_start) |
+ this.groupContents(start, end, in_start, _)
+ )
+ }
+
+ private predicate group_start(int start, int end) {
+ this.non_capturing_group_start(start, end)
+ or
+ this.flag_group_start(start, end, _)
+ or
+ this.named_group_start(start, end)
+ or
+ this.named_backreference_start(start, end)
+ or
+ this.lookahead_assertion_start(start, end)
+ or
+ this.negative_lookahead_assertion_start(start, end)
+ or
+ this.lookbehind_assertion_start(start, end)
+ or
+ this.negative_lookbehind_assertion_start(start, end)
+ or
+ this.comment_group_start(start, end)
+ or
+ this.simple_group_start(start, end)
+ }
+
+ /** Matches the start of a non-capturing group, e.g. `(?:` */
+ private predicate non_capturing_group_start(int start, int end) {
+ this.isGroupStart(start) and
+ this.getChar(start + 1) = "?" and
+ this.getChar(start + 2) = ":" and
+ end = start + 3
+ }
+
+ /** Matches the start of a simple group, e.g. `(a+)`. */
+ private predicate simple_group_start(int start, int end) {
+ this.isGroupStart(start) and
+ this.getChar(start + 1) != "?" and
+ end = start + 1
+ }
+
+ /**
+ * Matches the start of a named group, such as:
+ * - `(?\w+)`
+ * - `(?'name'\w+)`
+ */
+ private predicate named_group_start(int start, int end) {
+ this.isGroupStart(start) and
+ this.getChar(start + 1) = "?" and
+ this.getChar(start + 2) = "P" and
+ this.getChar(start + 3) = "<" and
+ not this.getChar(start + 4) = "=" and
+ not this.getChar(start + 4) = "!" and
+ exists(int name_end |
+ name_end = min(int i | i > start + 4 and this.getChar(i) = ">") and
+ end = name_end + 1
+ )
+ }
+
+ private predicate named_backreference_start(int start, int end) {
+ this.isGroupStart(start) and
+ this.getChar(start + 1) = "?" and
+ this.getChar(start + 2) = "P" and
+ this.getChar(start + 3) = "=" and
+ // Should this be looking for unescaped ")"?
+ // TODO: test this
+ end = min(int i | i > start + 4 and this.getChar(i) = "?")
+ }
+
+ private predicate flag_group_start(int start, int end, string c) {
+ this.isGroupStart(start) and
+ this.getChar(start + 1) = "?" and
+ end = start + 3 and
+ c = this.getChar(start + 2) and
+ c in ["i", "L", "m", "s", "u", "x"]
+ }
+
+ /**
+ * Gets the mode of this regular expression string if
+ * it is defined by a prefix.
+ */
+ string getModeFromPrefix() {
+ exists(string c | this.flag_group_start(_, _, c) |
+ c = "i" and result = "IGNORECASE"
+ or
+ c = "L" and result = "LOCALE"
+ or
+ c = "m" and result = "MULTILINE"
+ or
+ c = "s" and result = "DOTALL"
+ or
+ c = "u" and result = "UNICODE"
+ or
+ c = "x" and result = "VERBOSE"
+ )
+ }
+
+ /** Matches the start of a positive lookahead assertion, i.e. `(?=`. */
+ private predicate lookahead_assertion_start(int start, int end) {
+ this.isGroupStart(start) and
+ this.getChar(start + 1) = "?" and
+ this.getChar(start + 2) = "=" and
+ end = start + 3
+ }
+
+ /** Matches the start of a negative lookahead assertion, i.e. `(?!`. */
+ private predicate negative_lookahead_assertion_start(int start, int end) {
+ this.isGroupStart(start) and
+ this.getChar(start + 1) = "?" and
+ this.getChar(start + 2) = "!" and
+ end = start + 3
+ }
+
+ /** Matches the start of a positive lookbehind assertion, i.e. `(?<=`. */
+ private predicate lookbehind_assertion_start(int start, int end) {
+ this.isGroupStart(start) and
+ this.getChar(start + 1) = "?" and
+ this.getChar(start + 2) = "<" and
+ this.getChar(start + 3) = "=" and
+ end = start + 4
+ }
+
+ /** Matches the start of a negative lookbehind assertion, i.e. `(?`. */
+ private predicate named_backreference(int start, int end, string name) {
+ this.named_backreference_start(start, start + 4) and
+ end = min(int i | i > start + 4 and this.getChar(i) = ")") + 1 and
+ name = this.getText().substring(start + 4, end - 2)
+ }
+
+ /** Matches a numbered backreference, e.g. `\1`. */
+ private predicate numbered_backreference(int start, int end, int value) {
+ this.escapingChar(start) and
+ // starting with 0 makes it an octal escape
+ not this.getChar(start + 1) = "0" and
+ exists(string text, string svalue, int len |
+ end = start + len and
+ text = this.getText() and
+ len in [2 .. 3]
+ |
+ svalue = text.substring(start + 1, start + len) and
+ value = svalue.toInt() and
+ // value is composed of digits
+ forall(int i | i in [start + 1 .. start + len - 1] | this.getChar(i) = [0 .. 9].toString()) and
+ // a longer reference is not possible
+ not (
+ len = 2 and
+ exists(text.substring(start + 1, start + len + 1).toInt())
+ ) and
+ // 3 octal digits makes it an octal escape
+ not forall(int i | i in [start + 1 .. start + 4] | this.isOctal(i))
+ // TODO: Inside a character set, all numeric escapes are treated as characters.
+ )
+ }
+
+ /** Whether the text in the range `start,end` is a back reference */
+ predicate backreference(int start, int end) {
+ this.numbered_backreference(start, end, _)
+ or
+ this.named_backreference(start, end, _)
+ }
+
+ /** Gets the number of the back reference in start,end */
+ int getBackrefNumber(int start, int end) { this.numbered_backreference(start, end, result) }
+
+ /** Gets the name, if it has one, of the back reference in start,end */
+ string getBackrefName(int start, int end) { this.named_backreference(start, end, result) }
+
+ private predicate baseItem(int start, int end) {
+ this.characterItem(start, end) and
+ not exists(int x, int y | this.charSet(x, y) and x <= start and y >= end)
+ or
+ this.group(start, end)
+ or
+ this.charSet(start, end)
+ or
+ this.backreference(start, end)
+ }
+
+ private predicate qualifier(int start, int end, boolean maybe_empty, boolean may_repeat_forever) {
+ this.short_qualifier(start, end, maybe_empty, may_repeat_forever) and
+ not this.getChar(end) = "?"
+ or
+ exists(int short_end | this.short_qualifier(start, short_end, maybe_empty, may_repeat_forever) |
+ if this.getChar(short_end) = "?" then end = short_end + 1 else end = short_end
+ )
+ }
+
+ private predicate short_qualifier(
+ int start, int end, boolean maybe_empty, boolean may_repeat_forever
+ ) {
+ (
+ this.getChar(start) = "+" and maybe_empty = false and may_repeat_forever = true
+ or
+ this.getChar(start) = "*" and maybe_empty = true and may_repeat_forever = true
+ or
+ this.getChar(start) = "?" and maybe_empty = true and may_repeat_forever = false
+ ) and
+ end = start + 1
+ or
+ exists(string lower, string upper |
+ this.multiples(start, end, lower, upper) and
+ (if lower = "" or lower.toInt() = 0 then maybe_empty = true else maybe_empty = false) and
+ if upper = "" then may_repeat_forever = true else may_repeat_forever = false
+ )
+ }
+
+ /**
+ * Holds if a repetition quantifier is found between `start` and `end`,
+ * with the given lower and upper bounds. If a bound is omitted, the corresponding
+ * string is empty.
+ */
+ predicate multiples(int start, int end, string lower, string upper) {
+ exists(string text, string match, string inner |
+ text = this.getText() and
+ end = start + match.length() and
+ inner = match.substring(1, match.length() - 1)
+ |
+ match = text.regexpFind("\\{[0-9]+\\}", _, start) and
+ lower = inner and
+ upper = lower
+ or
+ match = text.regexpFind("\\{[0-9]*,[0-9]*\\}", _, start) and
+ exists(int commaIndex |
+ commaIndex = inner.indexOf(",") and
+ lower = inner.prefix(commaIndex) and
+ upper = inner.suffix(commaIndex + 1)
+ )
+ )
+ }
+
+ /**
+ * Whether the text in the range start,end is a qualified item, where item is a character,
+ * a character set or a group.
+ */
+ predicate qualifiedItem(int start, int end, boolean maybe_empty, boolean may_repeat_forever) {
+ this.qualifiedPart(start, _, end, maybe_empty, may_repeat_forever)
+ }
+
+ /**
+ * Holds if a qualified part is found between `start` and `part_end` and the qualifier is
+ * found between `part_end` and `end`.
+ *
+ * `maybe_empty` is true if the part is optional.
+ * `may_repeat_forever` is true if the part may be repeated unboundedly.
+ */
+ predicate qualifiedPart(
+ int start, int part_end, int end, boolean maybe_empty, boolean may_repeat_forever
+ ) {
+ this.baseItem(start, part_end) and
+ this.qualifier(part_end, end, maybe_empty, may_repeat_forever)
+ }
+
+ /** Holds if the range `start`, `end` contains a character, a quantifier, a character set or a group. */
+ predicate item(int start, int end) {
+ this.qualifiedItem(start, end, _, _)
+ or
+ this.baseItem(start, end) and not this.qualifier(end, _, _, _)
+ }
+
+ private predicate subsequence(int start, int end) {
+ (
+ start = 0 or
+ this.group_start(_, start) or
+ this.isOptionDivider(start - 1)
+ ) and
+ this.item(start, end)
+ or
+ exists(int mid |
+ this.subsequence(start, mid) and
+ this.item(mid, end)
+ )
+ }
+
+ /**
+ * Whether the text in the range start,end is a sequence of 1 or more items, where an item is a character,
+ * a character set or a group.
+ */
+ predicate sequence(int start, int end) {
+ this.sequenceOrQualified(start, end) and
+ not this.qualifiedItem(start, end, _, _)
+ }
+
+ private predicate sequenceOrQualified(int start, int end) {
+ this.subsequence(start, end) and
+ not this.item_start(end)
+ }
+
+ private predicate item_start(int start) {
+ this.characterItem(start, _) or
+ this.isGroupStart(start) or
+ this.charSet(start, _) or
+ this.backreference(start, _)
+ }
+
+ private predicate item_end(int end) {
+ this.characterItem(_, end)
+ or
+ exists(int endm1 | this.isGroupEnd(endm1) and end = endm1 + 1)
+ or
+ this.charSet(_, end)
+ or
+ this.qualifier(_, end, _, _)
+ }
+
+ private predicate top_level(int start, int end) {
+ this.subalternation(start, end, _) and
+ not this.isOptionDivider(end)
+ }
+
+ private predicate subalternation(int start, int end, int item_start) {
+ this.sequenceOrQualified(start, end) and
+ not this.isOptionDivider(start - 1) and
+ item_start = start
+ or
+ start = end and
+ not this.item_end(start) and
+ this.isOptionDivider(end) and
+ item_start = start
+ or
+ exists(int mid |
+ this.subalternation(start, mid, _) and
+ this.isOptionDivider(mid) and
+ item_start = mid + 1
+ |
+ this.sequenceOrQualified(item_start, end)
+ or
+ not this.item_start(end) and end = item_start
+ )
+ }
+
+ /**
+ * Whether the text in the range start,end is an alternation
+ */
+ predicate alternation(int start, int end) {
+ this.top_level(start, end) and
+ exists(int less | this.subalternation(start, less, _) and less < end)
+ }
+
+ /**
+ * Whether the text in the range start,end is an alternation and the text in part_start, part_end is one of the
+ * options in that alternation.
+ */
+ predicate alternationOption(int start, int end, int part_start, int part_end) {
+ this.alternation(start, end) and
+ this.subalternation(start, part_end, part_start)
+ }
+
+ /** A part of the regex that may match the start of the string. */
+ private predicate firstPart(int start, int end) {
+ start = 0 and end = this.getText().length()
+ or
+ exists(int x | this.firstPart(x, end) |
+ this.emptyMatchAtStartGroup(x, start) or
+ this.qualifiedItem(x, start, true, _) or
+ // ^ and \A match the start of the string
+ this.specialCharacter(x, start, ["^", "\\A"])
+ )
+ or
+ exists(int y | this.firstPart(start, y) |
+ this.item(start, end)
+ or
+ this.qualifiedPart(start, end, y, _, _)
+ )
+ or
+ exists(int x, int y | this.firstPart(x, y) |
+ this.groupContents(x, y, start, end)
+ or
+ this.alternationOption(x, y, start, end)
+ )
+ }
+
+ /** A part of the regex that may match the end of the string. */
+ private predicate lastPart(int start, int end) {
+ start = 0 and end = this.getText().length()
+ or
+ exists(int y | this.lastPart(start, y) |
+ this.emptyMatchAtEndGroup(end, y)
+ or
+ this.qualifiedItem(end, y, true, _)
+ or
+ // $ and \Z match the end of the string.
+ this.specialCharacter(end, y, ["$", "\\Z"])
+ )
+ or
+ this.lastPart(_, end) and
+ this.item(start, end)
+ or
+ exists(int y | this.lastPart(start, y) | this.qualifiedPart(start, end, y, _, _))
+ or
+ exists(int x, int y | this.lastPart(x, y) |
+ this.groupContents(x, y, start, end)
+ or
+ this.alternationOption(x, y, start, end)
+ )
+ }
+
+ /**
+ * Whether the item at [start, end) is one of the first items
+ * to be matched.
+ */
+ predicate firstItem(int start, int end) {
+ (
+ this.characterItem(start, end)
+ or
+ this.qualifiedItem(start, end, _, _)
+ or
+ this.charSet(start, end)
+ ) and
+ this.firstPart(start, end)
+ }
+
+ /**
+ * Whether the item at [start, end) is one of the last items
+ * to be matched.
+ */
+ predicate lastItem(int start, int end) {
+ (
+ this.characterItem(start, end)
+ or
+ this.qualifiedItem(start, end, _, _)
+ or
+ this.charSet(start, end)
+ ) and
+ this.lastPart(start, end)
+ }
+}
diff --git a/python/ql/src/Expressions/Regex/BackspaceEscape.ql b/python/ql/src/Expressions/Regex/BackspaceEscape.ql
index ce69dabec44..e67ced94312 100644
--- a/python/ql/src/Expressions/Regex/BackspaceEscape.ql
+++ b/python/ql/src/Expressions/Regex/BackspaceEscape.ql
@@ -13,7 +13,7 @@
import python
import semmle.python.regex
-from Regex r, int offset
+from RegExp r, int offset
where
r.escapingChar(offset) and
r.getChar(offset + 1) = "b" and
diff --git a/python/ql/src/Expressions/Regex/DuplicateCharacterInSet.ql b/python/ql/src/Expressions/Regex/DuplicateCharacterInSet.ql
index dc760df424f..1c7cfc39de9 100644
--- a/python/ql/src/Expressions/Regex/DuplicateCharacterInSet.ql
+++ b/python/ql/src/Expressions/Regex/DuplicateCharacterInSet.ql
@@ -13,7 +13,7 @@
import python
import semmle.python.regex
-predicate duplicate_char_in_class(Regex r, string char) {
+predicate duplicate_char_in_class(RegExp r, string char) {
exists(int i, int j, int x, int y, int start, int end |
i != x and
j != y and
@@ -36,7 +36,7 @@ predicate duplicate_char_in_class(Regex r, string char) {
)
}
-from Regex r, string char
+from RegExp r, string char
where duplicate_char_in_class(r, char)
select r,
"This regular expression includes duplicate character '" + char + "' in a set of characters."
diff --git a/python/ql/src/Expressions/Regex/MissingPartSpecialGroup.ql b/python/ql/src/Expressions/Regex/MissingPartSpecialGroup.ql
index ea5deffa7de..e03fc65518a 100644
--- a/python/ql/src/Expressions/Regex/MissingPartSpecialGroup.ql
+++ b/python/ql/src/Expressions/Regex/MissingPartSpecialGroup.ql
@@ -13,6 +13,6 @@
import python
import semmle.python.regex
-from Regex r, string missing, string part
+from RegExp r, string missing, string part
where r.getText().regexpMatch(".*\\(P<\\w+>.*") and missing = "?" and part = "named group"
select r, "Regular expression is missing '" + missing + "' in " + part + "."
diff --git a/python/ql/src/Expressions/Regex/UnmatchableCaret.ql b/python/ql/src/Expressions/Regex/UnmatchableCaret.ql
index f954169ae02..0dcf88a5d08 100644
--- a/python/ql/src/Expressions/Regex/UnmatchableCaret.ql
+++ b/python/ql/src/Expressions/Regex/UnmatchableCaret.ql
@@ -13,14 +13,14 @@
import python
import semmle.python.regex
-predicate unmatchable_caret(Regex r, int start) {
+predicate unmatchable_caret(RegExp r, int start) {
not r.getAMode() = "MULTILINE" and
not r.getAMode() = "VERBOSE" and
r.specialCharacter(start, start + 1, "^") and
not r.firstItem(start, start + 1)
}
-from Regex r, int offset
+from RegExp r, int offset
where unmatchable_caret(r, offset)
select r,
"This regular expression includes an unmatchable caret at offset " + offset.toString() + "."
diff --git a/python/ql/src/Expressions/Regex/UnmatchableDollar.ql b/python/ql/src/Expressions/Regex/UnmatchableDollar.ql
index 3f9457f5bd2..00b14998a04 100644
--- a/python/ql/src/Expressions/Regex/UnmatchableDollar.ql
+++ b/python/ql/src/Expressions/Regex/UnmatchableDollar.ql
@@ -13,14 +13,14 @@
import python
import semmle.python.regex
-predicate unmatchable_dollar(Regex r, int start) {
+predicate unmatchable_dollar(RegExp r, int start) {
not r.getAMode() = "MULTILINE" and
not r.getAMode() = "VERBOSE" and
r.specialCharacter(start, start + 1, "$") and
not r.lastItem(start, start + 1)
}
-from Regex r, int offset
+from RegExp r, int offset
where unmatchable_dollar(r, offset)
select r,
"This regular expression includes an unmatchable dollar at offset " + offset.toString() + "."
From f2adc4f958b38ab4bc54eb486ef7b4849d98aec1 Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Mon, 20 Mar 2023 16:13:55 +0100
Subject: [PATCH 093/870] add missing qldoc
---
python/ql/lib/semmle/python/regexp/internal/ParseRegExp.qll | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/python/ql/lib/semmle/python/regexp/internal/ParseRegExp.qll b/python/ql/lib/semmle/python/regexp/internal/ParseRegExp.qll
index 7606743ea07..65e7ea93bae 100644
--- a/python/ql/lib/semmle/python/regexp/internal/ParseRegExp.qll
+++ b/python/ql/lib/semmle/python/regexp/internal/ParseRegExp.qll
@@ -1,3 +1,7 @@
+/**
+ * Library for parsing for Python regular expressions.
+ */
+
import python
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.Concepts as Concepts
From ffa34251957ff9bc6476d4be0a064c1e1f083192 Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Tue, 21 Mar 2023 11:53:58 +0100
Subject: [PATCH 094/870] rename away from deprecated alias in test-files
---
python/ql/test/library-tests/regex/Alternation.ql | 2 +-
python/ql/test/library-tests/regex/Characters.ql | 2 +-
python/ql/test/library-tests/regex/Consistency.ql | 2 +-
python/ql/test/library-tests/regex/FirstLast.ql | 4 ++--
python/ql/test/library-tests/regex/GroupContents.ql | 2 +-
python/ql/test/library-tests/regex/Mode.ql | 2 +-
python/ql/test/library-tests/regex/Qualified.ql | 2 +-
python/ql/test/library-tests/regex/Regex.ql | 4 ++--
python/ql/test/library-tests/regex/SubstructureTests.ql | 8 ++++----
9 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/python/ql/test/library-tests/regex/Alternation.ql b/python/ql/test/library-tests/regex/Alternation.ql
index d172e778943..a47f016a399 100644
--- a/python/ql/test/library-tests/regex/Alternation.ql
+++ b/python/ql/test/library-tests/regex/Alternation.ql
@@ -1,7 +1,7 @@
import python
import semmle.python.regex
-from Regex r, int start, int end, int part_start, int part_end
+from RegExp r, int start, int end, int part_start, int part_end
where
r.getLocation().getFile().getBaseName() = "test.py" and
r.alternationOption(start, end, part_start, part_end)
diff --git a/python/ql/test/library-tests/regex/Characters.ql b/python/ql/test/library-tests/regex/Characters.ql
index 1444c37cd57..4a570acfbd9 100644
--- a/python/ql/test/library-tests/regex/Characters.ql
+++ b/python/ql/test/library-tests/regex/Characters.ql
@@ -6,6 +6,6 @@
import python
import semmle.python.regex
-from Regex r, int start, int end
+from RegExp r, int start, int end
where r.character(start, end) and r.getLocation().getFile().getBaseName() = "test.py"
select r.getText(), start, end
diff --git a/python/ql/test/library-tests/regex/Consistency.ql b/python/ql/test/library-tests/regex/Consistency.ql
index 26e0a1cbfb5..2432a36d870 100644
--- a/python/ql/test/library-tests/regex/Consistency.ql
+++ b/python/ql/test/library-tests/regex/Consistency.ql
@@ -7,6 +7,6 @@ import semmle.python.regex
from string str, Location loc, int counter
where
- counter = strictcount(Regex term | term.getLocation() = loc and term.getText() = str) and
+ counter = strictcount(RegExp term | term.getLocation() = loc and term.getText() = str) and
counter > 1
select str, counter, loc
diff --git a/python/ql/test/library-tests/regex/FirstLast.ql b/python/ql/test/library-tests/regex/FirstLast.ql
index 5bca6fdf542..b0882faf61a 100644
--- a/python/ql/test/library-tests/regex/FirstLast.ql
+++ b/python/ql/test/library-tests/regex/FirstLast.ql
@@ -1,12 +1,12 @@
import python
import semmle.python.regex
-predicate part(Regex r, int start, int end, string kind) {
+predicate part(RegExp r, int start, int end, string kind) {
r.lastItem(start, end) and kind = "last"
or
r.firstItem(start, end) and kind = "first"
}
-from Regex r, int start, int end, string kind
+from RegExp r, int start, int end, string kind
where part(r, start, end, kind) and r.getLocation().getFile().getBaseName() = "test.py"
select r.getText(), kind, start, end
diff --git a/python/ql/test/library-tests/regex/GroupContents.ql b/python/ql/test/library-tests/regex/GroupContents.ql
index 221edbe44ba..ddefebb9c55 100644
--- a/python/ql/test/library-tests/regex/GroupContents.ql
+++ b/python/ql/test/library-tests/regex/GroupContents.ql
@@ -1,7 +1,7 @@
import python
import semmle.python.regex
-from Regex r, int start, int end, int part_start, int part_end
+from RegExp r, int start, int end, int part_start, int part_end
where
r.getLocation().getFile().getBaseName() = "test.py" and
r.groupContents(start, end, part_start, part_end)
diff --git a/python/ql/test/library-tests/regex/Mode.ql b/python/ql/test/library-tests/regex/Mode.ql
index 62449fbb330..b369018fff0 100644
--- a/python/ql/test/library-tests/regex/Mode.ql
+++ b/python/ql/test/library-tests/regex/Mode.ql
@@ -1,6 +1,6 @@
import python
import semmle.python.regex
-from Regex r
+from RegExp r
where r.getLocation().getFile().getBaseName() = "test.py"
select r.getLocation().getStartLine(), r.getAMode()
diff --git a/python/ql/test/library-tests/regex/Qualified.ql b/python/ql/test/library-tests/regex/Qualified.ql
index 26400b3440f..c40eaca0d68 100644
--- a/python/ql/test/library-tests/regex/Qualified.ql
+++ b/python/ql/test/library-tests/regex/Qualified.ql
@@ -1,7 +1,7 @@
import python
import semmle.python.regex
-from Regex r, int start, int end, boolean maybe_empty, boolean may_repeat_forever
+from RegExp r, int start, int end, boolean maybe_empty, boolean may_repeat_forever
where
r.getLocation().getFile().getBaseName() = "test.py" and
r.qualifiedItem(start, end, maybe_empty, may_repeat_forever)
diff --git a/python/ql/test/library-tests/regex/Regex.ql b/python/ql/test/library-tests/regex/Regex.ql
index 4c799ac2574..9c390474778 100644
--- a/python/ql/test/library-tests/regex/Regex.ql
+++ b/python/ql/test/library-tests/regex/Regex.ql
@@ -1,7 +1,7 @@
import python
import semmle.python.regex
-predicate part(Regex r, int start, int end, string kind) {
+predicate part(RegExp r, int start, int end, string kind) {
r.alternation(start, end) and kind = "choice"
or
r.normalCharacter(start, end) and kind = "char"
@@ -23,6 +23,6 @@ predicate part(Regex r, int start, int end, string kind) {
r.qualifiedItem(start, end, _, _) and kind = "qualified"
}
-from Regex r, int start, int end, string kind
+from RegExp r, int start, int end, string kind
where part(r, start, end, kind) and r.getLocation().getFile().getBaseName() = "test.py"
select r.getText(), kind, start, end
diff --git a/python/ql/test/library-tests/regex/SubstructureTests.ql b/python/ql/test/library-tests/regex/SubstructureTests.ql
index cba3e56d08c..e189c13b15e 100644
--- a/python/ql/test/library-tests/regex/SubstructureTests.ql
+++ b/python/ql/test/library-tests/regex/SubstructureTests.ql
@@ -10,7 +10,7 @@ class CharacterSetTest extends InlineExpectationsTest {
override predicate hasActualResult(Location location, string element, string tag, string value) {
exists(location.getFile().getRelativePath()) and
location.getFile().getBaseName() = "charSetTest.py" and
- exists(Regex re, int start, int end |
+ exists(RegExp re, int start, int end |
re.charSet(start, end) and
location = re.getLocation() and
element = re.getText().substring(start, end) and
@@ -28,7 +28,7 @@ class CharacterRangeTest extends InlineExpectationsTest {
override predicate hasActualResult(Location location, string element, string tag, string value) {
exists(location.getFile().getRelativePath()) and
location.getFile().getBaseName() = "charRangeTest.py" and
- exists(Regex re, int start, int lower_end, int upper_start, int end |
+ exists(RegExp re, int start, int lower_end, int upper_start, int end |
re.charRange(_, start, lower_end, upper_start, end) and
location = re.getLocation() and
element = re.getText().substring(start, end) and
@@ -46,7 +46,7 @@ class EscapeTest extends InlineExpectationsTest {
override predicate hasActualResult(Location location, string element, string tag, string value) {
exists(location.getFile().getRelativePath()) and
location.getFile().getBaseName() = "escapedCharacterTest.py" and
- exists(Regex re, int start, int end |
+ exists(RegExp re, int start, int end |
re.escapedCharacter(start, end) and
location = re.getLocation() and
element = re.getText().substring(start, end) and
@@ -64,7 +64,7 @@ class GroupTest extends InlineExpectationsTest {
override predicate hasActualResult(Location location, string element, string tag, string value) {
exists(location.getFile().getRelativePath()) and
location.getFile().getBaseName() = "groupTest.py" and
- exists(Regex re, int start, int end |
+ exists(RegExp re, int start, int end |
re.group(start, end) and
location = re.getLocation() and
element = re.getText().substring(start, end) and
From 3cde11efc8f17baa8f7d7087fa136397ca0d21bd Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Fri, 24 Mar 2023 11:24:34 +0100
Subject: [PATCH 095/870] use StrConst instead of Bytes and Unicode
---
.../semmle/python/regexp/internal/ParseRegExp.qll | 13 +++----------
.../python/regexp/internal/RegExpTracking.qll | 5 +----
2 files changed, 4 insertions(+), 14 deletions(-)
diff --git a/python/ql/lib/semmle/python/regexp/internal/ParseRegExp.qll b/python/ql/lib/semmle/python/regexp/internal/ParseRegExp.qll
index 65e7ea93bae..f635332944b 100644
--- a/python/ql/lib/semmle/python/regexp/internal/ParseRegExp.qll
+++ b/python/ql/lib/semmle/python/regexp/internal/ParseRegExp.qll
@@ -110,13 +110,10 @@ private module FindRegexMode {
deprecated class Regex = RegExp;
/** A StrConst used as a regular expression */
-class RegExp extends Expr {
+class RegExp extends Expr instanceof StrConst {
DataFlow::Node use;
- RegExp() {
- (this instanceof Bytes or this instanceof Unicode) and
- this = RegExpTracking::regExpSource(use).asExpr()
- }
+ RegExp() { this = RegExpTracking::regExpSource(use).asExpr() }
/** Gets a data-flow node where this string value is used as a regular expression. */
DataFlow::Node getAUse() { result = use }
@@ -332,11 +329,7 @@ class RegExp extends Expr {
}
/** Gets the text of this regex */
- string getText() {
- result = this.(Unicode).getS()
- or
- result = this.(Bytes).getS()
- }
+ string getText() { result = super.getText() }
/** Gets the `i`th character of this regex */
string getChar(int i) { result = this.getText().charAt(i) }
diff --git a/python/ql/lib/semmle/python/regexp/internal/RegExpTracking.qll b/python/ql/lib/semmle/python/regexp/internal/RegExpTracking.qll
index fb67f0e8c2c..b62e975cf82 100644
--- a/python/ql/lib/semmle/python/regexp/internal/RegExpTracking.qll
+++ b/python/ql/lib/semmle/python/regexp/internal/RegExpTracking.qll
@@ -15,10 +15,7 @@ private import semmle.python.dataflow.new.DataFlow
private import semmle.python.Concepts as Concepts
/** Gets a constant string value that may be used as a regular expression. */
-DataFlow::LocalSourceNode strStart() {
- result.asExpr() instanceof Bytes or
- result.asExpr() instanceof Unicode
-}
+DataFlow::Node strStart() { result.asExpr() instanceof StrConst }
private import semmle.python.regex as Regex
From 2d2602b66883bf448a67e6e79141222a5852d8a1 Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Fri, 24 Mar 2023 11:50:41 +0100
Subject: [PATCH 096/870] use that strings are local-source-nodes in
regex-tracking
---
.../lib/semmle/python/regexp/internal/RegExpTracking.qll | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/python/ql/lib/semmle/python/regexp/internal/RegExpTracking.qll b/python/ql/lib/semmle/python/regexp/internal/RegExpTracking.qll
index b62e975cf82..d883dfe68aa 100644
--- a/python/ql/lib/semmle/python/regexp/internal/RegExpTracking.qll
+++ b/python/ql/lib/semmle/python/regexp/internal/RegExpTracking.qll
@@ -15,7 +15,7 @@ private import semmle.python.dataflow.new.DataFlow
private import semmle.python.Concepts as Concepts
/** Gets a constant string value that may be used as a regular expression. */
-DataFlow::Node strStart() { result.asExpr() instanceof StrConst }
+DataFlow::LocalSourceNode strStart() { result.asExpr() instanceof StrConst }
private import semmle.python.regex as Regex
@@ -44,7 +44,7 @@ private DataFlow::TypeTrackingNode backwards(DataFlow::TypeBackTracker t) {
private DataFlow::TypeTrackingNode forwards(DataFlow::TypeTracker t) {
t.start() and
result = backwards(DataFlow::TypeBackTracker::end()) and
- result.flowsTo(strStart())
+ result = strStart()
or
exists(DataFlow::TypeTracker t2 | result = forwards(t2).track(t2, t)) and
result = backwards(_)
@@ -57,11 +57,11 @@ private DataFlow::TypeTrackingNode forwards(DataFlow::TypeTracker t) {
* The result of the exploratory phase is used to limit the size of the search space in this precise analysis.
*/
private DataFlow::TypeTrackingNode regexTracking(DataFlow::Node start, DataFlow::TypeTracker t) {
- result = forwards(_) and
+ result = forwards(t) and
(
t.start() and
start = strStart() and
- result = start.getALocalSource()
+ result = start
or
exists(DataFlow::TypeTracker t2 | result = regexTracking(start, t2).track(t2, t))
)
From 113ce61d405b002bfba5eadf925f6a7d39026c83 Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Fri, 24 Mar 2023 11:59:36 +0100
Subject: [PATCH 097/870] fix nit in qldoc
---
python/ql/lib/semmle/python/regexp/internal/ParseRegExp.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/lib/semmle/python/regexp/internal/ParseRegExp.qll b/python/ql/lib/semmle/python/regexp/internal/ParseRegExp.qll
index f635332944b..465f9be6a4f 100644
--- a/python/ql/lib/semmle/python/regexp/internal/ParseRegExp.qll
+++ b/python/ql/lib/semmle/python/regexp/internal/ParseRegExp.qll
@@ -1,5 +1,5 @@
/**
- * Library for parsing for Python regular expressions.
+ * Library for parsing Python regular expressions.
*/
import python
From a64848c022a1b17865c343902fcf0b0df21d09f5 Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Fri, 24 Mar 2023 12:09:34 +0100
Subject: [PATCH 098/870] simplify StdLibRegExpInterpretation to only consider
re.compile, because the rest is handled by RegexExecution
---
python/ql/lib/semmle/python/regex.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/lib/semmle/python/regex.qll b/python/ql/lib/semmle/python/regex.qll
index 0dfc74b5e6f..23debaea8cf 100644
--- a/python/ql/lib/semmle/python/regex.qll
+++ b/python/ql/lib/semmle/python/regex.qll
@@ -33,7 +33,7 @@ private import semmle.python.ApiGraphs
class StdLibRegExpInterpretation extends RegExpInterpretation::Range {
StdLibRegExpInterpretation() {
this =
- API::moduleImport("re").getMember(any(string name | name != "escape")).getACall().getArg(0)
+ API::moduleImport("re").getMember("compile").getACall().getParameter(0, "pattern").asSink()
}
}
From 2fad406b5c4494931985c24f22487c8f80604db6 Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Fri, 24 Mar 2023 12:14:29 +0100
Subject: [PATCH 099/870] move StdLibRegExpInterpretation to Stdlib.qll
---
python/ql/lib/semmle/python/frameworks/Stdlib.qll | 13 +++++++++++++
python/ql/lib/semmle/python/regex.qll | 14 +-------------
2 files changed, 14 insertions(+), 13 deletions(-)
diff --git a/python/ql/lib/semmle/python/frameworks/Stdlib.qll b/python/ql/lib/semmle/python/frameworks/Stdlib.qll
index 961b4c808d7..e8ad01cc1f0 100644
--- a/python/ql/lib/semmle/python/frameworks/Stdlib.qll
+++ b/python/ql/lib/semmle/python/frameworks/Stdlib.qll
@@ -3015,6 +3015,19 @@ private module StdlibPrivate {
override string getKind() { result = Escaping::getRegexKind() }
}
+ private import semmle.python.regex as Regex
+
+ /**
+ * A node interpreted as a regular expression.
+ * Speficically nodes where string values are interpreted as regular expressions.
+ */
+ class StdLibRegExpInterpretation extends Regex::RegExpInterpretation::Range {
+ StdLibRegExpInterpretation() {
+ this =
+ API::moduleImport("re").getMember("compile").getACall().getParameter(0, "pattern").asSink()
+ }
+ }
+
// ---------------------------------------------------------------------------
// urllib
// ---------------------------------------------------------------------------
diff --git a/python/ql/lib/semmle/python/regex.qll b/python/ql/lib/semmle/python/regex.qll
index 23debaea8cf..b041e1cfca9 100644
--- a/python/ql/lib/semmle/python/regex.qll
+++ b/python/ql/lib/semmle/python/regex.qll
@@ -4,6 +4,7 @@ private import semmle.python.Frameworks
private import regexp.internal.RegExpTracking as RegExpTracking
private import semmle.python.Concepts as Concepts
private import semmle.python.regexp.RegexTreeView
+private import semmle.python.dataflow.new.DataFlow
import regexp.internal.ParseRegExp
/** Gets a parsed regular expression term that is executed at `exec`. */
@@ -24,19 +25,6 @@ module RegExpInterpretation {
abstract class Range extends DataFlow::Node { }
}
-private import semmle.python.ApiGraphs
-
-/**
- * A node interpreted as a regular expression.
- * Speficically nodes where string values are interpreted as regular expressions.
- */
-class StdLibRegExpInterpretation extends RegExpInterpretation::Range {
- StdLibRegExpInterpretation() {
- this =
- API::moduleImport("re").getMember("compile").getACall().getParameter(0, "pattern").asSink()
- }
-}
-
/** A StrConst used as a regular expression */
deprecated class RegexString extends Regex {
RegexString() { this = RegExpTracking::regExpSource(_).asExpr() }
From a7f733ab8c0bb1a9a67cec7bdc033e44401c38dd Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Fri, 24 Mar 2023 12:22:19 +0100
Subject: [PATCH 100/870] move RegExpInterpretation into Concepts.qll
---
python/ql/lib/semmle/python/Concepts.qll | 20 +++++++++++++++++++
.../lib/semmle/python/frameworks/Stdlib.qll | 4 +---
python/ql/lib/semmle/python/regex.qll | 10 ----------
.../python/regexp/internal/ParseRegExp.qll | 2 +-
.../python/regexp/internal/RegExpTracking.qll | 2 +-
5 files changed, 23 insertions(+), 15 deletions(-)
diff --git a/python/ql/lib/semmle/python/Concepts.qll b/python/ql/lib/semmle/python/Concepts.qll
index 0264da4c360..c80805e5b8f 100644
--- a/python/ql/lib/semmle/python/Concepts.qll
+++ b/python/ql/lib/semmle/python/Concepts.qll
@@ -421,6 +421,26 @@ module RegexExecution {
}
}
+/**
+ * A node that is not a regular expression literal, but is used in places that
+ * may interpret it as one. Instances of this class are typically strings that
+ * flow to method calls like `re.compile`.
+ *
+ * Extend this class to refine existing API models. If you want to model new APIs,
+ * extend `RegExpInterpretation::Range` instead.
+ */
+class RegExpInterpretation extends DataFlow::Node instanceof RegExpInterpretation::Range { }
+
+/** Provides a class for modeling regular expression interpretations. */
+module RegExpInterpretation {
+ /**
+ * A node that is not a regular expression literal, but is used in places that
+ * may interpret it as one. Instances of this class are typically strings that
+ * flow to method calls like `re.compile`.
+ */
+ abstract class Range extends DataFlow::Node { }
+}
+
/** Provides classes for modeling XML-related APIs. */
module XML {
/**
diff --git a/python/ql/lib/semmle/python/frameworks/Stdlib.qll b/python/ql/lib/semmle/python/frameworks/Stdlib.qll
index e8ad01cc1f0..7e62a5b033e 100644
--- a/python/ql/lib/semmle/python/frameworks/Stdlib.qll
+++ b/python/ql/lib/semmle/python/frameworks/Stdlib.qll
@@ -3015,13 +3015,11 @@ private module StdlibPrivate {
override string getKind() { result = Escaping::getRegexKind() }
}
- private import semmle.python.regex as Regex
-
/**
* A node interpreted as a regular expression.
* Speficically nodes where string values are interpreted as regular expressions.
*/
- class StdLibRegExpInterpretation extends Regex::RegExpInterpretation::Range {
+ private class StdLibRegExpInterpretation extends RegExpInterpretation::Range {
StdLibRegExpInterpretation() {
this =
API::moduleImport("re").getMember("compile").getACall().getParameter(0, "pattern").asSink()
diff --git a/python/ql/lib/semmle/python/regex.qll b/python/ql/lib/semmle/python/regex.qll
index b041e1cfca9..827f6b89e34 100644
--- a/python/ql/lib/semmle/python/regex.qll
+++ b/python/ql/lib/semmle/python/regex.qll
@@ -15,16 +15,6 @@ RegExpTerm getTermForExecution(Concepts::RegexExecution exec) {
)
}
-/** Provides a class for modeling regular expression interpretations. */
-module RegExpInterpretation {
- /**
- * A node that is not a regular expression literal, but is used in places that
- * may interpret it as one. Instances of this class are typically strings that
- * flow to method calls like `re.compile`.
- */
- abstract class Range extends DataFlow::Node { }
-}
-
/** A StrConst used as a regular expression */
deprecated class RegexString extends Regex {
RegexString() { this = RegExpTracking::regExpSource(_).asExpr() }
diff --git a/python/ql/lib/semmle/python/regexp/internal/ParseRegExp.qll b/python/ql/lib/semmle/python/regexp/internal/ParseRegExp.qll
index 465f9be6a4f..5484ee12613 100644
--- a/python/ql/lib/semmle/python/regexp/internal/ParseRegExp.qll
+++ b/python/ql/lib/semmle/python/regexp/internal/ParseRegExp.qll
@@ -24,7 +24,7 @@ private module FindRegexMode {
sink = call.(Concepts::RegexExecution).getRegex()
or
call.getArg(_) = sink and
- sink instanceof RegExpInterpretation::Range
+ sink instanceof Concepts::RegExpInterpretation::Range
|
exists(DataFlow::CallCfgNode callNode |
call = callNode and
diff --git a/python/ql/lib/semmle/python/regexp/internal/RegExpTracking.qll b/python/ql/lib/semmle/python/regexp/internal/RegExpTracking.qll
index d883dfe68aa..41f839bdc04 100644
--- a/python/ql/lib/semmle/python/regexp/internal/RegExpTracking.qll
+++ b/python/ql/lib/semmle/python/regexp/internal/RegExpTracking.qll
@@ -23,7 +23,7 @@ private import semmle.python.regex as Regex
DataFlow::Node regSink() {
result = any(Concepts::RegexExecution exec).getRegex()
or
- result instanceof Regex::RegExpInterpretation::Range
+ result instanceof Concepts::RegExpInterpretation
}
/**
From d5029c94b6be20ebf08fe948ddb6f8427072f276 Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Mon, 1 May 2023 10:41:30 +0200
Subject: [PATCH 101/870] changes based on review
---
python/ql/lib/semmle/python/Concepts.qll | 10 ++++------
.../lib/semmle/python/dataflow/new/Regexp.qll | 4 ++--
.../lib/semmle/python/regexp/RegexTreeView.qll | 4 ++++
.../python/regexp/internal/ParseRegExp.qll | 14 +++++---------
.../python/regexp/internal/RegExpTracking.qll | 17 +++++++++--------
5 files changed, 24 insertions(+), 25 deletions(-)
diff --git a/python/ql/lib/semmle/python/Concepts.qll b/python/ql/lib/semmle/python/Concepts.qll
index c80805e5b8f..21ef902f2e5 100644
--- a/python/ql/lib/semmle/python/Concepts.qll
+++ b/python/ql/lib/semmle/python/Concepts.qll
@@ -422,9 +422,8 @@ module RegexExecution {
}
/**
- * A node that is not a regular expression literal, but is used in places that
- * may interpret it as one. Instances of this class are typically strings that
- * flow to method calls like `re.compile`.
+ * A node where a string is interpreted as a regular expression,
+ * for instance an argument to `re.compile`.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `RegExpInterpretation::Range` instead.
@@ -434,9 +433,8 @@ class RegExpInterpretation extends DataFlow::Node instanceof RegExpInterpretatio
/** Provides a class for modeling regular expression interpretations. */
module RegExpInterpretation {
/**
- * A node that is not a regular expression literal, but is used in places that
- * may interpret it as one. Instances of this class are typically strings that
- * flow to method calls like `re.compile`.
+ * A node where a string is interpreted as a regular expression,
+ * for instance an argument to `re.compile`.
*/
abstract class Range extends DataFlow::Node { }
}
diff --git a/python/ql/lib/semmle/python/dataflow/new/Regexp.qll b/python/ql/lib/semmle/python/dataflow/new/Regexp.qll
index 8fa3427256c..e1f824b2935 100644
--- a/python/ql/lib/semmle/python/dataflow/new/Regexp.qll
+++ b/python/ql/lib/semmle/python/dataflow/new/Regexp.qll
@@ -26,7 +26,7 @@ deprecated module RegExpPatterns {
* as a part of a regular expression.
*/
class RegExpPatternSource extends DataFlow::CfgNode {
- private DataFlow::Node sink;
+ private RegExpSink sink;
RegExpPatternSource() { this = regExpSource(sink) }
@@ -34,7 +34,7 @@ class RegExpPatternSource extends DataFlow::CfgNode {
* Gets a node where the pattern of this node is parsed as a part of
* a regular expression.
*/
- DataFlow::Node getAParse() { result = sink }
+ RegExpSink getAParse() { result = sink }
/**
* Gets the root term of the regular expression parsed from this pattern.
diff --git a/python/ql/lib/semmle/python/regexp/RegexTreeView.qll b/python/ql/lib/semmle/python/regexp/RegexTreeView.qll
index 192476274a3..49db4470343 100644
--- a/python/ql/lib/semmle/python/regexp/RegexTreeView.qll
+++ b/python/ql/lib/semmle/python/regexp/RegexTreeView.qll
@@ -525,6 +525,10 @@ module Impl implements RegexTreeViewSig {
*/
private predicate isUnicode() { this.getText().prefix(2) = ["\\u", "\\U"] }
+ /**
+ * Gets the unicode char for this escape.
+ * E.g. for `\u0061` this returns "a".
+ */
private string getUnicode() {
result = Numbers::parseHexInt(this.getText().suffix(2)).toUnicode()
}
diff --git a/python/ql/lib/semmle/python/regexp/internal/ParseRegExp.qll b/python/ql/lib/semmle/python/regexp/internal/ParseRegExp.qll
index 5484ee12613..c6f8e7f76aa 100644
--- a/python/ql/lib/semmle/python/regexp/internal/ParseRegExp.qll
+++ b/python/ql/lib/semmle/python/regexp/internal/ParseRegExp.qll
@@ -26,15 +26,11 @@ private module FindRegexMode {
call.getArg(_) = sink and
sink instanceof Concepts::RegExpInterpretation::Range
|
- exists(DataFlow::CallCfgNode callNode |
- call = callNode and
- result =
- mode_from_node([
- callNode
- .getArg(re_member_flags_arg(callNode.(DataFlow::MethodCallNode).getMethodName())),
- callNode.getArgByName("flags")
- ])
- )
+ result =
+ mode_from_node([
+ call.getArg(re_member_flags_arg(call.(DataFlow::MethodCallNode).getMethodName())),
+ call.getArgByName("flags")
+ ])
)
)
}
diff --git a/python/ql/lib/semmle/python/regexp/internal/RegExpTracking.qll b/python/ql/lib/semmle/python/regexp/internal/RegExpTracking.qll
index 41f839bdc04..39d3e918de9 100644
--- a/python/ql/lib/semmle/python/regexp/internal/RegExpTracking.qll
+++ b/python/ql/lib/semmle/python/regexp/internal/RegExpTracking.qll
@@ -19,11 +19,13 @@ DataFlow::LocalSourceNode strStart() { result.asExpr() instanceof StrConst }
private import semmle.python.regex as Regex
-/** Gets a node where regular expressions that flow to the node are used. */
-DataFlow::Node regSink() {
- result = any(Concepts::RegexExecution exec).getRegex()
- or
- result instanceof Concepts::RegExpInterpretation
+/** A node where regular expressions that flow to the node are used. */
+class RegExpSink extends DataFlow::Node {
+ RegExpSink() {
+ this = any(Concepts::RegexExecution exec).getRegex()
+ or
+ this instanceof Concepts::RegExpInterpretation
+ }
}
/**
@@ -32,7 +34,7 @@ DataFlow::Node regSink() {
*/
private DataFlow::TypeTrackingNode backwards(DataFlow::TypeBackTracker t) {
t.start() and
- result = regSink().getALocalSource()
+ result = any(RegExpSink sink).getALocalSource()
or
exists(DataFlow::TypeBackTracker t2 | result = backwards(t2).backtrack(t2, t))
}
@@ -69,7 +71,6 @@ private DataFlow::TypeTrackingNode regexTracking(DataFlow::Node start, DataFlow:
/** Gets a node holding a value for the regular expression that is evaluated at `re`. */
cached
-DataFlow::Node regExpSource(DataFlow::Node re) {
- re = regSink() and
+DataFlow::Node regExpSource(RegExpSink re) {
regexTracking(result, DataFlow::TypeTracker::end()).flowsTo(re)
}
From 3d208c0a6271967178f69c6b96c66c714684368d Mon Sep 17 00:00:00 2001
From: Asger F
Date: Mon, 17 Apr 2023 11:27:33 +0200
Subject: [PATCH 102/870] JS: Port Actions sources based on PR from R3x
---
javascript/ql/lib/javascript.qll | 1 +
.../javascript/frameworks/ActionsLib.qll | 40 +++++++++++++++++++
2 files changed, 41 insertions(+)
create mode 100644 javascript/ql/lib/semmle/javascript/frameworks/ActionsLib.qll
diff --git a/javascript/ql/lib/javascript.qll b/javascript/ql/lib/javascript.qll
index 53bb91797aa..ed38db6550e 100644
--- a/javascript/ql/lib/javascript.qll
+++ b/javascript/ql/lib/javascript.qll
@@ -67,6 +67,7 @@ import semmle.javascript.YAML
import semmle.javascript.dataflow.DataFlow
import semmle.javascript.dataflow.TaintTracking
import semmle.javascript.dataflow.TypeInference
+import semmle.javascript.frameworks.ActionsLib
import semmle.javascript.frameworks.Angular2
import semmle.javascript.frameworks.AngularJS
import semmle.javascript.frameworks.Anser
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/ActionsLib.qll b/javascript/ql/lib/semmle/javascript/frameworks/ActionsLib.qll
new file mode 100644
index 00000000000..970c7d20ac5
--- /dev/null
+++ b/javascript/ql/lib/semmle/javascript/frameworks/ActionsLib.qll
@@ -0,0 +1,40 @@
+private import javascript
+
+private API::Node payload() {
+ result = API::moduleImport("@actions/github").getMember("context").getMember("payload")
+}
+
+private API::Node workflowRun() { result = payload().getMember("workflow_run") }
+
+private API::Node commitObj() {
+ result = workflowRun().getMember("head_commit")
+ or
+ result = payload().getMember("commits").getAMember()
+}
+
+private API::Node pullRequest() {
+ result = payload().getMember("pull_request")
+ or
+ result = commitObj().getMember("pull_requests").getAMember()
+}
+
+private API::Node taintSource() {
+ result = pullRequest().getMember("head").getMember(["ref", "label"])
+ or
+ result =
+ [pullRequest(), payload().getMember(["discussion", "issue"])].getMember(["title", "body"])
+ or
+ result = payload().getMember(["review", "review_comment", "comment"]).getMember("body")
+ or
+ result = workflowRun().getMember("head_branch")
+ or
+ result = commitObj().getMember("message")
+ or
+ result = commitObj().getMember("author").getMember(["name", "email"])
+}
+
+private class GitHubActionsSource extends RemoteFlowSource {
+ GitHubActionsSource() { this = taintSource().asSource() }
+
+ override string getSourceType() { result = "GitHub Actions input" }
+}
From 18f8c69261060762e15681c9f34a44372e8804b6 Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Mon, 1 May 2023 10:49:51 +0200
Subject: [PATCH 103/870] satisfy the signature of `HostnameRegexpSig`, which
doesn't understand RegExpSink
---
.../semmle/python/security/regexp/HostnameRegex.qll | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/python/ql/lib/semmle/python/security/regexp/HostnameRegex.qll b/python/ql/lib/semmle/python/security/regexp/HostnameRegex.qll
index e7ec80ac804..c6636092b52 100644
--- a/python/ql/lib/semmle/python/security/regexp/HostnameRegex.qll
+++ b/python/ql/lib/semmle/python/security/regexp/HostnameRegex.qll
@@ -12,7 +12,18 @@ private import codeql.regex.HostnameRegexp as Shared
private module Impl implements Shared::HostnameRegexpSig {
class DataFlowNode = DataFlow::Node;
- class RegExpPatternSource = Regexp::RegExpPatternSource;
+ class RegExpPatternSource extends DataFlow::Node instanceof Regexp::RegExpPatternSource {
+ /**
+ * Gets a node where the pattern of this node is parsed as a part of
+ * a regular expression.
+ */
+ DataFlow::Node getAParse() { result = super.getAParse() }
+
+ /**
+ * Gets the root term of the regular expression parsed from this pattern.
+ */
+ TreeImpl::RegExpTerm getRegExpTerm() { result = super.getRegExpTerm() }
+ }
}
import Shared::Make
From cb9b01cbb7f12476df1be1a2cf2cb7397168b4e6 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Mon, 1 May 2023 10:38:09 +0200
Subject: [PATCH 104/870] JS: Port new sources based on comment from JarLob
---
.../ql/lib/semmle/javascript/frameworks/ActionsLib.qll | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/ActionsLib.qll b/javascript/ql/lib/semmle/javascript/frameworks/ActionsLib.qll
index 970c7d20ac5..c97cff73dfc 100644
--- a/javascript/ql/lib/semmle/javascript/frameworks/ActionsLib.qll
+++ b/javascript/ql/lib/semmle/javascript/frameworks/ActionsLib.qll
@@ -26,11 +26,13 @@ private API::Node taintSource() {
or
result = payload().getMember(["review", "review_comment", "comment"]).getMember("body")
or
- result = workflowRun().getMember("head_branch")
+ result = workflowRun().getMember(["head_branch", "display_title"])
+ or
+ result = workflowRun().getMember("head_repository").getMember("description")
or
result = commitObj().getMember("message")
or
- result = commitObj().getMember("author").getMember(["name", "email"])
+ result = commitObj().getMember(["author", "committer"]).getMember(["name", "email"])
}
private class GitHubActionsSource extends RemoteFlowSource {
From 0497e60ce2c8efe07f8961146897a259a742903e Mon Sep 17 00:00:00 2001
From: Asger F
Date: Mon, 1 May 2023 11:05:59 +0200
Subject: [PATCH 105/870] JS: Model actions/exec
---
.../semmle/javascript/frameworks/ActionsLib.qll | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/ActionsLib.qll b/javascript/ql/lib/semmle/javascript/frameworks/ActionsLib.qll
index c97cff73dfc..74b65ee5adc 100644
--- a/javascript/ql/lib/semmle/javascript/frameworks/ActionsLib.qll
+++ b/javascript/ql/lib/semmle/javascript/frameworks/ActionsLib.qll
@@ -40,3 +40,17 @@ private class GitHubActionsSource extends RemoteFlowSource {
override string getSourceType() { result = "GitHub Actions input" }
}
+
+private class ExecActionsCall extends SystemCommandExecution, DataFlow::CallNode {
+ ExecActionsCall() {
+ this = API::moduleImport("@actions/exec").getMember(["exec", "getExecOutput"]).getACall()
+ }
+
+ override DataFlow::Node getACommandArgument() { result = this.getArgument(0) }
+
+ override DataFlow::Node getArgumentList() { result = this.getArgument(1) }
+
+ override DataFlow::Node getOptionsArg() { result = this.getArgument(2) }
+
+ override predicate isSync() { none() }
+}
From cb95dbfa14124e27ebb6dce58cb6dc30c61784fa Mon Sep 17 00:00:00 2001
From: Asger F
Date: Mon, 1 May 2023 11:06:13 +0200
Subject: [PATCH 106/870] JS: Add tests
---
.../CommandInjection.expected | 24 +++++++++++++++++++
.../CWE-078/CommandInjection/actions.js | 22 +++++++++++++++++
.../CodeInjection/CodeInjection.expected | 5 ++++
.../HeuristicSourceCodeInjection.expected | 4 ++++
.../Security/CWE-094/CodeInjection/actions.js | 8 +++++++
5 files changed, 63 insertions(+)
create mode 100644 javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/actions.js
create mode 100644 javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/actions.js
diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected
index 3d18fcf4b2e..fb8bc60e673 100644
--- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected
@@ -1,4 +1,16 @@
nodes
+| actions.js:8:9:8:57 | title |
+| actions.js:8:17:8:57 | github. ... t.title |
+| actions.js:8:17:8:57 | github. ... t.title |
+| actions.js:9:8:9:22 | `echo ${title}` |
+| actions.js:9:8:9:22 | `echo ${title}` |
+| actions.js:9:16:9:20 | title |
+| actions.js:18:9:18:63 | head_ref |
+| actions.js:18:20:18:63 | github. ... ead.ref |
+| actions.js:18:20:18:63 | github. ... ead.ref |
+| actions.js:19:14:19:31 | `echo ${head_ref}` |
+| actions.js:19:14:19:31 | `echo ${head_ref}` |
+| actions.js:19:22:19:29 | head_ref |
| child_process-test.js:6:9:6:49 | cmd |
| child_process-test.js:6:15:6:38 | url.par ... , true) |
| child_process-test.js:6:15:6:44 | url.par ... ).query |
@@ -179,6 +191,16 @@ nodes
| third-party-command-injection.js:6:21:6:27 | command |
| third-party-command-injection.js:6:21:6:27 | command |
edges
+| actions.js:8:9:8:57 | title | actions.js:9:16:9:20 | title |
+| actions.js:8:17:8:57 | github. ... t.title | actions.js:8:9:8:57 | title |
+| actions.js:8:17:8:57 | github. ... t.title | actions.js:8:9:8:57 | title |
+| actions.js:9:16:9:20 | title | actions.js:9:8:9:22 | `echo ${title}` |
+| actions.js:9:16:9:20 | title | actions.js:9:8:9:22 | `echo ${title}` |
+| actions.js:18:9:18:63 | head_ref | actions.js:19:22:19:29 | head_ref |
+| actions.js:18:20:18:63 | github. ... ead.ref | actions.js:18:9:18:63 | head_ref |
+| actions.js:18:20:18:63 | github. ... ead.ref | actions.js:18:9:18:63 | head_ref |
+| actions.js:19:22:19:29 | head_ref | actions.js:19:14:19:31 | `echo ${head_ref}` |
+| actions.js:19:22:19:29 | head_ref | actions.js:19:14:19:31 | `echo ${head_ref}` |
| child_process-test.js:6:9:6:49 | cmd | child_process-test.js:17:13:17:15 | cmd |
| child_process-test.js:6:9:6:49 | cmd | child_process-test.js:17:13:17:15 | cmd |
| child_process-test.js:6:9:6:49 | cmd | child_process-test.js:18:17:18:19 | cmd |
@@ -344,6 +366,8 @@ edges
| third-party-command-injection.js:5:20:5:26 | command | third-party-command-injection.js:6:21:6:27 | command |
| third-party-command-injection.js:5:20:5:26 | command | third-party-command-injection.js:6:21:6:27 | command |
#select
+| actions.js:9:8:9:22 | `echo ${title}` | actions.js:8:17:8:57 | github. ... t.title | actions.js:9:8:9:22 | `echo ${title}` | This command line depends on a $@. | actions.js:8:17:8:57 | github. ... t.title | user-provided value |
+| actions.js:19:14:19:31 | `echo ${head_ref}` | actions.js:18:20:18:63 | github. ... ead.ref | actions.js:19:14:19:31 | `echo ${head_ref}` | This command line depends on a $@. | actions.js:18:20:18:63 | github. ... ead.ref | user-provided value |
| child_process-test.js:17:13:17:15 | cmd | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:17:13:17:15 | cmd | This command line depends on a $@. | child_process-test.js:6:25:6:31 | req.url | user-provided value |
| child_process-test.js:18:17:18:19 | cmd | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:18:17:18:19 | cmd | This command line depends on a $@. | child_process-test.js:6:25:6:31 | req.url | user-provided value |
| child_process-test.js:19:17:19:19 | cmd | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:19:17:19:19 | cmd | This command line depends on a $@. | child_process-test.js:6:25:6:31 | req.url | user-provided value |
diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/actions.js b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/actions.js
new file mode 100644
index 00000000000..1cfea0118bc
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/actions.js
@@ -0,0 +1,22 @@
+const github = require('@actions/github');
+const aexec = require('@actions/exec');
+const { exec } = require('child_process');
+
+// function to echo title
+function echo_title() {
+ // get the title from the event pull request
+ const title = github.context.payload.pull_request.title;
+ exec(`echo ${title}`, (err, stdout, stderr) => { // NOT OK
+ if (err) {
+ return;
+ }
+ });
+}
+
+// function which passes the issue title into an exec
+function exec_head_ref() {
+ const head_ref = github.context.payload.pull_request.head.ref;
+ aexec.exec(`echo ${head_ref}`).then((res) => { // NOT OK
+ console.log(res);
+ });
+}
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected
index 545b9d71d7c..ddfe2c78f07 100644
--- a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected
@@ -13,6 +13,9 @@ nodes
| NoSQLCodeInjection.js:22:36:22:43 | req.body |
| NoSQLCodeInjection.js:22:36:22:43 | req.body |
| NoSQLCodeInjection.js:22:36:22:48 | req.body.name |
+| actions.js:5:10:5:50 | github. ... message |
+| actions.js:5:10:5:50 | github. ... message |
+| actions.js:5:10:5:50 | github. ... message |
| angularjs.js:10:22:10:36 | location.search |
| angularjs.js:10:22:10:36 | location.search |
| angularjs.js:10:22:10:36 | location.search |
@@ -191,6 +194,7 @@ edges
| NoSQLCodeInjection.js:22:36:22:43 | req.body | NoSQLCodeInjection.js:22:36:22:48 | req.body.name |
| NoSQLCodeInjection.js:22:36:22:48 | req.body.name | NoSQLCodeInjection.js:22:24:22:48 | "name = ... dy.name |
| NoSQLCodeInjection.js:22:36:22:48 | req.body.name | NoSQLCodeInjection.js:22:24:22:48 | "name = ... dy.name |
+| actions.js:5:10:5:50 | github. ... message | actions.js:5:10:5:50 | github. ... message |
| angularjs.js:10:22:10:36 | location.search | angularjs.js:10:22:10:36 | location.search |
| angularjs.js:13:23:13:37 | location.search | angularjs.js:13:23:13:37 | location.search |
| angularjs.js:16:28:16:42 | location.search | angularjs.js:16:28:16:42 | location.search |
@@ -306,6 +310,7 @@ edges
| NoSQLCodeInjection.js:18:24:18:37 | req.body.query | NoSQLCodeInjection.js:18:24:18:31 | req.body | NoSQLCodeInjection.js:18:24:18:37 | req.body.query | This code execution depends on a $@. | NoSQLCodeInjection.js:18:24:18:31 | req.body | user-provided value |
| NoSQLCodeInjection.js:19:24:19:48 | "name = ... dy.name | NoSQLCodeInjection.js:19:36:19:43 | req.body | NoSQLCodeInjection.js:19:24:19:48 | "name = ... dy.name | This code execution depends on a $@. | NoSQLCodeInjection.js:19:36:19:43 | req.body | user-provided value |
| NoSQLCodeInjection.js:22:24:22:48 | "name = ... dy.name | NoSQLCodeInjection.js:22:36:22:43 | req.body | NoSQLCodeInjection.js:22:24:22:48 | "name = ... dy.name | This code execution depends on a $@. | NoSQLCodeInjection.js:22:36:22:43 | req.body | user-provided value |
+| actions.js:5:10:5:50 | github. ... message | actions.js:5:10:5:50 | github. ... message | actions.js:5:10:5:50 | github. ... message | This code execution depends on a $@. | actions.js:5:10:5:50 | github. ... message | user-provided value |
| angularjs.js:10:22:10:36 | location.search | angularjs.js:10:22:10:36 | location.search | angularjs.js:10:22:10:36 | location.search | This code execution depends on a $@. | angularjs.js:10:22:10:36 | location.search | user-provided value |
| angularjs.js:13:23:13:37 | location.search | angularjs.js:13:23:13:37 | location.search | angularjs.js:13:23:13:37 | location.search | This code execution depends on a $@. | angularjs.js:13:23:13:37 | location.search | user-provided value |
| angularjs.js:16:28:16:42 | location.search | angularjs.js:16:28:16:42 | location.search | angularjs.js:16:28:16:42 | location.search | This code execution depends on a $@. | angularjs.js:16:28:16:42 | location.search | user-provided value |
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected
index 0c4f02406d6..64620c6d3bf 100644
--- a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected
@@ -13,6 +13,9 @@ nodes
| NoSQLCodeInjection.js:22:36:22:43 | req.body |
| NoSQLCodeInjection.js:22:36:22:43 | req.body |
| NoSQLCodeInjection.js:22:36:22:48 | req.body.name |
+| actions.js:5:10:5:50 | github. ... message |
+| actions.js:5:10:5:50 | github. ... message |
+| actions.js:5:10:5:50 | github. ... message |
| angularjs.js:10:22:10:36 | location.search |
| angularjs.js:10:22:10:36 | location.search |
| angularjs.js:10:22:10:36 | location.search |
@@ -195,6 +198,7 @@ edges
| NoSQLCodeInjection.js:22:36:22:43 | req.body | NoSQLCodeInjection.js:22:36:22:48 | req.body.name |
| NoSQLCodeInjection.js:22:36:22:48 | req.body.name | NoSQLCodeInjection.js:22:24:22:48 | "name = ... dy.name |
| NoSQLCodeInjection.js:22:36:22:48 | req.body.name | NoSQLCodeInjection.js:22:24:22:48 | "name = ... dy.name |
+| actions.js:5:10:5:50 | github. ... message | actions.js:5:10:5:50 | github. ... message |
| angularjs.js:10:22:10:36 | location.search | angularjs.js:10:22:10:36 | location.search |
| angularjs.js:13:23:13:37 | location.search | angularjs.js:13:23:13:37 | location.search |
| angularjs.js:16:28:16:42 | location.search | angularjs.js:16:28:16:42 | location.search |
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/actions.js b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/actions.js
new file mode 100644
index 00000000000..ee49ec3888e
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/actions.js
@@ -0,0 +1,8 @@
+const core = require('@actions/core');
+const github = require('@actions/github');
+
+function test() {
+ eval(github.context.payload.commits[1].message); // NOT OK
+ eval(core.getInput('numbers')); // NOT OK
+ eval(core.getMultilineInput('numbers').join('\n')); // NOT OK
+}
From 08785a4063f2638100675aae4fe1c6b151088f3e Mon Sep 17 00:00:00 2001
From: Asger F
Date: Mon, 1 May 2023 11:03:24 +0200
Subject: [PATCH 107/870] JS: Add sources from actions/core
---
.../semmle/javascript/frameworks/ActionsLib.qll | 3 +++
.../CWE-094/CodeInjection/CodeInjection.expected | 14 ++++++++++++++
.../HeuristicSourceCodeInjection.expected | 12 ++++++++++++
3 files changed, 29 insertions(+)
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/ActionsLib.qll b/javascript/ql/lib/semmle/javascript/frameworks/ActionsLib.qll
index 74b65ee5adc..8f10144269c 100644
--- a/javascript/ql/lib/semmle/javascript/frameworks/ActionsLib.qll
+++ b/javascript/ql/lib/semmle/javascript/frameworks/ActionsLib.qll
@@ -33,6 +33,9 @@ private API::Node taintSource() {
result = commitObj().getMember("message")
or
result = commitObj().getMember(["author", "committer"]).getMember(["name", "email"])
+ or
+ result =
+ API::moduleImport("@actions/core").getMember(["getInput", "getMultilineInput"]).getReturn()
}
private class GitHubActionsSource extends RemoteFlowSource {
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected
index ddfe2c78f07..181b4d91d34 100644
--- a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected
@@ -16,6 +16,13 @@ nodes
| actions.js:5:10:5:50 | github. ... message |
| actions.js:5:10:5:50 | github. ... message |
| actions.js:5:10:5:50 | github. ... message |
+| actions.js:6:10:6:33 | core.ge ... mbers') |
+| actions.js:6:10:6:33 | core.ge ... mbers') |
+| actions.js:6:10:6:33 | core.ge ... mbers') |
+| actions.js:7:10:7:42 | core.ge ... mbers') |
+| actions.js:7:10:7:42 | core.ge ... mbers') |
+| actions.js:7:10:7:53 | core.ge ... n('\\n') |
+| actions.js:7:10:7:53 | core.ge ... n('\\n') |
| angularjs.js:10:22:10:36 | location.search |
| angularjs.js:10:22:10:36 | location.search |
| angularjs.js:10:22:10:36 | location.search |
@@ -195,6 +202,11 @@ edges
| NoSQLCodeInjection.js:22:36:22:48 | req.body.name | NoSQLCodeInjection.js:22:24:22:48 | "name = ... dy.name |
| NoSQLCodeInjection.js:22:36:22:48 | req.body.name | NoSQLCodeInjection.js:22:24:22:48 | "name = ... dy.name |
| actions.js:5:10:5:50 | github. ... message | actions.js:5:10:5:50 | github. ... message |
+| actions.js:6:10:6:33 | core.ge ... mbers') | actions.js:6:10:6:33 | core.ge ... mbers') |
+| actions.js:7:10:7:42 | core.ge ... mbers') | actions.js:7:10:7:53 | core.ge ... n('\\n') |
+| actions.js:7:10:7:42 | core.ge ... mbers') | actions.js:7:10:7:53 | core.ge ... n('\\n') |
+| actions.js:7:10:7:42 | core.ge ... mbers') | actions.js:7:10:7:53 | core.ge ... n('\\n') |
+| actions.js:7:10:7:42 | core.ge ... mbers') | actions.js:7:10:7:53 | core.ge ... n('\\n') |
| angularjs.js:10:22:10:36 | location.search | angularjs.js:10:22:10:36 | location.search |
| angularjs.js:13:23:13:37 | location.search | angularjs.js:13:23:13:37 | location.search |
| angularjs.js:16:28:16:42 | location.search | angularjs.js:16:28:16:42 | location.search |
@@ -311,6 +323,8 @@ edges
| NoSQLCodeInjection.js:19:24:19:48 | "name = ... dy.name | NoSQLCodeInjection.js:19:36:19:43 | req.body | NoSQLCodeInjection.js:19:24:19:48 | "name = ... dy.name | This code execution depends on a $@. | NoSQLCodeInjection.js:19:36:19:43 | req.body | user-provided value |
| NoSQLCodeInjection.js:22:24:22:48 | "name = ... dy.name | NoSQLCodeInjection.js:22:36:22:43 | req.body | NoSQLCodeInjection.js:22:24:22:48 | "name = ... dy.name | This code execution depends on a $@. | NoSQLCodeInjection.js:22:36:22:43 | req.body | user-provided value |
| actions.js:5:10:5:50 | github. ... message | actions.js:5:10:5:50 | github. ... message | actions.js:5:10:5:50 | github. ... message | This code execution depends on a $@. | actions.js:5:10:5:50 | github. ... message | user-provided value |
+| actions.js:6:10:6:33 | core.ge ... mbers') | actions.js:6:10:6:33 | core.ge ... mbers') | actions.js:6:10:6:33 | core.ge ... mbers') | This code execution depends on a $@. | actions.js:6:10:6:33 | core.ge ... mbers') | user-provided value |
+| actions.js:7:10:7:53 | core.ge ... n('\\n') | actions.js:7:10:7:42 | core.ge ... mbers') | actions.js:7:10:7:53 | core.ge ... n('\\n') | This code execution depends on a $@. | actions.js:7:10:7:42 | core.ge ... mbers') | user-provided value |
| angularjs.js:10:22:10:36 | location.search | angularjs.js:10:22:10:36 | location.search | angularjs.js:10:22:10:36 | location.search | This code execution depends on a $@. | angularjs.js:10:22:10:36 | location.search | user-provided value |
| angularjs.js:13:23:13:37 | location.search | angularjs.js:13:23:13:37 | location.search | angularjs.js:13:23:13:37 | location.search | This code execution depends on a $@. | angularjs.js:13:23:13:37 | location.search | user-provided value |
| angularjs.js:16:28:16:42 | location.search | angularjs.js:16:28:16:42 | location.search | angularjs.js:16:28:16:42 | location.search | This code execution depends on a $@. | angularjs.js:16:28:16:42 | location.search | user-provided value |
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected
index 64620c6d3bf..841b942f82a 100644
--- a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected
@@ -16,6 +16,13 @@ nodes
| actions.js:5:10:5:50 | github. ... message |
| actions.js:5:10:5:50 | github. ... message |
| actions.js:5:10:5:50 | github. ... message |
+| actions.js:6:10:6:33 | core.ge ... mbers') |
+| actions.js:6:10:6:33 | core.ge ... mbers') |
+| actions.js:6:10:6:33 | core.ge ... mbers') |
+| actions.js:7:10:7:42 | core.ge ... mbers') |
+| actions.js:7:10:7:42 | core.ge ... mbers') |
+| actions.js:7:10:7:53 | core.ge ... n('\\n') |
+| actions.js:7:10:7:53 | core.ge ... n('\\n') |
| angularjs.js:10:22:10:36 | location.search |
| angularjs.js:10:22:10:36 | location.search |
| angularjs.js:10:22:10:36 | location.search |
@@ -199,6 +206,11 @@ edges
| NoSQLCodeInjection.js:22:36:22:48 | req.body.name | NoSQLCodeInjection.js:22:24:22:48 | "name = ... dy.name |
| NoSQLCodeInjection.js:22:36:22:48 | req.body.name | NoSQLCodeInjection.js:22:24:22:48 | "name = ... dy.name |
| actions.js:5:10:5:50 | github. ... message | actions.js:5:10:5:50 | github. ... message |
+| actions.js:6:10:6:33 | core.ge ... mbers') | actions.js:6:10:6:33 | core.ge ... mbers') |
+| actions.js:7:10:7:42 | core.ge ... mbers') | actions.js:7:10:7:53 | core.ge ... n('\\n') |
+| actions.js:7:10:7:42 | core.ge ... mbers') | actions.js:7:10:7:53 | core.ge ... n('\\n') |
+| actions.js:7:10:7:42 | core.ge ... mbers') | actions.js:7:10:7:53 | core.ge ... n('\\n') |
+| actions.js:7:10:7:42 | core.ge ... mbers') | actions.js:7:10:7:53 | core.ge ... n('\\n') |
| angularjs.js:10:22:10:36 | location.search | angularjs.js:10:22:10:36 | location.search |
| angularjs.js:13:23:13:37 | location.search | angularjs.js:13:23:13:37 | location.search |
| angularjs.js:16:28:16:42 | location.search | angularjs.js:16:28:16:42 | location.search |
From 5eaaa7e07410d2d61e2ed0c9990fd9b3f1c87895 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Mon, 1 May 2023 11:42:55 +0200
Subject: [PATCH 108/870] JS: Add qldoc
---
javascript/ql/lib/semmle/javascript/frameworks/ActionsLib.qll | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/ActionsLib.qll b/javascript/ql/lib/semmle/javascript/frameworks/ActionsLib.qll
index 8f10144269c..2b0948cb721 100644
--- a/javascript/ql/lib/semmle/javascript/frameworks/ActionsLib.qll
+++ b/javascript/ql/lib/semmle/javascript/frameworks/ActionsLib.qll
@@ -1,3 +1,7 @@
+/**
+ * Contains models for `@actions/core` related libraries.
+ */
+
private import javascript
private API::Node payload() {
From e65ff6854786bac963024bb95180efdb91a2867d Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Mon, 1 May 2023 14:51:15 +0200
Subject: [PATCH 109/870] python: update debug queries
---
.../test/experimental/dataflow/testConfig.qll | 2 --
.../experimental/dataflow/testTaintConfig.qll | 2 --
.../meta/debug/InlineTaintTestPaths.ql | 28 +++++++++++++------
.../meta/debug/dataflowTestPaths.ql | 27 ++++++++++++------
4 files changed, 38 insertions(+), 21 deletions(-)
diff --git a/python/ql/test/experimental/dataflow/testConfig.qll b/python/ql/test/experimental/dataflow/testConfig.qll
index addbeefeebf..ab5f125d898 100644
--- a/python/ql/test/experimental/dataflow/testConfig.qll
+++ b/python/ql/test/experimental/dataflow/testConfig.qll
@@ -46,6 +46,4 @@ class TestConfiguration extends DataFlow::Configuration {
}
override predicate isBarrierIn(DataFlow::Node node) { this.isSource(node) }
-
- override int explorationLimit() { result = 5 }
}
diff --git a/python/ql/test/experimental/dataflow/testTaintConfig.qll b/python/ql/test/experimental/dataflow/testTaintConfig.qll
index 13d0620be92..09496895c9a 100644
--- a/python/ql/test/experimental/dataflow/testTaintConfig.qll
+++ b/python/ql/test/experimental/dataflow/testTaintConfig.qll
@@ -46,6 +46,4 @@ class TestConfiguration extends TaintTracking::Configuration {
}
override predicate isSanitizerIn(DataFlow::Node node) { this.isSource(node) }
-
- override int explorationLimit() { result = 5 }
}
diff --git a/python/ql/test/experimental/meta/debug/InlineTaintTestPaths.ql b/python/ql/test/experimental/meta/debug/InlineTaintTestPaths.ql
index 54323acf64b..8e7595fbbb3 100644
--- a/python/ql/test/experimental/meta/debug/InlineTaintTestPaths.ql
+++ b/python/ql/test/experimental/meta/debug/InlineTaintTestPaths.ql
@@ -9,17 +9,29 @@
// 3. if necessary, look at partial paths by (un)commenting appropriate lines
import python
import semmle.python.dataflow.new.DataFlow
+import semmle.python.dataflow.new.TaintTracking
import experimental.meta.InlineTaintTest::Conf
-// import DataFlow::PartialPathGraph
-import DataFlow::PathGraph
-class Conf extends TestTaintTrackingConfiguration {
- // override int explorationLimit() { result = 5 }
+module Conf implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node source) {
+ any (TestTaintTrackingConfiguration c).isSource(source)
+ }
+ predicate isSink(DataFlow::Node source) {
+ any (TestTaintTrackingConfiguration c).isSink(source)
+ }
}
+int explorationLimit() { result = 5 }
-// from Conf config, DataFlow::PartialPathNode source, DataFlow::PartialPathNode sink
-// where config.hasPartialFlow(source, sink, _)
-from Conf config, DataFlow::PathNode source, DataFlow::PathNode sink
-where config.hasFlowPath(source, sink)
+module Flows = TaintTracking::Global;
+
+module FlowsPartial = Flows::FlowExploration;
+
+// import FlowsPartial::PartialPathGraph
+import Flows::PathGraph
+
+// from FlowsPartial::PartialPathNode source, FlowsPartial::PartialPathNode sink
+// where FlowsPartial::partialFlow(source, sink, _)
+from Flows::PathNode source, Flows::PathNode sink
+where Flows::flowPath(source, sink)
select sink.getNode(), source, sink, "This node receives taint from $@.", source.getNode(),
"this source"
diff --git a/python/ql/test/experimental/meta/debug/dataflowTestPaths.ql b/python/ql/test/experimental/meta/debug/dataflowTestPaths.ql
index 545bfbab1a2..c1cb0ff13c8 100644
--- a/python/ql/test/experimental/meta/debug/dataflowTestPaths.ql
+++ b/python/ql/test/experimental/meta/debug/dataflowTestPaths.ql
@@ -10,16 +10,25 @@
import python
import semmle.python.dataflow.new.DataFlow
import experimental.dataflow.testConfig
-// import DataFlow::PartialPathGraph
-import DataFlow::PathGraph
-class Conf extends TestConfiguration {
- override int explorationLimit() { result = 5 }
+module Conf implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node source) { any(TestConfiguration c).isSource(source) }
+
+ predicate isSink(DataFlow::Node source) { any(TestConfiguration c).isSink(source) }
}
-// from Conf config, DataFlow::PartialPathNode source, DataFlow::PartialPathNode sink
-// where config.hasPartialFlow(source, sink, _)
-from Conf config, DataFlow::PathNode source, DataFlow::PathNode sink
-where config.hasFlowPath(source, sink)
-select sink.getNode(), source, sink, "This node receives taint from $@.", source.getNode(),
+int explorationLimit() { result = 5 }
+
+module Flows = DataFlow::Global;
+
+module FlowsPartial = Flows::FlowExploration;
+
+// import FlowsPartial::PartialPathGraph
+import Flows::PathGraph
+
+// from FlowsPartial::PartialPathNode source, FlowsPartial::PartialPathNode sink
+// where FlowsPartial::partialFlow(source, sink, _)
+from Flows::PathNode source, Flows::PathNode sink
+where Flows::flowPath(source, sink)
+select sink.getNode(), source, sink, "This node receives flow from $@.", source.getNode(),
"this source"
From 0d991574ec7d87f671b39182679d19575ee20d03 Mon Sep 17 00:00:00 2001
From: tyage
Date: Tue, 2 May 2023 12:00:42 +0900
Subject: [PATCH 110/870] Fix typo in test
---
.../NPM/src/node_modules/parent-modue/main.js | 1 -
.../parent-modue/sub-module/main.js | 1 -
.../src/node_modules/parent-module/main.js | 1 +
.../package.json | 0
.../parent-module/sub-module/main.js | 1 +
.../sub-module/package.json | 0
.../ql/test/library-tests/NPM/tests.expected | 20 ++++++++++---------
7 files changed, 13 insertions(+), 11 deletions(-)
delete mode 100644 javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/main.js
delete mode 100644 javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/sub-module/main.js
create mode 100644 javascript/ql/test/library-tests/NPM/src/node_modules/parent-module/main.js
rename javascript/ql/test/library-tests/NPM/src/node_modules/{parent-modue => parent-module}/package.json (100%)
create mode 100644 javascript/ql/test/library-tests/NPM/src/node_modules/parent-module/sub-module/main.js
rename javascript/ql/test/library-tests/NPM/src/node_modules/{parent-modue => parent-module}/sub-module/package.json (100%)
diff --git a/javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/main.js b/javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/main.js
deleted file mode 100644
index 8ba4196fbca..00000000000
--- a/javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/main.js
+++ /dev/null
@@ -1 +0,0 @@
-export default "parent";
diff --git a/javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/sub-module/main.js b/javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/sub-module/main.js
deleted file mode 100644
index cfc0568dea7..00000000000
--- a/javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/sub-module/main.js
+++ /dev/null
@@ -1 +0,0 @@
-export default "sub";
diff --git a/javascript/ql/test/library-tests/NPM/src/node_modules/parent-module/main.js b/javascript/ql/test/library-tests/NPM/src/node_modules/parent-module/main.js
new file mode 100644
index 00000000000..8871fb179ac
--- /dev/null
+++ b/javascript/ql/test/library-tests/NPM/src/node_modules/parent-module/main.js
@@ -0,0 +1 @@
+module.exports = "parent";
diff --git a/javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/package.json b/javascript/ql/test/library-tests/NPM/src/node_modules/parent-module/package.json
similarity index 100%
rename from javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/package.json
rename to javascript/ql/test/library-tests/NPM/src/node_modules/parent-module/package.json
diff --git a/javascript/ql/test/library-tests/NPM/src/node_modules/parent-module/sub-module/main.js b/javascript/ql/test/library-tests/NPM/src/node_modules/parent-module/sub-module/main.js
new file mode 100644
index 00000000000..0bf6641bb0a
--- /dev/null
+++ b/javascript/ql/test/library-tests/NPM/src/node_modules/parent-module/sub-module/main.js
@@ -0,0 +1 @@
+module.exports = "sub";
diff --git a/javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/sub-module/package.json b/javascript/ql/test/library-tests/NPM/src/node_modules/parent-module/sub-module/package.json
similarity index 100%
rename from javascript/ql/test/library-tests/NPM/src/node_modules/parent-modue/sub-module/package.json
rename to javascript/ql/test/library-tests/NPM/src/node_modules/parent-module/sub-module/package.json
diff --git a/javascript/ql/test/library-tests/NPM/tests.expected b/javascript/ql/test/library-tests/NPM/tests.expected
index 1262db527c9..a75c705be26 100644
--- a/javascript/ql/test/library-tests/NPM/tests.expected
+++ b/javascript/ql/test/library-tests/NPM/tests.expected
@@ -8,6 +8,8 @@ importedFile
| src/node_modules/nested/tst3.js:1:1:1:29 | require ... odule') | src/node_modules/third-party-module/fancy.js:0:0:0:0 | src/node_modules/third-party-module/fancy.js |
| src/node_modules/nested/tst3.js:2:1:2:12 | require('a') | src/node_modules/nested/node_modules/a/index.js:0:0:0:0 | src/node_modules/nested/node_modules/a/index.js |
| src/node_modules/tst2.js:1:1:1:38 | require ... cy.js') | src/node_modules/third-party-module/fancy.js:0:0:0:0 | src/node_modules/third-party-module/fancy.js |
+| src/test-submodule.js:1:1:1:24 | require ... odule") | src/node_modules/parent-module/main.js:0:0:0:0 | src/node_modules/parent-module/main.js |
+| src/test-submodule.js:2:1:2:35 | require ... odule") | src/node_modules/parent-module/sub-module/main.js:0:0:0:0 | src/node_modules/parent-module/sub-module/main.js |
| src/tst2.js:1:1:1:12 | require(".") | src/index.js:0:0:0:0 | src/index.js |
| src/tst.js:1:1:1:38 | require ... cy.js') | src/node_modules/third-party-module/fancy.js:0:0:0:0 | src/node_modules/third-party-module/fancy.js |
| src/tst.js:2:1:2:37 | require ... ckage') | src/node_modules/third-party-module/package.json:0:0:0:0 | src/node_modules/third-party-module/package.json |
@@ -16,8 +18,8 @@ importedModule
| src/node_modules/nested/tst3.js:1:1:1:29 | require ... odule') | src/node_modules/third-party-module/fancy.js:1:1:4:0 | |
| src/node_modules/nested/tst3.js:2:1:2:12 | require('a') | src/node_modules/nested/node_modules/a/index.js:1:1:1:25 | |
| src/node_modules/tst2.js:1:1:1:38 | require ... cy.js') | src/node_modules/third-party-module/fancy.js:1:1:4:0 | |
-| src/test-submodule.js:1:1:1:24 | require ... odule") | src/node_modules/parent-modue/main.js:1:1:2:0 | |
-| src/test-submodule.js:2:1:2:35 | require ... odule") | src/node_modules/parent-modue/sub-module/main.js:1:1:2:0 | |
+| src/test-submodule.js:1:1:1:24 | require ... odule") | src/node_modules/parent-module/main.js:1:1:2:0 | |
+| src/test-submodule.js:2:1:2:35 | require ... odule") | src/node_modules/parent-module/sub-module/main.js:1:1:2:0 | |
| src/tst2.js:1:1:1:12 | require(".") | src/index.js:1:1:4:0 | |
| src/tst.js:1:1:1:38 | require ... cy.js') | src/node_modules/third-party-module/fancy.js:1:1:4:0 | |
modules
@@ -31,9 +33,9 @@ modules
| src/node_modules/b | b | src/node_modules/b/lib/util.ts:1:1:2:0 | |
| src/node_modules/c | c | src/node_modules/c/src/index.js:1:1:2:0 | |
| src/node_modules/d | d | src/node_modules/d/main.js:1:1:2:0 | |
-| src/node_modules/parent-modue | parent-module | src/node_modules/parent-modue/main.js:1:1:2:0 | |
-| src/node_modules/parent-modue | parent-module | src/node_modules/parent-modue/sub-module/main.js:1:1:2:0 | |
-| src/node_modules/parent-modue/sub-module | parent-module/sub-module | src/node_modules/parent-modue/sub-module/main.js:1:1:2:0 | |
+| src/node_modules/parent-module | parent-module | src/node_modules/parent-module/main.js:1:1:2:0 | |
+| src/node_modules/parent-module | parent-module | src/node_modules/parent-module/sub-module/main.js:1:1:2:0 | |
+| src/node_modules/parent-module/sub-module | parent-module/sub-module | src/node_modules/parent-module/sub-module/main.js:1:1:2:0 | |
| src/node_modules/third-party-module | third-party-module | src/node_modules/third-party-module/fancy.js:1:1:4:0 | |
npm
| src/node_modules/third-party-module/package.json:1:1:5:1 | {\\n "na ... y.js"\\n} | third-party-module | 23.4.0 |
@@ -42,16 +44,16 @@ getMainModule
| src/node_modules/b/package.json:1:1:4:1 | {\\n "na ... "lib"\\n} | b | src/node_modules/b/lib/index.js:1:1:2:0 | |
| src/node_modules/c/package.json:1:1:4:1 | {\\n "na ... src/"\\n} | c | src/node_modules/c/src/index.js:1:1:2:0 | |
| src/node_modules/d/package.json:1:1:4:1 | {\\n "na ... main"\\n} | d | src/node_modules/d/main.js:1:1:2:0 | |
-| src/node_modules/parent-modue/package.json:1:1:4:1 | {\\n "na ... n.js"\\n} | parent-module | src/node_modules/parent-modue/main.js:1:1:2:0 | |
-| src/node_modules/parent-modue/sub-module/package.json:1:1:3:1 | {\\n "ma ... n.js"\\n} | parent-module/sub-module | src/node_modules/parent-modue/sub-module/main.js:1:1:2:0 | |
+| src/node_modules/parent-module/package.json:1:1:4:1 | {\\n "na ... n.js"\\n} | parent-module | src/node_modules/parent-module/main.js:1:1:2:0 | |
+| src/node_modules/parent-module/sub-module/package.json:1:1:3:1 | {\\n "ma ... n.js"\\n} | parent-module/sub-module | src/node_modules/parent-module/sub-module/main.js:1:1:2:0 | |
| src/node_modules/third-party-module/package.json:1:1:5:1 | {\\n "na ... y.js"\\n} | third-party-module | src/node_modules/third-party-module/fancy.js:1:1:4:0 | |
| src/package.json:1:1:20:1 | {\\n "na ... "\\n }\\n} | test-package | src/index.js:1:1:4:0 | |
packageJson
| src/node_modules/b/package.json:1:1:4:1 | {\\n "na ... "lib"\\n} |
| src/node_modules/c/package.json:1:1:4:1 | {\\n "na ... src/"\\n} |
| src/node_modules/d/package.json:1:1:4:1 | {\\n "na ... main"\\n} |
-| src/node_modules/parent-modue/package.json:1:1:4:1 | {\\n "na ... n.js"\\n} |
-| src/node_modules/parent-modue/sub-module/package.json:1:1:3:1 | {\\n "ma ... n.js"\\n} |
+| src/node_modules/parent-module/package.json:1:1:4:1 | {\\n "na ... n.js"\\n} |
+| src/node_modules/parent-module/sub-module/package.json:1:1:3:1 | {\\n "ma ... n.js"\\n} |
| src/node_modules/third-party-module/package.json:1:1:5:1 | {\\n "na ... y.js"\\n} |
| src/package.json:1:1:20:1 | {\\n "na ... "\\n }\\n} |
dependencyInfo
From be9c8d28b5bbbe16eadaef4ba7e89bf2d1c2a92e Mon Sep 17 00:00:00 2001
From: tyage
Date: Tue, 2 May 2023 12:41:03 +0900
Subject: [PATCH 111/870] JS: drop string comparison
---
javascript/ql/lib/semmle/javascript/NPM.qll | 18 ++++++++----------
1 file changed, 8 insertions(+), 10 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/NPM.qll b/javascript/ql/lib/semmle/javascript/NPM.qll
index 53220a566b8..36297be4d23 100644
--- a/javascript/ql/lib/semmle/javascript/NPM.qll
+++ b/javascript/ql/lib/semmle/javascript/NPM.qll
@@ -19,16 +19,14 @@ class PackageJson extends JsonObject {
string getPackageName() {
result = this.getPropStringValue("name")
or
- exists(
- PackageJson parentPackage, string currentDir, string parentDir, string parentPackageName
- |
- currentDir = this.getJsonFile().getParentContainer().getAbsolutePath() and
- parentDir = parentPackage.getJsonFile().getParentContainer().getAbsolutePath() and
- parentPackageName = parentPackage.getPropStringValue("name") and
- parentDir.indexOf("node_modules") != -1 and
- currentDir != parentDir and
- currentDir.indexOf(parentDir) = 0 and
- result = parentPackageName + currentDir.suffix(parentDir.length())
+ exists(PackageJson parentPkg, Container currentDir, Container parentDir |
+ currentDir = this.getJsonFile().getParentContainer() and
+ parentDir = parentPkg.getJsonFile().getParentContainer() and
+ parentDir.getParentContainer+().getBaseName() = "node_modules" and
+ parentDir.getAChildContainer+() = currentDir and
+ result =
+ parentPkg.getPropStringValue("name") +
+ currentDir.getAbsolutePath().suffix(parentDir.getAbsolutePath().length())
)
}
From 04e393fcf8b83fe4c1dd1af4d2bce14f61fa2dc6 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Tue, 2 May 2023 11:02:58 +0200
Subject: [PATCH 112/870] JS: Change note
---
.../ql/src/change-notes/2023-05-02-github-actions-sources.md | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 ruby/ql/src/change-notes/2023-05-02-github-actions-sources.md
diff --git a/ruby/ql/src/change-notes/2023-05-02-github-actions-sources.md b/ruby/ql/src/change-notes/2023-05-02-github-actions-sources.md
new file mode 100644
index 00000000000..a9cf1339421
--- /dev/null
+++ b/ruby/ql/src/change-notes/2023-05-02-github-actions-sources.md
@@ -0,0 +1,5 @@
+---
+category: majorAnalysis
+---
+* Added taint sources from the `@actions/core` and `@actions/github` packages.
+* Added command-injection sinks from the `@actions/exec` package.
From 564bb1ccb02935f451598532608c2725a6f6c07d Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Tue, 2 May 2023 11:22:47 +0200
Subject: [PATCH 113/870] Manual fixes
---
.../org.apache.commons.net.model.yml | 48 +++++++------------
1 file changed, 18 insertions(+), 30 deletions(-)
diff --git a/java/ql/lib/ext/generated/org.apache.commons.net.model.yml b/java/ql/lib/ext/generated/org.apache.commons.net.model.yml
index 49c61eb4328..458dc493960 100644
--- a/java/ql/lib/ext/generated/org.apache.commons.net.model.yml
+++ b/java/ql/lib/ext/generated/org.apache.commons.net.model.yml
@@ -5,43 +5,31 @@ extensions:
pack: codeql/java-all
extensible: sinkModel
data:
- - ["org.apache.commons.net.ftp", "FTPClient", true, "appendFile", "(String,InputStream)", "", "Argument[this]", "open-url", "df-generated"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "appendFileStream", "(String)", "", "Argument[this]", "open-url", "df-generated"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "initiateListParsing", "()", "", "Argument[this]", "open-url", "df-generated"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "initiateListParsing", "(String)", "", "Argument[this]", "open-url", "df-generated"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "initiateListParsing", "(String,String)", "", "Argument[this]", "open-url", "df-generated"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "initiateMListParsing", "()", "", "Argument[this]", "open-url", "df-generated"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "initiateMListParsing", "(String)", "", "Argument[this]", "open-url", "df-generated"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "listDirectories", "()", "", "Argument[this]", "open-url", "df-generated"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "listDirectories", "(String)", "", "Argument[this]", "open-url", "df-generated"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "listFiles", "()", "", "Argument[this]", "open-url", "df-generated"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "listFiles", "(String)", "", "Argument[this]", "open-url", "df-generated"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "listFiles", "(String,FTPFileFilter)", "", "Argument[this]", "open-url", "df-generated"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "listNames", "()", "", "Argument[this]", "open-url", "df-generated"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "listNames", "(String)", "", "Argument[this]", "open-url", "df-generated"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "mlistDir", "()", "", "Argument[this]", "open-url", "df-generated"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "mlistDir", "(String)", "", "Argument[this]", "open-url", "df-generated"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "mlistDir", "(String,FTPFileFilter)", "", "Argument[this]", "open-url", "df-generated"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "retrieveFile", "(String,OutputStream)", "", "Argument[this]", "open-url", "df-generated"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "retrieveFileStream", "(String)", "", "Argument[this]", "open-url", "df-generated"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "storeFile", "(String,InputStream)", "", "Argument[this]", "open-url", "df-generated"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "storeFileStream", "(String)", "", "Argument[this]", "open-url", "df-generated"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "storeUniqueFile", "(InputStream)", "", "Argument[this]", "open-url", "df-generated"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "storeUniqueFile", "(String,InputStream)", "", "Argument[this]", "open-url", "df-generated"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "storeUniqueFileStream", "()", "", "Argument[this]", "open-url", "df-generated"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "storeUniqueFileStream", "(String)", "", "Argument[this]", "open-url", "df-generated"]
+ - ["org.apache.commons.net", "SocketClient", true, "connect", "(InetAddress)", "", "Argument[0]", "open-url", "manual"]
+ - ["org.apache.commons.net", "SocketClient", true, "connect", "(InetAddress,int)", "", "Argument[0]", "open-url", "manual"]
+ - ["org.apache.commons.net", "SocketClient", true, "connect", "(InetAddress,int,InetAddress,int)", "", "Argument[0]", "open-url", "manual"]
+ - ["org.apache.commons.net", "SocketClient", true, "connect", "(String)", "", "Argument[0]", "open-url", "manual"]
+ - ["org.apache.commons.net", "SocketClient", true, "connect", "(String,int)", "", "Argument[0]", "open-url", "manual"]
+ - ["org.apache.commons.net", "SocketClient", true, "connect", "(String,int,InetAddress,int)", "", "Argument[0]", "open-url", "manual"]
- ["org.apache.commons.net.util", "KeyManagerUtils", false, "createClientKeyManager", "(File,String)", "", "Argument[0]", "read-file", "df-generated"]
- ["org.apache.commons.net.util", "KeyManagerUtils", false, "createClientKeyManager", "(File,String,String)", "", "Argument[0]", "read-file", "df-generated"]
- ["org.apache.commons.net.util", "KeyManagerUtils", false, "createClientKeyManager", "(String,File,String,String,String)", "", "Argument[1]", "read-file", "df-generated"]
- - ["org.apache.commons.net", "SocketClient", true, "connect", "(String,int)", "", "Argument[this]", "open-url", "df-generated"]
- addsTo:
pack: codeql/java-all
extensible: sourceModel
data:
- - ["org.apache.commons.net.ftp", "FTPClient", true, "listNames", "()", "", "ReturnValue", "remote", "df-generated"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "listNames", "(String)", "", "ReturnValue", "remote", "df-generated"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "retrieveFile", "(String,OutputStream)", "", "Argument[1]", "remote", "df-generated"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "retrieveFileStream", "(String)", "", "ReturnValue", "remote", "df-generated"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listDirectories", "()", "", "ReturnValue", "remote", "df-manual"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listDirectories", "(String)", "", "ReturnValue", "remote", "df-manual"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listFiles", "()", "", "ReturnValue", "remote", "df-manual"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listFiles", "(String)", "", "ReturnValue", "remote", "df-manual"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listFiles", "(String,FTPFileFilter)", "", "ReturnValue", "remote", "df-manual"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listNames", "()", "", "ReturnValue", "remote", "df-manual"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listNames", "(String)", "", "ReturnValue", "remote", "df-manual"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "mlistDir", "()", "", "ReturnValue", "remote", "df-manual"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "mlistDir", "(String)", "", "ReturnValue", "remote", "df-manual"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "mlistDir", "(String,FTPFileFilter)", "", "ReturnValue", "remote", "df-manual"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "retrieveFile", "(String,OutputStream)", "", "Argument[1]", "remote", "df-manual"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "retrieveFileStream", "(String)", "", "ReturnValue", "remote", "df-manual"]
- addsTo:
pack: codeql/java-all
extensible: summaryModel
From f7f6f104d0797f25f1e753218f658d6404df643b Mon Sep 17 00:00:00 2001
From: Stephan Brandauer
Date: Tue, 2 May 2023 13:15:30 +0200
Subject: [PATCH 114/870] use NegativeEndpointType class; replace link to slack
discussion
---
.../AutomodelEndpointCharacteristics.qll | 12 ++++++---
.../AutomodelSharedCharacteristics.qll | 27 +++++++++----------
2 files changed, 20 insertions(+), 19 deletions(-)
diff --git a/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll b/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll
index 77425071792..f9639743fa3 100644
--- a/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll
@@ -21,9 +21,7 @@ module CandidatesImpl implements SharedCharacteristics::CandidateSig {
class EndpointType = AutomodelEndpointTypes::EndpointType;
- predicate isNegative(AutomodelEndpointTypes::EndpointType t) {
- t instanceof AutomodelEndpointTypes::NegativeSinkType
- }
+ class NegativeEndpointType = AutomodelEndpointTypes::NegativeSinkType;
// Sanitizers are currently not modeled in MaD. TODO: check if this has large negative impact.
predicate isSanitizer(Endpoint e, EndpointType t) { none() }
@@ -95,7 +93,13 @@ module CandidatesImpl implements SharedCharacteristics::CandidateSig {
hasMetadata(e, package, type, name, signature, input, isFinal, isStatic, isPublic,
calleeJavaDoc) and
(if isFinal = true or isStatic = true then subtypes = false else subtypes = true) and
- ext = "" and // see https://github.slack.com/archives/CP9127VUK/p1673979477496069
+ ext = "" and
+ /*
+ * "ext" will always be empty for automodeling; it's a mechanism for
+ * specifying that the model should apply for parameters that have
+ * a certain annotation.
+ */
+
provenance = "ai-generated" and
metadata =
"{" //
diff --git a/java/ql/src/Telemetry/AutomodelSharedCharacteristics.qll b/java/ql/src/Telemetry/AutomodelSharedCharacteristics.qll
index 58c46ceabd8..d0d05563105 100644
--- a/java/ql/src/Telemetry/AutomodelSharedCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelSharedCharacteristics.qll
@@ -16,6 +16,11 @@ signature module CandidateSig {
class EndpointType;
+ /**
+ * An EndpointType that denotes the absence of any sink.
+ */
+ class NegativeEndpointType extends EndpointType;
+
/** Gets the string representing the file+range of the endpoint. */
string getLocationString(Endpoint e);
@@ -24,12 +29,6 @@ signature module CandidateSig {
*/
predicate isKnownLabel(string label, string humanReadableLabel, EndpointType type);
- /**
- * EndpointType must have a 'negative' type that denotes the absence of any sink.
- * This predicate should hold for that type, and that type only.
- */
- predicate isNegative(EndpointType t);
-
/**
* Should hold for any endpoint that is a flow sanitizer.
*/
@@ -68,8 +67,6 @@ signature module CandidateSig {
* implementations of endpoint characteristics exported by this module.
*/
module SharedCharacteristics {
- predicate isNegative(Candidate::EndpointType e) { Candidate::isNegative(e) }
-
predicate isSink(Candidate::Endpoint e, string label) { Candidate::isSink(e, label) }
predicate isNeutral(Candidate::Endpoint e) { Candidate::isNeutral(e) }
@@ -80,7 +77,7 @@ module SharedCharacteristics {
predicate isKnownSink(Candidate::Endpoint sink, Candidate::EndpointType endpointType) {
// If the list of characteristics includes positive indicators with maximal confidence for this class, then it's a
// known sink for the class.
- not isNegative(endpointType) and
+ not endpointType instanceof Candidate::NegativeEndpointType and
exists(EndpointCharacteristic characteristic |
characteristic.appliesToEndpoint(sink) and
characteristic.hasImplications(endpointType, true, maximalConfidence())
@@ -93,7 +90,7 @@ module SharedCharacteristics {
* characteristics.
*/
predicate isSinkCandidate(Candidate::Endpoint candidateSink, Candidate::EndpointType sinkType) {
- not isNegative(sinkType) and
+ not sinkType instanceof Candidate::NegativeEndpointType and
not exists(getAReasonSinkExcluded(candidateSink, sinkType))
}
@@ -109,13 +106,13 @@ module SharedCharacteristics {
Candidate::Endpoint candidateSink, Candidate::EndpointType sinkType
) {
// An endpoint is a sink candidate if none of its characteristics give much indication whether or not it is a sink.
- not isNegative(sinkType) and
+ not sinkType instanceof Candidate::NegativeEndpointType and
result.appliesToEndpoint(candidateSink) and
// Exclude endpoints that have a characteristic that implies they're not sinks for _any_ sink type.
(
exists(float confidence |
confidence >= mediumConfidence() and
- result.hasImplications(any(Candidate::EndpointType t | isNegative(t)), true, confidence)
+ result.hasImplications(any(Candidate::NegativeEndpointType t), true, confidence)
)
or
// Exclude endpoints that have a characteristic that implies they're not sinks for _this particular_ sink type.
@@ -195,7 +192,7 @@ module SharedCharacteristics {
override predicate hasImplications(
Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
) {
- Candidate::isNegative(endpointType) and
+ endpointType instanceof Candidate::NegativeEndpointType and
isPositiveIndicator = true and
confidence = highConfidence()
}
@@ -214,7 +211,7 @@ module SharedCharacteristics {
override predicate hasImplications(
Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
) {
- Candidate::isNegative(endpointType) and
+ endpointType instanceof Candidate::NegativeEndpointType and
isPositiveIndicator = true and
confidence = mediumConfidence()
}
@@ -235,7 +232,7 @@ module SharedCharacteristics {
override predicate hasImplications(
Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
) {
- Candidate::isNegative(endpointType) and
+ endpointType instanceof Candidate::NegativeEndpointType and
isPositiveIndicator = true and
confidence = mediumConfidence()
}
From bb7e473cbf1a807f59980d09136e9ca29860d7b1 Mon Sep 17 00:00:00 2001
From: Stephan Brandauer
Date: Tue, 2 May 2023 13:22:31 +0200
Subject: [PATCH 115/870] use the name callable, instead of callee for methods,
functions
---
.../AutomodelEndpointCharacteristics.qll | 46 +++++++++----------
1 file changed, 23 insertions(+), 23 deletions(-)
diff --git a/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll b/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll
index f9639743fa3..69ebca646df 100644
--- a/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelEndpointCharacteristics.qll
@@ -88,10 +88,10 @@ module CandidatesImpl implements SharedCharacteristics::CandidateSig {
exists(
string package, string type, boolean subtypes, string name, string signature, string ext,
int input, string provenance, boolean isPublic, boolean isFinal, boolean isStatic,
- string calleeJavaDoc
+ string callableJavaDoc
|
hasMetadata(e, package, type, name, signature, input, isFinal, isStatic, isPublic,
- calleeJavaDoc) and
+ callableJavaDoc) and
(if isFinal = true or isStatic = true then subtypes = false else subtypes = true) and
ext = "" and
/*
@@ -113,7 +113,7 @@ module CandidatesImpl implements SharedCharacteristics::CandidateSig {
+ "', 'Argument index': " + input //
+ ", 'Provenance': '" + provenance //
+ "', 'Is public': " + isPublic //
- + "', 'Callee JavaDoc': '" + calleeJavaDoc.replaceAll("'", "\"") //
+ + "', 'Callable JavaDoc': '" + callableJavaDoc.replaceAll("'", "\"") //
+ "'}" // TODO: Why are the curly braces added twice?
)
}
@@ -136,28 +136,28 @@ class Endpoint = CandidatesImpl::Endpoint;
*/
predicate hasMetadata(
Endpoint n, string package, string type, string name, string signature, int input,
- boolean isFinal, boolean isStatic, boolean isPublic, string calleeJavaDoc
+ boolean isFinal, boolean isStatic, boolean isPublic, string callableJavaDoc
) {
- exists(Callable callee |
- n.asParameter() = callee.getParameter(input) and
- package = callee.getDeclaringType().getPackage().getName() and
- type = callee.getDeclaringType().getErasure().(RefType).nestedName() and
+ exists(Callable callable |
+ n.asParameter() = callable.getParameter(input) and
+ package = callable.getDeclaringType().getPackage().getName() and
+ type = callable.getDeclaringType().getErasure().(RefType).nestedName() and
(
- if callee.isStatic() or callee.getDeclaringType().isStatic()
+ if callable.isStatic() or callable.getDeclaringType().isStatic()
then isStatic = true
else isStatic = false
) and
(
- if callee.isFinal() or callee.getDeclaringType().isFinal()
+ if callable.isFinal() or callable.getDeclaringType().isFinal()
then isFinal = true
else isFinal = false
) and
- name = callee.getSourceDeclaration().getName() and
- signature = ExternalFlow::paramsString(callee) and // TODO: Why are brackets being escaped (`\[\]` vs `[]`)?
- (if callee.isPublic() then isPublic = true else isPublic = false) and
- if exists(callee.(Documentable).getJavadoc())
- then calleeJavaDoc = callee.(Documentable).getJavadoc().toString()
- else calleeJavaDoc = ""
+ name = callable.getSourceDeclaration().getName() and
+ signature = ExternalFlow::paramsString(callable) and // TODO: Why are brackets being escaped (`\[\]` vs `[]`)?
+ (if callable.isPublic() then isPublic = true else isPublic = false) and
+ if exists(callable.(Documentable).getJavadoc())
+ then callableJavaDoc = callable.(Documentable).getJavadoc().toString()
+ else callableJavaDoc = ""
)
}
@@ -168,7 +168,7 @@ predicate hasMetadata(
/**
* A negative characteristic that indicates that an is-style boolean method is unexploitable even if it is a sink.
*
- * A sink is highly unlikely to be exploitable if its callee's name starts with `is` and the callee has a boolean return
+ * A sink is highly unlikely to be exploitable if its callable's name starts with `is` and the callable has a boolean return
* type (e.g. `isDirectory`). These kinds of calls normally do only checks, and appear before the proper call that does
* the dangerous/interesting thing, so we want the latter to be modeled as the sink.
*
@@ -188,7 +188,7 @@ private class UnexploitableIsCharacteristic extends CharacteristicsImpl::NotASin
* A negative characteristic that indicates that an existence-checking boolean method is unexploitable even if it is a
* sink.
*
- * A sink is highly unlikely to be exploitable if its callee's name is `exists` or `notExists` and the callee has a
+ * A sink is highly unlikely to be exploitable if its callable's name is `exists` or `notExists` and the callable has a
* boolean return type. These kinds of calls normally do only checks, and appear before the proper call that does the
* dangerous/interesting thing, so we want the latter to be modeled as the sink.
*/
@@ -197,13 +197,13 @@ private class UnexploitableExistsCharacteristic extends CharacteristicsImpl::Not
override predicate appliesToEndpoint(Endpoint e) {
not CandidatesImpl::isSink(e, _) and
- exists(Callable callee |
- callee = e.getEnclosingCallable() and
+ exists(Callable callable |
+ callable = e.getEnclosingCallable() and
(
- callee.getName().toLowerCase() = "exists" or
- callee.getName().toLowerCase() = "notexists"
+ callable.getName().toLowerCase() = "exists" or
+ callable.getName().toLowerCase() = "notexists"
) and
- callee.getReturnType() instanceof BooleanType
+ callable.getReturnType() instanceof BooleanType
)
}
}
From f1644adca951dc8c60070ab4615a7eae3a509a0e Mon Sep 17 00:00:00 2001
From: Stephan Brandauer
Date: Tue, 2 May 2023 13:30:22 +0200
Subject: [PATCH 116/870] add internal tag to extraction queries; use 'ml' in
query ids, instead of 'ml-powered'
---
java/ql/src/Telemetry/AutomodelExtractCandidates.ql | 4 ++--
java/ql/src/Telemetry/AutomodelExtractNegativeExamples.ql | 4 ++--
java/ql/src/Telemetry/AutomodelExtractPositiveExamples.ql | 4 ++--
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/java/ql/src/Telemetry/AutomodelExtractCandidates.ql b/java/ql/src/Telemetry/AutomodelExtractCandidates.ql
index 0bf1c7e4c6c..ad900c0823b 100644
--- a/java/ql/src/Telemetry/AutomodelExtractCandidates.ql
+++ b/java/ql/src/Telemetry/AutomodelExtractCandidates.ql
@@ -8,8 +8,8 @@
* @description A query to extract automodel candidates.
* @kind problem
* @severity info
- * @id java/ml-powered/extract-automodel-candidates
- * @tags automodel extract candidates
+ * @id java/ml/extract-automodel-candidates
+ * @tags internal automodel extract candidates
*/
import AutomodelEndpointCharacteristics
diff --git a/java/ql/src/Telemetry/AutomodelExtractNegativeExamples.ql b/java/ql/src/Telemetry/AutomodelExtractNegativeExamples.ql
index 08328e9e767..3dad8a28b0a 100644
--- a/java/ql/src/Telemetry/AutomodelExtractNegativeExamples.ql
+++ b/java/ql/src/Telemetry/AutomodelExtractNegativeExamples.ql
@@ -4,8 +4,8 @@
* @name Negative examples (experimental)
* @kind problem
* @severity info
- * @id java/ml-powered/non-sink
- * @tags automodel extract examples negative
+ * @id java/ml/non-sink
+ * @tags internal automodel extract examples negative
*/
import AutomodelEndpointCharacteristics
diff --git a/java/ql/src/Telemetry/AutomodelExtractPositiveExamples.ql b/java/ql/src/Telemetry/AutomodelExtractPositiveExamples.ql
index 95e9f682508..dfc06b80a25 100644
--- a/java/ql/src/Telemetry/AutomodelExtractPositiveExamples.ql
+++ b/java/ql/src/Telemetry/AutomodelExtractPositiveExamples.ql
@@ -4,8 +4,8 @@
* @name Positive examples (experimental)
* @kind problem
* @severity info
- * @id java/ml-powered/known-sink
- * @tags automodel extract examples positive
+ * @id java/ml/known-sink
+ * @tags internal automodel extract examples positive
*/
private import java
From 34f978ed2661915cef8dd6be6439b04cd349e376 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Tue, 2 May 2023 15:29:28 +0200
Subject: [PATCH 117/870] Move manual models out of the generated directory
---
.../org.apache.commons.net.model.yml | 29 ------------------
.../lib/ext/org.apache.commons.net.model.yml | 30 +++++++++++++++++++
2 files changed, 30 insertions(+), 29 deletions(-)
create mode 100644 java/ql/lib/ext/org.apache.commons.net.model.yml
diff --git a/java/ql/lib/ext/generated/org.apache.commons.net.model.yml b/java/ql/lib/ext/generated/org.apache.commons.net.model.yml
index 458dc493960..f4807f0967b 100644
--- a/java/ql/lib/ext/generated/org.apache.commons.net.model.yml
+++ b/java/ql/lib/ext/generated/org.apache.commons.net.model.yml
@@ -1,35 +1,6 @@
# THIS FILE IS AN AUTO-GENERATED MODELS AS DATA FILE. DO NOT EDIT.
# Definitions of models for the org.apache.commons.net framework.
extensions:
- - addsTo:
- pack: codeql/java-all
- extensible: sinkModel
- data:
- - ["org.apache.commons.net", "SocketClient", true, "connect", "(InetAddress)", "", "Argument[0]", "open-url", "manual"]
- - ["org.apache.commons.net", "SocketClient", true, "connect", "(InetAddress,int)", "", "Argument[0]", "open-url", "manual"]
- - ["org.apache.commons.net", "SocketClient", true, "connect", "(InetAddress,int,InetAddress,int)", "", "Argument[0]", "open-url", "manual"]
- - ["org.apache.commons.net", "SocketClient", true, "connect", "(String)", "", "Argument[0]", "open-url", "manual"]
- - ["org.apache.commons.net", "SocketClient", true, "connect", "(String,int)", "", "Argument[0]", "open-url", "manual"]
- - ["org.apache.commons.net", "SocketClient", true, "connect", "(String,int,InetAddress,int)", "", "Argument[0]", "open-url", "manual"]
- - ["org.apache.commons.net.util", "KeyManagerUtils", false, "createClientKeyManager", "(File,String)", "", "Argument[0]", "read-file", "df-generated"]
- - ["org.apache.commons.net.util", "KeyManagerUtils", false, "createClientKeyManager", "(File,String,String)", "", "Argument[0]", "read-file", "df-generated"]
- - ["org.apache.commons.net.util", "KeyManagerUtils", false, "createClientKeyManager", "(String,File,String,String,String)", "", "Argument[1]", "read-file", "df-generated"]
- - addsTo:
- pack: codeql/java-all
- extensible: sourceModel
- data:
- - ["org.apache.commons.net.ftp", "FTPClient", true, "listDirectories", "()", "", "ReturnValue", "remote", "df-manual"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "listDirectories", "(String)", "", "ReturnValue", "remote", "df-manual"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "listFiles", "()", "", "ReturnValue", "remote", "df-manual"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "listFiles", "(String)", "", "ReturnValue", "remote", "df-manual"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "listFiles", "(String,FTPFileFilter)", "", "ReturnValue", "remote", "df-manual"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "listNames", "()", "", "ReturnValue", "remote", "df-manual"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "listNames", "(String)", "", "ReturnValue", "remote", "df-manual"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "mlistDir", "()", "", "ReturnValue", "remote", "df-manual"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "mlistDir", "(String)", "", "ReturnValue", "remote", "df-manual"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "mlistDir", "(String,FTPFileFilter)", "", "ReturnValue", "remote", "df-manual"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "retrieveFile", "(String,OutputStream)", "", "Argument[1]", "remote", "df-manual"]
- - ["org.apache.commons.net.ftp", "FTPClient", true, "retrieveFileStream", "(String)", "", "ReturnValue", "remote", "df-manual"]
- addsTo:
pack: codeql/java-all
extensible: summaryModel
diff --git a/java/ql/lib/ext/org.apache.commons.net.model.yml b/java/ql/lib/ext/org.apache.commons.net.model.yml
new file mode 100644
index 00000000000..1ea8876a4e1
--- /dev/null
+++ b/java/ql/lib/ext/org.apache.commons.net.model.yml
@@ -0,0 +1,30 @@
+extensions:
+ - addsTo:
+ pack: codeql/java-all
+ extensible: sinkModel
+ data:
+ - ["org.apache.commons.net", "SocketClient", true, "connect", "(InetAddress)", "", "Argument[0]", "open-url", "manual"]
+ - ["org.apache.commons.net", "SocketClient", true, "connect", "(InetAddress,int)", "", "Argument[0]", "open-url", "manual"]
+ - ["org.apache.commons.net", "SocketClient", true, "connect", "(InetAddress,int,InetAddress,int)", "", "Argument[0]", "open-url", "manual"]
+ - ["org.apache.commons.net", "SocketClient", true, "connect", "(String)", "", "Argument[0]", "open-url", "manual"]
+ - ["org.apache.commons.net", "SocketClient", true, "connect", "(String,int)", "", "Argument[0]", "open-url", "df-manual"]
+ - ["org.apache.commons.net", "SocketClient", true, "connect", "(String,int,InetAddress,int)", "", "Argument[0]", "open-url", "manual"]
+ - ["org.apache.commons.net.util", "KeyManagerUtils", false, "createClientKeyManager", "(File,String)", "", "Argument[0]", "read-file", "df-manual"]
+ - ["org.apache.commons.net.util", "KeyManagerUtils", false, "createClientKeyManager", "(File,String,String)", "", "Argument[0]", "read-file", "df-manual"]
+ - ["org.apache.commons.net.util", "KeyManagerUtils", false, "createClientKeyManager", "(String,File,String,String,String)", "", "Argument[1]", "read-file", "df-manual"]
+ - addsTo:
+ pack: codeql/java-all
+ extensible: sourceModel
+ data:
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listDirectories", "()", "", "ReturnValue", "remote", "df-manual"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listDirectories", "(String)", "", "ReturnValue", "remote", "df-manual"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listFiles", "()", "", "ReturnValue", "remote", "df-manual"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listFiles", "(String)", "", "ReturnValue", "remote", "df-manual"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listFiles", "(String,FTPFileFilter)", "", "ReturnValue", "remote", "df-manual"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listNames", "()", "", "ReturnValue", "remote", "df-manual"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "listNames", "(String)", "", "ReturnValue", "remote", "df-manual"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "mlistDir", "()", "", "ReturnValue", "remote", "df-manual"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "mlistDir", "(String)", "", "ReturnValue", "remote", "df-manual"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "mlistDir", "(String,FTPFileFilter)", "", "ReturnValue", "remote", "df-manual"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "retrieveFile", "(String,OutputStream)", "", "Argument[1]", "remote", "df-manual"]
+ - ["org.apache.commons.net.ftp", "FTPClient", true, "retrieveFileStream", "(String)", "", "ReturnValue", "remote", "df-manual"]
From ec44aa2597973c3e09f8737dcf7b402d84791c45 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Tue, 2 May 2023 15:31:20 +0200
Subject: [PATCH 118/870] Add change note
---
.../lib/change-notes/2023-05-02-apache-commons-net-models.md | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 java/ql/lib/change-notes/2023-05-02-apache-commons-net-models.md
diff --git a/java/ql/lib/change-notes/2023-05-02-apache-commons-net-models.md b/java/ql/lib/change-notes/2023-05-02-apache-commons-net-models.md
new file mode 100644
index 00000000000..fb918f48932
--- /dev/null
+++ b/java/ql/lib/change-notes/2023-05-02-apache-commons-net-models.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* Added models for the Apache Commons Net library,
From 1fa1a4e268becb3250953892a1754879fe00c3a5 Mon Sep 17 00:00:00 2001
From: Sim4n6
Date: Tue, 2 May 2023 15:09:16 +0100
Subject: [PATCH 119/870] Add Unicode Bypass Validation query tests and help
---
.../UnicodeBypassValidationCustomizations.qll | 30 +++++++++
.../dataflow/UnicodeBypassValidationQuery.qll | 62 ++++++++++++++++++
.../CWE-176/UnicodeBypassValidation.qhelp | 36 ++++++++++
.../CWE-176/UnicodeBypassValidation.ql | 24 +++++++
.../Security/CWE-176/escape-bypass.py | 11 ++++
.../Security/CWE-176/vulnerability-flow.png | Bin 0 -> 37706 bytes
.../CWE-176/UnicodeBypassValidation.expected | 31 +++++++++
.../CWE-176/UnicodeBypassValidation.qlref | 1 +
.../query-tests/Security/CWE-176/samples.py | 30 +++++++++
9 files changed, 225 insertions(+)
create mode 100644 python/ql/lib/semmle/python/security/dataflow/UnicodeBypassValidationCustomizations.qll
create mode 100644 python/ql/lib/semmle/python/security/dataflow/UnicodeBypassValidationQuery.qll
create mode 100644 python/ql/src/experimental/Security/CWE-176/UnicodeBypassValidation.qhelp
create mode 100644 python/ql/src/experimental/Security/CWE-176/UnicodeBypassValidation.ql
create mode 100644 python/ql/src/experimental/Security/CWE-176/escape-bypass.py
create mode 100644 python/ql/src/experimental/Security/CWE-176/vulnerability-flow.png
create mode 100644 python/ql/test/experimental/query-tests/Security/CWE-176/UnicodeBypassValidation.expected
create mode 100644 python/ql/test/experimental/query-tests/Security/CWE-176/UnicodeBypassValidation.qlref
create mode 100644 python/ql/test/experimental/query-tests/Security/CWE-176/samples.py
diff --git a/python/ql/lib/semmle/python/security/dataflow/UnicodeBypassValidationCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/UnicodeBypassValidationCustomizations.qll
new file mode 100644
index 00000000000..dd8e148dcd2
--- /dev/null
+++ b/python/ql/lib/semmle/python/security/dataflow/UnicodeBypassValidationCustomizations.qll
@@ -0,0 +1,30 @@
+/**
+ * Provides default sources, sinks and sanitizers for detecting
+ * "Unicode transformation"
+ * vulnerabilities, as well as extension points for adding your own.
+ */
+
+private import python
+private import semmle.python.dataflow.new.DataFlow
+
+/**
+ * Provides default sources, sinks and sanitizers for detecting
+ * "Unicode transformation"
+ * vulnerabilities, as well as extension points for adding your own.
+ */
+module UnicodeBypassValidation {
+ /**
+ * A data flow source for "Unicode transformation" vulnerabilities.
+ */
+ abstract class Source extends DataFlow::Node { }
+
+ /**
+ * A data flow sink for "Unicode transformation" vulnerabilities.
+ */
+ abstract class Sink extends DataFlow::Node { }
+
+ /**
+ * A sanitizer for "Unicode transformation" vulnerabilities.
+ */
+ abstract class Sanitizer extends DataFlow::Node { }
+}
diff --git a/python/ql/lib/semmle/python/security/dataflow/UnicodeBypassValidationQuery.qll b/python/ql/lib/semmle/python/security/dataflow/UnicodeBypassValidationQuery.qll
new file mode 100644
index 00000000000..504fd49e6f1
--- /dev/null
+++ b/python/ql/lib/semmle/python/security/dataflow/UnicodeBypassValidationQuery.qll
@@ -0,0 +1,62 @@
+/**
+ * Provides a taint-tracking configuration for detecting "Unicode transformation mishandling" vulnerabilities.
+ */
+
+private import python
+import semmle.python.ApiGraphs
+import semmle.python.Concepts
+import semmle.python.dataflow.new.DataFlow
+import semmle.python.dataflow.new.internal.DataFlowPublic
+import semmle.python.dataflow.new.TaintTracking
+import semmle.python.dataflow.new.internal.TaintTrackingPrivate
+import semmle.python.dataflow.new.RemoteFlowSources
+import UnicodeBypassValidationCustomizations::UnicodeBypassValidation
+
+/** A state signifying that a logical validation has not been performed. */
+class PreValidation extends DataFlow::FlowState {
+ PreValidation() { this = "PreValidation" }
+}
+
+/** A state signifying that a logical validation has been performed. */
+class PostValidation extends DataFlow::FlowState {
+ PostValidation() { this = "PostValidation" }
+}
+
+/**
+ * A taint-tracking configuration for detecting "Unicode transformation mishandling" vulnerabilities.
+ *
+ * This configuration uses two flow states, `PreValidation` and `PostValidation`,
+ * to track the requirement that a logical validation has been performed before the Unicode Transformation.
+ */
+class Configuration extends TaintTracking::Configuration {
+ Configuration() { this = "UnicodeBypassValidation" }
+
+ override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
+ source instanceof RemoteFlowSource and state instanceof PreValidation
+ }
+
+ override predicate isAdditionalTaintStep(
+ DataFlow::Node nodeFrom, DataFlow::FlowState stateFrom, DataFlow::Node nodeTo,
+ DataFlow::FlowState stateTo
+ ) {
+ (
+ exists(Escaping escaping | nodeFrom = escaping.getAnInput() and nodeTo = escaping.getOutput())
+ or
+ exists(RegexExecution re | nodeFrom = re.getString() and nodeTo = re)
+ or
+ stringManipulation(nodeFrom, nodeTo)
+ ) and
+ stateFrom instanceof PreValidation and
+ stateTo instanceof PostValidation
+ }
+
+ /* A Unicode Tranformation (Unicode tranformation) is considered a sink when the algorithm used is either NFC or NFKC. */
+ override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
+ exists(API::CallNode cn |
+ cn = API::moduleImport("unicodedata").getMember("normalize").getACall() and
+ cn.getArg(0).asExpr().(Str).getS() = ["NFC", "NFKC"] and
+ sink = cn.getArg(1) and
+ state instanceof PostValidation
+ )
+ }
+}
diff --git a/python/ql/src/experimental/Security/CWE-176/UnicodeBypassValidation.qhelp b/python/ql/src/experimental/Security/CWE-176/UnicodeBypassValidation.qhelp
new file mode 100644
index 00000000000..89b843e237c
--- /dev/null
+++ b/python/ql/src/experimental/Security/CWE-176/UnicodeBypassValidation.qhelp
@@ -0,0 +1,36 @@
+
+
+
+ Security checks bypass due to a Unicode transformation
+
+ If ever a unicode tranformation is performed after some security checks or logical
+ validation, the
+ latter could be bypassed due to a potential Unicode characters collision.
+ The validation of concern are any character escaping, any regex validation or any string
+ verification.
+
+
+
+
+ Perform a Unicode normalization before the logical validation.
+
+
+
+ The following example showcases the bypass of all checks performed by
+ flask.escape() due to a post-unicode normalization.
+ For instance: the character U+FE64 (﹤) is not filtered-out by the flask
+ escape function. But due to the Unicode normalization, the character is transformed and
+ would become U+003C ( < ).
+
+
+
+
+
+ Research study:
+ Unicode vulnerabilities that could bYte you
+ and Unicode pentest
+ cheatsheet.
+
+
\ No newline at end of file
diff --git a/python/ql/src/experimental/Security/CWE-176/UnicodeBypassValidation.ql b/python/ql/src/experimental/Security/CWE-176/UnicodeBypassValidation.ql
new file mode 100644
index 00000000000..f0232b59034
--- /dev/null
+++ b/python/ql/src/experimental/Security/CWE-176/UnicodeBypassValidation.ql
@@ -0,0 +1,24 @@
+/**
+ * @name Bypass Logical Validation Using Unicode Characters
+ * @description A Unicode transformation is using a remote user-controlled data. The transformation is a Unicode normalization using the algorithms "NFC" or "NFKC". In all cases, the security measures implemented or the logical validation performed to escape any injection characters, to validate using regex patterns or to perform string-based checks, before the Unicode transformation are **bypassable** by special Unicode characters.
+ * @kind path-problem
+ * @id py/unicode-bypass-validation
+ * @precision high
+ * @problem.severity error
+ * @tags security
+ * experimental
+ * external/cwe/cwe-176
+ * external/cwe/cwe-179
+ * external/cwe/cwe-180
+ */
+
+import python
+import semmle.python.security.dataflow.UnicodeBypassValidationQuery
+import DataFlow::PathGraph
+
+from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink
+where config.hasFlowPath(source, sink)
+select sink.getNode(), source, sink,
+ "This $@ processes unsafely $@ and any logical validation in-between could be bypassed using special Unicode characters.",
+ sink.getNode(), "Unicode transformation (Unicode normalization)", source.getNode(),
+ "remote user-controlled data"
diff --git a/python/ql/src/experimental/Security/CWE-176/escape-bypass.py b/python/ql/src/experimental/Security/CWE-176/escape-bypass.py
new file mode 100644
index 00000000000..15390c6b8f4
--- /dev/null
+++ b/python/ql/src/experimental/Security/CWE-176/escape-bypass.py
@@ -0,0 +1,11 @@
+import unicodedata
+from flask import Flask, request, escape, render_template
+
+app = Flask(__name__)
+
+
+@app.route("/unsafe1")
+def unsafe1():
+ user_input = escape(request.args.get("ui"))
+ normalized_user_input = unicodedata.normalize("NFKC", user_input)
+ return render_template("result.html", normalized_user_input=normalized_user_input)
diff --git a/python/ql/src/experimental/Security/CWE-176/vulnerability-flow.png b/python/ql/src/experimental/Security/CWE-176/vulnerability-flow.png
new file mode 100644
index 0000000000000000000000000000000000000000..e1a354717ef1273043075e966df13a19e4402e1c
GIT binary patch
literal 37706
zcmeFZcTkkivo9>LE0K*+Ulf4j6_$iTp`s!
zspwz1fX`OS;8Tb(@xfNQS&`PIcOGiHsE+*$PeY2M)T?eI3
zWOL4ZypKEvUH7XFeE+P~%lnpp^n9m05gG$S1^t&-9z~Dpc8i1(HW%_AubHqbogr6m
z`GxB>5h{_`_<_ToTp&k$0xabQuw|M7b5
zHN*JN&)|7WB?!|>%_Fb!50W7A0HH{1EY1ptGqR&i_+~gs+E1zbR6Uf~-Y!dty8?85
z2~9uw3FDD=RIj~ITJ=tMxZ*8gc>NdmJf2Dl!MGZRo6_wa2Gxs
zzO|xYJP;2eO4#x0N~dzei%JD50v@c$2#blVD)5f~4vUI_9()dB$QmL|A|TU(!fYM5tqL^B}h=p5?8_!JcabVc63=mfnK*VEZ5#wi@_sfw$zQ
z0goWim7S+B^3cTQ*9Kp9l6K4{AR7pEWjkK+op1(Q0EP*Qrljn_JCJ5@5Lgs~v{pzs
z?ASEqQm~Y%L}Vm2kcu;ovlkb|Gw|g$&zEx)JPmO0+}3j-1H9Mbq2Lza9oxo9I~C@F
z1{{$ETi5)b&1OaWOlQ>9)FQ16)JMA?1#T-ti3))MM@!Rz3pM?(7ODp2laAgu^7tNX
ze6$o}wl;xOL$k_7pNrxp;O~x#A5e!V@U;4?%B;?1=UYp#euahN5hhAx{^akP^L|l@HtTymfP02Y;gA3_w(gidman!Sj%0n@ADZR46{MuA!LN73;I(
zze$Wg%KEk_Ijw$HTBjfPWXaS_j(x}CYB&UHtlDh2`fFRUZ6g|4*Ox)l)}C
z3NQ{1rwb7m1r8X8{`K}XE{hxBX{6@&u#Oye81dBjGUr{^b|J1fOGU-|@s&cSo^W0AUf
zD%DmAD%ldbN_dxBt<_+n(yQ*YEB`JYBFW_QB6%Up9bCqn+1C|R9zYmq@aoP@l
z{^+;cFIjo#y}uhUI(5~l2dRS@aT9nNj{oK8Gm@B+Eqrml7@KroRb;uwnbv{f{NQ}
zvevoMSXHs+WYg15g6GW@<1(e?S#{Q4@x5yjkDL$gd-g^@&grM%0;~QqKUVtI{^s=H
z9^d;Y9>PIASuQMTa}8{MB(N2@(Jk!pNDg2txDq|qH{ZOIAg`J9tD+7@%D64ZpYHUD
zRNC}29vQ8{l{l#ka=g`cN{0Q5i#SO>2c~c2lz8=&4g$R=m}!5zT=_EquZ<1
z49(b|PE;op-!+3z%sRfjX6VtiJ>@ywyO3%w{Pb^q(%(AkzSPG)WU8~4cPIS{+c1hO
zY_H_Nj#>Xet9}cZQ(*LTGS;JDf^g)vMR0+c=^-OMRn8}*zP
zieZvWQO6@q{84784zthhi#$ycGA;0rAoF10oyjlsA~?jJ*~8HUvZ*Lq>Ik_74jae5
z9KW63AtiefyRmmdWg`#S3Swn8f|1AHQU&HZs}H_%>OdB5cj^AhFT>GtQ{I@r^ZBDbJCrcgvdmWvoyC&T
zw;ds+-d@_AMDLr9?TS8(${m>T=VENW=cx|7xW%^|0Jf3!PBOpxS3+CEdXGQ1p0+!=
zsRMI1lL!Bg(`oGrWGA1_8NM~rn`zmctQ*$QAWB^f@uhcS!;xS6-B^Ev9gT&4uAw8(
z53G_$7nkz{RVbdeost$-WZGc4fx6G$GP~>2z{|%uzQS`qcNY3&x*ItvXH{j>hEtr(
zakTG>sh2A`&1CUaZcG+N%{++INa7*XPm(T;SIi2xP196o8Kd@*9;TARql+mPD%@w3
z$j|qxXxC=u{>K51at6Dc-x1F8`{USywGoLKD^41kaWyw+ajaK
z`H-lzUyj_g=BXd+_9C>N8ny6AFzRY@uUgAGI
z0~%i7a$xYJWgCV^K6FtQ-`(pmFxyGt)nVvAr4>X{zN*JFoIlE$9LNdD&gXgBJX
zdEV%IAbp?=FMN`f-7)WocA9)wqiXo=368Kw`=9TFwlsPBQcH-MJDw{OcLUCkmP#;K
z{v19|g^@Iq*8%O5CtDaz5vffN$;s_sl(HWeB=xDhzk
zo?P*(U`G8ZC1y6laYbNk{#O~7QhjaN9Xs?oz<*_=uFBQGb7iQ|8WT!bxtAYjHBzGI
z?EgpZV3N~tnRIrF%?o+cagyF(dV86(=&J82RcDC0u3t24C>k)D!H_)69{dD$ijLq=
zjqIr|HE8$n-kMQ7I@xSqh+qp$_WT|2%elqDj`rHUKv;0dL*cMqm10
zoL{?USRQ#|7Q(~2b@Hp$*)mVjib~lkH=<2
z3wpNQ`p?8iG4C%l3g)|e4&OT3>)cmSajbIl+V|Ihq1ox^8C0er4|uX|Ood_t3%_Ei
zIS>kyr9`XBI-Jhpul#QG9Y1G><0%}MF#E25h~+#V)=}_gRIg-fON)~~UPhwv2^y6n
z$O!XPODA_q39^nRU3W%JpIP&>KFZPo^Oe{978o=4Wa(5E5b=qPfeC`6%OdwX!iuVg
zEZ8dwKYC$6W-}T1pFJHKN%L8rzkBH)%(jIsgfltDPb_6}Lf&bh1PkO4?o2$#t^D#b
z1@bah#&bhR7^Lq+(q7}~&$#(AvG5il2kWKz@MD=$ZOrv{m0BrCvg#hl+L^^?>me3t
zO6z4G$Wdq(|FQYNZ8l85=r+Srm3iRV-QWJ3a+gjLGF8O63Sq7(vp0(?7t`MQ
zPlMHwcR~l%Pp^SJs9w~a?X#n?)V!T;yTtd3$M8NkZOLNY{a0_|HOkqGN8e2bwuNf-L#zV
z-<@RN!0)P8d+!=P;J5BeabFMfJCl0ffQjy%5^k8m=HHPkBMj`(h^XgOi2i=&vvWLbvMvsjy
z9%nm;&ktn%nw2;?T3S%LAM(JS^>i=T(;_DSLQG)t1KFpizj!oLg}hp=#AwpH$)d%-u}odiiQYMeg=Ti?5z#9w!D~9H|T3eZxiZfCAO%
zbKsHbUrqSMqd9=>?CHWQCb>Htm|9W__T+UR-Pv5F!NSDY!BC0dWx{>
ziI0|s5wyK}_F1Fp;@r18{Dho2{>|eoZVFZfJ5rL+Nasi0u?(C?Bdt@pj~1gO?B{m}
z6;iR)S-5MKeFaM5eXYL1jT?PF&Jm2=3nst{cCSF?lbe2WNYHw9(^`s6!E&2}_4F;)
z+rsak7d2A^SE2#WO-W+iEKBI{oWS#=i&5j`=eEuMlf|mje?KlWMQ<&|GggNai4oj;
zn=5MBEzQ%lN$lV@0nWqa@A=k)zZ+vMB3|!Ls8#sd@mzJ9^&Rw(Bt8mFCA8Y=%m6HFi*K;tim+o&|Ky9!J_7u
zI65K1eT)ga?N1N8oK2bZ=tc&{FvhZZl{&!%ZSkuY7daJIgL1_+RQZkwK=Z~D$Cw*-xyb<06ZMhdgSY)o^
z6|9nunGW}YhqQ37o-++9R>;2oB+7v`2(0&0mwt({o@(1+W-RngwQiS0qCDd&8!1YX
z;p^%uoOET4Jj|c2Mvk+MEf2qX)|Afwlz+(YG`2I!YpV*6LbPocefl+9PR7!a!W|VA
z@;ZhG+F*P*L-=~S@_rrY+cvm0M6?1u_XXxJj@Lw%s^kUu*ej6_1d%VSS;%2s3WDN&Bi
z^7FJWso!O};|K3wQ9TQx)0TyHN>4yX2*a)gt@*fW>|f8%RgN6+VF@9d
z^V6gB>>KJi0i`!dS^B0JC7jje;pn2}dZ#x-`KwRzRjJ4m7Uw(#A9RG|NPIIKyGcf}
z{5Fr4lFBpWM%5TQ!^du9ihP)
zxhK3k3D=S|DO^#@C6(E#cU$^3;%*^`(s+JfME-qxL>R3oM(59nsL
z)eh*uIYyix&SmWh7l^QXvOX;RlP8P`
z_34`3lM2eQvWFR-aO>VI8@glR-OLw^VF6@{uaBf%x>-D}r0AM4?2}+SaRX-S@px7;
zLi*dv>pU_NJy!}-)xg}x$ZDH{qHF9E;yU^A=!x8SxcFEzo|K+eKuf?DTFudmjCi}e
zb%P3J4!Jb;rUTKiHY#+g7ji><$U2fvXtjXs>F)-qIe$dC&1WL40@~bgc9WgB
zPLC&E_2A?&qdi`SMdCMG#)=oe?P$N@s=Q2|(edZ`2>gk+BM1Gkh0)SmN!N7WrX;o&V0^&@Q7ll3jY$xROe2n*xaM
zUkhujN~%i`=bn|-qx^)e==GU#`B-b`vyD-WwHV>1Lre*#^%)xt`S*Zdhf;*FIIyT&
z4g(DMnaG9?oa@jf(i^FhWW2kOD&{oPa@&em;j9`?3e&b)=2vhUn#64bGj)9@s&S2F
zu~>)IM|rf5${y0Y7Fb)Y(ocMRwn=04D*U<2h%J1GN<%j7vDPu!>h0_iOG6P5D5^7b
zolquz(Z4&h-UJh*+qSS{%^ntQVx-b|o~1dX!Y9)KFz>{}00zepxLCkTxRuVg`
z6^(}fz}pQPj?VKMc8bHvd~E+yY7N%2iZmCwVyZK|!8a;dlonQ_x-0ntr-^7umn!UAgG
zLPyN)nVsmFe2t#toHb+Y9?eV*Y$N1FsD4Ll(~NQ$Pcj7=B+po2(zL6+(D-{HNm+I)
zE0VSNSjAUc72g+olFu}peVY@HkUOt+t>*3^-H2@w%?s#aw$;ik4SS1Vq?7sX#P{73
zTr3vMrW(~Bx+%)N`4qD;nV-%GTOXNwhm+YEHyLnUAkXuWg?z0WNq?qbs1Jb(+{6CE
zVW|L`f)MFLzj5yn+_TYlWoLYO3UNR3QE<>d^Oq|nI*^@F;ftmu?f!rJ(Dl_vw(TG^jedQ6cU)!w0o{nd6?gb)^uG?;>kpDX<3CtS#$V-G
zwLhlVXlc>7jWChW&muB9Ucvb&V^o78Rvvkr^MD{?E;_30wXb2A%qaE7QcD(3?Qknv
z>XZbZ5#CSomnz^G92>=fXHr+G(B$CSdFW&%!ev1UT~BVG^2nXx^0xS$Zg-0*>BxzZ
z#!`ByItSBwd^&jJdKyA^iXpq=`M@7+$jtj@aQs$XM#cCyZ!MOc`R?-MC_qv
zTRPoq&9Up3yV7^X7T=P@%J9yNaKzxpN_4b}3vsDg!LH7K9Ad;1FL?Dj1#3__l;!U0
zpt#~kqaHz&65^Q-#JZ|wK5nyEL$^HBC(I&3KR_D(M&Q;MP-K2DmU{8=|{f?H@=
zW|AP^>+lC(N@JWZh&IB5=>LM_XDt(Vc=WVtFgOuu24O3-yfU1@BfK-}vIXZ32qh|b
zuoZMZ(|I?g!V;nQVcqGG2Nep!azZ(mh&A9sPza;YSc;zt4Wf$d0vz&4>=jk;)VSXJ
zGagw91&yO*r!}#Axj|UQ?A%X=kPC`Dv?c6@amA-V?$XfPOd3KJ&`d!~$p#PJgCL^^
z`|}PcxG`u>RL$Px4xWcV4B^10EEp9O)|l-%Qldg>f|J75_MUbmEDtoJBhlpXNFRt?
z5nq>^T=9aSxtC+`AQ9;Tg@RKRrnRvNq2Q3yydOJZ0XN{GTKpQXDz+YIR#EN1N5LBb
zxdo|mQcP4RI?!yDu;i-&uY;)Q%X!8d90~c_1>q}|J-dxu@n#Se3xhRD$To-yW+`(Nwk-sE3H5)`NI>2J&3csESX3wx
z@U&vBGY}3-gyd&DfAoL~B@C#T-&y*l0iOkfQ^jVAa0OR`>v(*M%|ZbRE&!TK(1%wD
z3$B8vy-ME4%GkY-`~-LWLr3;Er$)lFn+wDMwoOzAn&7>DWCP*HC=G0;n-1}E
zzWb=w=eUJRVMP2=YGjd@fHhnSQquOUXWRg14o-&lT4m8}3xr(5SD+?GY_dxjTp!X*
zyPqNX>`=!$kgnVsoiV!Xhzn_d5G4Y3Ih!6(8Gbu@vM6YR|5Qnc5P#5A`iwIcV8}r*
zDrn#5)