From 15a3068f8a0463bbd64d1b1b6c41e1f70c09d94c Mon Sep 17 00:00:00 2001
From: Timo Mueller An improperly set environment variable during the creation of an RMI or JMX server can lead
+to an unauthenticated remote code execution vulnerability. This is due to the fact that the
+RMI/JMX server environment allows attackers to supply arbitrary objects to the authentication
+method, resulting in the attempted deserialization of an attacker-controlled object.
+ During the creation/initialitation of an RMI or JMX server a properly set environment (Map) variable has
+to be passed as second parameter.
+In order to disallow the deserialization of arbitrary objects the passed environment needs to set a deserialization filter.
+Ideally this filter only allows the deserialization to The following examples show how an RMI or JMX server can be initialized securely.
+
+ The first example shows how an RMI server can be initialized with a secure environment. The second example shows how the environment for a JMX server can be initialized securely.java.lang.String.
+
+The filter can be configured by setting the key jmx.remote.rmi.server.credentials.filter.pattern (CONST variable RMIConnectorServer.CREDENTIALS_FILTER_PATTERN).
+The filter should (ideally) blacklist all classes, and only whitelist java.lang.String for deserialization: ( "java.lang.String;!*").
+
+The key-value pair can be set as following:
+
+
+String my_filter = "java.lang.String;!*"; // Deny everything but java.lang.String
+
+Map
+
+For applications using < Java 10:
+
+
+// This is deprecated in Java 10+ !
+Map
+
+Please note that the authentication implementation is vulnerable by default.
+For this reason an initialitation with a null environment is also vulnerable .
+java.lang.String.
The filter can be configured by setting the key jmx.remote.rmi.server.credentials.filter.pattern (CONST variable RMIConnectorServer.CREDENTIALS_FILTER_PATTERN).
-The filter should (ideally) blacklist all classes, and only whitelist java.lang.String for deserialization: ( "java.lang.String;!*").
+The filter should (ideally) only allow java.lang.String and disallow all other classes for deserialization: ("java.lang.String;!*").
The key-value pair can be set as following:
@@ -65,4 +65,4 @@ For this reason an initialitation with a null environment is also v
null environment is also vulnerable .
+For this reason an initialization with a null environment is also vulnerable.
null environment is also v
null environment is also v
null environment is also v
null environment is also v
There are many different serialization frameworks. This query currently
-supports Kryo, XmlDecoder, XStream, SnakeYaml, and Java IO serialization through
-ObjectInputStream/ObjectOutputStream.
+supports Kryo, XmlDecoder, XStream, SnakeYaml, Hessian, JsonIO, YAMLBeans, Castor, Burlap,
+and Java IO serialization through ObjectInputStream/ObjectOutputStream.
There are many different serialization frameworks. This query currently
-supports Kryo, XmlDecoder, XStream, SnakeYaml, Hessian, JsonIO, YAMLBeans, Castor, Burlap,
+supports Kryo, XmlDecoder, XStream, SnakeYaml, JYaml, JsonIO, YAMLBeans, HessianBurlap, Castor, Burlap
and Java IO serialization through ObjectInputStream/ObjectOutputStream.
An improperly set environment variable during the creation of an RMI or JMX server can lead -to an unauthenticated remote code execution vulnerability. This is due to the fact that the +to an unauthenticated remote code execution vulnerability. This is because the RMI/JMX server environment allows attackers to supply arbitrary objects to the authentication method, resulting in the attempted deserialization of an attacker-controlled object.
java.lang.String.
-The filter can be configured by setting the key jmx.remote.rmi.server.credentials.filter.pattern (CONST variable RMIConnectorServer.CREDENTIALS_FILTER_PATTERN).
+The filter can be configured by setting the key jmx.remote.rmi.server.credentials.filter.pattern (given by the constant RMIConnectorServer.CREDENTIALS_FILTER_PATTERN).
The filter should (ideally) only allow java.lang.String and disallow all other classes for deserialization: ("java.lang.String;!*").
The key-value pair can be set as following:
@@ -27,7 +27,7 @@ Map
// This is deprecated in Java 10+ !
diff --git a/java/ql/src/experimental/Security/CWE/CWE-665/InsecureRmiJmxEnvironmentConfiguration.ql b/java/ql/src/experimental/Security/CWE/CWE-665/InsecureRmiJmxEnvironmentConfiguration.ql
index 7448f49784e..9bf195548c2 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-665/InsecureRmiJmxEnvironmentConfiguration.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-665/InsecureRmiJmxEnvironmentConfiguration.ql
@@ -1,6 +1,6 @@
/**
* @name InsecureRmiJmxAuthenticationEnvironment
- * @description This query detects if a JMX/RMI server is created with a potentially dangerous environment, which could lead to code execution through insecure deserialization.
+ * @description Creating a JMX/RMI server could lead to code execution through insecure deserialization if its environment does not restrict the types that can be deserialized.
* @kind path-problem
* @problem.severity error
* @tags security
@@ -17,14 +17,14 @@ import DataFlow::PathGraph
import semmle.code.java.dataflow.NullGuards
import semmle.code.java.dataflow.Nullness
-/** Predicate which detects vulnerable Constructors */
+/** Holds if `constructor` instantiates an RMI or JMX server. */
predicate isRmiOrJmxServerCreateConstructor(Constructor constructor) {
constructor
.getDeclaringType()
.hasQualifiedName("javax.management.remote.rmi", "RMIConnectorServer")
}
-/** Predicate which detects vulnerable server creations via methods */
+/** Holds if `method` creates an RMI or JMX server. */
predicate isRmiOrJmxServerCreateMethod(Method method) {
method.getName() = "newJMXConnectorServer" and
method.getDeclaringType().hasQualifiedName("javax.management.remote", "JMXConnectorServerFactory")
From f44b97c1c3b7109f7647d9761d3e56eb08f7ad63 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timo=20M=C3=BCller?=
Date: Tue, 25 May 2021 13:03:07 +0200
Subject: [PATCH 038/839] Apply suggestions from code review
Improved variable naming in examples and some documentation clearup
Co-authored-by: Chris Smowton
---
.../CWE-665/CorrectJmxEnvironmentInitialisation.java | 4 ++--
.../CWE-665/CorrectRmiEnvironmentInitialisation.java | 4 ++--
.../InsecureRmiJmxEnvironmentConfiguration.qhelp | 10 ++++------
3 files changed, 8 insertions(+), 10 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-665/CorrectJmxEnvironmentInitialisation.java b/java/ql/src/experimental/Security/CWE/CWE-665/CorrectJmxEnvironmentInitialisation.java
index 4152e97ddd0..0212687862a 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-665/CorrectJmxEnvironmentInitialisation.java
+++ b/java/ql/src/experimental/Security/CWE/CWE-665/CorrectJmxEnvironmentInitialisation.java
@@ -17,8 +17,8 @@ public class CorrectJmxInitialisation {
/* Restrict the login function to String Objects only (see CVE-2016-3427) */
Map env = new HashMap();
// For Java 10+
- String my_filter = "java.lang.String;!*"; // Deny everything but java.lang.String
- env.put(RMIConnectorServer.CREDENTIALS_FILTER_PATTERN, my_filter);
+ String stringsOnlyFilter = "java.lang.String;!*"; // Deny everything but java.lang.String
+ env.put(RMIConnectorServer.CREDENTIALS_FILTER_PATTERN, stringsOnlyFilter);
/* Java 9 or below:
env.put("jmx.remote.rmi.server.credential.types",
diff --git a/java/ql/src/experimental/Security/CWE/CWE-665/CorrectRmiEnvironmentInitialisation.java b/java/ql/src/experimental/Security/CWE/CWE-665/CorrectRmiEnvironmentInitialisation.java
index 724427ef597..400038b6853 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-665/CorrectRmiEnvironmentInitialisation.java
+++ b/java/ql/src/experimental/Security/CWE/CWE-665/CorrectRmiEnvironmentInitialisation.java
@@ -7,8 +7,8 @@ public class CorrectRmiInitialisation {
/* Restrict the login function to String Objects only (see CVE-2016-3427) */
Map env = new HashMap();
// For Java 10+
- String my_filter = "java.lang.String;!*"; // Deny everything but java.lang.String
- env.put(RMIConnectorServer.CREDENTIALS_FILTER_PATTERN, my_filter);
+ String stringsOnlyFilter = "java.lang.String;!*"; // Deny everything but java.lang.String
+ env.put(RMIConnectorServer.CREDENTIALS_FILTER_PATTERN, stringsOnlyFilter);
/* Java 9 or below
env.put("jmx.remote.rmi.server.credential.types",
diff --git a/java/ql/src/experimental/Security/CWE/CWE-665/InsecureRmiJmxEnvironmentConfiguration.qhelp b/java/ql/src/experimental/Security/CWE/CWE-665/InsecureRmiJmxEnvironmentConfiguration.qhelp
index 83bf7519ebd..f20b55d1a1a 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-665/InsecureRmiJmxEnvironmentConfiguration.qhelp
+++ b/java/ql/src/experimental/Security/CWE/CWE-665/InsecureRmiJmxEnvironmentConfiguration.qhelp
@@ -10,10 +10,8 @@ method, resulting in the attempted deserialization of an attacker-controlled obj
-During the creation/initialitation of an RMI or JMX server a properly set environment (Map) variable has
-to be passed as second parameter.
-In order to disallow the deserialization of arbitrary objects the passed environment needs to set a deserialization filter.
-Ideally this filter only allows the deserialization to java.lang.String.
+
During the creation/initialization of an RMI or JMX server an environment should be supplied that sets a deserialization filter.
+Ideally this filter only allows the deserialization of java.lang.String.
The filter can be configured by setting the key jmx.remote.rmi.server.credentials.filter.pattern (given by the constant RMIConnectorServer.CREDENTIALS_FILTER_PATTERN).
The filter should (ideally) only allow java.lang.String and disallow all other classes for deserialization: ("java.lang.String;!*").
@@ -21,10 +19,10 @@ The filter should (ideally) only allow java.lang.String and disallow all other c
The key-value pair can be set as following:
-String my_filter = "java.lang.String;!*"; // Deny everything but java.lang.String
+String stringsOnlyFilter = "java.lang.String;!*"; // Deny everything but java.lang.String
Map env = new HashMap;
-env.put(RMIConnectorServer.CREDENTIALS_FILTER_PATTERN, my_filter);
+env.put(RMIConnectorServer.CREDENTIALS_FILTER_PATTERN, stringsOnlyFilter);
For applications using Java 9 or below:
From ee3477c20a41f64c89b3eed6c75f0be9f014818f Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Tue, 25 May 2021 14:27:29 +0200
Subject: [PATCH 039/839] Python: Remove dummy clickhouse SQL injection query
---
.../CWE-089/ClickHouseSQLInjection.py | 28 ---------
.../CWE-089/ClickHouseSQLInjection.qhelp | 59 -------------------
.../CWE-089/ClickHouseSQLInjection.ql | 22 -------
3 files changed, 109 deletions(-)
delete mode 100644 python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.py
delete mode 100644 python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.qhelp
delete mode 100644 python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.ql
diff --git a/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.py b/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.py
deleted file mode 100644
index 6668d8eb15f..00000000000
--- a/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.py
+++ /dev/null
@@ -1,28 +0,0 @@
-from django.conf.urls import url
-from clickhouse_driver import Client
-from clickhouse_driver import connect
-from aioch import Client as aiochClient
-
-class MyClient(Client):
- def dummy(self):
- return None
-
-def show_user(request, username):
-
- # BAD -- Untrusted user input is directly injected into the sql query using async library 'aioch'
- aclient = aiochClient("localhost")
- progress = await aclient.execute_with_progress("SELECT * FROM users WHERE username = '%s'" % username)
-
- # BAD -- Untrusted user input is directly injected into the sql query using native client of library 'clickhouse_driver'
- Client('localhost').execute("SELECT * FROM users WHERE username = '%s'" % username)
-
- # GOOD -- query uses prepared statements
- query = "SELECT * FROM users WHERE username = %(username)s"
- Client('localhost').execute(query, {"username": username})
-
- # BAD -- PEP249 interface
- conn = connect('clickhouse://localhost')
- cursor = conn.cursor()
- cursor.execute("SELECT * FROM users WHERE username = '%s'" % username)
-
-urlpatterns = [url(r'^users/(?P[^/]+)$', show_user)]
diff --git a/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.qhelp b/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.qhelp
deleted file mode 100644
index be007545632..00000000000
--- a/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.qhelp
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
-
-
-If a database query (such as a SQL or NoSQL query) is built from
-user-provided data without sufficient sanitization, a user
-may be able to run malicious database queries.
-
-
-
-
-
-Most database connector libraries offer a way of safely
-embedding untrusted data into a query by means of query parameters
-or prepared statements.
-
-
-
-
-
-In the following snippet, a user is fetched from a ClickHouse database
-using five different queries. In the "BAD" cases the query is built directly from user-controlled data.
-In the "GOOD" case the user-controlled data is safely embedded into the query by using query parameters.
-
-
-
-In the first case, the query executed via aioch Client. aioch - is a module
-for asynchronous queries to database.
-
-
-
-In the second and third cases, the connection is established via `Client` class.
-This class implement different method to execute a query.
-
-
-
-In the forth case, good pattern is presented. Query parameters are send through
-second dict-like argument.
-
-
-
-In the fifth case, there is example of PEP249 interface usage.
-
-
-
-In the sixth case, there is custom Class usge which is a subclass of default Client.
-
-
-
-
-
-
-Wikipedia: SQL injection.
-OWASP: SQL Injection Prevention Cheat Sheet.
-
-
diff --git a/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.ql b/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.ql
deleted file mode 100644
index f0efb523756..00000000000
--- a/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.ql
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * @id py/yandex/clickhouse-sql-injection
- * @name Clickhouse SQL query built from user-controlled sources
- * @description Building a SQL query from user-controlled sources is vulnerable to insertion of
- * malicious SQL code by the user.
- * @kind path-problem
- * @problem.severity error
- * @precision high
- * @tags security
- * external/cwe/cwe-089
- * external/owasp/owasp-a1
- */
-
-import python
-import experimental.semmle.python.frameworks.ClickHouseDriver
-import semmle.python.security.dataflow.SqlInjection
-import DataFlow::PathGraph
-
-from SQLInjectionConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
-where config.hasFlowPath(source, sink)
-select sink.getNode(), source, sink, "This SQL query depends on $@.", source.getNode(),
- "a user-provided value"
From c9a9535dbcc6b3565032cadb97ead4b883756f36 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Tue, 25 May 2021 14:53:09 +0200
Subject: [PATCH 040/839] Python: Use ConceptsTests for ClickHouse SQL libs
This did reveal a few places where we do not detect the incoming SQL
---
.../frameworks/aioch/ConceptsTest.expected | 0
.../ConceptsTest.ql} | 5 +--
.../semmle/python/frameworks/aioch/options | 1 +
.../python/frameworks/aioch/sql_test.py | 30 +++++++++++++
.../ClickHouseDriver.expected | 5 ---
.../clickhouse-driver/ClickHouseDriver.py | 32 --------------
.../clickhouse_driver/ConceptsTest.expected | 0
.../clickhouse_driver/ConceptsTest.ql | 3 ++
.../frameworks/clickhouse_driver/sql_test.py | 42 +++++++++++++++++++
.../semmle/python/frameworks/options | 1 +
10 files changed, 78 insertions(+), 41 deletions(-)
create mode 100644 python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.expected
rename python/ql/test/experimental/semmle/python/frameworks/{clickhouse-driver/ClickHouseDriver.ql => aioch/ConceptsTest.ql} (51%)
create mode 100644 python/ql/test/experimental/semmle/python/frameworks/aioch/options
create mode 100644 python/ql/test/experimental/semmle/python/frameworks/aioch/sql_test.py
delete mode 100644 python/ql/test/experimental/semmle/python/frameworks/clickhouse-driver/ClickHouseDriver.expected
delete mode 100644 python/ql/test/experimental/semmle/python/frameworks/clickhouse-driver/ClickHouseDriver.py
create mode 100644 python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.expected
create mode 100644 python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.ql
create mode 100644 python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/sql_test.py
create mode 100644 python/ql/test/experimental/semmle/python/frameworks/options
diff --git a/python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.expected b/python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.expected
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/python/ql/test/experimental/semmle/python/frameworks/clickhouse-driver/ClickHouseDriver.ql b/python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.ql
similarity index 51%
rename from python/ql/test/experimental/semmle/python/frameworks/clickhouse-driver/ClickHouseDriver.ql
rename to python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.ql
index bedd47c0af6..65b2c78c74a 100644
--- a/python/ql/test/experimental/semmle/python/frameworks/clickhouse-driver/ClickHouseDriver.ql
+++ b/python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.ql
@@ -1,6 +1,3 @@
import python
+import experimental.meta.ConceptsTest
import experimental.semmle.python.frameworks.ClickHouseDriver
-import semmle.python.Concepts
-
-from SqlExecution s
-select s, s.getSql()
diff --git a/python/ql/test/experimental/semmle/python/frameworks/aioch/options b/python/ql/test/experimental/semmle/python/frameworks/aioch/options
new file mode 100644
index 00000000000..cfef58cf2b2
--- /dev/null
+++ b/python/ql/test/experimental/semmle/python/frameworks/aioch/options
@@ -0,0 +1 @@
+semmle-extractor-options: --max-import-depth=1 --lang=3
diff --git a/python/ql/test/experimental/semmle/python/frameworks/aioch/sql_test.py b/python/ql/test/experimental/semmle/python/frameworks/aioch/sql_test.py
new file mode 100644
index 00000000000..468791aa3dc
--- /dev/null
+++ b/python/ql/test/experimental/semmle/python/frameworks/aioch/sql_test.py
@@ -0,0 +1,30 @@
+import aioch
+
+
+SQL = "SOME SQL"
+
+
+async def aioch_test():
+ client = aioch.Client("localhost")
+
+ await client.execute(SQL) # $ getSql=SQL
+ await client.execute(query=SQL) # $ MISSING: getSql=SQL
+
+ await client.execute_with_progress(SQL) # $ getSql=SQL
+ await client.execute_with_progress(query=SQL) # $ MISSING: getSql=SQL
+
+ await client.execute_iter(SQL) # $ getSql=SQL
+ await client.execute_iter(query=SQL) # $ MISSING: getSql=SQL
+
+
+# Using custom client (this has been seen done for the blocking version in
+# `clickhouse_driver` PyPI package)
+
+
+class MyClient(aioch.Client):
+ pass
+
+
+async def test_custom_client():
+ client = MyClient("localhost")
+ await client.execute(SQL) # $ getSql=SQL
diff --git a/python/ql/test/experimental/semmle/python/frameworks/clickhouse-driver/ClickHouseDriver.expected b/python/ql/test/experimental/semmle/python/frameworks/clickhouse-driver/ClickHouseDriver.expected
deleted file mode 100644
index 52aa30cb8c1..00000000000
--- a/python/ql/test/experimental/semmle/python/frameworks/clickhouse-driver/ClickHouseDriver.expected
+++ /dev/null
@@ -1,5 +0,0 @@
-| ClickHouseDriver.py:15:22:15:106 | ControlFlowNode for Attribute() | ClickHouseDriver.py:15:52:15:105 | ControlFlowNode for BinaryExpr |
-| ClickHouseDriver.py:18:5:18:87 | ControlFlowNode for Attribute() | ClickHouseDriver.py:18:33:18:86 | ControlFlowNode for BinaryExpr |
-| ClickHouseDriver.py:22:5:22:62 | ControlFlowNode for Attribute() | ClickHouseDriver.py:22:33:22:37 | ControlFlowNode for query |
-| ClickHouseDriver.py:27:5:27:74 | ControlFlowNode for Attribute() | ClickHouseDriver.py:27:20:27:73 | ControlFlowNode for BinaryExpr |
-| ClickHouseDriver.py:30:5:30:89 | ControlFlowNode for Attribute() | ClickHouseDriver.py:30:35:30:88 | ControlFlowNode for BinaryExpr |
diff --git a/python/ql/test/experimental/semmle/python/frameworks/clickhouse-driver/ClickHouseDriver.py b/python/ql/test/experimental/semmle/python/frameworks/clickhouse-driver/ClickHouseDriver.py
deleted file mode 100644
index 7684f3df795..00000000000
--- a/python/ql/test/experimental/semmle/python/frameworks/clickhouse-driver/ClickHouseDriver.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from django.conf.urls import url
-from clickhouse_driver import Client
-from clickhouse_driver import connect
-from aioch import Client as aiochClient
-
-# Dummy Client subclass
-class MyClient(Client):
- def dummy(self):
- return None
-
-def show_user(request, username):
-
- # BAD -- Untrusted user input is directly injected into the sql query using async library 'aioch'
- aclient = aiochClient("localhost")
- progress = await aclient.execute_with_progress("SELECT * FROM users WHERE username = '%s'" % username)
-
- # BAD -- Untrusted user input is directly injected into the sql query using native client of library 'clickhouse_driver'
- Client('localhost').execute("SELECT * FROM users WHERE username = '%s'" % username)
-
- # GOOD -- query uses prepared statements
- query = "SELECT * FROM users WHERE username = %(username)s"
- Client('localhost').execute(query, {"username": username})
-
- # BAD -- Untrusted user input is directly injected into the sql query using PEP249 interface
- conn = connect('clickhouse://localhost')
- cursor = conn.cursor()
- cursor.execute("SELECT * FROM users WHERE username = '%s'" % username)
-
- # BAD -- Untrusted user input is directly injected into the sql query using MyClient, which is a subclass of Client
- MyClient('localhost').execute("SELECT * FROM users WHERE username = '%s'" % username)
-
-urlpatterns = [url(r'^users/(?P[^/]+)$', show_user)]
diff --git a/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.expected b/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.expected
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.ql b/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.ql
new file mode 100644
index 00000000000..65b2c78c74a
--- /dev/null
+++ b/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.ql
@@ -0,0 +1,3 @@
+import python
+import experimental.meta.ConceptsTest
+import experimental.semmle.python.frameworks.ClickHouseDriver
diff --git a/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/sql_test.py b/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/sql_test.py
new file mode 100644
index 00000000000..d1e2491e86e
--- /dev/null
+++ b/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/sql_test.py
@@ -0,0 +1,42 @@
+import clickhouse_driver
+
+
+SQL = "SOME SQL"
+
+
+# Normal operation
+client = clickhouse_driver.client.Client("localhost")
+
+client.execute(SQL) # $ MISSING: getSql=SQL
+client.execute(query=SQL) # $ MISSING: getSql=SQL
+
+client.execute_with_progress(SQL) # $ MISSING: getSql=SQL
+client.execute_with_progress(query=SQL) # $ MISSING: getSql=SQL
+
+client.execute_iter(SQL) # $ MISSING: getSql=SQL
+client.execute_iter(query=SQL) # $ MISSING: getSql=SQL
+
+
+# commonly used alias
+client = clickhouse_driver.Client("localhost")
+client.execute(SQL) # $ getSql=SQL
+
+
+# Using PEP249 interface
+conn = clickhouse_driver.connect('clickhouse://localhost')
+cursor = conn.cursor()
+cursor.execute(SQL) # $ getSql=SQL
+
+
+# Using custom client
+#
+# examples from real world code
+# https://github.com/Altinity/clickhouse-mysql-data-reader/blob/3b1b7088751b05e5bbf45890c5949b58208c2343/clickhouse_mysql/dbclient/chclient.py#L10
+# https://github.com/Felixoid/clickhouse-plantuml/blob/d8b2ba7d164a836770ec21f5e4035dfb04c41d9c/clickhouse_plantuml/client.py#L9
+
+
+class MyClient(clickhouse_driver.Client):
+ pass
+
+
+MyClient("localhost").execute(SQL) # $ getSql=SQL
diff --git a/python/ql/test/experimental/semmle/python/frameworks/options b/python/ql/test/experimental/semmle/python/frameworks/options
new file mode 100644
index 00000000000..eb214fc2931
--- /dev/null
+++ b/python/ql/test/experimental/semmle/python/frameworks/options
@@ -0,0 +1 @@
+semmle-extractor-options: --max-import-depth=1
From eb1da152a0d54bff63094056f12ba92a309a9a91 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Tue, 25 May 2021 16:13:31 +0200
Subject: [PATCH 041/839] Python: Rewrite ClickHouse SQL lib modeling
This did turn into a few changes, that maybe could have been split into
separate PRs :shrug:
* Rename `ClickHouseDriver` => `ClickhouseDriver`, to better follow
import name in `.qll` name
* Rewrote modeling to use API graphs
* Split modeling of `aioch` into separate `.qll` file, which does re-use
the `getExecuteMethodName` predicate. I feel that sharing code between
the modeling like this was the best approach, and stuck the
`INTERNAL: Do not use.` labels on both modules.
* I also added handling of keyword arguments (see change in .py files)
---
.../semmle/python/frameworks/Aioch.qll | 52 ++++++++++++
.../python/frameworks/ClickHouseDriver.qll | 85 -------------------
.../python/frameworks/ClickhouseDriver.qll | 65 ++++++++++++++
.../python/frameworks/aioch/ConceptsTest.ql | 2 +-
.../python/frameworks/aioch/sql_test.py | 6 +-
.../clickhouse_driver/ConceptsTest.ql | 2 +-
.../frameworks/clickhouse_driver/sql_test.py | 12 +--
7 files changed, 128 insertions(+), 96 deletions(-)
create mode 100644 python/ql/src/experimental/semmle/python/frameworks/Aioch.qll
delete mode 100644 python/ql/src/experimental/semmle/python/frameworks/ClickHouseDriver.qll
create mode 100644 python/ql/src/experimental/semmle/python/frameworks/ClickhouseDriver.qll
diff --git a/python/ql/src/experimental/semmle/python/frameworks/Aioch.qll b/python/ql/src/experimental/semmle/python/frameworks/Aioch.qll
new file mode 100644
index 00000000000..dde97047046
--- /dev/null
+++ b/python/ql/src/experimental/semmle/python/frameworks/Aioch.qll
@@ -0,0 +1,52 @@
+/**
+ * Provides classes modeling security-relevant aspects of the `aioch` PyPI package (an
+ * async-io version of the `clickhouse-driver` PyPI package).
+ *
+ * See https://pypi.org/project/aioch/
+ */
+
+private import python
+private import semmle.python.Concepts
+private import semmle.python.ApiGraphs
+private import semmle.python.frameworks.PEP249
+private import experimental.semmle.python.frameworks.ClickhouseDriver
+
+/**
+ * INTERNAL: Do not use.
+ *
+ * Provides models for `aioch` PyPI package (an async-io version of the
+ * `clickhouse-driver` PyPI package).
+ *
+ * See https://pypi.org/project/aioch/
+ */
+module Aioch {
+ /** Provides models for `aioch.Client` class and subclasses. */
+ module Client {
+ /** Gets a reference to the `aioch.Client` class or any subclass. */
+ API::Node subclassRef() {
+ result = API::moduleImport("aioch").getMember("Client").getASubclass*()
+ }
+
+ /** Gets a reference to an instance of `clickhouse_driver.Client` or any subclass. */
+ API::Node instance() { result = subclassRef().getReturn() }
+ }
+
+ /**
+ * A call to any of the the execute methods on a `aioch.Client`, which are just async
+ * versions of the methods in the `clickhouse-driver` PyPI package.
+ *
+ * See
+ * - https://clickhouse-driver.readthedocs.io/en/latest/api.html#clickhouse_driver.Client.execute
+ * - https://clickhouse-driver.readthedocs.io/en/latest/api.html#clickhouse_driver.Client.execute_iter
+ * - https://clickhouse-driver.readthedocs.io/en/latest/api.html#clickhouse_driver.Client.execute_with_progress
+ */
+ class ClientExecuteCall extends SqlExecution::Range, DataFlow::CallCfgNode {
+ ClientExecuteCall() {
+ exists(string methodName | methodName = ClickhouseDriver::getExecuteMethodName() |
+ this = Client::instance().getMember(methodName).getACall()
+ )
+ }
+
+ override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("query")] }
+ }
+}
diff --git a/python/ql/src/experimental/semmle/python/frameworks/ClickHouseDriver.qll b/python/ql/src/experimental/semmle/python/frameworks/ClickHouseDriver.qll
deleted file mode 100644
index c456b6bdb89..00000000000
--- a/python/ql/src/experimental/semmle/python/frameworks/ClickHouseDriver.qll
+++ /dev/null
@@ -1,85 +0,0 @@
-/**
- * Provides classes modeling security-relevant aspects of `clickhouse-driver` and `aioch` PyPI packages.
- * See
- * - https://pypi.org/project/clickhouse-driver/
- * - https://pypi.org/project/aioch/
- * - https://clickhouse-driver.readthedocs.io/en/latest/
- */
-
-private import python
-private import semmle.python.Concepts
-private import semmle.python.ApiGraphs
-private import semmle.python.frameworks.PEP249
-
-/**
- * Provides models for `clickhouse-driver` and `aioch` PyPI packages.
- * See
- * - https://pypi.org/project/clickhouse-driver/
- * - https://pypi.org/project/aioch/
- * - https://clickhouse-driver.readthedocs.io/en/latest/
- */
-module ClickHouseDriver {
- /** Gets a reference to the `clickhouse_driver` module. */
- API::Node clickhouse_driver() { result = API::moduleImport("clickhouse_driver") }
-
- /** Gets a reference to the `aioch` module. This module allows to make async db queries. */
- API::Node aioch() { result = API::moduleImport("aioch") }
-
- /**
- * `clickhouse_driver` implements PEP249,
- * providing ways to execute SQL statements against a database.
- */
- class ClickHouseDriverPEP249 extends PEP249ModuleApiNode {
- ClickHouseDriverPEP249() { this = clickhouse_driver() }
- }
-
- module Client {
- /** Gets a reference to a Client call. */
- private DataFlow::Node client_ref() {
- result = clickhouse_driver().getMember("Client").getASubclass*().getAUse()
- or
- result = aioch().getMember("Client").getASubclass*().getAUse()
- }
-
- /** A direct instantiation of `clickhouse_driver.Client`. */
- private class ClientInstantiation extends DataFlow::CallCfgNode {
- ClientInstantiation() { this.getFunction() = client_ref() }
- }
-
- /** Gets a reference to an instance of `clickhouse_driver.Client`. */
- private DataFlow::LocalSourceNode instance(DataFlow::TypeTracker t) {
- t.start() and
- result instanceof ClientInstantiation
- or
- exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
- }
-
- /** Gets a reference to an instance of `clickhouse_driver.Client`. */
- DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
- }
-
- /** clickhouse_driver.Client execute methods */
- private string execute_function() {
- result in ["execute_with_progress", "execute", "execute_iter"]
- }
-
- /** Gets a reference to the `clickhouse_driver.Client.execute` method */
- private DataFlow::LocalSourceNode clickhouse_execute(DataFlow::TypeTracker t) {
- t.startInAttr(execute_function()) and
- result = Client::instance()
- or
- exists(DataFlow::TypeTracker t2 | result = clickhouse_execute(t2).track(t2, t))
- }
-
- /** Gets a reference to the `clickhouse_driver.Client.execute` method */
- DataFlow::Node clickhouse_execute() {
- clickhouse_execute(DataFlow::TypeTracker::end()).flowsTo(result)
- }
-
- /** A call to the `clickhouse_driver.Client.execute` method */
- private class ExecuteCall extends SqlExecution::Range, DataFlow::CallCfgNode {
- ExecuteCall() { this.getFunction() = clickhouse_execute() }
-
- override DataFlow::Node getSql() { result.asCfgNode() = node.getArg(0) }
- }
-}
diff --git a/python/ql/src/experimental/semmle/python/frameworks/ClickhouseDriver.qll b/python/ql/src/experimental/semmle/python/frameworks/ClickhouseDriver.qll
new file mode 100644
index 00000000000..8863b1dbe66
--- /dev/null
+++ b/python/ql/src/experimental/semmle/python/frameworks/ClickhouseDriver.qll
@@ -0,0 +1,65 @@
+/**
+ * Provides classes modeling security-relevant aspects of the `clickhouse-driver` PyPI package.
+ * See
+ * - https://pypi.org/project/clickhouse-driver/
+ * - https://clickhouse-driver.readthedocs.io/en/latest/
+ */
+
+private import python
+private import semmle.python.Concepts
+private import semmle.python.ApiGraphs
+private import semmle.python.frameworks.PEP249
+
+/**
+ * INTERNAL: Do not use.
+ *
+ * Provides models for `clickhouse-driver` PyPI package (imported as `clickhouse_driver`).
+ * See
+ * - https://pypi.org/project/clickhouse-driver/
+ * - https://clickhouse-driver.readthedocs.io/en/latest/
+ */
+module ClickhouseDriver {
+ /**
+ * `clickhouse_driver` implements PEP249,
+ * providing ways to execute SQL statements against a database.
+ */
+ class ClickHouseDriverPEP249 extends PEP249ModuleApiNode {
+ ClickHouseDriverPEP249() { this = API::moduleImport("clickhouse_driver") }
+ }
+
+ /** Provides models for `clickhouse_driver.Client` class and subclasses. */
+ module Client {
+ /** Gets a reference to the `clickhouse_driver.Client` class or any subclass. */
+ API::Node subclassRef() {
+ exists(API::Node classRef |
+ // canonical definition
+ classRef = API::moduleImport("clickhouse_driver").getMember("client").getMember("Client")
+ or
+ // commonly used alias
+ classRef = API::moduleImport("clickhouse_driver").getMember("Client")
+ |
+ result = classRef.getASubclass*()
+ )
+ }
+
+ /** Gets a reference to an instance of `clickhouse_driver.Client` or any subclass. */
+ API::Node instance() { result = subclassRef().getReturn() }
+ }
+
+ /** `clickhouse_driver.Client` execute method names */
+ string getExecuteMethodName() { result in ["execute_with_progress", "execute", "execute_iter"] }
+
+ /**
+ * A call to any of the the execute methods on a `clickhouse_driver.Client` method
+ *
+ * See
+ * - https://clickhouse-driver.readthedocs.io/en/latest/api.html#clickhouse_driver.Client.execute
+ * - https://clickhouse-driver.readthedocs.io/en/latest/api.html#clickhouse_driver.Client.execute_iter
+ * - https://clickhouse-driver.readthedocs.io/en/latest/api.html#clickhouse_driver.Client.execute_with_progress
+ */
+ class ClientExecuteCall extends SqlExecution::Range, DataFlow::CallCfgNode {
+ ClientExecuteCall() { this = Client::instance().getMember(getExecuteMethodName()).getACall() }
+
+ override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("query")] }
+ }
+}
diff --git a/python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.ql b/python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.ql
index 65b2c78c74a..c0c26131055 100644
--- a/python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.ql
+++ b/python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.ql
@@ -1,3 +1,3 @@
import python
import experimental.meta.ConceptsTest
-import experimental.semmle.python.frameworks.ClickHouseDriver
+import experimental.semmle.python.frameworks.Aioch
diff --git a/python/ql/test/experimental/semmle/python/frameworks/aioch/sql_test.py b/python/ql/test/experimental/semmle/python/frameworks/aioch/sql_test.py
index 468791aa3dc..de6b018b0d4 100644
--- a/python/ql/test/experimental/semmle/python/frameworks/aioch/sql_test.py
+++ b/python/ql/test/experimental/semmle/python/frameworks/aioch/sql_test.py
@@ -8,13 +8,13 @@ async def aioch_test():
client = aioch.Client("localhost")
await client.execute(SQL) # $ getSql=SQL
- await client.execute(query=SQL) # $ MISSING: getSql=SQL
+ await client.execute(query=SQL) # $ getSql=SQL
await client.execute_with_progress(SQL) # $ getSql=SQL
- await client.execute_with_progress(query=SQL) # $ MISSING: getSql=SQL
+ await client.execute_with_progress(query=SQL) # $ getSql=SQL
await client.execute_iter(SQL) # $ getSql=SQL
- await client.execute_iter(query=SQL) # $ MISSING: getSql=SQL
+ await client.execute_iter(query=SQL) # $ getSql=SQL
# Using custom client (this has been seen done for the blocking version in
diff --git a/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.ql b/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.ql
index 65b2c78c74a..86e878cf8c7 100644
--- a/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.ql
+++ b/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.ql
@@ -1,3 +1,3 @@
import python
import experimental.meta.ConceptsTest
-import experimental.semmle.python.frameworks.ClickHouseDriver
+import experimental.semmle.python.frameworks.ClickhouseDriver
diff --git a/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/sql_test.py b/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/sql_test.py
index d1e2491e86e..36d4966c186 100644
--- a/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/sql_test.py
+++ b/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/sql_test.py
@@ -7,14 +7,14 @@ SQL = "SOME SQL"
# Normal operation
client = clickhouse_driver.client.Client("localhost")
-client.execute(SQL) # $ MISSING: getSql=SQL
-client.execute(query=SQL) # $ MISSING: getSql=SQL
+client.execute(SQL) # $ getSql=SQL
+client.execute(query=SQL) # $ getSql=SQL
-client.execute_with_progress(SQL) # $ MISSING: getSql=SQL
-client.execute_with_progress(query=SQL) # $ MISSING: getSql=SQL
+client.execute_with_progress(SQL) # $ getSql=SQL
+client.execute_with_progress(query=SQL) # $ getSql=SQL
-client.execute_iter(SQL) # $ MISSING: getSql=SQL
-client.execute_iter(query=SQL) # $ MISSING: getSql=SQL
+client.execute_iter(SQL) # $ getSql=SQL
+client.execute_iter(query=SQL) # $ getSql=SQL
# commonly used alias
From 1b3f857a2fdeb03e38fe0964c68d984fc23c5feb Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Tue, 25 May 2021 16:27:23 +0200
Subject: [PATCH 042/839] Python: Promote ClickHouse SQL models
---
docs/codeql/support/reusables/frameworks.rst | 2 ++
python/change-notes/2021-05-25-add-ClickHouse-sql-libs.md | 2 ++
python/ql/src/semmle/python/Frameworks.qll | 2 ++
.../src/{experimental => }/semmle/python/frameworks/Aioch.qll | 2 +-
.../semmle/python/frameworks/ClickhouseDriver.qll | 0
.../semmle/python/frameworks/clickhouse_driver/ConceptsTest.ql | 3 ---
python/ql/test/experimental/semmle/python/frameworks/options | 1 -
.../frameworks/aioch/ConceptsTest.expected | 0
.../python => library-tests}/frameworks/aioch/ConceptsTest.ql | 1 -
.../semmle/python => library-tests}/frameworks/aioch/options | 0
.../python => library-tests}/frameworks/aioch/sql_test.py | 0
.../frameworks/clickhouse_driver/ConceptsTest.expected | 0
.../library-tests/frameworks/clickhouse_driver/ConceptsTest.ql | 2 ++
.../frameworks/clickhouse_driver/sql_test.py | 0
14 files changed, 9 insertions(+), 6 deletions(-)
create mode 100644 python/change-notes/2021-05-25-add-ClickHouse-sql-libs.md
rename python/ql/src/{experimental => }/semmle/python/frameworks/Aioch.qll (96%)
rename python/ql/src/{experimental => }/semmle/python/frameworks/ClickhouseDriver.qll (100%)
delete mode 100644 python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.ql
delete mode 100644 python/ql/test/experimental/semmle/python/frameworks/options
rename python/ql/test/{experimental/semmle/python => library-tests}/frameworks/aioch/ConceptsTest.expected (100%)
rename python/ql/test/{experimental/semmle/python => library-tests}/frameworks/aioch/ConceptsTest.ql (50%)
rename python/ql/test/{experimental/semmle/python => library-tests}/frameworks/aioch/options (100%)
rename python/ql/test/{experimental/semmle/python => library-tests}/frameworks/aioch/sql_test.py (100%)
rename python/ql/test/{experimental/semmle/python => library-tests}/frameworks/clickhouse_driver/ConceptsTest.expected (100%)
create mode 100644 python/ql/test/library-tests/frameworks/clickhouse_driver/ConceptsTest.ql
rename python/ql/test/{experimental/semmle/python => library-tests}/frameworks/clickhouse_driver/sql_test.py (100%)
diff --git a/docs/codeql/support/reusables/frameworks.rst b/docs/codeql/support/reusables/frameworks.rst
index be9949aad77..532456d76b4 100644
--- a/docs/codeql/support/reusables/frameworks.rst
+++ b/docs/codeql/support/reusables/frameworks.rst
@@ -162,6 +162,8 @@ Python built-in support
fabric, Utility library
invoke, Utility library
idna, Utility library
+ aioch, Database
+ clickhouse-driver, Database
mysql-connector-python, Database
MySQLdb, Database
psycopg2, Database
diff --git a/python/change-notes/2021-05-25-add-ClickHouse-sql-libs.md b/python/change-notes/2021-05-25-add-ClickHouse-sql-libs.md
new file mode 100644
index 00000000000..4638180f8c5
--- /dev/null
+++ b/python/change-notes/2021-05-25-add-ClickHouse-sql-libs.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* Added model of SQL execution in `clickhouse-driver` and `aioch` PyPI packages, resulting in additional sinks for the SQL Injection query (`py/sql-injection`). This modeling was originally [submitted as a contribution by @japroc](https://github.com/github/codeql/pull/5889).
diff --git a/python/ql/src/semmle/python/Frameworks.qll b/python/ql/src/semmle/python/Frameworks.qll
index 3115c3ffac6..96ae176465e 100644
--- a/python/ql/src/semmle/python/Frameworks.qll
+++ b/python/ql/src/semmle/python/Frameworks.qll
@@ -4,6 +4,8 @@
// If you add modeling of a new framework/library, remember to add it it to the docs in
// `docs/codeql/support/reusables/frameworks.rst`
+private import semmle.python.frameworks.Aioch
+private import semmle.python.frameworks.ClickhouseDriver
private import semmle.python.frameworks.Cryptodome
private import semmle.python.frameworks.Cryptography
private import semmle.python.frameworks.Dill
diff --git a/python/ql/src/experimental/semmle/python/frameworks/Aioch.qll b/python/ql/src/semmle/python/frameworks/Aioch.qll
similarity index 96%
rename from python/ql/src/experimental/semmle/python/frameworks/Aioch.qll
rename to python/ql/src/semmle/python/frameworks/Aioch.qll
index dde97047046..ede732e35dc 100644
--- a/python/ql/src/experimental/semmle/python/frameworks/Aioch.qll
+++ b/python/ql/src/semmle/python/frameworks/Aioch.qll
@@ -9,7 +9,7 @@ private import python
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
private import semmle.python.frameworks.PEP249
-private import experimental.semmle.python.frameworks.ClickhouseDriver
+private import semmle.python.frameworks.ClickhouseDriver
/**
* INTERNAL: Do not use.
diff --git a/python/ql/src/experimental/semmle/python/frameworks/ClickhouseDriver.qll b/python/ql/src/semmle/python/frameworks/ClickhouseDriver.qll
similarity index 100%
rename from python/ql/src/experimental/semmle/python/frameworks/ClickhouseDriver.qll
rename to python/ql/src/semmle/python/frameworks/ClickhouseDriver.qll
diff --git a/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.ql b/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.ql
deleted file mode 100644
index 86e878cf8c7..00000000000
--- a/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.ql
+++ /dev/null
@@ -1,3 +0,0 @@
-import python
-import experimental.meta.ConceptsTest
-import experimental.semmle.python.frameworks.ClickhouseDriver
diff --git a/python/ql/test/experimental/semmle/python/frameworks/options b/python/ql/test/experimental/semmle/python/frameworks/options
deleted file mode 100644
index eb214fc2931..00000000000
--- a/python/ql/test/experimental/semmle/python/frameworks/options
+++ /dev/null
@@ -1 +0,0 @@
-semmle-extractor-options: --max-import-depth=1
diff --git a/python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/aioch/ConceptsTest.expected
similarity index 100%
rename from python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.expected
rename to python/ql/test/library-tests/frameworks/aioch/ConceptsTest.expected
diff --git a/python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/aioch/ConceptsTest.ql
similarity index 50%
rename from python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.ql
rename to python/ql/test/library-tests/frameworks/aioch/ConceptsTest.ql
index c0c26131055..b557a0bccb6 100644
--- a/python/ql/test/experimental/semmle/python/frameworks/aioch/ConceptsTest.ql
+++ b/python/ql/test/library-tests/frameworks/aioch/ConceptsTest.ql
@@ -1,3 +1,2 @@
import python
import experimental.meta.ConceptsTest
-import experimental.semmle.python.frameworks.Aioch
diff --git a/python/ql/test/experimental/semmle/python/frameworks/aioch/options b/python/ql/test/library-tests/frameworks/aioch/options
similarity index 100%
rename from python/ql/test/experimental/semmle/python/frameworks/aioch/options
rename to python/ql/test/library-tests/frameworks/aioch/options
diff --git a/python/ql/test/experimental/semmle/python/frameworks/aioch/sql_test.py b/python/ql/test/library-tests/frameworks/aioch/sql_test.py
similarity index 100%
rename from python/ql/test/experimental/semmle/python/frameworks/aioch/sql_test.py
rename to python/ql/test/library-tests/frameworks/aioch/sql_test.py
diff --git a/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/clickhouse_driver/ConceptsTest.expected
similarity index 100%
rename from python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/ConceptsTest.expected
rename to python/ql/test/library-tests/frameworks/clickhouse_driver/ConceptsTest.expected
diff --git a/python/ql/test/library-tests/frameworks/clickhouse_driver/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/clickhouse_driver/ConceptsTest.ql
new file mode 100644
index 00000000000..b557a0bccb6
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/clickhouse_driver/ConceptsTest.ql
@@ -0,0 +1,2 @@
+import python
+import experimental.meta.ConceptsTest
diff --git a/python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/sql_test.py b/python/ql/test/library-tests/frameworks/clickhouse_driver/sql_test.py
similarity index 100%
rename from python/ql/test/experimental/semmle/python/frameworks/clickhouse_driver/sql_test.py
rename to python/ql/test/library-tests/frameworks/clickhouse_driver/sql_test.py
From 59ebe08c78b3dfa39abbf4df58560e82142ef3bc Mon Sep 17 00:00:00 2001
From: Timo Mueller
Date: Tue, 25 May 2021 16:40:41 +0200
Subject: [PATCH 043/839] Added stup for RMIConnectorServer for valid test case
---
...ureRmiJmxEnvironmentConfiguration.expected | 44 ++++++++++++-------
...nsecureRmiJmxEnvironmentConfiguration.java | 12 ++---
.../query-tests/security/CWE-665/options | 1 +
.../remote/rmi/RMIConnectorServer.java | 27 ++++++++++++
.../management/remote/rmi/RMIServerImpl.java | 10 +++++
5 files changed, 74 insertions(+), 20 deletions(-)
create mode 100644 java/ql/test/experimental/query-tests/security/CWE-665/options
create mode 100644 java/ql/test/experimental/stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java
create mode 100644 java/ql/test/experimental/stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIServerImpl.java
diff --git a/java/ql/test/experimental/query-tests/security/CWE-665/InsecureRmiJmxEnvironmentConfiguration.expected b/java/ql/test/experimental/query-tests/security/CWE-665/InsecureRmiJmxEnvironmentConfiguration.expected
index 16e321022ae..b22542457f3 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-665/InsecureRmiJmxEnvironmentConfiguration.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-665/InsecureRmiJmxEnvironmentConfiguration.expected
@@ -1,18 +1,32 @@
edges
-| InsecureRmiServerInitialisation.java:32:31:32:45 | new HashMap(...) : HashMap | InsecureRmiServerInitialisation.java:34:59:34:61 | env |
-| InsecureRmiServerInitialisation.java:39:31:39:45 | new HashMap(...) : HashMap | InsecureRmiServerInitialisation.java:43:59:43:61 | env |
-| InsecureRmiServerInitialisation.java:57:31:57:45 | new HashMap(...) : HashMap | InsecureRmiServerInitialisation.java:61:59:61:61 | env |
+| ../../../stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java:23:12:23:29 | this [post update] : RMIConnectorServer | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) |
+| InsecureRmiJmxEnvironmentConfiguration.java:25:31:25:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:27:34:27:36 | env |
+| InsecureRmiJmxEnvironmentConfiguration.java:33:31:33:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:35:59:35:61 | env |
+| InsecureRmiJmxEnvironmentConfiguration.java:40:31:40:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:44:59:44:61 | env |
+| InsecureRmiJmxEnvironmentConfiguration.java:49:31:49:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:53:34:53:36 | env |
+| InsecureRmiJmxEnvironmentConfiguration.java:58:31:58:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:62:59:62:61 | env |
+| InsecureRmiJmxEnvironmentConfiguration.java:67:31:67:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:71:34:71:36 | env |
nodes
-| InsecureRmiServerInitialisation.java:13:5:13:69 | newJMXConnectorServer(...) | semmle.label | newJMXConnectorServer(...) |
-| InsecureRmiServerInitialisation.java:32:31:32:45 | new HashMap(...) : HashMap | semmle.label | new HashMap(...) : HashMap |
-| InsecureRmiServerInitialisation.java:34:59:34:61 | env | semmle.label | env |
-| InsecureRmiServerInitialisation.java:39:31:39:45 | new HashMap(...) : HashMap | semmle.label | new HashMap(...) : HashMap |
-| InsecureRmiServerInitialisation.java:43:59:43:61 | env | semmle.label | env |
-| InsecureRmiServerInitialisation.java:57:31:57:45 | new HashMap(...) : HashMap | semmle.label | new HashMap(...) : HashMap |
-| InsecureRmiServerInitialisation.java:61:59:61:61 | env | semmle.label | env |
+| ../../../stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java:23:12:23:29 | this [post update] : RMIConnectorServer | semmle.label | this [post update] : RMIConnectorServer |
+| InsecureRmiJmxEnvironmentConfiguration.java:13:5:13:69 | newJMXConnectorServer(...) | semmle.label | newJMXConnectorServer(...) |
+| InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | semmle.label | new RMIConnectorServer(...) |
+| InsecureRmiJmxEnvironmentConfiguration.java:25:31:25:45 | new HashMap(...) : HashMap | semmle.label | new HashMap(...) : HashMap |
+| InsecureRmiJmxEnvironmentConfiguration.java:27:34:27:36 | env | semmle.label | env |
+| InsecureRmiJmxEnvironmentConfiguration.java:33:31:33:45 | new HashMap(...) : HashMap | semmle.label | new HashMap(...) : HashMap |
+| InsecureRmiJmxEnvironmentConfiguration.java:35:59:35:61 | env | semmle.label | env |
+| InsecureRmiJmxEnvironmentConfiguration.java:40:31:40:45 | new HashMap(...) : HashMap | semmle.label | new HashMap(...) : HashMap |
+| InsecureRmiJmxEnvironmentConfiguration.java:44:59:44:61 | env | semmle.label | env |
+| InsecureRmiJmxEnvironmentConfiguration.java:49:31:49:45 | new HashMap(...) : HashMap | semmle.label | new HashMap(...) : HashMap |
+| InsecureRmiJmxEnvironmentConfiguration.java:53:34:53:36 | env | semmle.label | env |
+| InsecureRmiJmxEnvironmentConfiguration.java:58:31:58:45 | new HashMap(...) : HashMap | semmle.label | new HashMap(...) : HashMap |
+| InsecureRmiJmxEnvironmentConfiguration.java:62:59:62:61 | env | semmle.label | env |
+| InsecureRmiJmxEnvironmentConfiguration.java:67:31:67:45 | new HashMap(...) : HashMap | semmle.label | new HashMap(...) : HashMap |
+| InsecureRmiJmxEnvironmentConfiguration.java:71:34:71:36 | env | semmle.label | env |
#select
-| InsecureRmiServerInitialisation.java:13:5:13:69 | newJMXConnectorServer(...) | InsecureRmiServerInitialisation.java:13:5:13:69 | newJMXConnectorServer(...) | InsecureRmiServerInitialisation.java:13:5:13:69 | newJMXConnectorServer(...) | RMI/JMX server initialized with 'null' environment $@. Missing type restriction in RMI authentication method exposes the application to deserialization attacks. | InsecureRmiServerInitialisation.java:13:5:13:69 | newJMXConnectorServer(...) | here | InsecureRmiServerInitialisation.java:13:5:13:69 | newJMXConnectorServer(...) | source environment 'Map' |
-| InsecureRmiServerInitialisation.java:34:59:34:61 | env | InsecureRmiServerInitialisation.java:32:31:32:45 | new HashMap(...) : HashMap | InsecureRmiServerInitialisation.java:34:59:34:61 | env | RMI/JMX server initialized with insecure environment $@. The $@ never restricts accepted client objects to 'java.lang.String'. This exposes to deserialization attacks against the RMI authentication method. | InsecureRmiServerInitialisation.java:34:59:34:61 | env | here | InsecureRmiServerInitialisation.java:32:31:32:45 | new HashMap(...) | source environment 'Map' |
-| InsecureRmiServerInitialisation.java:61:59:61:61 | env | InsecureRmiServerInitialisation.java:57:31:57:45 | new HashMap(...) : HashMap | InsecureRmiServerInitialisation.java:61:59:61:61 | env | RMI/JMX server initialized with insecure environment $@. The $@ never restricts accepted client objects to 'java.lang.String'. This exposes to deserialization attacks against the RMI authentication method. | InsecureRmiServerInitialisation.java:61:59:61:61 | env | here | InsecureRmiServerInitialisation.java:57:31:57:45 | new HashMap(...) | source environment 'Map' |
-
-TODO RMI Server is missing due to import errors (See test java file)
+| InsecureRmiJmxEnvironmentConfiguration.java:13:5:13:69 | newJMXConnectorServer(...) | InsecureRmiJmxEnvironmentConfiguration.java:13:5:13:69 | newJMXConnectorServer(...) | InsecureRmiJmxEnvironmentConfiguration.java:13:5:13:69 | newJMXConnectorServer(...) | RMI/JMX server initialized with 'null' environment $@. Missing type restriction in RMI authentication method exposes the application to deserialization attacks. | InsecureRmiJmxEnvironmentConfiguration.java:13:5:13:69 | newJMXConnectorServer(...) | here | InsecureRmiJmxEnvironmentConfiguration.java:13:5:13:69 | newJMXConnectorServer(...) | source environment 'Map' |
+| InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | ../../../stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java:23:12:23:29 | this [post update] : RMIConnectorServer | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | RMI/JMX server initialized with 'null' environment $@. Missing type restriction in RMI authentication method exposes the application to deserialization attacks. | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | here | ../../../stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java:23:12:23:29 | this [post update] | source environment 'Map' |
+| InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | RMI/JMX server initialized with 'null' environment $@. Missing type restriction in RMI authentication method exposes the application to deserialization attacks. | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | here | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | source environment 'Map' |
+| InsecureRmiJmxEnvironmentConfiguration.java:27:34:27:36 | env | InsecureRmiJmxEnvironmentConfiguration.java:25:31:25:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:27:34:27:36 | env | RMI/JMX server initialized with insecure environment $@. The $@ never restricts accepted client objects to 'java.lang.String'. This exposes to deserialization attacks against the RMI authentication method. | InsecureRmiJmxEnvironmentConfiguration.java:27:34:27:36 | env | here | InsecureRmiJmxEnvironmentConfiguration.java:25:31:25:45 | new HashMap(...) | source environment 'Map' |
+| InsecureRmiJmxEnvironmentConfiguration.java:35:59:35:61 | env | InsecureRmiJmxEnvironmentConfiguration.java:33:31:33:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:35:59:35:61 | env | RMI/JMX server initialized with insecure environment $@. The $@ never restricts accepted client objects to 'java.lang.String'. This exposes to deserialization attacks against the RMI authentication method. | InsecureRmiJmxEnvironmentConfiguration.java:35:59:35:61 | env | here | InsecureRmiJmxEnvironmentConfiguration.java:33:31:33:45 | new HashMap(...) | source environment 'Map' |
+| InsecureRmiJmxEnvironmentConfiguration.java:62:59:62:61 | env | InsecureRmiJmxEnvironmentConfiguration.java:58:31:58:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:62:59:62:61 | env | RMI/JMX server initialized with insecure environment $@. The $@ never restricts accepted client objects to 'java.lang.String'. This exposes to deserialization attacks against the RMI authentication method. | InsecureRmiJmxEnvironmentConfiguration.java:62:59:62:61 | env | here | InsecureRmiJmxEnvironmentConfiguration.java:58:31:58:45 | new HashMap(...) | source environment 'Map' |
+| InsecureRmiJmxEnvironmentConfiguration.java:71:34:71:36 | env | InsecureRmiJmxEnvironmentConfiguration.java:67:31:67:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:71:34:71:36 | env | RMI/JMX server initialized with insecure environment $@. The $@ never restricts accepted client objects to 'java.lang.String'. This exposes to deserialization attacks against the RMI authentication method. | InsecureRmiJmxEnvironmentConfiguration.java:71:34:71:36 | env | here | InsecureRmiJmxEnvironmentConfiguration.java:67:31:67:45 | new HashMap(...) | source environment 'Map' |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-665/InsecureRmiJmxEnvironmentConfiguration.java b/java/ql/test/experimental/query-tests/security/CWE-665/InsecureRmiJmxEnvironmentConfiguration.java
index 1c2104218ea..b58d6841d3d 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-665/InsecureRmiJmxEnvironmentConfiguration.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-665/InsecureRmiJmxEnvironmentConfiguration.java
@@ -1,7 +1,7 @@
import java.io.IOException;
import javax.management.remote.JMXConnectorServerFactory;
-// import javax.management.remote.rmi.RMIConnectorServer; Importing this throws an error, therefore we can't test this
+import javax.management.remote.rmi.RMIConnectorServer;
import java.util.HashMap;
import java.util.Map;
@@ -15,7 +15,8 @@ public class InsecureRmiJmxEnvironmentConfiguration {
public void initInsecureRmiDueToNullEnv() throws IOException {
// Bad initializing env (arg1) with null
- // new RMIConnectorServer(null, null, null, null); Importing this throws an error, therefore we can't test this
+ new RMIConnectorServer(null, null, null, null);
+
}
public void initInsecureRmiDueToMissingEnvKeyValue() throws IOException {
@@ -23,7 +24,7 @@ public class InsecureRmiJmxEnvironmentConfiguration {
// "jmx.remote.rmi.server.credential.types"
Map env = new HashMap<>();
env.put("jmx.remote.x.daemon", "true");
- // new RMIConnectorServer(null, env, null, null); Importing this throws an error, therefore we can't test this
+ new RMIConnectorServer(null, env, null, null);
}
public void initInsecureJmxDueToMissingEnvKeyValue() throws IOException {
@@ -49,7 +50,7 @@ public class InsecureRmiJmxEnvironmentConfiguration {
env.put("jmx.remote.x.daemon", "true");
env.put("jmx.remote.rmi.server.credential.types",
new String[] { String[].class.getName(), String.class.getName() });
- // new RMIConnectorServer(null, env, null, null); Importing this throws an error, therefore we can't test this
+ new RMIConnectorServer(null, env, null, null);
}
public void secureeJmxConnectorServerConstants() throws IOException {
@@ -60,12 +61,13 @@ public class InsecureRmiJmxEnvironmentConfiguration {
new String[] { String[].class.getName(), String.class.getName() });
JMXConnectorServerFactory.newJMXConnectorServer(null, env, null);
}
+
public void secureeRmiConnectorServerConstants() throws IOException {
// Good
Map env = new HashMap<>();
env.put("jmx.remote.x.daemon", "true");
env.put("RMIConnectorServer.SERIAL_FILTER_PATTERN",
new String[] { String[].class.getName(), String.class.getName() });
- // new RMIConnectorServer(null, env, null, null); Importing this throws an error, therefore we can't test this
+ new RMIConnectorServer(null, env, null, null);
}
}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-665/options b/java/ql/test/experimental/query-tests/security/CWE-665/options
new file mode 100644
index 00000000000..7fc584f11e3
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-665/options
@@ -0,0 +1 @@
+//semmle-extractor-options: --javac-args -source 11 -target 11 -cp ${testdir}/../../../../experimental/stubs/javax-management-remote-rmi-0.0.1
diff --git a/java/ql/test/experimental/stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java b/java/ql/test/experimental/stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java
new file mode 100644
index 00000000000..677d2e49afe
--- /dev/null
+++ b/java/ql/test/experimental/stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java
@@ -0,0 +1,27 @@
+package javax.management.remote.rmi;
+
+import java.io.IOException;
+import java.util.Map;
+import java.io.IOException;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXServiceURL;
+import javax.management.MBeanServer;
+import javax.management.remote.rmi.RMIServerImpl;
+//import javax.management.remote.JMXConnectorServer;
+
+//public class RMIConnectorServerTEST extends JMXConnectorServer{
+public class RMIConnectorServer extends java.lang.Object {
+ public RMIConnectorServer(JMXServiceURL url, Map environment) throws IOException {
+ // stub;
+ }
+
+ public RMIConnectorServer(JMXServiceURL url, Map environment, MBeanServer mbeanServer)
+ throws IOException {
+ // stub;
+ }
+
+ public RMIConnectorServer(JMXServiceURL url, Map environment, RMIServerImpl rmiServerImpl,
+ MBeanServer mbeanServer) throws IOException {
+ // stub;
+ }
+}
diff --git a/java/ql/test/experimental/stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIServerImpl.java b/java/ql/test/experimental/stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIServerImpl.java
new file mode 100644
index 00000000000..e863a55a33b
--- /dev/null
+++ b/java/ql/test/experimental/stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIServerImpl.java
@@ -0,0 +1,10 @@
+package javax.management.remote.rmi;
+
+import java.util.Map;
+
+public class RMIServerImpl {
+ public RMIServerImpl(Map env) {
+ // stub;
+ }
+
+}
From 75f6ec1f0dabaef7b26c3ccfd8cca28edf21f129 Mon Sep 17 00:00:00 2001
From: Timo Mueller
Date: Tue, 25 May 2021 17:08:58 +0200
Subject: [PATCH 044/839] Updated test cases to include test for java10+
CREDENTIALS_FILTER_PATTERN constant
---
...ureRmiJmxEnvironmentConfiguration.expected | 14 +++++----
...nsecureRmiJmxEnvironmentConfiguration.java | 30 +++++++++++++++----
.../remote/rmi/RMIConnectorServer.java | 3 ++
3 files changed, 36 insertions(+), 11 deletions(-)
diff --git a/java/ql/test/experimental/query-tests/security/CWE-665/InsecureRmiJmxEnvironmentConfiguration.expected b/java/ql/test/experimental/query-tests/security/CWE-665/InsecureRmiJmxEnvironmentConfiguration.expected
index b22542457f3..fc3cb78662e 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-665/InsecureRmiJmxEnvironmentConfiguration.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-665/InsecureRmiJmxEnvironmentConfiguration.expected
@@ -1,13 +1,15 @@
edges
-| ../../../stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java:23:12:23:29 | this [post update] : RMIConnectorServer | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) |
+| ../../../stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java:26:12:26:29 | this [post update] : RMIConnectorServer | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) |
| InsecureRmiJmxEnvironmentConfiguration.java:25:31:25:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:27:34:27:36 | env |
| InsecureRmiJmxEnvironmentConfiguration.java:33:31:33:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:35:59:35:61 | env |
| InsecureRmiJmxEnvironmentConfiguration.java:40:31:40:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:44:59:44:61 | env |
| InsecureRmiJmxEnvironmentConfiguration.java:49:31:49:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:53:34:53:36 | env |
| InsecureRmiJmxEnvironmentConfiguration.java:58:31:58:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:62:59:62:61 | env |
| InsecureRmiJmxEnvironmentConfiguration.java:67:31:67:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:71:34:71:36 | env |
+| InsecureRmiJmxEnvironmentConfiguration.java:76:31:76:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:80:59:80:61 | env |
+| InsecureRmiJmxEnvironmentConfiguration.java:85:31:85:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:89:34:89:36 | env |
nodes
-| ../../../stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java:23:12:23:29 | this [post update] : RMIConnectorServer | semmle.label | this [post update] : RMIConnectorServer |
+| ../../../stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java:26:12:26:29 | this [post update] : RMIConnectorServer | semmle.label | this [post update] : RMIConnectorServer |
| InsecureRmiJmxEnvironmentConfiguration.java:13:5:13:69 | newJMXConnectorServer(...) | semmle.label | newJMXConnectorServer(...) |
| InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | semmle.label | new RMIConnectorServer(...) |
| InsecureRmiJmxEnvironmentConfiguration.java:25:31:25:45 | new HashMap(...) : HashMap | semmle.label | new HashMap(...) : HashMap |
@@ -22,11 +24,13 @@ nodes
| InsecureRmiJmxEnvironmentConfiguration.java:62:59:62:61 | env | semmle.label | env |
| InsecureRmiJmxEnvironmentConfiguration.java:67:31:67:45 | new HashMap(...) : HashMap | semmle.label | new HashMap(...) : HashMap |
| InsecureRmiJmxEnvironmentConfiguration.java:71:34:71:36 | env | semmle.label | env |
+| InsecureRmiJmxEnvironmentConfiguration.java:76:31:76:45 | new HashMap(...) : HashMap | semmle.label | new HashMap(...) : HashMap |
+| InsecureRmiJmxEnvironmentConfiguration.java:80:59:80:61 | env | semmle.label | env |
+| InsecureRmiJmxEnvironmentConfiguration.java:85:31:85:45 | new HashMap(...) : HashMap | semmle.label | new HashMap(...) : HashMap |
+| InsecureRmiJmxEnvironmentConfiguration.java:89:34:89:36 | env | semmle.label | env |
#select
| InsecureRmiJmxEnvironmentConfiguration.java:13:5:13:69 | newJMXConnectorServer(...) | InsecureRmiJmxEnvironmentConfiguration.java:13:5:13:69 | newJMXConnectorServer(...) | InsecureRmiJmxEnvironmentConfiguration.java:13:5:13:69 | newJMXConnectorServer(...) | RMI/JMX server initialized with 'null' environment $@. Missing type restriction in RMI authentication method exposes the application to deserialization attacks. | InsecureRmiJmxEnvironmentConfiguration.java:13:5:13:69 | newJMXConnectorServer(...) | here | InsecureRmiJmxEnvironmentConfiguration.java:13:5:13:69 | newJMXConnectorServer(...) | source environment 'Map' |
-| InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | ../../../stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java:23:12:23:29 | this [post update] : RMIConnectorServer | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | RMI/JMX server initialized with 'null' environment $@. Missing type restriction in RMI authentication method exposes the application to deserialization attacks. | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | here | ../../../stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java:23:12:23:29 | this [post update] | source environment 'Map' |
+| InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | ../../../stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java:26:12:26:29 | this [post update] : RMIConnectorServer | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | RMI/JMX server initialized with 'null' environment $@. Missing type restriction in RMI authentication method exposes the application to deserialization attacks. | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | here | ../../../stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java:26:12:26:29 | this [post update] | source environment 'Map' |
| InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | RMI/JMX server initialized with 'null' environment $@. Missing type restriction in RMI authentication method exposes the application to deserialization attacks. | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | here | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | source environment 'Map' |
| InsecureRmiJmxEnvironmentConfiguration.java:27:34:27:36 | env | InsecureRmiJmxEnvironmentConfiguration.java:25:31:25:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:27:34:27:36 | env | RMI/JMX server initialized with insecure environment $@. The $@ never restricts accepted client objects to 'java.lang.String'. This exposes to deserialization attacks against the RMI authentication method. | InsecureRmiJmxEnvironmentConfiguration.java:27:34:27:36 | env | here | InsecureRmiJmxEnvironmentConfiguration.java:25:31:25:45 | new HashMap(...) | source environment 'Map' |
| InsecureRmiJmxEnvironmentConfiguration.java:35:59:35:61 | env | InsecureRmiJmxEnvironmentConfiguration.java:33:31:33:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:35:59:35:61 | env | RMI/JMX server initialized with insecure environment $@. The $@ never restricts accepted client objects to 'java.lang.String'. This exposes to deserialization attacks against the RMI authentication method. | InsecureRmiJmxEnvironmentConfiguration.java:35:59:35:61 | env | here | InsecureRmiJmxEnvironmentConfiguration.java:33:31:33:45 | new HashMap(...) | source environment 'Map' |
-| InsecureRmiJmxEnvironmentConfiguration.java:62:59:62:61 | env | InsecureRmiJmxEnvironmentConfiguration.java:58:31:58:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:62:59:62:61 | env | RMI/JMX server initialized with insecure environment $@. The $@ never restricts accepted client objects to 'java.lang.String'. This exposes to deserialization attacks against the RMI authentication method. | InsecureRmiJmxEnvironmentConfiguration.java:62:59:62:61 | env | here | InsecureRmiJmxEnvironmentConfiguration.java:58:31:58:45 | new HashMap(...) | source environment 'Map' |
-| InsecureRmiJmxEnvironmentConfiguration.java:71:34:71:36 | env | InsecureRmiJmxEnvironmentConfiguration.java:67:31:67:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:71:34:71:36 | env | RMI/JMX server initialized with insecure environment $@. The $@ never restricts accepted client objects to 'java.lang.String'. This exposes to deserialization attacks against the RMI authentication method. | InsecureRmiJmxEnvironmentConfiguration.java:71:34:71:36 | env | here | InsecureRmiJmxEnvironmentConfiguration.java:67:31:67:45 | new HashMap(...) | source environment 'Map' |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-665/InsecureRmiJmxEnvironmentConfiguration.java b/java/ql/test/experimental/query-tests/security/CWE-665/InsecureRmiJmxEnvironmentConfiguration.java
index b58d6841d3d..67ba5c89ea8 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-665/InsecureRmiJmxEnvironmentConfiguration.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-665/InsecureRmiJmxEnvironmentConfiguration.java
@@ -53,21 +53,39 @@ public class InsecureRmiJmxEnvironmentConfiguration {
new RMIConnectorServer(null, env, null, null);
}
- public void secureeJmxConnectorServerConstants() throws IOException {
+ public void secureeJmxConnectorServerConstants1() throws IOException {
// Good
Map env = new HashMap<>();
env.put("jmx.remote.x.daemon", "true");
- env.put("RMIConnectorServer.SERIAL_FILTER_PATTERN",
- new String[] { String[].class.getName(), String.class.getName() });
+ env.put(RMIConnectorServer.CREDENTIALS_FILTER_PATTERN, "java.lang.String;!*"); // Deny everything but
+ // java.lang.String
JMXConnectorServerFactory.newJMXConnectorServer(null, env, null);
}
- public void secureeRmiConnectorServerConstants() throws IOException {
+ public void secureeRmiConnectorServerConstants1() throws IOException {
// Good
Map env = new HashMap<>();
env.put("jmx.remote.x.daemon", "true");
- env.put("RMIConnectorServer.SERIAL_FILTER_PATTERN",
- new String[] { String[].class.getName(), String.class.getName() });
+ String stringsOnlyFilter = "java.lang.String;!*"; // Deny everything but java.lang.String
+ env.put(RMIConnectorServer.CREDENTIALS_FILTER_PATTERN, stringsOnlyFilter);
+ new RMIConnectorServer(null, env, null, null);
+ }
+
+ public void secureeJmxConnectorServerConstants2() throws IOException {
+ // Good
+ Map env = new HashMap<>();
+ env.put("jmx.remote.x.daemon", "true");
+ env.put("jmx.remote.rmi.server.credentials.filter.pattern", "java.lang.String;!*"); // Deny everything but
+ // java.lang.String
+ JMXConnectorServerFactory.newJMXConnectorServer(null, env, null);
+ }
+
+ public void secureeRmiConnectorServerConstants2() throws IOException {
+ // Good
+ Map env = new HashMap<>();
+ env.put("jmx.remote.x.daemon", "true");
+ String stringsOnlyFilter = "java.lang.String;!*"; // Deny everything but java.lang.String
+ env.put("jmx.remote.rmi.server.credentials.filter.pattern", stringsOnlyFilter);
new RMIConnectorServer(null, env, null, null);
}
}
diff --git a/java/ql/test/experimental/stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java b/java/ql/test/experimental/stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java
index 677d2e49afe..0833611e02c 100644
--- a/java/ql/test/experimental/stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java
+++ b/java/ql/test/experimental/stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java
@@ -11,6 +11,9 @@ import javax.management.remote.rmi.RMIServerImpl;
//public class RMIConnectorServerTEST extends JMXConnectorServer{
public class RMIConnectorServer extends java.lang.Object {
+
+ public static final String CREDENTIALS_FILTER_PATTERN = "jmx.remote.rmi.server.credentials.filter.pattern";
+
public RMIConnectorServer(JMXServiceURL url, Map environment) throws IOException {
// stub;
}
From d6782767b7c289a68baaaf037245047a4ba9d844 Mon Sep 17 00:00:00 2001
From: haby0
Date: Mon, 31 May 2021 11:12:22 +0800
Subject: [PATCH 045/839] Fix typos
---
java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.ql | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.ql b/java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.ql
index fe990179de4..27ababae1d4 100644
--- a/java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.ql
+++ b/java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.ql
@@ -22,9 +22,9 @@ class UnsafeDeserializationConfig extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) { sink instanceof UnsafeDeserializationSink }
- override predicate isAdditionalTaintStep(DataFlow::Node prod, DataFlow::Node succ) {
+ override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(ClassInstanceExpr cie |
- cie.getArgument(0) = prod.asExpr() and
+ cie.getArgument(0) = pred.asExpr() and
cie = succ.asExpr() and
(
cie.getConstructor().getDeclaringType() instanceof JsonIoJsonReader or
@@ -36,7 +36,7 @@ class UnsafeDeserializationConfig extends TaintTracking::Configuration {
or
exists(MethodAccess ma |
ma.getMethod() instanceof BurlapInputInitMethod and
- ma.getArgument(0) = prod.asExpr() and
+ ma.getArgument(0) = pred.asExpr() and
ma.getQualifier() = succ.asExpr()
)
}
From d450aa2ce4de9be456ba42eca569181b060d1482 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Thu, 3 Jun 2021 18:02:29 +0200
Subject: [PATCH 046/839] C++: Add some testcases that require path
sensitivity.
---
.../semmle/tests/UseAfterFree.expected | 3 +++
.../CWE/CWE-416/semmle/tests/test.cpp | 21 +++++++++++++++++++
2 files changed, 24 insertions(+)
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-416/semmle/tests/UseAfterFree.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-416/semmle/tests/UseAfterFree.expected
index 92aa7a96756..6b1f8fa7801 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-416/semmle/tests/UseAfterFree.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-416/semmle/tests/UseAfterFree.expected
@@ -7,3 +7,6 @@
| test.cpp:170:6:170:9 | data | Memory pointed to by 'data' may have been previously freed $@ | test.cpp:165:2:165:5 | call to free | here |
| test.cpp:193:6:193:9 | data | Memory pointed to by 'data' may have been previously freed $@ | test.cpp:191:3:191:6 | call to free | here |
| test.cpp:201:6:201:6 | x | Memory pointed to by 'x' may have been previously freed $@ | test.cpp:200:2:200:9 | delete | here |
+| test.cpp:222:9:222:12 | data | Memory pointed to by 'data' may have been previously freed $@ | test.cpp:223:5:223:8 | call to free | here |
+| test.cpp:223:10:223:13 | data | Memory pointed to by 'data' may have been previously freed $@ | test.cpp:223:5:223:8 | call to free | here |
+| test.cpp:234:9:234:12 | data | Memory pointed to by 'data' may have been previously freed $@ | test.cpp:230:5:230:8 | call to free | here |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-416/semmle/tests/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-416/semmle/tests/test.cpp
index 7018af457ba..5dc4bb026e1 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-416/semmle/tests/test.cpp
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-416/semmle/tests/test.cpp
@@ -213,3 +213,24 @@ void regression_test_for_static_var_handling()
data = (char *)malloc(100*sizeof(char));
use(data); // GOOD
}
+
+void test16(int n, bool b) {
+ char* data = NULL;
+ for(int i = 0; i < n; ++i) {
+ if(b) data = (char*)malloc(10 * sizeof(char));
+ if(!b || data == NULL) return;
+ use(data); // GOOD [FALSE POSITIVE]
+ free(data); // GOOD [FALSE POSITIVE]
+ }
+}
+
+void test17(int n, bool b) {
+ char* data = (char*)malloc(10);
+ if(b) {
+ free(data);
+ }
+
+ if(!b) {
+ use(data); // GOOD [FALSE POSITIVE]
+ }
+}
From 4ddf4558a76792036eac5309afc5e90c53541ac5 Mon Sep 17 00:00:00 2001
From: Chris Smowton
Date: Fri, 4 Jun 2021 12:09:52 +0100
Subject: [PATCH 047/839] Merged simplified query
---
.../InsecureRmiJmxEnvironmentConfiguration.ql | 124 +++++-------------
...ureRmiJmxEnvironmentConfiguration.expected | 40 +-----
...nsecureRmiJmxEnvironmentConfiguration.java | 6 +-
.../query-tests/security/CWE-665/options | 2 +-
.../stubs/rmi-remote-0.0.0/README | 1 +
.../management/remote/rmi/RMIConnection.java | 6 +
.../remote/rmi/RMIConnectorServer.java | 34 +++++
.../management/remote/rmi/RMIServer.java | 3 +
.../management/remote/rmi/RMIServerImpl.java | 12 ++
9 files changed, 93 insertions(+), 135 deletions(-)
create mode 100644 java/ql/test/experimental/stubs/rmi-remote-0.0.0/README
create mode 100644 java/ql/test/experimental/stubs/rmi-remote-0.0.0/javax/management/remote/rmi/RMIConnection.java
create mode 100644 java/ql/test/experimental/stubs/rmi-remote-0.0.0/javax/management/remote/rmi/RMIConnectorServer.java
create mode 100644 java/ql/test/experimental/stubs/rmi-remote-0.0.0/javax/management/remote/rmi/RMIServer.java
create mode 100644 java/ql/test/experimental/stubs/rmi-remote-0.0.0/javax/management/remote/rmi/RMIServerImpl.java
diff --git a/java/ql/src/experimental/Security/CWE/CWE-665/InsecureRmiJmxEnvironmentConfiguration.ql b/java/ql/src/experimental/Security/CWE/CWE-665/InsecureRmiJmxEnvironmentConfiguration.ql
index 9bf195548c2..eab6f1f7717 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-665/InsecureRmiJmxEnvironmentConfiguration.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-665/InsecureRmiJmxEnvironmentConfiguration.ql
@@ -1,7 +1,7 @@
/**
* @name InsecureRmiJmxAuthenticationEnvironment
- * @description Creating a JMX/RMI server could lead to code execution through insecure deserialization if its environment does not restrict the types that can be deserialized.
- * @kind path-problem
+ * @description This query detects if a JMX/RMI server is created with a potentially dangerous environment, which could lead to code execution through insecure deserialization.
+ * @kind problem
* @problem.severity error
* @tags security
* external/cwe/cwe-665
@@ -11,11 +11,7 @@
import java
import semmle.code.java.dataflow.DataFlow
-import semmle.code.java.dataflow.DataFlow2
import semmle.code.java.Maps
-import DataFlow::PathGraph
-import semmle.code.java.dataflow.NullGuards
-import semmle.code.java.dataflow.Nullness
/** Holds if `constructor` instantiates an RMI or JMX server. */
predicate isRmiOrJmxServerCreateConstructor(Constructor constructor) {
@@ -31,18 +27,24 @@ predicate isRmiOrJmxServerCreateMethod(Method method) {
}
/**
- * Models flow from `new HashMap<>()` to a
- * `map.put("jmx.remote.rmi.server.credential.types", value)` call.
+ * Models flow from the qualifier of a
+ * `map.put("jmx.remote.rmi.server.credential.types", value)` call
+ * to an RMI or JMX initialisation call.
*/
-class MapToPutCredentialstypeConfiguration extends DataFlow2::Configuration {
- MapToPutCredentialstypeConfiguration() { this = "MapToPutCredentialstypeConfiguration" }
+class SafeFlow extends DataFlow::Configuration {
+ SafeFlow() { this = "MapToPutCredentialstypeConfiguration" }
- override predicate isSource(DataFlow2::Node source) {
- source.asExpr().(ClassInstanceExpr).getConstructedType() instanceof MapType
+ override predicate isSource(DataFlow::Node source) { putsCredentialtypesKey(source.asExpr()) }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(Call c |
+ isRmiOrJmxServerCreateConstructor(c.getCallee()) or
+ isRmiOrJmxServerCreateMethod(c.getCallee())
+ |
+ sink.asExpr() = c.getArgument(1)
+ )
}
- override predicate isSink(DataFlow2::Node sink) { putsCredentialtypesKey(sink.asExpr()) }
-
/**
* Holds if a `put` call on `qualifier` puts a key match
* into the map.
@@ -53,8 +55,11 @@ class MapToPutCredentialstypeConfiguration extends DataFlow2::Configuration {
"jmx.remote.rmi.server.credential.types" or
put.getKey().(CompileTimeConstantExpr).getStringValue() =
"jmx.remote.rmi.server.credentials.filter.pattern" or
- put.getKey().toString() = "RMIConnectorServer.CREDENTIAL_TYPES" or // This can probably be solved more nicely
- put.getKey().toString() = "RMIConnectorServer.CREDENTIALS_FILTER_PATTERN" // This can probably be solved more nicely
+ put.getKey()
+ .(FieldAccess)
+ .getField()
+ .hasQualifiedName("javax.management.remote.rmi", "RMIConnectorServer",
+ ["CREDENTIAL_TYPES", "CREDENTIALS_FILTER_PATTERN", "SERIAL_FILTER_PATTERN"])
|
put.getQualifier() = qualifier and
put.getMethod().(MapMethod).getReceiverKeyType() instanceof TypeString and
@@ -63,90 +68,21 @@ class MapToPutCredentialstypeConfiguration extends DataFlow2::Configuration {
}
}
-/**
- * Models flow from `new HashMap<>()` variable which is later used as environment during
- * a JMX/RMI server initialization with `newJMXConnectorServer(...)` or `RMIConnectorServer(...)`
- */
-class MapToRmiServerInitConfiguration extends DataFlow::Configuration {
- MapToRmiServerInitConfiguration() { this = "MapToRmiServerInitConfiguration" }
-
- override predicate isSource(DataFlow::Node source) {
- source.asExpr().(ClassInstanceExpr).getConstructedType() instanceof MapType
- }
-
- override predicate isSink(DataFlow::Node sink) {
- exists(ConstructorCall ccall |
- sink.asExpr() = ccall.getArgument(1) and
- isRmiOrJmxServerCreateConstructor(ccall.getConstructor())
- )
- or
- exists(MethodAccess ma |
- sink.asExpr() = ma.getArgument(1) and
- isRmiOrJmxServerCreateMethod(ma.getMethod())
- )
- }
-}
-
-/** Models if any JMX/RMI server are initialized with a null environment */
-class FlowServerInitializedWithNullEnv extends DataFlow::Configuration {
- FlowServerInitializedWithNullEnv() { this = "FlowServerInitializedWithNullEnv" }
-
- override predicate isSource(DataFlow::Node source) { any() }
-
- override predicate isSink(DataFlow::Node sink) {
- exists(ConstructorCall ccall |
- sink.asExpr() = ccall and
- isRmiOrJmxServerCreateConstructor(ccall.getConstructor()) and
- ccall.getArgument(1) = alwaysNullExpr()
- )
- or
- exists(MethodAccess ma |
- sink.asExpr() = ma and
- isRmiOrJmxServerCreateMethod(ma.getMethod()) and
- ma.getArgument(1) = alwaysNullExpr()
- )
- }
-}
-
-/** Holds if within the passed PathNode a "jmx.remote.rmi.server.credential.types" is set. */
-predicate mapFlowContainsCredentialtype(DataFlow::PathNode source) {
- exists(MapToPutCredentialstypeConfiguration conf | conf.hasFlow(source.getNode(), _))
-}
-
/** Gets a string describing why the application is vulnerable, depending on if the vulnerability is present due to a) a null environment b) an insecurely set environment map */
-bindingset[source]
-string getRmiResult(DataFlow::PathNode source) {
+string getRmiResult(Expr e) {
// We got a Map so we have a source and a sink node
- if source.getNode().getType() instanceof MapType
+ if e instanceof NullLiteral
then
result =
- "RMI/JMX server initialized with insecure environment $@. The $@ never restricts accepted client objects to 'java.lang.String'. This exposes to deserialization attacks against the RMI authentication method."
+ "RMI/JMX server initialized with a null environment. Missing type restriction in RMI authentication method exposes the application to deserialization attacks."
else
- // The environment is not a map so we most likely have a "null" environment and therefore only a sink
result =
- "RMI/JMX server initialized with 'null' environment $@. Missing type restriction in RMI authentication method exposes the application to deserialization attacks."
+ "RMI/JMX server initialized with insecure environment $@, which never restricts accepted client objects to 'java.lang.String'. This exposes to deserialization attacks against the RMI authentication method."
}
-/** Holds for any map flow paths with **no** jmx.remote.rmi.server.credential.types set */
-predicate hasVulnerableMapFlow(DataFlow::PathNode source, DataFlow::PathNode sink) {
- exists(MapToRmiServerInitConfiguration dataflow |
- dataflow.hasFlowPath(source, sink) and
- not mapFlowContainsCredentialtype(source)
- )
-}
-
-from
- DataFlow::PathNode source, DataFlow::PathNode sink,
- FlowServerInitializedWithNullEnv initNullDataflow
+from Call c, Expr envArg
where
- // Check if server is created with null env
- initNullDataflow.hasFlowPath(source, sink)
- or
- /*
- * The map created by `new HashMap()` has to a) flow to the sink and
- * b) there must not exist a (different) sink that would put `"jmx.remote.rmi.server.credential.types"` into `source`.
- */
-
- hasVulnerableMapFlow(source, sink)
-select sink.getNode(), source, sink, getRmiResult(source), sink.getNode(), "here", source.getNode(),
- "source environment 'Map'"
+ (isRmiOrJmxServerCreateConstructor(c.getCallee()) or isRmiOrJmxServerCreateMethod(c.getCallee())) and
+ envArg = c.getArgument(1) and
+ not any(SafeFlow conf).hasFlowToExpr(envArg)
+select c, getRmiResult(envArg), envArg, envArg.toString()
diff --git a/java/ql/test/experimental/query-tests/security/CWE-665/InsecureRmiJmxEnvironmentConfiguration.expected b/java/ql/test/experimental/query-tests/security/CWE-665/InsecureRmiJmxEnvironmentConfiguration.expected
index fc3cb78662e..b2bd1f2ffb1 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-665/InsecureRmiJmxEnvironmentConfiguration.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-665/InsecureRmiJmxEnvironmentConfiguration.expected
@@ -1,36 +1,4 @@
-edges
-| ../../../stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java:26:12:26:29 | this [post update] : RMIConnectorServer | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) |
-| InsecureRmiJmxEnvironmentConfiguration.java:25:31:25:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:27:34:27:36 | env |
-| InsecureRmiJmxEnvironmentConfiguration.java:33:31:33:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:35:59:35:61 | env |
-| InsecureRmiJmxEnvironmentConfiguration.java:40:31:40:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:44:59:44:61 | env |
-| InsecureRmiJmxEnvironmentConfiguration.java:49:31:49:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:53:34:53:36 | env |
-| InsecureRmiJmxEnvironmentConfiguration.java:58:31:58:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:62:59:62:61 | env |
-| InsecureRmiJmxEnvironmentConfiguration.java:67:31:67:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:71:34:71:36 | env |
-| InsecureRmiJmxEnvironmentConfiguration.java:76:31:76:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:80:59:80:61 | env |
-| InsecureRmiJmxEnvironmentConfiguration.java:85:31:85:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:89:34:89:36 | env |
-nodes
-| ../../../stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java:26:12:26:29 | this [post update] : RMIConnectorServer | semmle.label | this [post update] : RMIConnectorServer |
-| InsecureRmiJmxEnvironmentConfiguration.java:13:5:13:69 | newJMXConnectorServer(...) | semmle.label | newJMXConnectorServer(...) |
-| InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | semmle.label | new RMIConnectorServer(...) |
-| InsecureRmiJmxEnvironmentConfiguration.java:25:31:25:45 | new HashMap(...) : HashMap | semmle.label | new HashMap(...) : HashMap |
-| InsecureRmiJmxEnvironmentConfiguration.java:27:34:27:36 | env | semmle.label | env |
-| InsecureRmiJmxEnvironmentConfiguration.java:33:31:33:45 | new HashMap(...) : HashMap | semmle.label | new HashMap(...) : HashMap |
-| InsecureRmiJmxEnvironmentConfiguration.java:35:59:35:61 | env | semmle.label | env |
-| InsecureRmiJmxEnvironmentConfiguration.java:40:31:40:45 | new HashMap(...) : HashMap | semmle.label | new HashMap(...) : HashMap |
-| InsecureRmiJmxEnvironmentConfiguration.java:44:59:44:61 | env | semmle.label | env |
-| InsecureRmiJmxEnvironmentConfiguration.java:49:31:49:45 | new HashMap(...) : HashMap | semmle.label | new HashMap(...) : HashMap |
-| InsecureRmiJmxEnvironmentConfiguration.java:53:34:53:36 | env | semmle.label | env |
-| InsecureRmiJmxEnvironmentConfiguration.java:58:31:58:45 | new HashMap(...) : HashMap | semmle.label | new HashMap(...) : HashMap |
-| InsecureRmiJmxEnvironmentConfiguration.java:62:59:62:61 | env | semmle.label | env |
-| InsecureRmiJmxEnvironmentConfiguration.java:67:31:67:45 | new HashMap(...) : HashMap | semmle.label | new HashMap(...) : HashMap |
-| InsecureRmiJmxEnvironmentConfiguration.java:71:34:71:36 | env | semmle.label | env |
-| InsecureRmiJmxEnvironmentConfiguration.java:76:31:76:45 | new HashMap(...) : HashMap | semmle.label | new HashMap(...) : HashMap |
-| InsecureRmiJmxEnvironmentConfiguration.java:80:59:80:61 | env | semmle.label | env |
-| InsecureRmiJmxEnvironmentConfiguration.java:85:31:85:45 | new HashMap(...) : HashMap | semmle.label | new HashMap(...) : HashMap |
-| InsecureRmiJmxEnvironmentConfiguration.java:89:34:89:36 | env | semmle.label | env |
-#select
-| InsecureRmiJmxEnvironmentConfiguration.java:13:5:13:69 | newJMXConnectorServer(...) | InsecureRmiJmxEnvironmentConfiguration.java:13:5:13:69 | newJMXConnectorServer(...) | InsecureRmiJmxEnvironmentConfiguration.java:13:5:13:69 | newJMXConnectorServer(...) | RMI/JMX server initialized with 'null' environment $@. Missing type restriction in RMI authentication method exposes the application to deserialization attacks. | InsecureRmiJmxEnvironmentConfiguration.java:13:5:13:69 | newJMXConnectorServer(...) | here | InsecureRmiJmxEnvironmentConfiguration.java:13:5:13:69 | newJMXConnectorServer(...) | source environment 'Map' |
-| InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | ../../../stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java:26:12:26:29 | this [post update] : RMIConnectorServer | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | RMI/JMX server initialized with 'null' environment $@. Missing type restriction in RMI authentication method exposes the application to deserialization attacks. | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | here | ../../../stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java:26:12:26:29 | this [post update] | source environment 'Map' |
-| InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | RMI/JMX server initialized with 'null' environment $@. Missing type restriction in RMI authentication method exposes the application to deserialization attacks. | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | here | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | source environment 'Map' |
-| InsecureRmiJmxEnvironmentConfiguration.java:27:34:27:36 | env | InsecureRmiJmxEnvironmentConfiguration.java:25:31:25:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:27:34:27:36 | env | RMI/JMX server initialized with insecure environment $@. The $@ never restricts accepted client objects to 'java.lang.String'. This exposes to deserialization attacks against the RMI authentication method. | InsecureRmiJmxEnvironmentConfiguration.java:27:34:27:36 | env | here | InsecureRmiJmxEnvironmentConfiguration.java:25:31:25:45 | new HashMap(...) | source environment 'Map' |
-| InsecureRmiJmxEnvironmentConfiguration.java:35:59:35:61 | env | InsecureRmiJmxEnvironmentConfiguration.java:33:31:33:45 | new HashMap(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:35:59:35:61 | env | RMI/JMX server initialized with insecure environment $@. The $@ never restricts accepted client objects to 'java.lang.String'. This exposes to deserialization attacks against the RMI authentication method. | InsecureRmiJmxEnvironmentConfiguration.java:35:59:35:61 | env | here | InsecureRmiJmxEnvironmentConfiguration.java:33:31:33:45 | new HashMap(...) | source environment 'Map' |
+| InsecureRmiJmxEnvironmentConfiguration.java:12:5:12:69 | newJMXConnectorServer(...) | RMI/JMX server initialized with a null environment. Missing type restriction in RMI authentication method exposes the application to deserialization attacks. | InsecureRmiJmxEnvironmentConfiguration.java:12:59:12:62 | null | null |
+| InsecureRmiJmxEnvironmentConfiguration.java:17:5:17:50 | new RMIConnectorServer(...) | RMI/JMX server initialized with a null environment. Missing type restriction in RMI authentication method exposes the application to deserialization attacks. | InsecureRmiJmxEnvironmentConfiguration.java:17:34:17:37 | null | null |
+| InsecureRmiJmxEnvironmentConfiguration.java:25:5:25:49 | new RMIConnectorServer(...) | RMI/JMX server initialized with insecure environment $@, which never restricts accepted client objects to 'java.lang.String'. This exposes to deserialization attacks against the RMI authentication method. | InsecureRmiJmxEnvironmentConfiguration.java:25:34:25:36 | env | env |
+| InsecureRmiJmxEnvironmentConfiguration.java:33:5:33:68 | newJMXConnectorServer(...) | RMI/JMX server initialized with insecure environment $@, which never restricts accepted client objects to 'java.lang.String'. This exposes to deserialization attacks against the RMI authentication method. | InsecureRmiJmxEnvironmentConfiguration.java:33:59:33:61 | env | env |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-665/InsecureRmiJmxEnvironmentConfiguration.java b/java/ql/test/experimental/query-tests/security/CWE-665/InsecureRmiJmxEnvironmentConfiguration.java
index 67ba5c89ea8..f1294847fcc 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-665/InsecureRmiJmxEnvironmentConfiguration.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-665/InsecureRmiJmxEnvironmentConfiguration.java
@@ -1,6 +1,5 @@
import java.io.IOException;
import javax.management.remote.JMXConnectorServerFactory;
-
import javax.management.remote.rmi.RMIConnectorServer;
import java.util.HashMap;
@@ -16,7 +15,6 @@ public class InsecureRmiJmxEnvironmentConfiguration {
public void initInsecureRmiDueToNullEnv() throws IOException {
// Bad initializing env (arg1) with null
new RMIConnectorServer(null, null, null, null);
-
}
public void initInsecureRmiDueToMissingEnvKeyValue() throws IOException {
@@ -71,7 +69,7 @@ public class InsecureRmiJmxEnvironmentConfiguration {
new RMIConnectorServer(null, env, null, null);
}
- public void secureeJmxConnectorServerConstants2() throws IOException {
+ public void secureJmxConnectorServerConstants2() throws IOException {
// Good
Map env = new HashMap<>();
env.put("jmx.remote.x.daemon", "true");
@@ -80,7 +78,7 @@ public class InsecureRmiJmxEnvironmentConfiguration {
JMXConnectorServerFactory.newJMXConnectorServer(null, env, null);
}
- public void secureeRmiConnectorServerConstants2() throws IOException {
+ public void secureRmiConnectorServerConstants2() throws IOException {
// Good
Map env = new HashMap<>();
env.put("jmx.remote.x.daemon", "true");
diff --git a/java/ql/test/experimental/query-tests/security/CWE-665/options b/java/ql/test/experimental/query-tests/security/CWE-665/options
index 7fc584f11e3..4ecad5fd356 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-665/options
+++ b/java/ql/test/experimental/query-tests/security/CWE-665/options
@@ -1 +1 @@
-//semmle-extractor-options: --javac-args -source 11 -target 11 -cp ${testdir}/../../../../experimental/stubs/javax-management-remote-rmi-0.0.1
+//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/rmi-remote-0.0.0
diff --git a/java/ql/test/experimental/stubs/rmi-remote-0.0.0/README b/java/ql/test/experimental/stubs/rmi-remote-0.0.0/README
new file mode 100644
index 00000000000..3ff60bac644
--- /dev/null
+++ b/java/ql/test/experimental/stubs/rmi-remote-0.0.0/README
@@ -0,0 +1 @@
+This is a workaround for a bug in which the extractor can't resolve type javax.management.remote.rmi.RMIConnectorServer even though it has been part of the JDK since Java 5
diff --git a/java/ql/test/experimental/stubs/rmi-remote-0.0.0/javax/management/remote/rmi/RMIConnection.java b/java/ql/test/experimental/stubs/rmi-remote-0.0.0/javax/management/remote/rmi/RMIConnection.java
new file mode 100644
index 00000000000..68b41b3eb3a
--- /dev/null
+++ b/java/ql/test/experimental/stubs/rmi-remote-0.0.0/javax/management/remote/rmi/RMIConnection.java
@@ -0,0 +1,6 @@
+package javax.management.remote.rmi;
+
+import java.rmi.Remote;
+import java.io.Closeable;
+
+interface RMIConnection extends Closeable, Remote { }
diff --git a/java/ql/test/experimental/stubs/rmi-remote-0.0.0/javax/management/remote/rmi/RMIConnectorServer.java b/java/ql/test/experimental/stubs/rmi-remote-0.0.0/javax/management/remote/rmi/RMIConnectorServer.java
new file mode 100644
index 00000000000..d6f454c787c
--- /dev/null
+++ b/java/ql/test/experimental/stubs/rmi-remote-0.0.0/javax/management/remote/rmi/RMIConnectorServer.java
@@ -0,0 +1,34 @@
+package javax.management.remote.rmi;
+
+import java.util.Map;
+
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXServiceURL;
+import javax.management.remote.MBeanServerForwarder;
+import javax.management.MBeanServer;
+
+// Note this is a partial stub sufficient to the needs of tests for CWE-665
+public class RMIConnectorServer extends JMXConnectorServer {
+
+ public RMIConnectorServer(JMXServiceURL url, Map environment) { }
+ public RMIConnectorServer(JMXServiceURL url, Map environment, MBeanServer mbeanServer) { }
+ public RMIConnectorServer(JMXServiceURL url, Map environment, RMIServerImpl rmiServerImpl, MBeanServer mbeanServer) { }
+
+ public static String CREDENTIAL_TYPES = "";
+ public static String CREDENTIALS_FILTER_PATTERN = "";
+ public static String JNDI_REBIND_ATTRIBUTE = "";
+ public static String RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE = "";
+ public static String RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE = "";
+ public static String SERIAL_FILTER_PATTERN = "";
+
+ public Map getAttributes() { return null; }
+ public JMXServiceURL getAddress() { return null; }
+ public String[] getConnectionIds() { return null; }
+ public boolean isActive() { return true; }
+ public void setMBeanServerForwarder(MBeanServerForwarder mbsf) { }
+ public void start() { }
+ public void stop() { }
+ public JMXConnector toJMXConnector(Map env) { return null; }
+
+}
diff --git a/java/ql/test/experimental/stubs/rmi-remote-0.0.0/javax/management/remote/rmi/RMIServer.java b/java/ql/test/experimental/stubs/rmi-remote-0.0.0/javax/management/remote/rmi/RMIServer.java
new file mode 100644
index 00000000000..d08429b1dd6
--- /dev/null
+++ b/java/ql/test/experimental/stubs/rmi-remote-0.0.0/javax/management/remote/rmi/RMIServer.java
@@ -0,0 +1,3 @@
+package javax.management.remote.rmi;
+
+interface RMIServer { }
diff --git a/java/ql/test/experimental/stubs/rmi-remote-0.0.0/javax/management/remote/rmi/RMIServerImpl.java b/java/ql/test/experimental/stubs/rmi-remote-0.0.0/javax/management/remote/rmi/RMIServerImpl.java
new file mode 100644
index 00000000000..91466e087b3
--- /dev/null
+++ b/java/ql/test/experimental/stubs/rmi-remote-0.0.0/javax/management/remote/rmi/RMIServerImpl.java
@@ -0,0 +1,12 @@
+package javax.management.remote.rmi;
+
+import java.io.Closeable;
+import java.rmi.Remote;
+
+public class RMIServerImpl implements Closeable, RMIServer {
+
+ public void close() { }
+ public String getVersion() { return null; }
+ public RMIConnection newClient(Object credentials) { return null; }
+
+}
From d77d0c9e1071d4bd3af7ab85e9ace2c997531020 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Mon, 7 Jun 2021 17:35:03 +0200
Subject: [PATCH 048/839] Added summaries for Spring PropertyValues
---
.../code/java/dataflow/ExternalFlow.qll | 1 +
.../code/java/frameworks/spring/Spring.qll | 1 +
.../java/frameworks/spring/SpringBeans.qll | 41 ++++++
.../frameworks/spring/beans/Test.java | 83 +++++++++++
.../frameworks/spring/beans/options | 1 +
.../frameworks/spring/beans/test.expected | 0
.../frameworks/spring/beans/test.ql | 67 +++++++++
.../beans/MutablePropertyValues.java | 135 ++++++++++++++++++
.../springframework/beans/PropertyValue.java | 70 +++++++++
.../springframework/beans/PropertyValues.java | 46 ++++++
10 files changed, 445 insertions(+)
create mode 100644 java/ql/src/semmle/code/java/frameworks/spring/SpringBeans.qll
create mode 100644 java/ql/test/library-tests/frameworks/spring/beans/Test.java
create mode 100644 java/ql/test/library-tests/frameworks/spring/beans/options
create mode 100644 java/ql/test/library-tests/frameworks/spring/beans/test.expected
create mode 100644 java/ql/test/library-tests/frameworks/spring/beans/test.ql
create mode 100644 java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/MutablePropertyValues.java
create mode 100644 java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/PropertyValue.java
create mode 100644 java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/PropertyValues.java
diff --git a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll
index 8080bd28ab6..506a3456b44 100644
--- a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll
+++ b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll
@@ -87,6 +87,7 @@ private module Frameworks {
private import semmle.code.java.security.LdapInjection
private import semmle.code.java.security.XPath
private import semmle.code.java.security.JexlInjection
+ private import semmle.code.java.frameworks.spring.Spring
}
private predicate sourceModelCsv(string row) {
diff --git a/java/ql/src/semmle/code/java/frameworks/spring/Spring.qll b/java/ql/src/semmle/code/java/frameworks/spring/Spring.qll
index 2b09288610e..648b9beb0a0 100644
--- a/java/ql/src/semmle/code/java/frameworks/spring/Spring.qll
+++ b/java/ql/src/semmle/code/java/frameworks/spring/Spring.qll
@@ -6,6 +6,7 @@ import semmle.code.java.frameworks.spring.SpringAttribute
import semmle.code.java.frameworks.spring.SpringAutowire
import semmle.code.java.frameworks.spring.SpringBean
import semmle.code.java.frameworks.spring.SpringBeanFile
+import semmle.code.java.frameworks.spring.SpringBeans
import semmle.code.java.frameworks.spring.SpringBeanRefType
import semmle.code.java.frameworks.spring.SpringComponentScan
import semmle.code.java.frameworks.spring.SpringConstructorArg
diff --git a/java/ql/src/semmle/code/java/frameworks/spring/SpringBeans.qll b/java/ql/src/semmle/code/java/frameworks/spring/SpringBeans.qll
new file mode 100644
index 00000000000..6c3936ee772
--- /dev/null
+++ b/java/ql/src/semmle/code/java/frameworks/spring/SpringBeans.qll
@@ -0,0 +1,41 @@
+import java
+import semmle.code.java.dataflow.ExternalFlow
+
+module SpringBeans {
+ private class FlowSummaries extends SummaryModelCsv {
+ override predicate row(string row) {
+ row =
+ [
+ "org.springframework.beans;PropertyValue;false;PropertyValue;(String,Object);;Argument[0];MapKey of Argument[-1];value",
+ "org.springframework.beans;PropertyValue;false;PropertyValue;(String,Object);;Argument[1];MapValue of Argument[-1];value",
+ "org.springframework.beans;PropertyValue;false;PropertyValue;(PropertyValue);;Argument[0];Argument[-1];value",
+ "org.springframework.beans;PropertyValue;false;PropertyValue;(PropertyValue,Object);;MapKey of Argument[0];MapKey of Argument[-1];value",
+ "org.springframework.beans;PropertyValue;false;getName;;;MapKey of Argument[-1];ReturnValue;value",
+ "org.springframework.beans;PropertyValue;false;getValue;;;MapValue of Argument[-1];ReturnValue;value",
+ "org.springframework.beans;PropertyValues;true;getPropertyValue;;;MapValue of Argument[-1];ReturnValue;value",
+ "org.springframework.beans;PropertyValues;true;getPropertyValues;;;Element of Argument[-1];ArrayElement of ReturnValue;value",
+ "org.springframework.beans;MutablePropertyValues;true;add;(String,Object);;Argument[0];MapKey of Element of Argument[-1];value",
+ "org.springframework.beans;MutablePropertyValues;true;add;(String,Object);;Argument[0];MapKey of Element of ReturnValue;value",
+ "org.springframework.beans;MutablePropertyValues;true;add;(String,Object);;Argument[1];MapValue of Element of Argument[-1];value",
+ "org.springframework.beans;MutablePropertyValues;true;add;(String,Object);;Argument[1];MapValue of Element of ReturnValue;value",
+ "org.springframework.beans;MutablePropertyValues;true;addPropertyValue;(PropertyValue);;Argument[0];Element of Argument[-1];value",
+ "org.springframework.beans;MutablePropertyValues;true;addPropertyValue;(PropertyValue);;Argument[0];Element of ReturnValue;value",
+ "org.springframework.beans;MutablePropertyValues;true;addPropertyValue;(String,Object);;Argument[0];MapKey of Element of Argument[-1];value",
+ "org.springframework.beans;MutablePropertyValues;true;addPropertyValue;(String,Object);;Argument[1];MapValue of Element of Argument[-1];value",
+ "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(Map);;MapKey of Argument[0];MapKey of Element of Argument[-1];value",
+ "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(Map);;MapKey of Argument[0];MapKey of Element of ReturnValue;value",
+ "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(Map);;MapValue of Argument[0];MapValue of Element of Argument[-1];value",
+ "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(Map);;MapValue of Argument[0];MapValue of Element of ReturnValue;value",
+ "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(PropertyValues);;MapKey of Element of Argument[0];MapKey of Element of Argument[-1];value",
+ "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(PropertyValues);;MapKey of Element of Argument[0];MapKey of Element of ReturnValue;value",
+ "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(PropertyValues);;MapValue of Element of Argument[0];MapValue of Element of Argument[-1];value",
+ "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(PropertyValues);;MapValue of Element of Argument[0];MapValue of Element of ReturnValue;value",
+ "org.springframework.beans;MutablePropertyValues;true;get;;;MapValue of Element of Argument[-1];ReturnValue;value",
+ "org.springframework.beans;MutablePropertyValues;true;getPropertyValue;;;Element of Argument[-1];ReturnValue;value",
+ "org.springframework.beans;MutablePropertyValues;true;getPropertyValueList;;;Element of Argument[-1];Element of ReturnValue;value",
+ "org.springframework.beans;MutablePropertyValues;true;getPropertyValues;;;Element of Argument[-1];ArrayElement of ReturnValue;value",
+ "org.springframework.beans;MutablePropertyValues;true;setPropertyValueAt;;;Argument[0];Element of Argument[-1];value"
+ ]
+ }
+ }
+}
diff --git a/java/ql/test/library-tests/frameworks/spring/beans/Test.java b/java/ql/test/library-tests/frameworks/spring/beans/Test.java
new file mode 100644
index 00000000000..0075ea88cf9
--- /dev/null
+++ b/java/ql/test/library-tests/frameworks/spring/beans/Test.java
@@ -0,0 +1,83 @@
+package generatedtest;
+
+import org.springframework.beans.PropertyValue;
+
+
+public class Test {
+ Object getMapKey(Object container) {
+ return null;
+ }
+
+ Object getMapValue(Object container) {
+ return null;
+ }
+
+ Object newWithMapKey(Object element) {
+ return null;
+ }
+
+ Object newWithMapValue(Object element) {
+ return null;
+ }
+
+ Object source() {
+ return null;
+ }
+
+ void sink(Object o) {}
+
+ public void test() {
+ // @formatter:off
+ // "org.springframework.beans;PropertyValue;false;;(String,Object);;Argument[0];MapKey of Argument[-1];value",
+ {
+ PropertyValue v = new PropertyValue((String) source(), null);
+ sink(newWithMapKey(v)); // $hasValueFlow
+ sink(newWithMapValue(v)); // Safe
+ }
+ // "org.springframework.beans;PropertyValue;false;;(String,Object);;Argument[1];MapValue of Argument[-1];value",
+ {
+ PropertyValue v = new PropertyValue("", source());
+ sink(newWithMapKey(v)); // Safe
+ sink(newWithMapValue(v)); // $hasValueFlow
+ }
+ // "org.springframework.beans;PropertyValue;false;;(PropertyValue);;Argument[0];Argument[-1];value",
+ {
+ PropertyValue v1 = new PropertyValue((String) source(), null);
+ PropertyValue v2 = new PropertyValue(v1);
+ sink(newWithMapKey(v2)); // $hasValueFlow
+ sink(newWithMapValue(v2)); // Safe
+ PropertyValue v3 = new PropertyValue("", source());
+ PropertyValue v4 = new PropertyValue(v3);
+ sink(newWithMapKey(v4)); // Safe
+ sink(newWithMapValue(v4)); // $hasValueFlow
+ }
+ // "org.springframework.beans;PropertyValue;false;;(PropertyValue,Object);;MapKey of Argument[0];MapKey of Argument[-1];value",
+ // "org.springframework.beans;PropertyValue;false;getName;;;MapKey of Argument[-1];ReturnValue;value",
+ // "org.springframework.beans;PropertyValue;false;getValue;;;MapValue of Argument[-1];ReturnValue;value",
+ // "org.springframework.beans;PropertyValues;true;getPropertyValue;;;MapValue of Argument[-1];ReturnValue;value",
+ // "org.springframework.beans;PropertyValues;true;getPropertyValues;;;Element of Argument[-1];ArrayElement of ReturnValue;value",
+ // "org.springframework.beans;MutablePropertyValues;true;add;(String,Object);;Argument[0];MapKey of Element of Argument[-1];value",
+ // "org.springframework.beans;MutablePropertyValues;true;add;(String,Object);;Argument[0];MapKey of Element of ReturnValue;value",
+ // "org.springframework.beans;MutablePropertyValues;true;add;(String,Object);;Argument[1];MapValue of Element of Argument[-1];value",
+ // "org.springframework.beans;MutablePropertyValues;true;add;(String,Object);;Argument[1];MapValue of Element of ReturnValue;value",
+ // "org.springframework.beans;MutablePropertyValues;true;addPropertyValue;(PropertyValue);;Argument[0];Element of Argument[-1];value",
+ // "org.springframework.beans;MutablePropertyValues;true;addPropertyValue;(PropertyValue);;Argument[0];Element of ReturnValue;value",
+ // "org.springframework.beans;MutablePropertyValues;true;addPropertyValue;(String,Object);;Argument[0];MapKey of Element of Argument[-1];value",
+ // "org.springframework.beans;MutablePropertyValues;true;addPropertyValue;(String,Object);;Argument[1];MapValue of Element of Argument[-1];value",
+ // "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(Map);;MapKey of Argument[0];MapKey of Element of Argument[-1];value",
+ // "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(Map);;MapKey of Argument[0];MapKey of Element of ReturnValue;value",
+ // "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(Map);;MapValue of Argument[0];MapValue of Element of Argument[-1];value",
+ // "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(Map);;MapValue of Argument[0];MapValue of Element of ReturnValue;value",
+ // "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(PropertyValues);;MapKey of Element of Argument[0];MapKey of Element of Argument[-1];value",
+ // "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(PropertyValues);;MapKey of Element of Argument[0];MapKey of Element of ReturnValue;value",
+ // "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(PropertyValues);;MapValue of Element of Argument[0];MapValue of Element of Argument[-1];value",
+ // "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(PropertyValues);;MapValue of Element of Argument[0];MapValue of Element of ReturnValue;value",
+ // "org.springframework.beans;MutablePropertyValues;true;get;;;MapValue of Element of Argument[-1];ReturnValue;value",
+ // "org.springframework.beans;MutablePropertyValues;true;getPropertyValue;;;Element of Argument[-1];ReturnValue;value",
+ // "org.springframework.beans;MutablePropertyValues;true;getPropertyValueList;;;Element of Argument[-1];Element of ReturnValue;value",
+ // "org.springframework.beans;MutablePropertyValues;true;getPropertyValues;;;Element of Argument[-1];ArrayElement of ReturnValue;value",
+ // "org.springframework.beans;MutablePropertyValues;true;setPropertyValueAt;;;Argument[0];Element of Argument[-1];value"
+ // @formatter:on
+
+ }
+}
diff --git a/java/ql/test/library-tests/frameworks/spring/beans/options b/java/ql/test/library-tests/frameworks/spring/beans/options
new file mode 100644
index 00000000000..31b8e3f6935
--- /dev/null
+++ b/java/ql/test/library-tests/frameworks/spring/beans/options
@@ -0,0 +1 @@
+//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3
\ No newline at end of file
diff --git a/java/ql/test/library-tests/frameworks/spring/beans/test.expected b/java/ql/test/library-tests/frameworks/spring/beans/test.expected
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/java/ql/test/library-tests/frameworks/spring/beans/test.ql b/java/ql/test/library-tests/frameworks/spring/beans/test.ql
new file mode 100644
index 00000000000..610228239e3
--- /dev/null
+++ b/java/ql/test/library-tests/frameworks/spring/beans/test.ql
@@ -0,0 +1,67 @@
+import java
+import semmle.code.java.dataflow.ExternalFlow
+import semmle.code.java.dataflow.TaintTracking
+import TestUtilities.InlineExpectationsTest
+import semmle.code.java.dataflow.internal.FlowSummaryImpl
+
+class SummaryModelTest extends SummaryModelCsv {
+ override predicate row(string row) {
+ row =
+ [
+ //"package;type;overrides;name;signature;ext;inputspec;outputspec;kind",
+ "generatedtest;Test;false;getMapKey;;;MapKey of Argument[0];ReturnValue;value",
+ "generatedtest;Test;false;getMapValue;;;MapValue of Argument[0];ReturnValue;value",
+ "generatedtest;Test;false;newWithElement;;;Argument[0];Element of ReturnValue;value",
+ "generatedtest;Test;false;newWithMapKey;;;Argument[0];MapKey of ReturnValue;value",
+ "generatedtest;Test;false;newWithMapValue;;;Argument[0];MapValue of ReturnValue;value"
+ ]
+ }
+}
+
+class ValueFlowConf extends DataFlow::Configuration {
+ ValueFlowConf() { this = "qltest:valueFlowConf" }
+
+ override predicate isSource(DataFlow::Node n) {
+ n.asExpr().(MethodAccess).getMethod().hasName("source")
+ }
+
+ override predicate isSink(DataFlow::Node n) {
+ n.asExpr().(Argument).getCall().getCallee().hasName("sink")
+ }
+}
+
+class TaintFlowConf extends TaintTracking::Configuration {
+ TaintFlowConf() { this = "qltest:taintFlowConf" }
+
+ override predicate isSource(DataFlow::Node n) {
+ n.asExpr().(MethodAccess).getMethod().hasName("source")
+ }
+
+ override predicate isSink(DataFlow::Node n) {
+ n.asExpr().(Argument).getCall().getCallee().hasName("sink")
+ }
+}
+
+class HasFlowTest extends InlineExpectationsTest {
+ HasFlowTest() { this = "HasFlowTest" }
+
+ override string getARelevantTag() { result = ["hasValueFlow", "hasTaintFlow"] }
+
+ override predicate hasActualResult(Location location, string element, string tag, string value) {
+ tag = "hasValueFlow" and
+ exists(DataFlow::Node src, DataFlow::Node sink, ValueFlowConf conf | conf.hasFlow(src, sink) |
+ sink.getLocation() = location and
+ element = sink.toString() and
+ value = ""
+ )
+ or
+ tag = "hasTaintFlow" and
+ exists(DataFlow::Node src, DataFlow::Node sink, TaintFlowConf conf |
+ conf.hasFlow(src, sink) and not any(ValueFlowConf c).hasFlow(src, sink)
+ |
+ sink.getLocation() = location and
+ element = sink.toString() and
+ value = ""
+ )
+ }
+}
diff --git a/java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/MutablePropertyValues.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/MutablePropertyValues.java
new file mode 100644
index 00000000000..fed50425d90
--- /dev/null
+++ b/java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/MutablePropertyValues.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2002-2020 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.springframework.beans;
+
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Spliterator;
+import java.util.stream.Stream;
+import org.springframework.lang.Nullable;
+
+public class MutablePropertyValues implements PropertyValues, Serializable {
+ public MutablePropertyValues() {}
+
+ public MutablePropertyValues(@Nullable PropertyValues original) {}
+
+ public MutablePropertyValues(@Nullable Map, ?> original) {}
+
+ public MutablePropertyValues(@Nullable List propertyValueList) {}
+
+ public List getPropertyValueList() {
+ return null;
+ }
+
+ public int size() {
+ return 0;
+ }
+
+ public MutablePropertyValues addPropertyValues(@Nullable PropertyValues other) {
+ return null;
+ }
+
+ public MutablePropertyValues addPropertyValues(@Nullable Map, ?> other) {
+ return null;
+ }
+
+ public MutablePropertyValues addPropertyValue(PropertyValue pv) {
+ return null;
+ }
+
+ public void addPropertyValue(String propertyName, Object propertyValue) {}
+
+ public MutablePropertyValues add(String propertyName, @Nullable Object propertyValue) {
+ return null;
+ }
+
+ public void setPropertyValueAt(PropertyValue pv, int i) {}
+
+ public void removePropertyValue(PropertyValue pv) {}
+
+ public void removePropertyValue(String propertyName) {}
+
+ @Override
+ public Iterator iterator() {
+ return null;
+ }
+
+ @Override
+ public Spliterator spliterator() {
+ return null;
+ }
+
+ @Override
+ public Stream