Skip to content

Commit 91eb992

Browse files
authored
Merge pull request #22279 from forks-felickz/felickz-add-spring-r2dbc-models
Java: model R2DBC SQL injection sinks
2 parents b5b4852 + 239b16b commit 91eb992

9 files changed

Lines changed: 157 additions & 0 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Added SQL injection sink models for Spring R2DBC `DatabaseClient` and the R2DBC SPI.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
extensions:
2+
- addsTo:
3+
pack: codeql/java-all
4+
extensible: sinkModel
5+
data:
6+
- ["io.r2dbc.spi", "Batch", True, "add", "(String)", "", "Argument[0]", "sql-injection", "manual"]
7+
- ["io.r2dbc.spi", "Connection", True, "createSavepoint", "(String)", "", "Argument[0]", "sql-injection", "manual"]
8+
- ["io.r2dbc.spi", "Connection", True, "createStatement", "(String)", "", "Argument[0]", "sql-injection", "manual"]
9+
- ["io.r2dbc.spi", "Connection", True, "releaseSavepoint", "(String)", "", "Argument[0]", "sql-injection", "manual"]
10+
- ["io.r2dbc.spi", "Statement", True, "returnGeneratedValues", "(String[])", "", "Argument[0]", "sql-injection", "manual"]
11+
- ["io.r2dbc.spi", "Connection", True, "rollbackTransactionToSavepoint", "(String)", "", "Argument[0]", "sql-injection", "manual"]
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
extensions:
2+
- addsTo:
3+
pack: codeql/java-all
4+
extensible: sinkModel
5+
data:
6+
- ["org.springframework.r2dbc.core", "DatabaseClient", True, "sql", "(String)", "", "Argument[0]", "sql-injection", "manual"]
7+
- ["org.springframework.r2dbc.core", "DatabaseClient$GenericExecuteSpec", True, "flatMap", "(java.util.function.Function)", "", "Argument[this]", "sql-injection", "manual"]
8+
- ["org.springframework.r2dbc.core", "DatabaseClient$GenericExecuteSpec", True, "fetch", "", "", "Argument[this]", "sql-injection", "manual"]
9+
- ["org.springframework.r2dbc.core", "DatabaseClient$GenericExecuteSpec", True, "map", "(java.util.function.BiFunction)", "", "Argument[this]", "sql-injection", "manual"]
10+
- ["org.springframework.r2dbc.core", "DatabaseClient$GenericExecuteSpec", True, "map", "(java.util.function.Function)", "", "Argument[this]", "sql-injection", "manual"]
11+
- ["org.springframework.r2dbc.core", "DatabaseClient$GenericExecuteSpec", True, "mapProperties", "(Class)", "", "Argument[this]", "sql-injection", "manual"]
12+
- ["org.springframework.r2dbc.core", "DatabaseClient$GenericExecuteSpec", True, "mapValue", "(Class)", "", "Argument[this]", "sql-injection", "manual"]
13+
- ["org.springframework.r2dbc.core", "DatabaseClient$GenericExecuteSpec", True, "then", "", "", "Argument[this]", "sql-injection", "manual"]
14+
- addsTo:
15+
pack: codeql/java-all
16+
extensible: summaryModel
17+
data:
18+
- ["org.springframework.r2dbc.core", "DatabaseClient", True, "sql", "(java.util.function.Supplier)", "", "Argument[0].ReturnValue", "ReturnValue", "taint", "manual"]
19+
- ["org.springframework.r2dbc.core", "DatabaseClient$GenericExecuteSpec", True, "bind", "(String,Object)", "", "Argument[this]", "ReturnValue", "taint", "manual"]
20+
- ["org.springframework.r2dbc.core", "DatabaseClient$GenericExecuteSpec", True, "bind", "(int,Object)", "", "Argument[this]", "ReturnValue", "taint", "manual"]
21+
- ["org.springframework.r2dbc.core", "DatabaseClient$GenericExecuteSpec", True, "bindNull", "(String,Class)", "", "Argument[this]", "ReturnValue", "taint", "manual"]
22+
- ["org.springframework.r2dbc.core", "DatabaseClient$GenericExecuteSpec", True, "bindNull", "(int,Class)", "", "Argument[this]", "ReturnValue", "taint", "manual"]
23+
- ["org.springframework.r2dbc.core", "DatabaseClient$GenericExecuteSpec", True, "bindProperties", "(Object)", "", "Argument[this]", "ReturnValue", "taint", "manual"]
24+
- ["org.springframework.r2dbc.core", "DatabaseClient$GenericExecuteSpec", True, "bindValues", "(List)", "", "Argument[this]", "ReturnValue", "taint", "manual"]
25+
- ["org.springframework.r2dbc.core", "DatabaseClient$GenericExecuteSpec", True, "bindValues", "(Map)", "", "Argument[this]", "ReturnValue", "taint", "manual"]
26+
- ["org.springframework.r2dbc.core", "DatabaseClient$GenericExecuteSpec", True, "filter", "(java.util.function.Function)", "", "Argument[this]", "ReturnValue", "taint", "manual"]
27+
- ["org.springframework.r2dbc.core", "DatabaseClient$GenericExecuteSpec", True, "filter", "(org.springframework.r2dbc.core.StatementFilterFunction)", "", "Argument[this]", "ReturnValue", "taint", "manual"]

