Skip to content

Commit ba560f2

Browse files
committed
Rust: Model SQLx.
1 parent e96f15d commit ba560f2

File tree

3 files changed

+91
-50
lines changed

3 files changed

+91
-50
lines changed

rust/ql/lib/codeql/rust/Frameworks.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44

55
private import codeql.rust.frameworks.Reqwest
66
private import codeql.rust.frameworks.stdlib.Env
7+
private import codeql.rust.frameworks.Sqlx
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* Provides modeling for the `SQLx` library.
3+
*/
4+
5+
private import rust
6+
private import codeql.rust.Concepts
7+
private import codeql.rust.dataflow.DataFlow
8+
9+
/**
10+
* A call to `sqlx::query` and variations.
11+
*/
12+
private class SqlxQuery extends SqlConstruction::Range {
13+
CallExpr call;
14+
15+
SqlxQuery() {
16+
this.asExpr().getExpr() = call and
17+
call.getExpr().(PathExpr).getPath().getResolvedPath() =
18+
[
19+
"crate::query::query", "crate::query_as::query_as", "crate::query_with::query_with",
20+
"crate::query_as_with::query_as_with", "crate::query_scalar::query_scalar",
21+
"crate::query_scalar_with::query_scalar_with", "crate::raw_sql::raw_sql"
22+
]
23+
}
24+
25+
override DataFlow::Node getSql() { result.asExpr().getExpr() = call.getArgList().getArg(0) }
26+
}
27+
28+
/**
29+
* A call to `sqlx::Executor::execute`.
30+
*/
31+
private class SqlxExecute extends SqlConstruction::Range {
32+
MethodCallExpr call;
33+
34+
SqlxExecute() {
35+
this.asExpr().getExpr() = call and
36+
call.(Resolvable).getResolvedPath() = "crate::executor::Executor::execute"
37+
}
38+
39+
override DataFlow::Node getSql() { result.asExpr().getExpr() = call.getArgList().getArg(0) }
40+
}

rust/ql/test/query-tests/security/CWE-089/sqlx.rs

Lines changed: 50 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -57,31 +57,31 @@ async fn test_sqlx_mysql(url: &str, enable_remote: bool) -> Result<(), sqlx::Err
5757
let prepared_query_1 = String::from("SELECT * FROM people WHERE firstname=?"); // (prepared arguments are safe)
5858

5959
// direct execution
60-
let _ = conn.execute(safe_query_1.as_str()).await?; // $ MISSING: sql-sink
61-
let _ = conn.execute(safe_query_2.as_str()).await?; // $ MISSING: sql-sink
62-
let _ = conn.execute(safe_query_3.as_str()).await?; // $ MISSING: sql-sink
63-
let _ = conn.execute(unsafe_query_1.as_str()).await?; // $ MISSING: sql-sink Alert[sql-injection]=args1
60+
let _ = conn.execute(safe_query_1.as_str()).await?; // $ sql-sink
61+
let _ = conn.execute(safe_query_2.as_str()).await?; // $ sql-sink
62+
let _ = conn.execute(safe_query_3.as_str()).await?; // $ sql-sink
63+
let _ = conn.execute(unsafe_query_1.as_str()).await?; // $ sql-sink AMISSING: lert[sql-injection]=args1
6464
if enable_remote {
65-
let _ = conn.execute(unsafe_query_2.as_str()).await?; // $ MISSING: sql-sink Alert[sql-injection]=remote1
66-
let _ = conn.execute(unsafe_query_3.as_str()).await?; // $ MISSING: sql-sink Alert[sql-injection]=remote1
67-
let _ = conn.execute(unsafe_query_4.as_str()).await?; // $ MISSING: sql-sink Alert[sql-injection]=remote1
65+
let _ = conn.execute(unsafe_query_2.as_str()).await?; // $ sql-sink MISSING: Alert[sql-injection]=remote1
66+
let _ = conn.execute(unsafe_query_3.as_str()).await?; // $ sql-sink MISSING: Alert[sql-injection]=remote1
67+
let _ = conn.execute(unsafe_query_4.as_str()).await?; // $ sql-sink MISSING: Alert[sql-injection]=remote1
6868
}
6969

