diff --git a/rust/ql/lib/change-notes/2025-08-11-database-models.md b/rust/ql/lib/change-notes/2025-08-11-database-models.md new file mode 100644 index 000000000000..e8aa6dda7a62 --- /dev/null +++ b/rust/ql/lib/change-notes/2025-08-11-database-models.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added more detail to models of `postgres`, `rusqlite`, `sqlx` and `tokio-postgres`. This may improve query results, particularly for `rust/sql-injection` and `rust/cleartext-storage-database`. diff --git a/rust/ql/lib/codeql/rust/frameworks/postgres.model.yml b/rust/ql/lib/codeql/rust/frameworks/postgres.model.yml index 81877ed17bdf..32dadb2503f6 100644 --- a/rust/ql/lib/codeql/rust/frameworks/postgres.model.yml +++ b/rust/ql/lib/codeql/rust/frameworks/postgres.model.yml @@ -13,3 +13,12 @@ extensions: - ["::query_raw", "Argument[0]", "sql-injection", "manual"] - ["::query_typed", "Argument[0]", "sql-injection", "manual"] - ["::query_typed_raw", "Argument[0]", "sql-injection", "manual"] + - ["::simple_query", "Argument[0]", "sql-injection", "manual"] + - addsTo: + pack: codeql/rust-all + extensible: sourceModel + data: + - ["::get", "ReturnValue", "database", "manual"] + - ["::try_get", "ReturnValue.Field[core::result::Result::Ok(0)]", "database", "manual"] + - ["::get", "ReturnValue.Field[core::option::Option::Some(0)]", "database", "manual"] + - ["::try_get", "ReturnValue.Field[core::result::Result::Ok(0)].Field[core::option::Option::Some(0)]", "database", "manual"] diff --git a/rust/ql/lib/codeql/rust/frameworks/rusqlite.model.yml b/rust/ql/lib/codeql/rust/frameworks/rusqlite.model.yml index 43030de02d5b..832d31e661c5 100644 --- a/rust/ql/lib/codeql/rust/frameworks/rusqlite.model.yml +++ b/rust/ql/lib/codeql/rust/frameworks/rusqlite.model.yml @@ -5,10 +5,12 @@ extensions: data: - ["::execute", "Argument[0]", "sql-injection", "manual"] - ["::execute_batch", "Argument[0]", "sql-injection", "manual"] + - ["::prepare_cached", "Argument[0]", "sql-injection", "manual"] - ["::prepare", "Argument[0]", "sql-injection", "manual"] - - [::prepare_with_flags", "Argument[0]", "sql-injection", "manual"] + - ["::prepare_with_flags", "Argument[0]", "sql-injection", "manual"] - ["::query_row", "Argument[0]", "sql-injection", "manual"] - ["::query_row_and_then", "Argument[0]", "sql-injection", "manual"] + - ["::query_one", "Argument[0]", "sql-injection", "manual"] - addsTo: pack: codeql/rust-all extensible: sourceModel diff --git a/rust/ql/lib/codeql/rust/frameworks/sqlx.model.yml b/rust/ql/lib/codeql/rust/frameworks/sqlx.model.yml index efc6022b0c55..082bde1f9425 100644 --- a/rust/ql/lib/codeql/rust/frameworks/sqlx.model.yml +++ b/rust/ql/lib/codeql/rust/frameworks/sqlx.model.yml @@ -11,3 +11,11 @@ extensions: - ["sqlx_core::query_scalar_with::query_scalar_with", "Argument[0]", "sql-injection", "manual"] - ["sqlx_core::raw_sql::raw_sql", "Argument[0]", "sql-injection", "manual"] - ["<_ as sqlx_core::executor::Executor>::execute", "Argument[0]", "sql-injection", "manual"] + - addsTo: + pack: codeql/rust-all + extensible: sourceModel + data: + - ["::get", "ReturnValue", "database", "manual"] + - ["::get_unchecked", "ReturnValue", "database", "manual"] + - ["::try_get", "ReturnValue.Field[core::result::Result::Ok(0)]", "database", "manual"] + - ["::try_get_unchecked", "ReturnValue.Field[core::result::Result::Ok(0)]", "database", "manual"] diff --git a/rust/ql/lib/codeql/rust/frameworks/tokio-postgres.model.yml b/rust/ql/lib/codeql/rust/frameworks/tokio-postgres.model.yml index 9cac599357df..7e55b1c8bb7d 100644 --- a/rust/ql/lib/codeql/rust/frameworks/tokio-postgres.model.yml +++ b/rust/ql/lib/codeql/rust/frameworks/tokio-postgres.model.yml @@ -9,6 +9,7 @@ extensions: - ["::prepare", "Argument[0]", "sql-injection", "manual"] - ["::prepare_typed", "Argument[0]", "sql-injection", "manual"] - ["::query", "Argument[0]", "sql-injection", "manual"] + - ["::query_one", "Argument[0]", "sql-injection", "manual"] - ["::query_opt", "Argument[0]", "sql-injection", "manual"] - ["::query_raw", "Argument[0]", "sql-injection", "manual"] - ["::query_typed", "Argument[0]", "sql-injection", "manual"] @@ -21,3 +22,5 @@ extensions: data: - ["::get", "ReturnValue", "database", "manual"] - ["::try_get", "ReturnValue.Field[core::result::Result::Ok(0)]", "database", "manual"] + - ["::get", "ReturnValue.Field[core::option::Option::Some(0)]", "database", "manual"] + - ["::try_get", "ReturnValue.Field[core::result::Result::Ok(0)].Field[core::option::Option::Some(0)]", "database", "manual"] diff --git a/rust/ql/test/library-tests/frameworks/postgres/CONSISTENCY/PathResolutionConsistency.expected b/rust/ql/test/library-tests/frameworks/postgres/CONSISTENCY/PathResolutionConsistency.expected index 8c8a9767934d..52ccf4c06dc7 100644 --- a/rust/ql/test/library-tests/frameworks/postgres/CONSISTENCY/PathResolutionConsistency.expected +++ b/rust/ql/test/library-tests/frameworks/postgres/CONSISTENCY/PathResolutionConsistency.expected @@ -5,3 +5,5 @@ multipleCallTargets | main.rs:28:16:28:29 | query.as_str() | | main.rs:29:20:29:33 | query.as_str() | | main.rs:30:20:30:33 | query.as_str() | +| main.rs:32:20:32:33 | query.as_str() | +| main.rs:33:22:33:35 | query.as_str() | diff --git a/rust/ql/test/library-tests/frameworks/postgres/main.rs b/rust/ql/test/library-tests/frameworks/postgres/main.rs index dd1084b6ee51..262e22855bf1 100644 --- a/rust/ql/test/library-tests/frameworks/postgres/main.rs +++ b/rust/ql/test/library-tests/frameworks/postgres/main.rs @@ -1,5 +1,4 @@ - fn main() -> Result<(), Box> { // Get input from CLI let args: Vec = std::env::args().collect(); @@ -18,19 +17,22 @@ fn main() -> Result<(), Box> { )?; let query = format!("INSERT INTO person (name, age) VALUES ('{}', '{}')", name, age); + let query2 = "INSERT INTO person (id) VALUES ($1)"; conn.execute(query.as_str(), &[])?; // $ sql-sink conn.batch_execute(query.as_str())?; // $ sql-sink conn.prepare(query.as_str())?; // $ sql-sink - // conn.prepare_typed(query.as_str(), &[])?; + conn.prepare_typed(query2, &[postgres::types::Type::INT4])?; // $ sql-sink conn.query(query.as_str(), &[])?; // $ sql-sink conn.query_one(query.as_str(), &[])?; // $ sql-sink conn.query_opt(query.as_str(), &[])?; // $ sql-sink - // conn.query_raw(query.as_str(), &[])?; - // conn.query_typed(query.as_str(), &[])?; - // conn.query_typed_raw(query.as_str(), &[])?; + let params: Vec = vec![0]; + conn.query_raw(query.as_str(), params)?; // $ sql-sink + conn.query_typed(query.as_str(), &[])?; // $ sql-sink + let params: Vec<(i32, postgres::types::Type)> = vec![(0, postgres::types::Type::INT4)]; + conn.query_typed_raw(query2, params)?; // $ sql-sink for row in &conn.query("SELECT id, name, age FROM person", &[])? { // $ sql-sink let id: i32 = row.get("id"); // $ database-read @@ -39,5 +41,14 @@ fn main() -> Result<(), Box> { println!("found person: {} {} {}", id, name, age); } + for message in &conn.simple_query("SELECT id, name, age FROM person")? { // $ sql-sink + if let postgres::SimpleQueryMessage::Row(row) = message { + let id: i32 = row.get(0).unwrap().parse().unwrap(); // $ database-read + let name: &str = row.get(1).unwrap(); // $ database-read + let age: i32 = row.get(2).unwrap().parse().unwrap(); // $ database-read + println!("found person: {} {} {}", id, name, age); + } + } + Ok(()) } diff --git a/rust/ql/test/library-tests/frameworks/rusqlite/main.rs b/rust/ql/test/library-tests/frameworks/rusqlite/main.rs index 324ee5be8fa7..441aa35f798b 100644 --- a/rust/ql/test/library-tests/frameworks/rusqlite/main.rs +++ b/rust/ql/test/library-tests/frameworks/rusqlite/main.rs @@ -48,5 +48,13 @@ fn main() -> Result<(), Box> { }) })?; + _ = connection.prepare_cached("SELECT id, name, age FROM person")?; // $ sql-sink + _ = connection.prepare_with_flags("SELECT id, name, age FROM person", rusqlite::PrepFlags::empty())?; // $ sql-sink + _ = connection.query_row_and_then("SELECT id, name, age FROM person", [], |row| { // $ sql-sink + let row: &rusqlite::Row<'_> = row; + let result: Result = Ok(row.get(0)?); // $ database-read + result + })?; + Ok(()) }