java/ql/test/query-tests/security/CWE-089/semmle/examples/SpringJdbc.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import java.sql.ResultSet;
22
import java.util.Map;
3+
import java.util.function.Supplier;
4+
import io.r2dbc.spi.Batch;
5+
import io.r2dbc.spi.Connection;
36
import org.springframework.jdbc.core.JdbcTemplate;
47
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
58
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
@@ -13,6 +16,7 @@
1316
import org.springframework.jdbc.object.SqlFunction;
1417
import org.springframework.jdbc.object.SqlUpdate;
1518
import org.springframework.jdbc.object.UpdatableSqlQuery;
19+
import org.springframework.r2dbc.core.DatabaseClient;
1620

1721
public class SpringJdbc {
1822

@@ -80,4 +84,43 @@ public static void test(JdbcTemplate template, NamedParameterJdbcOperations name
8084
namedParamTemplate.update(source(), null, null, null); // $ sqlInjection
8185
}
8286

87+
public static void testR2dbc(DatabaseClient client, Connection connection, Batch batch) {
88+
client.sql(source()); // $ sqlInjection
89+
client.sql((Supplier<String>) SpringJdbc::source).fetch(); // $ sqlInjection
90+
client.sql((Supplier<String>) SpringJdbc::source).then(); // $ sqlInjection
91+
client.sql((Supplier<String>) SpringJdbc::source).map(row -> row); // $ sqlInjection
92+
client.sql((Supplier<String>) SpringJdbc::source).map((row, metadata) -> row); // $ sqlInjection
93+
client.sql((Supplier<String>) SpringJdbc::source).flatMap(row -> row); // $ sqlInjection
94+
client.sql((Supplier<String>) SpringJdbc::source).mapValue(String.class); // $ sqlInjection
95+
client.sql((Supplier<String>) SpringJdbc::source).mapProperties(String.class); // $ sqlInjection
96+
client.sql((Supplier<String>) SpringJdbc::source)
97+
.bind(0, "value")
98+
.bind("name", "value")
99+
.bindNull(0, String.class)
100+
.bindNull("name", String.class)
101+
.bindValues(java.util.List.of("value"))
102+
.bindValues(java.util.Map.of("name", "value"))
103+
.bindProperties(new Object())
104+
.filter(statement -> statement)
105+
.filter((org.springframework.r2dbc.core.StatementFilterFunction) null) // $ sqlInjection
106+
.fetch();
107+
connection.createStatement(source()); // $ sqlInjection
108+
batch.add(source()); // $ sqlInjection
109+
connection.createSavepoint(source()); // $ sqlInjection
110+
connection.releaseSavepoint(source()); // $ sqlInjection
111+
connection.rollbackTransactionToSavepoint(source()); // $ sqlInjection
112+
connection.createStatement("INSERT INTO test VALUES (1)")
113+
.returnGeneratedValues(source()); // $ sqlInjection
114+
115+
client.sql(() -> "SELECT * FROM test WHERE value = :value").bind("value", source()).fetch();
116+
client.sql(() -> "SELECT * FROM test").filter(statement -> source()).fetch();
117+
client.sql(() -> "SELECT * FROM test").map(row -> source());
118+
connection.createStatement("SELECT * FROM test");
119+
batch.add("SELECT * FROM test");
120+
connection.createSavepoint("savepoint");
121+
connection.releaseSavepoint("savepoint");
122+
connection.rollbackTransactionToSavepoint("savepoint");
123+
connection.createStatement("INSERT INTO test VALUES (1)").returnGeneratedValues("id");
124+
}
125+
83126
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package io.r2dbc.spi;
2+
3+
public interface Batch {
4+
Batch add(String sql);
5+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package io.r2dbc.spi;
2+
3+
public interface Connection {
4+
Statement createStatement(String sql);
5+
6+
void createSavepoint(String name);
7+
8+
void releaseSavepoint(String name);
9+
10+
void rollbackTransactionToSavepoint(String name);
11+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package io.r2dbc.spi;
2+
3+
public interface Statement {
4+
Statement returnGeneratedValues(String... columns);
5+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package org.springframework.r2dbc.core;
2+
3+
import java.util.List;
4+
import java.util.Map;
5+
import java.util.function.BiFunction;
6+
import java.util.function.Function;
7+
import java.util.function.Supplier;
8+
9+
public interface DatabaseClient {
10+
GenericExecuteSpec sql(String sql);
11+
12+
GenericExecuteSpec sql(Supplier<String> sqlSupplier);
13+
14+
interface GenericExecuteSpec {
15+
GenericExecuteSpec bind(int index, Object value);
16+
17+
GenericExecuteSpec bind(String name, Object value);
18+
19+
GenericExecuteSpec bindNull(int index, Class<?> type);
20+
21+
GenericExecuteSpec bindNull(String name, Class<?> type);
22+
23+
GenericExecuteSpec bindValues(List<Object> values);
24+
25+
GenericExecuteSpec bindValues(Map<String, Object> values);
26+
27+
GenericExecuteSpec bindProperties(Object source);
28+
29+
GenericExecuteSpec filter(Function<Object, Object> filterFunction);
30+
31+
GenericExecuteSpec filter(StatementFilterFunction filterFunction);
32+
33+
Object fetch();
34+
35+
Object then();
36+
37+
Object map(Function<Object, Object> mappingFunction);
38+
39+
Object map(BiFunction<Object, Object, Object> mappingFunction);
40+
41+
Object mapValue(Class<?> mappedClass);
42+
43+
Object mapProperties(Class<?> mappedClass);
44+
45+
Object flatMap(Function<Object, Object> mappingFunction);
46+
}
47+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package org.springframework.r2dbc.core;
2+
3+
public interface StatementFilterFunction {
4+
}

0 commit comments

Comments
 (0)