7070
// prepared queries
71-
let _ = sqlx::query(safe_query_1.as_str()).execute(&pool).await?; // $ MISSING: sql-sink
72-
let _ = sqlx::query(safe_query_2.as_str()).execute(&pool).await?; // $ MISSING: sql-sink
73-
let _ = sqlx::query(safe_query_3.as_str()).execute(&pool).await?; // $ MISSING: sql-sink
74-
let _ = sqlx::query(unsafe_query_1.as_str()).execute(&pool).await?; // $ MISSING: sql-sink Alert[sql-injection]=args1
71+
let _ = sqlx::query(safe_query_1.as_str()).execute(&pool).await?; // $ sql-sink
72+
let _ = sqlx::query(safe_query_2.as_str()).execute(&pool).await?; // $ sql-sink
73+
let _ = sqlx::query(safe_query_3.as_str()).execute(&pool).await?; // $ sql-sink
74+
let _ = sqlx::query(unsafe_query_1.as_str()).execute(&pool).await?; // $ sql-sink MISSING: Alert[sql-injection]=args1
7575
if enable_remote {
76-
let _ = sqlx::query(unsafe_query_2.as_str()).execute(&pool).await?; // $ MISSING: sql-sink Alert[sql-injection]=remote1
77-
let _ = sqlx::query(unsafe_query_3.as_str()).execute(&pool).await?; // $ MISSING: sql-sink Alert[sql-injection]=remote1
78-
let _ = sqlx::query(unsafe_query_4.as_str()).execute(&pool).await?; // $ MISSING: sql-sink Alert[sql-injection]=remote1
76+
let _ = sqlx::query(unsafe_query_2.as_str()).execute(&pool).await?; // $ sql-sink MISSING: Alert[sql-injection]=remote1
77+
let _ = sqlx::query(unsafe_query_3.as_str()).execute(&pool).await?; // $ sql-sink MISSING: Alert[sql-injection]=remote1
78+
let _ = sqlx::query(unsafe_query_4.as_str()).execute(&pool).await?; // $ sql-sink MISSING: Alert[sql-injection]=remote1
7979
}
80-
let _ = sqlx::query(prepared_query_1.as_str()).bind(const_string).execute(&pool).await?; // $ MISSING: sql-sink
81-
let _ = sqlx::query(prepared_query_1.as_str()).bind(arg_string).execute(&pool).await?; // $ MISSING: sql-sink
80+
let _ = sqlx::query(prepared_query_1.as_str()).bind(const_string).execute(&pool).await?; // $ sql-sink
81+
let _ = sqlx::query(prepared_query_1.as_str()).bind(arg_string).execute(&pool).await?; // $ sql-sink
8282
if enable_remote {
83-
let _ = sqlx::query(prepared_query_1.as_str()).bind(remote_string).execute(&pool).await?; // $ MISSING: sql-sink
84-
let _ = sqlx::query(prepared_query_1.as_str()).bind(remote_number).execute(&pool).await?; // $ MISSING: sql-sink
83+
let _ = sqlx::query(prepared_query_1.as_str()).bind(remote_string).execute(&pool).await?; // $ sql-sink
84+
let _ = sqlx::query(prepared_query_1.as_str()).bind(remote_number).execute(&pool).await?; // $ sql-sink
8585
}
8686

8787
Ok(())
@@ -99,56 +99,56 @@ async fn test_sqlx_sqlite(url: &str, enable_remote: bool) -> Result<(), sqlx::Er
9999
let prepared_query_1 = String::from("SELECT * FROM people WHERE firstname=?"); // (prepared arguments are safe)
100100

101101
// direct execution (with extra variants)
102-
let _ = conn.execute(safe_query_1.as_str()).await?; // $ MISSING: sql-sink
102+
let _ = conn.execute(safe_query_1.as_str()).await?; // $ sql-sink
103103
if enable_remote {
104-
let _ = conn.execute(unsafe_query_1.as_str()).await?; // $ MISSING: sql-sink Alert[sql-injection]=remote2
104+
let _ = conn.execute(unsafe_query_1.as_str()).await?; // $ sql-sink MISSING: Alert[sql-injection]=remote2
105105
}
106106
// ...
107-
let _ = sqlx::raw_sql(safe_query_1.as_str()).execute(&mut conn).await?; // $ MISSING: ql-sink
107+
let _ = sqlx::raw_sql(safe_query_1.as_str()).execute(&mut conn).await?; // $ sql-sink
108108
if enable_remote {
109-
let _ = sqlx::raw_sql(unsafe_query_1.as_str()).execute(&mut conn).await?; // $ MISSING: sql-sink Alert[sql-injection]=remote2
109+
let _ = sqlx::raw_sql(unsafe_query_1.as_str()).execute(&mut conn).await?; // $ sql-sink MISSING: Alert[sql-injection]=remote2
110110
}
111111

