diff --git a/rust/ql/lib/codeql/rust/frameworks/rusqlite.model.yml b/rust/ql/lib/codeql/rust/frameworks/rusqlite.model.yml new file mode 100644 index 000000000000..23bf804c6f50 --- /dev/null +++ b/rust/ql/lib/codeql/rust/frameworks/rusqlite.model.yml @@ -0,0 +1,11 @@ +extensions: + - addsTo: + pack: codeql/rust-all + extensible: sinkModel + data: + - ["repo:https://github.com/rusqlite/rusqlite:rusqlite", "::execute", "Argument[0]", "sql-injection", "manual"] + - ["repo:https://github.com/rusqlite/rusqlite:rusqlite", "::execute_batch", "Argument[0]", "sql-injection", "manual"] + - ["repo:https://github.com/rusqlite/rusqlite:rusqlite", "::prepare", "Argument[0]", "sql-injection", "manual"] + - ["repo:https://github.com/rusqlite/rusqlite:rusqlite", "::prepare_with_flags", "Argument[0]", "sql-injection", "manual"] + - ["repo:https://github.com/rusqlite/rusqlite:rusqlite", "::query_row", "Argument[0]", "sql-injection", "manual"] + - ["repo:https://github.com/rusqlite/rusqlite:rusqlite", "::query_row_and_then", "Argument[0]", "sql-injection", "manual"] diff --git a/rust/ql/test/library-tests/dataflow/taint/TaintFlowStep.expected b/rust/ql/test/library-tests/dataflow/taint/TaintFlowStep.expected index 21a028e8d41b..f4dccbe7ecd3 100644 --- a/rust/ql/test/library-tests/dataflow/taint/TaintFlowStep.expected +++ b/rust/ql/test/library-tests/dataflow/taint/TaintFlowStep.expected @@ -1,6 +1,6 @@ -| file://:0:0:0:0 | [summary param] 0 in lang:alloc::_::crate::fmt::format | file://:0:0:0:0 | [summary] to write: ReturnValue in lang:alloc::_::crate::fmt::format | MaD:35 | -| file://:0:0:0:0 | [summary param] self in lang:alloc::_::::as_str | file://:0:0:0:0 | [summary] to write: ReturnValue in lang:alloc::_::::as_str | MaD:33 | -| file://:0:0:0:0 | [summary param] self in repo:https://github.com/seanmonstar/reqwest:reqwest::_::::text | file://:0:0:0:0 | [summary] to write: ReturnValue.Variant[crate::result::Result::Ok(0)] in repo:https://github.com/seanmonstar/reqwest:reqwest::_::::text | MaD:12 | +| file://:0:0:0:0 | [summary param] 0 in lang:alloc::_::crate::fmt::format | file://:0:0:0:0 | [summary] to write: ReturnValue in lang:alloc::_::crate::fmt::format | MaD:34 | +| file://:0:0:0:0 | [summary param] self in lang:alloc::_::::as_str | file://:0:0:0:0 | [summary] to write: ReturnValue in lang:alloc::_::::as_str | MaD:32 | +| file://:0:0:0:0 | [summary param] self in repo:https://github.com/seanmonstar/reqwest:reqwest::_::::text | file://:0:0:0:0 | [summary] to write: ReturnValue.Variant[crate::result::Result::Ok(0)] in repo:https://github.com/seanmonstar/reqwest:reqwest::_::::text | MaD:10 | | main.rs:4:5:4:8 | 1000 | main.rs:4:5:4:12 | ... + ... | | | main.rs:4:12:4:12 | i | main.rs:4:5:4:12 | ... + ... | | | main.rs:8:20:8:20 | s | main.rs:8:14:8:20 | FormatArgsExpr | | diff --git a/rust/ql/test/library-tests/frameworks/rusqlite/Rusqlite.expected b/rust/ql/test/library-tests/frameworks/rusqlite/Rusqlite.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/rust/ql/test/library-tests/frameworks/rusqlite/Rusqlite.ql b/rust/ql/test/library-tests/frameworks/rusqlite/Rusqlite.ql new file mode 100644 index 000000000000..b8586d39c45c --- /dev/null +++ b/rust/ql/test/library-tests/frameworks/rusqlite/Rusqlite.ql @@ -0,0 +1,20 @@ +import rust +import codeql.rust.security.SqlInjectionExtensions +import codeql.rust.Concepts +import utils.test.InlineExpectationsTest + +module RusqliteTest implements TestSig { + string getARelevantTag() { result = ["sql-sink"] } + + predicate hasActualResult(Location location, string element, string tag, string value) { + exists(SqlInjection::Sink sink | + location = sink.getLocation() and + location.getFile().getBaseName() != "" and + element = sink.toString() and + tag = "sql-sink" and + value = "" + ) + } +} + +import MakeTest diff --git a/rust/ql/test/library-tests/frameworks/rusqlite/cargo.toml.manual b/rust/ql/test/library-tests/frameworks/rusqlite/cargo.toml.manual new file mode 100644 index 000000000000..c4c57f4b1bac --- /dev/null +++ b/rust/ql/test/library-tests/frameworks/rusqlite/cargo.toml.manual @@ -0,0 +1,13 @@ +[workspace] + +[package] +name = "rusqlite-test" +version = "0.1.0" +edition = "2021" + +[dependencies] +rusqlite = { version = "0.33", features = ["bundled"] } + +[[bin]] +name = "rusqlite" +path = "./main.rs" diff --git a/rust/ql/test/library-tests/frameworks/rusqlite/main.rs b/rust/ql/test/library-tests/frameworks/rusqlite/main.rs new file mode 100644 index 000000000000..54858d20b08e --- /dev/null +++ b/rust/ql/test/library-tests/frameworks/rusqlite/main.rs @@ -0,0 +1,50 @@ + +use rusqlite::Connection; + +#[derive(Debug)] +struct Person { + id: i32, + name: String, + age: i32, +} + +fn main() -> Result<(), Box> { + // Get input from CLI + let args: Vec = std::env::args().collect(); + let name = &args[1]; + let age = &args[2]; + + let connection = Connection::open_in_memory()?; + + connection.execute( // $ sql-sink + "CREATE TABLE person ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name VARCHAR NOT NULL, + age INT NOT NULL + )", + (), + )?; + + let query = format!("INSERT INTO person (name, age) VALUES ('{}', '{}')", name, age); + + connection.execute(&query, ())?; // $ sql-sink + + let person = connection.query_row(&query, (), |row| { // $ sql-sink + Ok(Person { + id: row.get(0)?, + name: row.get(1)?, + age: row.get(2)?, + }) + })?; + + let mut stmt = connection.prepare("SELECT id, name, age FROM person")?; // $ sql-sink + let people = stmt.query_map([], |row| { + Ok(Person { + id: row.get(0)?, + name: row.get(1)?, + age: row.get(2)?, + }) + })?; + + Ok(()) +} \ No newline at end of file diff --git a/rust/ql/test/library-tests/frameworks/rusqlite/options.yml b/rust/ql/test/library-tests/frameworks/rusqlite/options.yml new file mode 100644 index 000000000000..ee37af2b00db --- /dev/null +++ b/rust/ql/test/library-tests/frameworks/rusqlite/options.yml @@ -0,0 +1,3 @@ +qltest_cargo_check: true +qltest_dependencies: + - rusqlite = { version = "0.33", features = ["bundled"] }