From 5943cc16e4179e9e3fd9fd602ac154f6d846c8c9 Mon Sep 17 00:00:00 2001 From: GeekMasher Date: Fri, 7 Feb 2025 10:26:40 +0000 Subject: [PATCH 1/3] feat(rust): Add Database Sources + tokio-postgres support --- rust/ql/lib/codeql/rust/Concepts.qll | 26 +++++++++++++++++++ .../rust/frameworks/tokio-postgres.model.yml | 24 +++++++++++++++++ .../frameworks/postgres/Postgres.ql | 11 +++++++- .../library-tests/frameworks/postgres/main.rs | 6 ++--- 4 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 rust/ql/lib/codeql/rust/frameworks/tokio-postgres.model.yml diff --git a/rust/ql/lib/codeql/rust/Concepts.qll b/rust/ql/lib/codeql/rust/Concepts.qll index 136d5f94d73f..1ad206477aee 100644 --- a/rust/ql/lib/codeql/rust/Concepts.qll +++ b/rust/ql/lib/codeql/rust/Concepts.qll @@ -100,6 +100,32 @@ class ModeledEnvironmentSource extends EnvironmentSource::Range { ModeledEnvironmentSource() { sourceNode(this, "environment-source") } } +/** + * A data flow source corresponding to the program's database reads. + */ +final class DatabaseSource = DatabaseSource::Range; + +/** + * Provides a class for modeling new sources for the program's database reads. + */ +module DatabaseSource { + /** + * A data flow source corresponding to the program's database reads. + */ + abstract class Range extends ThreatModelSource::Range { + override string getThreatModel() { result = "database" } + + override string getSourceType() { result = "DatabaseSource" } + } +} + +/** + * An externally modeled source for data from the program's database. + */ +class ModeledDatabaseSource extends DatabaseSource::Range { + ModeledDatabaseSource() { sourceNode(this, "database") } +} + /** * A data flow source for remote (network) data. */ diff --git a/rust/ql/lib/codeql/rust/frameworks/tokio-postgres.model.yml b/rust/ql/lib/codeql/rust/frameworks/tokio-postgres.model.yml new file mode 100644 index 000000000000..e8ce24e82e1c --- /dev/null +++ b/rust/ql/lib/codeql/rust/frameworks/tokio-postgres.model.yml @@ -0,0 +1,24 @@ +extensions: + - addsTo: + pack: codeql/rust-all + extensible: sinkModel + data: + - ["repo:https://github.com/sfackler/rust-postgres:tokio-postgres", "::execute", "Argument[0]", "sql-injection", "manual"] + - ["repo:https://github.com/sfackler/rust-postgres:tokio-postgres", "::btah_execute", "Argument[0]", "sql-injection", "manual"] + - ["repo:https://github.com/sfackler/rust-postgres:tokio-postgres", "::execute_raw", "Argument[0]", "sql-injection", "manual"] + - ["repo:https://github.com/sfackler/rust-postgres:tokio-postgres", "::prepare", "Argument[0]", "sql-injection", "manual"] + - ["repo:https://github.com/sfackler/rust-postgres:tokio-postgres", "::prepare_typed", "Argument[0]", "sql-injection", "manual"] + - ["repo:https://github.com/sfackler/rust-postgres:tokio-postgres", "::query", "Argument[0]", "sql-injection", "manual"] + - ["repo:https://github.com/sfackler/rust-postgres:tokio-postgres", "::query_opt", "Argument[0]", "sql-injection", "manual"] + - ["repo:https://github.com/sfackler/rust-postgres:tokio-postgres", "::query_raw", "Argument[0]", "sql-injection", "manual"] + - ["repo:https://github.com/sfackler/rust-postgres:tokio-postgres", "::query_typed", "Argument[0]", "sql-injection", "manual"] + - ["repo:https://github.com/sfackler/rust-postgres:tokio-postgres", "::query_typed_raw", "Argument[0]", "sql-injection", "manual"] + - ["repo:https://github.com/sfackler/rust-postgres:tokio-postgres", "::simple_query", "Argument[0]", "sql-injection", "manual"] + - ["repo:https://github.com/sfackler/rust-postgres:tokio-postgres", "::simple_query_raw", "Argument[0]", "sql-injection", "manual"] + + - addsTo: + pack: codeql/rust-all + extensible: sourceModel + data: + - ["repo:https://github.com/sfackler/rust-postgres:tokio-postgres", "::get", "ReturnValue", "database", "manual"] + - ["repo:https://github.com/sfackler/rust-postgres:tokio-postgres", "::try_get", "ReturnValue.Variant[crate::result::Result::Ok(0)]", "database", "manual"] diff --git a/rust/ql/test/library-tests/frameworks/postgres/Postgres.ql b/rust/ql/test/library-tests/frameworks/postgres/Postgres.ql index 4681fee505f5..abfdcc27c13c 100644 --- a/rust/ql/test/library-tests/frameworks/postgres/Postgres.ql +++ b/rust/ql/test/library-tests/frameworks/postgres/Postgres.ql @@ -1,9 +1,10 @@ import rust +import codeql.rust.Concepts import codeql.rust.security.SqlInjectionExtensions import utils.test.InlineExpectationsTest module PostgresTest implements TestSig { - string getARelevantTag() { result = "sql-sink" } + string getARelevantTag() { result = ["sql-sink", "database-read"] } predicate hasActualResult(Location location, string element, string tag, string value) { exists(SqlInjection::Sink sink | @@ -13,6 +14,14 @@ module PostgresTest implements TestSig { tag = "sql-sink" and value = "" ) + or + exists(ModeledDatabaseSource sink | + location = sink.getLocation() and + location.getFile().getBaseName() != "" and + element = sink.toString() and + tag = "database-read" and + value = "" + ) } } diff --git a/rust/ql/test/library-tests/frameworks/postgres/main.rs b/rust/ql/test/library-tests/frameworks/postgres/main.rs index 5754ef427824..8a04f8d00e83 100644 --- a/rust/ql/test/library-tests/frameworks/postgres/main.rs +++ b/rust/ql/test/library-tests/frameworks/postgres/main.rs @@ -33,9 +33,9 @@ fn main() -> Result<(), Box> { // conn.query_typed_raw(query.as_str(), &[])?; for row in &conn.query("SELECT id, name, age FROM person", &[])? { // $ sql-sink - let id: i32 = row.get("id"); - let name: &str = row.get("name"); - let age: i32 = row.get("age"); + let id: i32 = row.get("id"); // $ database-read + let name: &str = row.try_get("name")?; // $ database-read + let age: i32 = row.try_get("age").unwrap(); // $ database-read println!("found person: {} {} {}", id, name, age); } From 5c656412d17e2f740e5ab5f0e922a8d8329dafe6 Mon Sep 17 00:00:00 2001 From: Mathew Payne <2772944+GeekMasher@users.noreply.github.com> Date: Fri, 7 Feb 2025 10:33:29 +0000 Subject: [PATCH 2/3] Update rust/ql/lib/codeql/rust/frameworks/tokio-postgres.model.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- rust/ql/lib/codeql/rust/frameworks/tokio-postgres.model.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e8ce24e82e1c..77c13f705dc3 100644 --- a/rust/ql/lib/codeql/rust/frameworks/tokio-postgres.model.yml +++ b/rust/ql/lib/codeql/rust/frameworks/tokio-postgres.model.yml @@ -4,7 +4,7 @@ extensions: extensible: sinkModel data: - ["repo:https://github.com/sfackler/rust-postgres:tokio-postgres", "::execute", "Argument[0]", "sql-injection", "manual"] - - ["repo:https://github.com/sfackler/rust-postgres:tokio-postgres", "::btah_execute", "Argument[0]", "sql-injection", "manual"] + - ["repo:https://github.com/sfackler/rust-postgres:tokio-postgres", "::batch_execute", "Argument[0]", "sql-injection", "manual"] - ["repo:https://github.com/sfackler/rust-postgres:tokio-postgres", "::execute_raw", "Argument[0]", "sql-injection", "manual"] - ["repo:https://github.com/sfackler/rust-postgres:tokio-postgres", "::prepare", "Argument[0]", "sql-injection", "manual"] - ["repo:https://github.com/sfackler/rust-postgres:tokio-postgres", "::prepare_typed", "Argument[0]", "sql-injection", "manual"] From be883ad4ccbe737a350182382e0ab7c510e7d8ed Mon Sep 17 00:00:00 2001 From: Mathew Payne <2772944+GeekMasher@users.noreply.github.com> Date: Fri, 7 Feb 2025 11:10:43 +0000 Subject: [PATCH 3/3] fix(rust): Update naming of the DB source --- rust/ql/test/library-tests/frameworks/postgres/Postgres.ql | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/ql/test/library-tests/frameworks/postgres/Postgres.ql b/rust/ql/test/library-tests/frameworks/postgres/Postgres.ql index abfdcc27c13c..482305e46cb3 100644 --- a/rust/ql/test/library-tests/frameworks/postgres/Postgres.ql +++ b/rust/ql/test/library-tests/frameworks/postgres/Postgres.ql @@ -15,10 +15,10 @@ module PostgresTest implements TestSig { value = "" ) or - exists(ModeledDatabaseSource sink | - location = sink.getLocation() and + exists(ModeledDatabaseSource source | + location = source.getLocation() and location.getFile().getBaseName() != "" and - element = sink.toString() and + element = source.toString() and tag = "database-read" and value = "" )