112112
// prepared queries (with extra variants)
113-
let _ = sqlx::query(safe_query_1.as_str()).execute(&mut conn).await?; // $ MISSING: sql-sink
114-
let _ = sqlx::query(prepared_query_1.as_str()).bind(&const_string).execute(&mut conn).await?; // $ MISSING: sql-sink
113+
let _ = sqlx::query(safe_query_1.as_str()).execute(&mut conn).await?; // $ sql-sink
114+
let _ = sqlx::query(prepared_query_1.as_str()).bind(&const_string).execute(&mut conn).await?; // $ sql-sink
115115
if enable_remote {
116-
let _ = sqlx::query(unsafe_query_1.as_str()).execute(&mut conn).await?; // $ MISSING: sql-sink Alert[sql-injection]=remote2
117-
let _ = sqlx::query(prepared_query_1.as_str()).bind(&remote_string).execute(&mut conn).await?; // $ MISSING: sql-sink
116+
let _ = sqlx::query(unsafe_query_1.as_str()).execute(&mut conn).await?; // $ sql-sink MISSING: Alert[sql-injection]=remote2
117+
let _ = sqlx::query(prepared_query_1.as_str()).bind(&remote_string).execute(&mut conn).await?; // $ sql-sink
118118
}
119119
// ...
120-
let _ = sqlx::query(safe_query_1.as_str()).fetch(&mut conn); // $ MISSING: sql-sink
121-
let _ = sqlx::query(prepared_query_1.as_str()).bind(&const_string).fetch(&mut conn); // $ MISSING: sql-sink
120+
let _ = sqlx::query(safe_query_1.as_str()).fetch(&mut conn); // $ sql-sink
121+
let _ = sqlx::query(prepared_query_1.as_str()).bind(&const_string).fetch(&mut conn); // $ sql-sink
122122
if enable_remote {
123-
let _ = sqlx::query(unsafe_query_1.as_str()).fetch(&mut conn); // $ MISSING: ql-sink Alert[sql-injection]=remote2
124-
let _ = sqlx::query(prepared_query_1.as_str()).bind(&remote_string).fetch(&mut conn); // $ MISSING: sql-sink
123+
let _ = sqlx::query(unsafe_query_1.as_str()).fetch(&mut conn); // $ sql-sink MISSING: Alert[sql-injection]=remote2
124+
let _ = sqlx::query(prepared_query_1.as_str()).bind(&remote_string).fetch(&mut conn); // $ sql-sink
125125
}
126126
// ...
127-
let row1: (i64, String, String) = sqlx::query_as(safe_query_1.as_str()).fetch_one(&mut conn).await?; // $ MISSING: sql-sink
127+
let row1: (i64, String, String) = sqlx::query_as(safe_query_1.as_str()).fetch_one(&mut conn).await?; // $ sql-sink
128128
println!(" row1 = {:?}", row1);
129-
let row2: (i64, String, String) = sqlx::query_as(prepared_query_1.as_str()).bind(&const_string).fetch_one(&mut conn).await?; // $ MISSING: sql-sink
129+
let row2: (i64, String, String) = sqlx::query_as(prepared_query_1.as_str()).bind(&const_string).fetch_one(&mut conn).await?; // $ sql-sink
130130
println!(" row2 = {:?}", row2);
131131
if enable_remote {
132-
let _: (i64, String, String) = sqlx::query_as(unsafe_query_1.as_str()).fetch_one(&mut conn).await?; // $ MISSING: sql-sink Alert[sql-injection]=remote2
133-
let _: (i64, String, String) = sqlx::query_as(prepared_query_1.as_str()).bind(&remote_string).fetch_one(&mut conn).await?; // $ MISSING: sql-sink
132+
let _: (i64, String, String) = sqlx::query_as(unsafe_query_1.as_str()).fetch_one(&mut conn).await?; // $ sql-sink MISSING: Alert[sql-injection]=remote2
133+
let _: (i64, String, String) = sqlx::query_as(prepared_query_1.as_str()).bind(&remote_string).fetch_one(&mut conn).await?; // $ sql-sink
134134
}
135135
// ...
136-
let row3: (i64, String, String) = sqlx::query_as(safe_query_1.as_str()).fetch_optional(&mut conn).await?.expect("no data"); // $ MISSING: sql-sink
136+
let row3: (i64, String, String) = sqlx::query_as(safe_query_1.as_str()).fetch_optional(&mut conn).await?.expect("no data"); // $ sql-sink
137137
println!(" row3 = {:?}", row3);
138-
let row4: (i64, String, String) = sqlx::query_as(prepared_query_1.as_str()).bind(&const_string).fetch_optional(&mut conn).await?.expect("no data"); // $ MISSING: sql-sink
138+
let row4: (i64, String, String) = sqlx::query_as(prepared_query_1.as_str()).bind(&const_string).fetch_optional(&mut conn).await?.expect("no data"); // $ sql-sink
139139
println!(" row4 = {:?}", row4);
140140
if enable_remote {
141-
let _: (i64, String, String) = sqlx::query_as(unsafe_query_1.as_str()).fetch_optional(&mut conn).await?.expect("no data"); // $ MISSING: sql-sink Alert[sql-injection]=remote2
142-
let _: (i64, String, String) = sqlx::query_as(prepared_query_1.as_str()).bind(&remote_string).fetch_optional(&mut conn).await?.expect("no data"); // $ MISSING: sql-sink
141+
let _: (i64, String, String) = sqlx::query_as(unsafe_query_1.as_str()).fetch_optional(&mut conn).await?.expect("no data"); // $ sql-sink $ MISSING: Alert[sql-injection]=remote2
142+
let _: (i64, String, String) = sqlx::query_as(prepared_query_1.as_str()).bind(&remote_string).fetch_optional(&mut conn).await?.expect("no data"); // $ sql-sink
143143
}
144144
// ...
145-
let _ = sqlx::query(safe_query_1.as_str()).fetch_all(&mut conn).await?; // $ MISSING: sql-sink
146-
let _ = sqlx::query(prepared_query_1.as_str()).bind(&const_string).fetch_all(&mut conn).await?; // $ MISSING: sql-sink
147-
let _ = sqlx::query("SELECT * FROM people WHERE firstname=?").bind(&const_string).fetch_all(&mut conn).await?; // $ MISSING: sql-sink
145+
let _ = sqlx::query(safe_query_1.as_str()).fetch_all(&mut conn).await?; // $ sql-sink
146+
let _ = sqlx::query(prepared_query_1.as_str()).bind(&const_string).fetch_all(&mut conn).await?; // $ sql-sink
147+
let _ = sqlx::query("SELECT * FROM people WHERE firstname=?").bind(&const_string).fetch_all(&mut conn).await?; // $ sql-sink
148148
if enable_remote {
149-
let _ = sqlx::query(unsafe_query_1.as_str()).fetch_all(&mut conn).await?; // $ MISSING: sql-sink Alert[sql-injection]=remote2
150-
let _ = sqlx::query(prepared_query_1.as_str()).bind(&remote_string).fetch_all(&mut conn).await?; // $ MISSING: sql-sink
151-
let _ = sqlx::query("SELECT * FROM people WHERE firstname=?").bind(&remote_string).fetch_all(&mut conn).await?; // $ MISSING: sql-sink
149+
let _ = sqlx::query(unsafe_query_1.as_str()).fetch_all(&mut conn).await?; // $ sql-sink MISSING: Alert[sql-injection]=remote2
150+
let _ = sqlx::query(prepared_query_1.as_str()).bind(&remote_string).fetch_all(&mut conn).await?; // $ sql-sink
151+
let _ = sqlx::query("SELECT * FROM people WHERE firstname=?").bind(&remote_string).fetch_all(&mut conn).await?; // $ sql-sink
152152
}
153153
// ...
154154
let _ = sqlx::query!("SELECT * FROM people WHERE firstname=$1", const_string).fetch_all(&mut conn).await?; // $ MISSING: sql-sink (only takes string literals, so can't be vulnerable)
@@ -172,17 +172,17 @@ async fn test_sqlx_postgres(url: &str, enable_remote: bool) -> Result<(), sqlx::
172172
let prepared_query_1 = String::from("SELECT * FROM people WHERE firstname=$1"); // (prepared arguments are safe)
173173

