From ee67d27b56c089f7772d043f0d0057c8ff73ab53 Mon Sep 17 00:00:00 2001 From: p0wn4j Date: Sun, 14 Nov 2021 05:08:32 +0400 Subject: [PATCH 1/3] Java: Add JDBC connection SSRF sinks --- .../code/java/dataflow/ExternalFlow.qll | 2 + .../semmle/code/java/frameworks/HikariCP.qll | 17 ++ .../lib/semmle/code/java/frameworks/Jdbc.qll | 13 ++ .../lib/semmle/code/java/frameworks/Jdbi.qll | 21 ++ .../code/java/frameworks/SpringJdbc.qll | 14 ++ .../code/java/security/RequestForgery.qll | 19 ++ .../security/CWE-918/JdbcUrlSSRF.java | 91 ++++++++ .../test/query-tests/security/CWE-918/options | 2 +- .../com/zaxxer/hikari/HikariConfig.java | 79 +++++++ .../com/zaxxer/hikari/HikariConfigMXBean.java | 193 +++++++++++++++++ .../com/zaxxer/hikari/HikariDataSource.java | 70 ++++++ .../org/jdbi/v3/core/Handle.java | 30 +++ .../org/jdbi/v3/core/Jdbi.java | 78 +++++++ .../org/jdbi/v3/core/config/Configurable.java | 24 +++ .../org/postgresql/Driver.java | 42 ++++ .../jdbc/datasource/AbstractDataSource.java | 90 ++++++++ .../AbstractDriverBasedDataSource.java | 201 ++++++++++++++++++ .../datasource/DriverManagerDataSource.java | 94 ++++++++ .../boot/jdbc/DataSourceBuilder.java | 27 +++ 19 files changed, 1106 insertions(+), 1 deletion(-) create mode 100644 java/ql/lib/semmle/code/java/frameworks/HikariCP.qll create mode 100644 java/ql/lib/semmle/code/java/frameworks/Jdbi.qll create mode 100644 java/ql/test/query-tests/security/CWE-918/JdbcUrlSSRF.java create mode 100644 java/ql/test/stubs/HikariCP-3.4.5/com/zaxxer/hikari/HikariConfig.java create mode 100644 java/ql/test/stubs/HikariCP-3.4.5/com/zaxxer/hikari/HikariConfigMXBean.java create mode 100644 java/ql/test/stubs/HikariCP-3.4.5/com/zaxxer/hikari/HikariDataSource.java create mode 100644 java/ql/test/stubs/jdbi3-core-3.27.2/org/jdbi/v3/core/Handle.java create mode 100644 java/ql/test/stubs/jdbi3-core-3.27.2/org/jdbi/v3/core/Jdbi.java create mode 100644 java/ql/test/stubs/jdbi3-core-3.27.2/org/jdbi/v3/core/config/Configurable.java create mode 100644 java/ql/test/stubs/postgresql-42.3.3/org/postgresql/Driver.java create mode 100644 java/ql/test/stubs/spring-jdbc-5.3.8/org/springframework/jdbc/datasource/AbstractDataSource.java create mode 100644 java/ql/test/stubs/spring-jdbc-5.3.8/org/springframework/jdbc/datasource/AbstractDriverBasedDataSource.java create mode 100644 java/ql/test/stubs/spring-jdbc-5.3.8/org/springframework/jdbc/datasource/DriverManagerDataSource.java create mode 100644 java/ql/test/stubs/springframework-5.3.8/org/springframework/boot/jdbc/DataSourceBuilder.java diff --git a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll index c8f1ff47bd2..631c47583e6 100644 --- a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll @@ -131,6 +131,8 @@ private module Frameworks { private import semmle.code.java.security.XPath private import semmle.code.java.security.XsltInjection private import semmle.code.java.frameworks.Jdbc + private import semmle.code.java.frameworks.Jdbi + private import semmle.code.java.frameworks.HikariCP private import semmle.code.java.frameworks.SpringJdbc private import semmle.code.java.frameworks.MyBatis private import semmle.code.java.frameworks.Hibernate diff --git a/java/ql/lib/semmle/code/java/frameworks/HikariCP.qll b/java/ql/lib/semmle/code/java/frameworks/HikariCP.qll new file mode 100644 index 00000000000..30b4ba63405 --- /dev/null +++ b/java/ql/lib/semmle/code/java/frameworks/HikariCP.qll @@ -0,0 +1,17 @@ +/** + * Definitions of sinks in the Hikari Connection Pool library. + */ + +import java +import semmle.code.java.dataflow.ExternalFlow + +private class SsrfSinkCsv extends SinkModelCsv { + override predicate row(string row) { + row = + [ + //"package;type;overrides;name;signature;ext;spec;kind" + "com.zaxxer.hikari;HikariConfig;false;HikariConfig;(Properties);;Argument[0];jdbc-url", + "com.zaxxer.hikari;HikariConfig;false;setJdbcUrl;(String);;Argument[0];jdbc-url" + ] + } +} diff --git a/java/ql/lib/semmle/code/java/frameworks/Jdbc.qll b/java/ql/lib/semmle/code/java/frameworks/Jdbc.qll index b851842dfd3..5a20399f9db 100644 --- a/java/ql/lib/semmle/code/java/frameworks/Jdbc.qll +++ b/java/ql/lib/semmle/code/java/frameworks/Jdbc.qll @@ -52,3 +52,16 @@ private class SqlSinkCsv extends SinkModelCsv { ] } } + +private class SsrfSinkCsv extends SinkModelCsv { + override predicate row(string row) { + row = + [ + //"package;type;overrides;name;signature;ext;spec;kind" + "java.sql;DriverManager;false;getConnection;(String);;Argument[0];jdbc-url", + "java.sql;DriverManager;false;getConnection;(String,Properties);;Argument[0];jdbc-url", + "java.sql;DriverManager;false;getConnection;(String,String,String);;Argument[0];jdbc-url", + "java.sql;Driver;false;connect;(String,Properties);;Argument[0];jdbc-url" + ] + } +} diff --git a/java/ql/lib/semmle/code/java/frameworks/Jdbi.qll b/java/ql/lib/semmle/code/java/frameworks/Jdbi.qll new file mode 100644 index 00000000000..a2984805ce7 --- /dev/null +++ b/java/ql/lib/semmle/code/java/frameworks/Jdbi.qll @@ -0,0 +1,21 @@ +/** + * Definitions of sinks in the JDBI library. + */ + +import java +import semmle.code.java.dataflow.ExternalFlow + +private class SsrfSinkCsv extends SinkModelCsv { + override predicate row(string row) { + row = + [ + //"package;type;overrides;name;signature;ext;spec;kind" + "org.jdbi.v3.core;Jdbi;false;create;(String);;Argument[0];jdbc-url", + "org.jdbi.v3.core;Jdbi;false;create;(String,Properties);;Argument[0];jdbc-url", + "org.jdbi.v3.core;Jdbi;false;create;(String,String,String);;Argument[0];jdbc-url", + "org.jdbi.v3.core;Jdbi;false;open;(String);;Argument[0];jdbc-url", + "org.jdbi.v3.core;Jdbi;false;open;(String,Properties);;Argument[0];jdbc-url", + "org.jdbi.v3.core;Jdbi;false;open;(String,String,String);;Argument[0];jdbc-url" + ] + } +} diff --git a/java/ql/lib/semmle/code/java/frameworks/SpringJdbc.qll b/java/ql/lib/semmle/code/java/frameworks/SpringJdbc.qll index ed7cdfd527f..067c686b912 100644 --- a/java/ql/lib/semmle/code/java/frameworks/SpringJdbc.qll +++ b/java/ql/lib/semmle/code/java/frameworks/SpringJdbc.qll @@ -37,3 +37,17 @@ private class SqlSinkCsv extends SinkModelCsv { ] } } + +private class SsrfSinkCsv extends SinkModelCsv { + override predicate row(string row) { + row = + [ + //"package;type;overrides;name;signature;ext;spec;kind" + "org.springframework.boot.jdbc;DataSourceBuilder;false;url;(String);;Argument[0];jdbc-url", + "org.springframework.jdbc.datasource;AbstractDriverBasedDataSource;false;setUrl;(String);;Argument[0];jdbc-url", + "org.springframework.jdbc.datasource;DriverManagerDataSource;false;DriverManagerDataSource;(String);;Argument[0];jdbc-url", + "org.springframework.jdbc.datasource;DriverManagerDataSource;false;DriverManagerDataSource;(String,String,String);;Argument[0];jdbc-url", + "org.springframework.jdbc.datasource;DriverManagerDataSource;false;DriverManagerDataSource;(String,Properties);;Argument[0];jdbc-url" + ] + } +} diff --git a/java/ql/lib/semmle/code/java/security/RequestForgery.qll b/java/ql/lib/semmle/code/java/security/RequestForgery.qll index b7d3e4a8a77..4a825cdb79d 100644 --- a/java/ql/lib/semmle/code/java/security/RequestForgery.qll +++ b/java/ql/lib/semmle/code/java/security/RequestForgery.qll @@ -7,6 +7,7 @@ import semmle.code.java.frameworks.spring.Spring import semmle.code.java.frameworks.JaxWS import semmle.code.java.frameworks.javase.Http import semmle.code.java.dataflow.DataFlow +import semmle.code.java.frameworks.Properties private import semmle.code.java.dataflow.StringPrefixes private import semmle.code.java.dataflow.ExternalFlow @@ -33,6 +34,20 @@ private class DefaultRequestForgeryAdditionalTaintStep extends RequestForgeryAdd } } +private class TypePropertiesRequestForgeryAdditionalTaintStep extends RequestForgeryAdditionalTaintStep { + override predicate propagatesTaint(DataFlow::Node pred, DataFlow::Node succ) { + exists(MethodAccess ma | + // Properties props = new Properties(); + // props.setProperty("jdbcUrl", tainted); + // Propagate tainted value to the qualifier `props` + ma.getMethod() instanceof PropertiesSetPropertyMethod and + ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "jdbcUrl" and + pred.asExpr() = ma.getArgument(1) and + succ.asExpr() = ma.getQualifier() + ) + } +} + /** A data flow sink for server-side request forgery (SSRF) vulnerabilities. */ abstract class RequestForgerySink extends DataFlow::Node { } @@ -40,6 +55,10 @@ private class UrlOpenSinkAsRequestForgerySink extends RequestForgerySink { UrlOpenSinkAsRequestForgerySink() { sinkNode(this, "open-url") } } +private class JdbcUrlSinkAsRequestForgerySink extends RequestForgerySink { + JdbcUrlSinkAsRequestForgerySink() { sinkNode(this, "jdbc-url") } +} + /** A sanitizer for request forgery vulnerabilities. */ abstract class RequestForgerySanitizer extends DataFlow::Node { } diff --git a/java/ql/test/query-tests/security/CWE-918/JdbcUrlSSRF.java b/java/ql/test/query-tests/security/CWE-918/JdbcUrlSSRF.java new file mode 100644 index 00000000000..fa202ef0373 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-918/JdbcUrlSSRF.java @@ -0,0 +1,91 @@ +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.sql.DriverManager; +import java.sql.Driver; +import java.sql.SQLException; +import java.io.IOException; +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import java.util.*; +import org.springframework.jdbc.datasource.*; +import org.jdbi.v3.core.Jdbi; +import org.springframework.boot.jdbc.DataSourceBuilder; + +public class JdbcUrlSSRF extends HttpServlet { + + protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + String jdbcUrl = request.getParameter("jdbcUrl"); + Driver driver = new org.postgresql.Driver(); + DataSourceBuilder dsBuilder = new DataSourceBuilder(); + + try { + driver.connect(jdbcUrl, null); // $ SSRF + + DriverManager.getConnection(jdbcUrl); // $ SSRF + DriverManager.getConnection(jdbcUrl, "user", "password"); // $ SSRF + DriverManager.getConnection(jdbcUrl, null); // $ SSRF + + dsBuilder.url(jdbcUrl); // $ SSRF + } + catch(SQLException e) {} + } + + protected void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + String jdbcUrl = request.getParameter("jdbcUrl"); + HikariConfig config = new HikariConfig(); + + config.setJdbcUrl(jdbcUrl); // $ SSRF + config.setUsername("database_username"); + config.setPassword("database_password"); + + HikariDataSource ds = new HikariDataSource(); + ds.setJdbcUrl(jdbcUrl); // $ SSRF + + Properties props = new Properties(); + props.setProperty("driverClassName", "org.postgresql.Driver"); + props.setProperty("jdbcUrl", jdbcUrl); + + HikariConfig config2 = new HikariConfig(props); // $ SSRF + } + + protected void doPut(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + String jdbcUrl = request.getParameter("jdbcUrl"); + + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + + dataSource.setDriverClassName("org.postgresql.Driver"); + dataSource.setUrl(jdbcUrl); // $ SSRF + + DriverManagerDataSource dataSource2 = new DriverManagerDataSource(jdbcUrl); // $ SSRF + dataSource2.setDriverClassName("org.postgresql.Driver"); + + DriverManagerDataSource dataSource3 = new DriverManagerDataSource(jdbcUrl, "user", "pass"); // $ SSRF + dataSource3.setDriverClassName("org.postgresql.Driver"); + + DriverManagerDataSource dataSource4 = new DriverManagerDataSource(jdbcUrl, null); // $ SSRF + dataSource4.setDriverClassName("org.postgresql.Driver"); + } + + protected void doDelete(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + String jdbcUrl = request.getParameter("jdbcUrl"); + + Jdbi.create(jdbcUrl); // $ SSRF + Jdbi.create(jdbcUrl, null); // $ SSRF + Jdbi.create(jdbcUrl, "user", "pass"); // $ SSRF + + Jdbi.open(jdbcUrl); // $ SSRF + Jdbi.open(jdbcUrl, null); // $ SSRF + Jdbi.open(jdbcUrl, "user", "pass"); // $ SSRF + } + +} \ No newline at end of file diff --git a/java/ql/test/query-tests/security/CWE-918/options b/java/ql/test/query-tests/security/CWE-918/options index 87db9eacec3..5776b35fe9f 100644 --- a/java/ql/test/query-tests/security/CWE-918/options +++ b/java/ql/test/query-tests/security/CWE-918/options @@ -1,2 +1,2 @@ -//semmle-extractor-options: --javac-args -source 11 -target 11 -cp ${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/javax-ws-rs-api-2.1.1:${testdir}/../../../stubs/javax-ws-rs-api-3.0.0:${testdir}/../../../stubs/apache-http-4.4.13/:${testdir}/../../../stubs/servlet-api-2.4/:${testdir}/../../../stubs/projectreactor-3.4.3/ +//semmle-extractor-options: --javac-args -source 11 -target 11 -cp ${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/javax-ws-rs-api-2.1.1:${testdir}/../../../stubs/javax-ws-rs-api-3.0.0:${testdir}/../../../stubs/apache-http-4.4.13/:${testdir}/../../../stubs/servlet-api-2.4/:${testdir}/../../../stubs/projectreactor-3.4.3/:${testdir}/../../../stubs/postgresql-42.3.3/:${testdir}/../../../stubs/HikariCP-3.4.5/:${testdir}/../../../stubs/spring-jdbc-5.3.8/:${testdir}/../../../stubs/jdbi3-core-3.27.2/ diff --git a/java/ql/test/stubs/HikariCP-3.4.5/com/zaxxer/hikari/HikariConfig.java b/java/ql/test/stubs/HikariCP-3.4.5/com/zaxxer/hikari/HikariConfig.java new file mode 100644 index 00000000000..f8ae9aaccb9 --- /dev/null +++ b/java/ql/test/stubs/HikariCP-3.4.5/com/zaxxer/hikari/HikariConfig.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2013, 2014 Brett Wooldridge + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.zaxxer.hikari; + + +public class HikariConfig implements HikariConfigMXBean { + + private String jdbcUrl; + + public HikariConfig() { + } + + public HikariConfig(java.util.Properties properties) { + + } + + public HikariConfig(String propertyFileName) { + } + + public String getJdbcUrl() { + return jdbcUrl; + } + + public void setJdbcUrl(String jdbcUrl) { + this.jdbcUrl = jdbcUrl; + } + + public long getConnectionTimeout() { return 0; } + + public void setConnectionTimeout(long connectionTimeoutMs) {} + + public long getValidationTimeout() { return 0; } + + public void setValidationTimeout(long validationTimeoutMs) {} + + public long getIdleTimeout() { return 0; } + + public void setIdleTimeout(long idleTimeoutMs) {} + + public long getLeakDetectionThreshold() { return 0; } + + public void setLeakDetectionThreshold(long leakDetectionThresholdMs) {} + + public long getMaxLifetime() { return 0; } + + public void setMaxLifetime(long maxLifetimeMs) {} + + public int getMinimumIdle() { return 0; } + + public void setMinimumIdle(int minIdle) {} + + public int getMaximumPoolSize() { return 0; } + + public void setMaximumPoolSize(int maxPoolSize) {} + + public void setPassword(String password) {} + + public void setUsername(String username) {} + + public String getPoolName() {return "";} + + public String getCatalog() {return "";} + + public void setCatalog(String catalog) {} +} \ No newline at end of file diff --git a/java/ql/test/stubs/HikariCP-3.4.5/com/zaxxer/hikari/HikariConfigMXBean.java b/java/ql/test/stubs/HikariCP-3.4.5/com/zaxxer/hikari/HikariConfigMXBean.java new file mode 100644 index 00000000000..2e510d53360 --- /dev/null +++ b/java/ql/test/stubs/HikariCP-3.4.5/com/zaxxer/hikari/HikariConfigMXBean.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2013 Brett Wooldridge + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.zaxxer.hikari; + +/** + * The javax.management MBean for a Hikari pool configuration. + * + * @author Brett Wooldridge + */ +public interface HikariConfigMXBean +{ + /** + * Get the maximum number of milliseconds that a client will wait for a connection from the pool. If this + * time is exceeded without a connection becoming available, a SQLException will be thrown from + * {@link javax.sql.DataSource#getConnection()}. + * + * @return the connection timeout in milliseconds + */ + long getConnectionTimeout(); + + /** + * Set the maximum number of milliseconds that a client will wait for a connection from the pool. If this + * time is exceeded without a connection becoming available, a SQLException will be thrown from + * {@link javax.sql.DataSource#getConnection()}. + * + * @param connectionTimeoutMs the connection timeout in milliseconds + */ + void setConnectionTimeout(long connectionTimeoutMs); + + /** + * Get the maximum number of milliseconds that the pool will wait for a connection to be validated as + * alive. + * + * @return the validation timeout in milliseconds + */ + long getValidationTimeout(); + + /** + * Sets the maximum number of milliseconds that the pool will wait for a connection to be validated as + * alive. + * + * @param validationTimeoutMs the validation timeout in milliseconds + */ + void setValidationTimeout(long validationTimeoutMs); + + /** + * This property controls the maximum amount of time (in milliseconds) that a connection is allowed to sit + * idle in the pool. Whether a connection is retired as idle or not is subject to a maximum variation of +30 + * seconds, and average variation of +15 seconds. A connection will never be retired as idle before this timeout. + * A value of 0 means that idle connections are never removed from the pool. + * + * @return the idle timeout in milliseconds + */ + long getIdleTimeout(); + + /** + * This property controls the maximum amount of time (in milliseconds) that a connection is allowed to sit + * idle in the pool. Whether a connection is retired as idle or not is subject to a maximum variation of +30 + * seconds, and average variation of +15 seconds. A connection will never be retired as idle before this timeout. + * A value of 0 means that idle connections are never removed from the pool. + * + * @param idleTimeoutMs the idle timeout in milliseconds + */ + void setIdleTimeout(long idleTimeoutMs); + + /** + * This property controls the amount of time that a connection can be out of the pool before a message is + * logged indicating a possible connection leak. A value of 0 means leak detection is disabled. + * + * @return the connection leak detection threshold in milliseconds + */ + long getLeakDetectionThreshold(); + + /** + * This property controls the amount of time that a connection can be out of the pool before a message is + * logged indicating a possible connection leak. A value of 0 means leak detection is disabled. + * + * @param leakDetectionThresholdMs the connection leak detection threshold in milliseconds + */ + void setLeakDetectionThreshold(long leakDetectionThresholdMs); + + /** + * This property controls the maximum lifetime of a connection in the pool. When a connection reaches this + * timeout, even if recently used, it will be retired from the pool. An in-use connection will never be + * retired, only when it is idle will it be removed. + * + * @return the maximum connection lifetime in milliseconds + */ + long getMaxLifetime(); + + /** + * This property controls the maximum lifetime of a connection in the pool. When a connection reaches this + * timeout, even if recently used, it will be retired from the pool. An in-use connection will never be + * retired, only when it is idle will it be removed. + * + * @param maxLifetimeMs the maximum connection lifetime in milliseconds + */ + void setMaxLifetime(long maxLifetimeMs); + + /** + * The property controls the minimum number of idle connections that HikariCP tries to maintain in the pool, + * including both idle and in-use connections. If the idle connections dip below this value, HikariCP will + * make a best effort to restore them quickly and efficiently. + * + * @return the minimum number of connections in the pool + */ + int getMinimumIdle(); + + /** + * The property controls the minimum number of idle connections that HikariCP tries to maintain in the pool, + * including both idle and in-use connections. If the idle connections dip below this value, HikariCP will + * make a best effort to restore them quickly and efficiently. + * + * @param minIdle the minimum number of idle connections in the pool to maintain + */ + void setMinimumIdle(int minIdle); + + /** + * The property controls the maximum number of connections that HikariCP will keep in the pool, + * including both idle and in-use connections. + * + * @return the maximum number of connections in the pool + */ + int getMaximumPoolSize(); + + /** + * The property controls the maximum size that the pool is allowed to reach, including both idle and in-use + * connections. Basically this value will determine the maximum number of actual connections to the database + * backend. + *

+ * When the pool reaches this size, and no idle connections are available, calls to getConnection() will + * block for up to connectionTimeout milliseconds before timing out. + * + * @param maxPoolSize the maximum number of connections in the pool + */ + void setMaximumPoolSize(int maxPoolSize); + + /** + * Set the password used for authentication. Changing this at runtime will apply to new connections only. + * Altering this at runtime only works for DataSource-based connections, not Driver-class or JDBC URL-based + * connections. + * + * @param password the database password + */ + void setPassword(String password); + + /** + * Set the username used for authentication. Changing this at runtime will apply to new connections only. + * Altering this at runtime only works for DataSource-based connections, not Driver-class or JDBC URL-based + * connections. + * + * @param username the database username + */ + void setUsername(String username); + + + /** + * The name of the connection pool. + * + * @return the name of the connection pool + */ + String getPoolName(); + + /** + * Get the default catalog name to be set on connections. + * + * @return the default catalog name + */ + String getCatalog(); + + /** + * Set the default catalog name to be set on connections. + *

+ * WARNING: THIS VALUE SHOULD ONLY BE CHANGED WHILE THE POOL IS SUSPENDED, AFTER CONNECTIONS HAVE BEEN EVICTED. + * + * @param catalog the catalog name, or null + */ + void setCatalog(String catalog); +} diff --git a/java/ql/test/stubs/HikariCP-3.4.5/com/zaxxer/hikari/HikariDataSource.java b/java/ql/test/stubs/HikariCP-3.4.5/com/zaxxer/hikari/HikariDataSource.java new file mode 100644 index 00000000000..2823f6dc3ce --- /dev/null +++ b/java/ql/test/stubs/HikariCP-3.4.5/com/zaxxer/hikari/HikariDataSource.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2013 Brett Wooldridge + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.zaxxer.hikari; + +import javax.sql.DataSource; +import java.io.Closeable; +import java.sql.*; +import java.util.logging.Logger; + + +public class HikariDataSource extends HikariConfig implements DataSource, Closeable { + + public HikariDataSource() { + } + + public HikariDataSource(HikariConfig configuration) { + } + + public Connection getConnection() throws SQLException { + return null; + } + + public Connection getConnection(String username, String password) + throws SQLException { + return null; + } + + public java.io.PrintWriter getLogWriter() throws SQLException { + return null; + } + + public void setLogWriter(java.io.PrintWriter out) throws SQLException { + } + + public void setLoginTimeout(int seconds) throws SQLException { + } + + public int getLoginTimeout() throws SQLException { + return 0; + } + + public Logger getParentLogger() throws SQLFeatureNotSupportedException { + return null; + } + + public T unwrap(java.lang.Class iface) throws java.sql.SQLException { + return null; + } + + public boolean isWrapperFor(java.lang.Class iface) throws java.sql.SQLException { + return true; + } + + public void close() { + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/jdbi3-core-3.27.2/org/jdbi/v3/core/Handle.java b/java/ql/test/stubs/jdbi3-core-3.27.2/org/jdbi/v3/core/Handle.java new file mode 100644 index 00000000000..f6183d30603 --- /dev/null +++ b/java/ql/test/stubs/jdbi3-core-3.27.2/org/jdbi/v3/core/Handle.java @@ -0,0 +1,30 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jdbi.v3.core; + +import java.io.Closeable; +import java.sql.Connection; +import org.jdbi.v3.core.config.Configurable; + + +/** + * This represents a connection to the database system. It is a wrapper around + * a JDBC Connection object. Handle provides essential methods for transaction + * management, statement creation, and other operations tied to the database session. + */ +public class Handle implements Closeable, Configurable { + + public void close() { + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/jdbi3-core-3.27.2/org/jdbi/v3/core/Jdbi.java b/java/ql/test/stubs/jdbi3-core-3.27.2/org/jdbi/v3/core/Jdbi.java new file mode 100644 index 00000000000..ca7d10180c0 --- /dev/null +++ b/java/ql/test/stubs/jdbi3-core-3.27.2/org/jdbi/v3/core/Jdbi.java @@ -0,0 +1,78 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jdbi.v3.core; + +import org.jdbi.v3.core.config.Configurable; +import java.util.Properties; + +public class Jdbi implements Configurable { + + public static Jdbi create(final String url) { + return null; + } + + /** + * Creates a new {@link Jdbi} instance from a database URL. + * + * @param url JDBC URL for connections + * @param properties Properties to pass to DriverManager.getConnection(url, props) for each new handle + * + * @return a Jdbi which uses {@link DriverManager} as a connection factory. + */ + public static Jdbi create(final String url, final Properties properties) { + return null; + } + + /** + * Creates a new {@link Jdbi} instance from a database URL. + * + * @param url JDBC URL for connections + * @param username User name for connection authentication + * @param password Password for connection authentication + * + * @return a Jdbi which uses {@link DriverManager} as a connection factory. + */ + public static Jdbi create(final String url, final String username, final String password) { + return null; + } + + public static Handle open(final String url) { + return null; + } + + /** + * Obtain a handle with just a JDBC URL + * + * @param url JDBC Url + * @param username JDBC username for authentication + * @param password JDBC password for authentication + * + * @return newly opened Handle + */ + public static Handle open(final String url, final String username, final String password) { + return null; + } + + /** + * Obtain a handle with just a JDBC URL + * + * @param url JDBC Url + * @param props JDBC properties + * + * @return newly opened Handle + */ + public static Handle open(final String url, final Properties props) { + return null; + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/jdbi3-core-3.27.2/org/jdbi/v3/core/config/Configurable.java b/java/ql/test/stubs/jdbi3-core-3.27.2/org/jdbi/v3/core/config/Configurable.java new file mode 100644 index 00000000000..ae3f5ebf6bc --- /dev/null +++ b/java/ql/test/stubs/jdbi3-core-3.27.2/org/jdbi/v3/core/config/Configurable.java @@ -0,0 +1,24 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jdbi.v3.core.config; + + +/** + * A type with access to access and modify arbitrary Jdbi configuration. + * + * @param The subtype that implements this interface. + */ +public interface Configurable { +} + diff --git a/java/ql/test/stubs/postgresql-42.3.3/org/postgresql/Driver.java b/java/ql/test/stubs/postgresql-42.3.3/org/postgresql/Driver.java new file mode 100644 index 00000000000..e84ba28d55e --- /dev/null +++ b/java/ql/test/stubs/postgresql-42.3.3/org/postgresql/Driver.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2003, PostgreSQL Global Development Group + * See the LICENSE file in the project root for more information. + */ +package org.postgresql; + +import java.util.logging.Logger; +import java.sql.*; + +public class Driver implements java.sql.Driver { + + public Connection connect(String url, java.util.Properties info) throws SQLException { + return null; + } + + public boolean acceptsURL(String url) throws SQLException { + return true; + } + + public DriverPropertyInfo[] getPropertyInfo(String url, java.util.Properties info) + throws SQLException { + return null; + } + + public int getMajorVersion() { + return 0; + } + + public int getMinorVersion() { + return 0; + } + + + public boolean jdbcCompliant() { + return true; + } + + + public Logger getParentLogger() throws SQLFeatureNotSupportedException { + return null; + } +} diff --git a/java/ql/test/stubs/spring-jdbc-5.3.8/org/springframework/jdbc/datasource/AbstractDataSource.java b/java/ql/test/stubs/spring-jdbc-5.3.8/org/springframework/jdbc/datasource/AbstractDataSource.java new file mode 100644 index 00000000000..5645c259de3 --- /dev/null +++ b/java/ql/test/stubs/spring-jdbc-5.3.8/org/springframework/jdbc/datasource/AbstractDataSource.java @@ -0,0 +1,90 @@ +/* + * Copyright 2002-2018 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.jdbc.datasource; + +import java.io.PrintWriter; +import java.sql.SQLException; +import java.util.logging.Logger; + +import javax.sql.DataSource; + +public abstract class AbstractDataSource implements DataSource { + + + /** + * Returns 0, indicating the default system timeout is to be used. + */ + @Override + public int getLoginTimeout() throws SQLException { + return 0; + } + + /** + * Setting a login timeout is not supported. + */ + @Override + public void setLoginTimeout(int timeout) throws SQLException { + throw new UnsupportedOperationException("setLoginTimeout"); + } + + /** + * LogWriter methods are not supported. + */ + @Override + public PrintWriter getLogWriter() { + throw new UnsupportedOperationException("getLogWriter"); + } + + /** + * LogWriter methods are not supported. + */ + @Override + public void setLogWriter(PrintWriter pw) throws SQLException { + throw new UnsupportedOperationException("setLogWriter"); + } + + + //--------------------------------------------------------------------- + // Implementation of JDBC 4.0's Wrapper interface + //--------------------------------------------------------------------- + + @Override + @SuppressWarnings("unchecked") + public T unwrap(Class iface) throws SQLException { + if (iface.isInstance(this)) { + return (T) this; + } + throw new SQLException("DataSource of type [" + getClass().getName() + + "] cannot be unwrapped as [" + iface.getName() + "]"); + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + return iface.isInstance(this); + } + + + //--------------------------------------------------------------------- + // Implementation of JDBC 4.1's getParentLogger method + //--------------------------------------------------------------------- + + @Override + public Logger getParentLogger() { + return null; + } + +} diff --git a/java/ql/test/stubs/spring-jdbc-5.3.8/org/springframework/jdbc/datasource/AbstractDriverBasedDataSource.java b/java/ql/test/stubs/spring-jdbc-5.3.8/org/springframework/jdbc/datasource/AbstractDriverBasedDataSource.java new file mode 100644 index 00000000000..b2182d1194d --- /dev/null +++ b/java/ql/test/stubs/spring-jdbc-5.3.8/org/springframework/jdbc/datasource/AbstractDriverBasedDataSource.java @@ -0,0 +1,201 @@ +/* + * Copyright 2002-2017 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.jdbc.datasource; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Properties; + +public abstract class AbstractDriverBasedDataSource extends AbstractDataSource { + + private String url; + + private String username; + + private String password; + + private String catalog; + + private String schema; + + private Properties connectionProperties; + + + /** + * Set the JDBC URL to use for connecting through the Driver. + * @see java.sql.Driver#connect(String, java.util.Properties) + */ + public void setUrl(String url) { + this.url = (url != null ? url.trim() : null); + } + + /** + * Return the JDBC URL to use for connecting through the Driver. + */ + public String getUrl() { + return this.url; + } + + /** + * Set the JDBC username to use for connecting through the Driver. + * @see java.sql.Driver#connect(String, java.util.Properties) + */ + public void setUsername(String username) { + this.username = username; + } + + /** + * Return the JDBC username to use for connecting through the Driver. + */ + public String getUsername() { + return this.username; + } + + /** + * Set the JDBC password to use for connecting through the Driver. + * @see java.sql.Driver#connect(String, java.util.Properties) + */ + public void setPassword(String password) { + this.password = password; + } + + /** + * Return the JDBC password to use for connecting through the Driver. + */ + public String getPassword() { + return this.password; + } + + /** + * Specify a database catalog to be applied to each Connection. + * @since 4.3.2 + * @see Connection#setCatalog + */ + public void setCatalog(String catalog) { + this.catalog = catalog; + } + + /** + * Return the database catalog to be applied to each Connection, if any. + * @since 4.3.2 + */ + public String getCatalog() { + return this.catalog; + } + + /** + * Specify a database schema to be applied to each Connection. + * @since 4.3.2 + * @see Connection#setSchema + */ + public void setSchema(String schema) { + this.schema = schema; + } + + /** + * Return the database schema to be applied to each Connection, if any. + * @since 4.3.2 + */ + public String getSchema() { + return this.schema; + } + + /** + * Specify arbitrary connection properties as key/value pairs, + * to be passed to the Driver. + *

Can also contain "user" and "password" properties. However, + * any "username" and "password" bean properties specified on this + * DataSource will override the corresponding connection properties. + * @see java.sql.Driver#connect(String, java.util.Properties) + */ + public void setConnectionProperties(Properties connectionProperties) { + this.connectionProperties = connectionProperties; + } + + /** + * Return the connection properties to be passed to the Driver, if any. + */ + public Properties getConnectionProperties() { + return this.connectionProperties; + } + + + /** + * This implementation delegates to {@code getConnectionFromDriver}, + * using the default username and password of this DataSource. + * @see #getConnectionFromDriver(String, String) + * @see #setUsername + * @see #setPassword + */ + @Override + public Connection getConnection() throws SQLException { + return getConnectionFromDriver(getUsername(), getPassword()); + } + + /** + * This implementation delegates to {@code getConnectionFromDriver}, + * using the given username and password. + * @see #getConnectionFromDriver(String, String) + */ + @Override + public Connection getConnection(String username, String password) throws SQLException { + return getConnectionFromDriver(username, password); + } + + + /** + * Build properties for the Driver, including the given username and password (if any), + * and obtain a corresponding Connection. + * @param username the name of the user + * @param password the password to use + * @return the obtained Connection + * @throws SQLException in case of failure + * @see java.sql.Driver#connect(String, java.util.Properties) + */ + protected Connection getConnectionFromDriver(String username, String password) throws SQLException { + Properties mergedProps = new Properties(); + Properties connProps = getConnectionProperties(); + if (connProps != null) { + mergedProps.putAll(connProps); + } + if (username != null) { + mergedProps.setProperty("user", username); + } + if (password != null) { + mergedProps.setProperty("password", password); + } + + Connection con = getConnectionFromDriver(mergedProps); + if (this.catalog != null) { + con.setCatalog(this.catalog); + } + if (this.schema != null) { + con.setSchema(this.schema); + } + return con; + } + + /** + * Obtain a Connection using the given properties. + *

Template method to be implemented by subclasses. + * @param props the merged connection properties + * @return the obtained Connection + * @throws SQLException in case of failure + */ + protected abstract Connection getConnectionFromDriver(Properties props) throws SQLException; + +} diff --git a/java/ql/test/stubs/spring-jdbc-5.3.8/org/springframework/jdbc/datasource/DriverManagerDataSource.java b/java/ql/test/stubs/spring-jdbc-5.3.8/org/springframework/jdbc/datasource/DriverManagerDataSource.java new file mode 100644 index 00000000000..d4fb9545347 --- /dev/null +++ b/java/ql/test/stubs/spring-jdbc-5.3.8/org/springframework/jdbc/datasource/DriverManagerDataSource.java @@ -0,0 +1,94 @@ +/* + * 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.jdbc.datasource; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Properties; + +public class DriverManagerDataSource extends AbstractDriverBasedDataSource { + + /** + * Constructor for bean-style configuration. + */ + public DriverManagerDataSource() { + } + + /** + * Create a new DriverManagerDataSource with the given JDBC URL, + * not specifying a username or password for JDBC access. + * @param url the JDBC URL to use for accessing the DriverManager + * @see java.sql.DriverManager#getConnection(String) + */ + public DriverManagerDataSource(String url) { + } + + /** + * Create a new DriverManagerDataSource with the given standard + * DriverManager parameters. + * @param url the JDBC URL to use for accessing the DriverManager + * @param username the JDBC username to use for accessing the DriverManager + * @param password the JDBC password to use for accessing the DriverManager + * @see java.sql.DriverManager#getConnection(String, String, String) + */ + public DriverManagerDataSource(String url, String username, String password) { + } + + /** + * Create a new DriverManagerDataSource with the given JDBC URL, + * not specifying a username or password for JDBC access. + * @param url the JDBC URL to use for accessing the DriverManager + * @param conProps the JDBC connection properties + * @see java.sql.DriverManager#getConnection(String) + */ + public DriverManagerDataSource(String url, Properties conProps) { + } + + + /** + * Set the JDBC driver class name. This driver will get initialized + * on startup, registering itself with the JDK's DriverManager. + *

NOTE: DriverManagerDataSource is primarily intended for accessing + * pre-registered JDBC drivers. If you need to register a new driver, + * consider using {@link SimpleDriverDataSource} instead. Alternatively, consider + * initializing the JDBC driver yourself before instantiating this DataSource. + * The "driverClassName" property is mainly preserved for backwards compatibility, + * as well as for migrating between Commons DBCP and this DataSource. + * @see java.sql.DriverManager#registerDriver(java.sql.Driver) + * @see SimpleDriverDataSource + */ + public void setDriverClassName(String driverClassName) { + } + + + @Override + protected Connection getConnectionFromDriver(Properties props) throws SQLException { + String url = getUrl(); + return getConnectionFromDriverManager(url, props); + } + + /** + * Getting a Connection using the nasty static from DriverManager is extracted + * into a protected method to allow for easy unit testing. + * @see java.sql.DriverManager#getConnection(String, java.util.Properties) + */ + protected Connection getConnectionFromDriverManager(String url, Properties props) throws SQLException { + return DriverManager.getConnection(url, props); + } + +} diff --git a/java/ql/test/stubs/springframework-5.3.8/org/springframework/boot/jdbc/DataSourceBuilder.java b/java/ql/test/stubs/springframework-5.3.8/org/springframework/boot/jdbc/DataSourceBuilder.java new file mode 100644 index 00000000000..d85bb46dfd7 --- /dev/null +++ b/java/ql/test/stubs/springframework-5.3.8/org/springframework/boot/jdbc/DataSourceBuilder.java @@ -0,0 +1,27 @@ +/* + * Copyright 2012-2021 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.boot.jdbc; + +import javax.sql.DataSource; + +public final class DataSourceBuilder { + + public DataSourceBuilder url(String url) { + return this; + } + +} \ No newline at end of file From f83ea25ead606e9f5173cc774041bdcfb60267d4 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Mon, 14 Mar 2022 12:14:37 +0000 Subject: [PATCH 2/3] Add change note --- java/ql/lib/change-notes/2022-03-14-new-jdbc-ssrf-sinks.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 java/ql/lib/change-notes/2022-03-14-new-jdbc-ssrf-sinks.md diff --git a/java/ql/lib/change-notes/2022-03-14-new-jdbc-ssrf-sinks.md b/java/ql/lib/change-notes/2022-03-14-new-jdbc-ssrf-sinks.md new file mode 100644 index 00000000000..c154b12cfad --- /dev/null +++ b/java/ql/lib/change-notes/2022-03-14-new-jdbc-ssrf-sinks.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- + * Added support for detection of SSRF via JDBC database URLs, including connections made using the standard library (`java.sql`), Hikari Connection Pool, JDBI and Spring JDBC. From b351d5bc2f0845afb4c96173df84513ebf8ee16a Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Mon, 14 Mar 2022 12:44:40 +0000 Subject: [PATCH 3/3] Autoformat --- java/ql/lib/semmle/code/java/security/RequestForgery.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/lib/semmle/code/java/security/RequestForgery.qll b/java/ql/lib/semmle/code/java/security/RequestForgery.qll index 4a825cdb79d..e6efc13c8a5 100644 --- a/java/ql/lib/semmle/code/java/security/RequestForgery.qll +++ b/java/ql/lib/semmle/code/java/security/RequestForgery.qll @@ -37,7 +37,7 @@ private class DefaultRequestForgeryAdditionalTaintStep extends RequestForgeryAdd private class TypePropertiesRequestForgeryAdditionalTaintStep extends RequestForgeryAdditionalTaintStep { override predicate propagatesTaint(DataFlow::Node pred, DataFlow::Node succ) { exists(MethodAccess ma | - // Properties props = new Properties(); + // Properties props = new Properties(); // props.setProperty("jdbcUrl", tainted); // Propagate tainted value to the qualifier `props` ma.getMethod() instanceof PropertiesSetPropertyMethod and