174174
// direct execution
175-
let _ = conn.execute(safe_query_1.as_str()).await?; // $ MISSING: sql-sink
175+
let _ = conn.execute(safe_query_1.as_str()).await?; // $ sql-sink
176176
if enable_remote {
177-
let _ = conn.execute(unsafe_query_1.as_str()).await?; // $ MISSING: sql-sink Alert[sql-injection]=remote3
177+
let _ = conn.execute(unsafe_query_1.as_str()).await?; // $ sql-sink MISSING: Alert[sql-injection]=remote3
178178
}
179179

180180
// prepared queries
181-
let _ = sqlx::query(safe_query_1.as_str()).execute(&pool).await?; // $ MISSING: sql-sink
182-
let _ = sqlx::query(prepared_query_1.as_str()).bind(&const_string).execute(&pool).await?; // $ MISSING: sql-sink
181+
let _ = sqlx::query(safe_query_1.as_str()).execute(&pool).await?; // $ sql-sink
182+
let _ = sqlx::query(prepared_query_1.as_str()).bind(&const_string).execute(&pool).await?; // $ sql-sink
183183
if enable_remote {
184-
let _ = sqlx::query(unsafe_query_1.as_str()).execute(&pool).await?; // $ MISSING: sql-sink Alert[sql-injection]=remote3
185-
let _ = sqlx::query(prepared_query_1.as_str()).bind(&remote_string).execute(&pool).await?; // $ MISSING: sql-sink
184+
let _ = sqlx::query(unsafe_query_1.as_str()).execute(&pool).await?; // $ sql-sink MISSING: Alert[sql-injection]=remote3
185+
let _ = sqlx::query(prepared_query_1.as_str()).bind(&remote_string).execute(&pool).await?; // $ sql-sink
186186
}
187187

188188
Ok(())

0 commit comments

Comments
 (0)