From dc08274aa294bae0d404573370d6221dcca3e89b Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 17 Jun 2025 14:55:45 +0100 Subject: [PATCH 001/208] Rust: Update SqlxQuery, SqlxExecute from getResolvedPath -> getCanonicalPath. --- rust/ql/lib/codeql/rust/frameworks/Sqlx.qll | 14 +++++++----- .../security/CWE-089/SqlInjection.expected | 12 ---------- .../test/query-tests/security/CWE-089/sqlx.rs | 22 +++++++++---------- 3 files changed, 20 insertions(+), 28 deletions(-) diff --git a/rust/ql/lib/codeql/rust/frameworks/Sqlx.qll b/rust/ql/lib/codeql/rust/frameworks/Sqlx.qll index 5504993ab744..5b33f72fdf68 100644 --- a/rust/ql/lib/codeql/rust/frameworks/Sqlx.qll +++ b/rust/ql/lib/codeql/rust/frameworks/Sqlx.qll @@ -5,6 +5,8 @@ private import rust private import codeql.rust.Concepts private import codeql.rust.dataflow.DataFlow +private import codeql.rust.internal.TypeInference +private import codeql.rust.internal.Type /** * A call to `sqlx::query` and variations. @@ -14,11 +16,12 @@ private class SqlxQuery extends SqlConstruction::Range { SqlxQuery() { this.asExpr().getExpr() = call and - call.getFunction().(PathExpr).getResolvedPath() = + call.getStaticTarget().(Addressable).getCanonicalPath() = [ - "crate::query::query", "crate::query_as::query_as", "crate::query_with::query_with", - "crate::query_as_with::query_as_with", "crate::query_scalar::query_scalar", - "crate::query_scalar_with::query_scalar_with", "crate::raw_sql::raw_sql" + "sqlx_core::query::query", "sqlx_core::query_as::query_as", + "sqlx_core::query_with::query_with", "sqlx_core::query_as_with::query_as_with", + "sqlx_core::query_scalar::query_scalar", "sqlx_core::query_scalar_with::query_scalar_with", + "sqlx_core::raw_sql::raw_sql" ] } @@ -33,7 +36,8 @@ private class SqlxExecute extends SqlExecution::Range { SqlxExecute() { this.asExpr().getExpr() = call and - call.(Resolvable).getResolvedPath() = "crate::executor::Executor::execute" + call.getStaticTarget().(Addressable).getCanonicalPath() = + "sqlx_core::executor::Executor::execute" } override DataFlow::Node getSql() { result.asExpr().getExpr() = call.getArgList().getArg(0) } diff --git a/rust/ql/test/query-tests/security/CWE-089/SqlInjection.expected b/rust/ql/test/query-tests/security/CWE-089/SqlInjection.expected index ab8e995be762..fcfa77cfda0f 100644 --- a/rust/ql/test/query-tests/security/CWE-089/SqlInjection.expected +++ b/rust/ql/test/query-tests/security/CWE-089/SqlInjection.expected @@ -1,8 +1,4 @@ #select -| sqlx.rs:66:26:66:46 | safe_query_3.as_str() | sqlx.rs:48:25:48:46 | ...::get | sqlx.rs:66:26:66:46 | safe_query_3.as_str() | This query depends on a $@. | sqlx.rs:48:25:48:46 | ...::get | user-provided value | -| sqlx.rs:67:26:67:48 | unsafe_query_1.as_str() | sqlx.rs:47:22:47:35 | ...::args | sqlx.rs:67:26:67:48 | unsafe_query_1.as_str() | This query depends on a $@. | sqlx.rs:47:22:47:35 | ...::args | user-provided value | -| sqlx.rs:69:30:69:52 | unsafe_query_2.as_str() | sqlx.rs:48:25:48:46 | ...::get | sqlx.rs:69:30:69:52 | unsafe_query_2.as_str() | This query depends on a $@. | sqlx.rs:48:25:48:46 | ...::get | user-provided value | -| sqlx.rs:71:30:71:52 | unsafe_query_4.as_str() | sqlx.rs:48:25:48:46 | ...::get | sqlx.rs:71:30:71:52 | unsafe_query_4.as_str() | This query depends on a $@. | sqlx.rs:48:25:48:46 | ...::get | user-provided value | | sqlx.rs:77:25:77:45 | safe_query_3.as_str() | sqlx.rs:48:25:48:46 | ...::get | sqlx.rs:77:25:77:45 | safe_query_3.as_str() | This query depends on a $@. | sqlx.rs:48:25:48:46 | ...::get | user-provided value | | sqlx.rs:78:25:78:47 | unsafe_query_1.as_str() | sqlx.rs:47:22:47:35 | ...::args | sqlx.rs:78:25:78:47 | unsafe_query_1.as_str() | This query depends on a $@. | sqlx.rs:47:22:47:35 | ...::args | user-provided value | | sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() | sqlx.rs:48:25:48:46 | ...::get | sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() | This query depends on a $@. | sqlx.rs:48:25:48:46 | ...::get | user-provided value | @@ -24,22 +20,18 @@ edges | sqlx.rs:49:9:49:21 | remote_number | sqlx.rs:52:32:52:87 | MacroExpr | provenance | | | sqlx.rs:49:25:49:52 | remote_string.parse() [Ok] | sqlx.rs:49:25:49:65 | ... .unwrap_or(...) | provenance | MaD:7 | | sqlx.rs:49:25:49:65 | ... .unwrap_or(...) | sqlx.rs:49:9:49:21 | remote_number | provenance | | -| sqlx.rs:52:9:52:20 | safe_query_3 | sqlx.rs:66:26:66:46 | safe_query_3.as_str() | provenance | MaD:3 | | sqlx.rs:52:9:52:20 | safe_query_3 | sqlx.rs:77:25:77:45 | safe_query_3.as_str() | provenance | MaD:3 | | sqlx.rs:52:24:52:88 | res | sqlx.rs:52:32:52:87 | { ... } | provenance | | | sqlx.rs:52:32:52:87 | ...::format(...) | sqlx.rs:52:24:52:88 | res | provenance | | | sqlx.rs:52:32:52:87 | ...::must_use(...) | sqlx.rs:52:9:52:20 | safe_query_3 | provenance | | | sqlx.rs:52:32:52:87 | MacroExpr | sqlx.rs:52:32:52:87 | ...::format(...) | provenance | MaD:4 | | sqlx.rs:52:32:52:87 | { ... } | sqlx.rs:52:32:52:87 | ...::must_use(...) | provenance | MaD:9 | -| sqlx.rs:53:9:53:22 | unsafe_query_1 [&ref] | sqlx.rs:67:26:67:48 | unsafe_query_1.as_str() | provenance | MaD:3 | | sqlx.rs:53:9:53:22 | unsafe_query_1 [&ref] | sqlx.rs:78:25:78:47 | unsafe_query_1.as_str() | provenance | MaD:3 | | sqlx.rs:53:26:53:36 | &arg_string [&ref] | sqlx.rs:53:9:53:22 | unsafe_query_1 [&ref] | provenance | | | sqlx.rs:53:27:53:36 | arg_string | sqlx.rs:53:26:53:36 | &arg_string [&ref] | provenance | | -| sqlx.rs:54:9:54:22 | unsafe_query_2 [&ref] | sqlx.rs:69:30:69:52 | unsafe_query_2.as_str() | provenance | MaD:3 | | sqlx.rs:54:9:54:22 | unsafe_query_2 [&ref] | sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() | provenance | MaD:3 | | sqlx.rs:54:26:54:39 | &remote_string [&ref] | sqlx.rs:54:9:54:22 | unsafe_query_2 [&ref] | provenance | | | sqlx.rs:54:27:54:39 | remote_string | sqlx.rs:54:26:54:39 | &remote_string [&ref] | provenance | | -| sqlx.rs:56:9:56:22 | unsafe_query_4 | sqlx.rs:71:30:71:52 | unsafe_query_4.as_str() | provenance | MaD:3 | | sqlx.rs:56:9:56:22 | unsafe_query_4 | sqlx.rs:82:29:82:51 | unsafe_query_4.as_str() | provenance | MaD:3 | | sqlx.rs:59:9:59:73 | res | sqlx.rs:59:17:59:72 | { ... } | provenance | | | sqlx.rs:59:17:59:72 | ...::format(...) | sqlx.rs:59:9:59:73 | res | provenance | | @@ -91,10 +83,6 @@ nodes | sqlx.rs:59:17:59:72 | ...::must_use(...) | semmle.label | ...::must_use(...) | | sqlx.rs:59:17:59:72 | MacroExpr | semmle.label | MacroExpr | | sqlx.rs:59:17:59:72 | { ... } | semmle.label | { ... } | -| sqlx.rs:66:26:66:46 | safe_query_3.as_str() | semmle.label | safe_query_3.as_str() | -| sqlx.rs:67:26:67:48 | unsafe_query_1.as_str() | semmle.label | unsafe_query_1.as_str() | -| sqlx.rs:69:30:69:52 | unsafe_query_2.as_str() | semmle.label | unsafe_query_2.as_str() | -| sqlx.rs:71:30:71:52 | unsafe_query_4.as_str() | semmle.label | unsafe_query_4.as_str() | | sqlx.rs:77:25:77:45 | safe_query_3.as_str() | semmle.label | safe_query_3.as_str() | | sqlx.rs:78:25:78:47 | unsafe_query_1.as_str() | semmle.label | unsafe_query_1.as_str() | | sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() | semmle.label | unsafe_query_2.as_str() | diff --git a/rust/ql/test/query-tests/security/CWE-089/sqlx.rs b/rust/ql/test/query-tests/security/CWE-089/sqlx.rs index 3de58350f20c..291b04257d5a 100644 --- a/rust/ql/test/query-tests/security/CWE-089/sqlx.rs +++ b/rust/ql/test/query-tests/security/CWE-089/sqlx.rs @@ -61,14 +61,14 @@ async fn test_sqlx_mysql(url: &str, enable_remote: bool) -> Result<(), sqlx::Err let prepared_query_1 = String::from("SELECT * FROM people WHERE firstname=?"); // (prepared arguments are safe) // direct execution - let _ = conn.execute(safe_query_1.as_str()).await?; // $ sql-sink - let _ = conn.execute(safe_query_2.as_str()).await?; // $ sql-sink - let _ = conn.execute(safe_query_3.as_str()).await?; // $ sql-sink SPURIOUS: Alert[rust/sql-injection]=remote1 - let _ = conn.execute(unsafe_query_1.as_str()).await?; // $ sql-sink Alert[rust/sql-injection]=args1 + let _ = conn.execute(safe_query_1.as_str()).await?; // $ MISSING: sql-sink + let _ = conn.execute(safe_query_2.as_str()).await?; // $ MISSING: sql-sink + let _ = conn.execute(safe_query_3.as_str()).await?; // $ MISSING: sql-sink + let _ = conn.execute(unsafe_query_1.as_str()).await?; // $ MISSING: sql-sink Alert[rust/sql-injection]=args1 if enable_remote { - let _ = conn.execute(unsafe_query_2.as_str()).await?; // $ sql-sink Alert[rust/sql-injection]=remote1 - let _ = conn.execute(unsafe_query_3.as_str()).await?; // $ sql-sink MISSING: Alert[rust/sql-injection]=remote1 - let _ = conn.execute(unsafe_query_4.as_str()).await?; // $ sql-sink Alert[rust/sql-injection]=remote1 + let _ = conn.execute(unsafe_query_2.as_str()).await?; // $ MISSING: sql-sink Alert[rust/sql-injection]=remote1 + let _ = conn.execute(unsafe_query_3.as_str()).await?; // $ MISSING: sql-sink Alert[rust/sql-injection]=remote1 + let _ = conn.execute(unsafe_query_4.as_str()).await?; // $ MISSING: sql-sink Alert[rust/sql-injection]=remote1 } // prepared queries @@ -103,9 +103,9 @@ async fn test_sqlx_sqlite(url: &str, enable_remote: bool) -> Result<(), sqlx::Er let prepared_query_1 = String::from("SELECT * FROM people WHERE firstname=?"); // (prepared arguments are safe) // direct execution (with extra variants) - let _ = conn.execute(safe_query_1.as_str()).await?; // $ sql-sink + let _ = conn.execute(safe_query_1.as_str()).await?; // $ MISSING: sql-sink if enable_remote { - let _ = conn.execute(unsafe_query_1.as_str()).await?; // $ sql-sink MISSING: Alert[rust/sql-injection]=remote2 + let _ = conn.execute(unsafe_query_1.as_str()).await?; // $ MISSING: sql-sink Alert[rust/sql-injection]=remote2 } // ... let _ = sqlx::raw_sql(safe_query_1.as_str()).execute(&mut conn).await?; // $ sql-sink @@ -176,9 +176,9 @@ async fn test_sqlx_postgres(url: &str, enable_remote: bool) -> Result<(), sqlx:: let prepared_query_1 = String::from("SELECT * FROM people WHERE firstname=$1"); // (prepared arguments are safe) // direct execution - let _ = conn.execute(safe_query_1.as_str()).await?; // $ sql-sink + let _ = conn.execute(safe_query_1.as_str()).await?; // $ MISSING: sql-sink if enable_remote { - let _ = conn.execute(unsafe_query_1.as_str()).await?; // $ sql-sink MISSING: Alert[rust/sql-injection]=remote3 + let _ = conn.execute(unsafe_query_1.as_str()).await?; // $ MISSING: sql-sink Alert[rust/sql-injection]=remote3 } // prepared queries From 87deab861fc6c6f09a1b8af1cdac3b8dd9d51ce7 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 16 Jul 2025 16:23:50 +0100 Subject: [PATCH 002/208] Rust: Remove Sqlx.qll. --- rust/ql/lib/codeql/rust/Frameworks.qll | 1 - rust/ql/lib/codeql/rust/frameworks/Sqlx.qll | 44 --------- .../security/CWE-089/SqlInjection.expected | 90 ++----------------- .../security/CWE-089/SqlSinks.expected | 39 ++++++++ 4 files changed, 45 insertions(+), 129 deletions(-) delete mode 100644 rust/ql/lib/codeql/rust/frameworks/Sqlx.qll diff --git a/rust/ql/lib/codeql/rust/Frameworks.qll b/rust/ql/lib/codeql/rust/Frameworks.qll index 0e91ed427ba4..317746f2d186 100644 --- a/rust/ql/lib/codeql/rust/Frameworks.qll +++ b/rust/ql/lib/codeql/rust/Frameworks.qll @@ -4,6 +4,5 @@ private import codeql.rust.frameworks.rustcrypto.RustCrypto private import codeql.rust.frameworks.Poem -private import codeql.rust.frameworks.Sqlx private import codeql.rust.frameworks.stdlib.Clone private import codeql.rust.frameworks.stdlib.Stdlib diff --git a/rust/ql/lib/codeql/rust/frameworks/Sqlx.qll b/rust/ql/lib/codeql/rust/frameworks/Sqlx.qll deleted file mode 100644 index 5b33f72fdf68..000000000000 --- a/rust/ql/lib/codeql/rust/frameworks/Sqlx.qll +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Provides modeling for the `SQLx` library. - */ - -private import rust -private import codeql.rust.Concepts -private import codeql.rust.dataflow.DataFlow -private import codeql.rust.internal.TypeInference -private import codeql.rust.internal.Type - -/** - * A call to `sqlx::query` and variations. - */ -private class SqlxQuery extends SqlConstruction::Range { - CallExpr call; - - SqlxQuery() { - this.asExpr().getExpr() = call and - call.getStaticTarget().(Addressable).getCanonicalPath() = - [ - "sqlx_core::query::query", "sqlx_core::query_as::query_as", - "sqlx_core::query_with::query_with", "sqlx_core::query_as_with::query_as_with", - "sqlx_core::query_scalar::query_scalar", "sqlx_core::query_scalar_with::query_scalar_with", - "sqlx_core::raw_sql::raw_sql" - ] - } - - override DataFlow::Node getSql() { result.asExpr().getExpr() = call.getArgList().getArg(0) } -} - -/** - * A call to `sqlx::Executor::execute`. - */ -private class SqlxExecute extends SqlExecution::Range { - MethodCallExpr call; - - SqlxExecute() { - this.asExpr().getExpr() = call and - call.getStaticTarget().(Addressable).getCanonicalPath() = - "sqlx_core::executor::Executor::execute" - } - - override DataFlow::Node getSql() { result.asExpr().getExpr() = call.getArgList().getArg(0) } -} diff --git a/rust/ql/test/query-tests/security/CWE-089/SqlInjection.expected b/rust/ql/test/query-tests/security/CWE-089/SqlInjection.expected index eea48df8d5b0..1570cd211c8f 100644 --- a/rust/ql/test/query-tests/security/CWE-089/SqlInjection.expected +++ b/rust/ql/test/query-tests/security/CWE-089/SqlInjection.expected @@ -1,88 +1,10 @@ #select -| sqlx.rs:77:25:77:45 | safe_query_3.as_str() | sqlx.rs:48:25:48:46 | ...::get | sqlx.rs:77:25:77:45 | safe_query_3.as_str() | This query depends on a $@. | sqlx.rs:48:25:48:46 | ...::get | user-provided value | -| sqlx.rs:78:25:78:47 | unsafe_query_1.as_str() | sqlx.rs:47:22:47:35 | ...::args | sqlx.rs:78:25:78:47 | unsafe_query_1.as_str() | This query depends on a $@. | sqlx.rs:47:22:47:35 | ...::args | user-provided value | -| sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() | sqlx.rs:48:25:48:46 | ...::get | sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() | This query depends on a $@. | sqlx.rs:48:25:48:46 | ...::get | user-provided value | edges -| sqlx.rs:47:9:47:18 | arg_string | sqlx.rs:53:27:53:36 | arg_string | provenance | | -| sqlx.rs:47:22:47:35 | ...::args | sqlx.rs:47:22:47:37 | ...::args(...) [element] | provenance | Src:MaD:2 | -| sqlx.rs:47:22:47:37 | ...::args(...) [element] | sqlx.rs:47:22:47:44 | ... .nth(...) [Some] | provenance | MaD:3 | -| sqlx.rs:47:22:47:44 | ... .nth(...) [Some] | sqlx.rs:47:22:47:77 | ... .unwrap_or(...) | provenance | MaD:5 | -| sqlx.rs:47:22:47:77 | ... .unwrap_or(...) | sqlx.rs:47:9:47:18 | arg_string | provenance | | -| sqlx.rs:48:9:48:21 | remote_string | sqlx.rs:49:25:49:52 | remote_string.parse() [Ok] | provenance | MaD:9 | -| sqlx.rs:48:9:48:21 | remote_string | sqlx.rs:49:25:49:52 | remote_string.parse() [Ok] | provenance | MaD:9 | -| sqlx.rs:48:9:48:21 | remote_string | sqlx.rs:54:27:54:39 | remote_string | provenance | | -| sqlx.rs:48:25:48:46 | ...::get | sqlx.rs:48:25:48:69 | ...::get(...) [Ok] | provenance | Src:MaD:1 | -| sqlx.rs:48:25:48:69 | ...::get(...) [Ok] | sqlx.rs:48:25:48:78 | ... .unwrap() | provenance | MaD:6 | -| sqlx.rs:48:25:48:78 | ... .unwrap() | sqlx.rs:48:25:48:85 | ... .text() [Ok] | provenance | MaD:10 | -| sqlx.rs:48:25:48:85 | ... .text() [Ok] | sqlx.rs:48:25:48:118 | ... .unwrap_or(...) | provenance | MaD:7 | -| sqlx.rs:48:25:48:118 | ... .unwrap_or(...) | sqlx.rs:48:9:48:21 | remote_string | provenance | | -| sqlx.rs:49:9:49:21 | remote_number | sqlx.rs:52:32:52:87 | MacroExpr | provenance | | -| sqlx.rs:49:25:49:52 | remote_string.parse() [Ok] | sqlx.rs:49:25:49:65 | ... .unwrap_or(...) | provenance | MaD:7 | -| sqlx.rs:49:25:49:65 | ... .unwrap_or(...) | sqlx.rs:49:9:49:21 | remote_number | provenance | | -| sqlx.rs:52:9:52:20 | safe_query_3 | sqlx.rs:77:25:77:36 | safe_query_3 | provenance | | -| sqlx.rs:52:9:52:20 | safe_query_3 | sqlx.rs:77:25:77:45 | safe_query_3.as_str() | provenance | MaD:8 | -| sqlx.rs:52:9:52:20 | safe_query_3 | sqlx.rs:77:25:77:45 | safe_query_3.as_str() | provenance | MaD:4 | -| sqlx.rs:52:9:52:20 | safe_query_3 | sqlx.rs:77:25:77:45 | safe_query_3.as_str() | provenance | MaD:8 | -| sqlx.rs:52:24:52:88 | res | sqlx.rs:52:32:52:87 | { ... } | provenance | | -| sqlx.rs:52:32:52:87 | ...::format(...) | sqlx.rs:52:24:52:88 | res | provenance | | -| sqlx.rs:52:32:52:87 | ...::must_use(...) | sqlx.rs:52:9:52:20 | safe_query_3 | provenance | | -| sqlx.rs:52:32:52:87 | MacroExpr | sqlx.rs:52:32:52:87 | ...::format(...) | provenance | MaD:11 | -| sqlx.rs:52:32:52:87 | { ... } | sqlx.rs:52:32:52:87 | ...::must_use(...) | provenance | MaD:12 | -| sqlx.rs:53:9:53:22 | unsafe_query_1 [&ref] | sqlx.rs:78:25:78:47 | unsafe_query_1.as_str() | provenance | MaD:8 | -| sqlx.rs:53:9:53:22 | unsafe_query_1 [&ref] | sqlx.rs:78:25:78:47 | unsafe_query_1.as_str() | provenance | MaD:4 | -| sqlx.rs:53:9:53:22 | unsafe_query_1 [&ref] | sqlx.rs:78:25:78:47 | unsafe_query_1.as_str() | provenance | MaD:8 | -| sqlx.rs:53:26:53:36 | &arg_string [&ref] | sqlx.rs:53:9:53:22 | unsafe_query_1 [&ref] | provenance | | -| sqlx.rs:53:27:53:36 | arg_string | sqlx.rs:53:26:53:36 | &arg_string [&ref] | provenance | | -| sqlx.rs:54:9:54:22 | unsafe_query_2 [&ref] | sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() | provenance | MaD:8 | -| sqlx.rs:54:9:54:22 | unsafe_query_2 [&ref] | sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() | provenance | MaD:4 | -| sqlx.rs:54:9:54:22 | unsafe_query_2 [&ref] | sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() | provenance | MaD:8 | -| sqlx.rs:54:26:54:39 | &remote_string [&ref] | sqlx.rs:54:9:54:22 | unsafe_query_2 [&ref] | provenance | | -| sqlx.rs:54:27:54:39 | remote_string | sqlx.rs:54:26:54:39 | &remote_string [&ref] | provenance | | -| sqlx.rs:77:25:77:36 | safe_query_3 | sqlx.rs:77:25:77:45 | safe_query_3.as_str() | provenance | MaD:8 | -| sqlx.rs:77:25:77:36 | safe_query_3 | sqlx.rs:77:25:77:45 | safe_query_3.as_str() | provenance | MaD:4 | -| sqlx.rs:77:25:77:36 | safe_query_3 | sqlx.rs:77:25:77:45 | safe_query_3.as_str() | provenance | MaD:8 | -models -| 1 | Source: reqwest::blocking::get; ReturnValue.Field[core::result::Result::Ok(0)]; remote | -| 2 | Source: std::env::args; ReturnValue.Element; commandargs | -| 3 | Summary: <_ as core::iter::traits::iterator::Iterator>::nth; Argument[self].Element; ReturnValue.Field[core::option::Option::Some(0)]; value | -| 4 | Summary: ::as_str; Argument[self]; ReturnValue; value | -| 5 | Summary: ::unwrap_or; Argument[self].Field[core::option::Option::Some(0)]; ReturnValue; value | -| 6 | Summary: ::unwrap; Argument[self].Field[core::result::Result::Ok(0)]; ReturnValue; value | -| 7 | Summary: ::unwrap_or; Argument[self].Field[core::result::Result::Ok(0)]; ReturnValue; value | -| 8 | Summary: ::as_str; Argument[self]; ReturnValue; value | -| 9 | Summary: ::parse; Argument[self]; ReturnValue.Field[core::result::Result::Ok(0)]; taint | -| 10 | Summary: ::text; Argument[self]; ReturnValue.Field[core::result::Result::Ok(0)]; taint | -| 11 | Summary: alloc::fmt::format; Argument[0]; ReturnValue; taint | -| 12 | Summary: core::hint::must_use; Argument[0]; ReturnValue; value | nodes -| sqlx.rs:47:9:47:18 | arg_string | semmle.label | arg_string | -| sqlx.rs:47:22:47:35 | ...::args | semmle.label | ...::args | -| sqlx.rs:47:22:47:37 | ...::args(...) [element] | semmle.label | ...::args(...) [element] | -| sqlx.rs:47:22:47:44 | ... .nth(...) [Some] | semmle.label | ... .nth(...) [Some] | -| sqlx.rs:47:22:47:77 | ... .unwrap_or(...) | semmle.label | ... .unwrap_or(...) | -| sqlx.rs:48:9:48:21 | remote_string | semmle.label | remote_string | -| sqlx.rs:48:25:48:46 | ...::get | semmle.label | ...::get | -| sqlx.rs:48:25:48:69 | ...::get(...) [Ok] | semmle.label | ...::get(...) [Ok] | -| sqlx.rs:48:25:48:78 | ... .unwrap() | semmle.label | ... .unwrap() | -| sqlx.rs:48:25:48:85 | ... .text() [Ok] | semmle.label | ... .text() [Ok] | -| sqlx.rs:48:25:48:118 | ... .unwrap_or(...) | semmle.label | ... .unwrap_or(...) | -| sqlx.rs:49:9:49:21 | remote_number | semmle.label | remote_number | -| sqlx.rs:49:25:49:52 | remote_string.parse() [Ok] | semmle.label | remote_string.parse() [Ok] | -| sqlx.rs:49:25:49:65 | ... .unwrap_or(...) | semmle.label | ... .unwrap_or(...) | -| sqlx.rs:52:9:52:20 | safe_query_3 | semmle.label | safe_query_3 | -| sqlx.rs:52:24:52:88 | res | semmle.label | res | -| sqlx.rs:52:32:52:87 | ...::format(...) | semmle.label | ...::format(...) | -| sqlx.rs:52:32:52:87 | ...::must_use(...) | semmle.label | ...::must_use(...) | -| sqlx.rs:52:32:52:87 | MacroExpr | semmle.label | MacroExpr | -| sqlx.rs:52:32:52:87 | { ... } | semmle.label | { ... } | -| sqlx.rs:53:9:53:22 | unsafe_query_1 [&ref] | semmle.label | unsafe_query_1 [&ref] | -| sqlx.rs:53:26:53:36 | &arg_string [&ref] | semmle.label | &arg_string [&ref] | -| sqlx.rs:53:27:53:36 | arg_string | semmle.label | arg_string | -| sqlx.rs:54:9:54:22 | unsafe_query_2 [&ref] | semmle.label | unsafe_query_2 [&ref] | -| sqlx.rs:54:26:54:39 | &remote_string [&ref] | semmle.label | &remote_string [&ref] | -| sqlx.rs:54:27:54:39 | remote_string | semmle.label | remote_string | -| sqlx.rs:77:25:77:36 | safe_query_3 | semmle.label | safe_query_3 | -| sqlx.rs:77:25:77:45 | safe_query_3.as_str() | semmle.label | safe_query_3.as_str() | -| sqlx.rs:78:25:78:47 | unsafe_query_1.as_str() | semmle.label | unsafe_query_1.as_str() | -| sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() | semmle.label | unsafe_query_2.as_str() | subpaths +testFailures +| sqlx.rs:47:80:47:96 | //... | Missing result: Source=args1 | +| sqlx.rs:48:121:48:139 | //... | Missing result: Source=remote1 | +| sqlx.rs:77:71:77:129 | //... | Fixed spurious result: Alert[rust/sql-injection]=remote1 | +| sqlx.rs:78:73:78:117 | //... | Missing result: Alert[rust/sql-injection]=args1 | +| sqlx.rs:80:77:80:123 | //... | Missing result: Alert[rust/sql-injection]=remote1 | diff --git a/rust/ql/test/query-tests/security/CWE-089/SqlSinks.expected b/rust/ql/test/query-tests/security/CWE-089/SqlSinks.expected index e69de29bb2d1..9c3ebb3fe83a 100644 --- a/rust/ql/test/query-tests/security/CWE-089/SqlSinks.expected +++ b/rust/ql/test/query-tests/security/CWE-089/SqlSinks.expected @@ -0,0 +1,39 @@ +| sqlx.rs:75:71:75:83 | //... | Missing result: sql-sink | +| sqlx.rs:76:71:76:83 | //... | Missing result: sql-sink | +| sqlx.rs:77:71:77:129 | //... | Missing result: sql-sink | +| sqlx.rs:78:73:78:117 | //... | Missing result: sql-sink | +| sqlx.rs:80:77:80:123 | //... | Missing result: sql-sink | +| sqlx.rs:81:77:81:132 | //... | Missing result: sql-sink | +| sqlx.rs:82:77:82:132 | //... | Missing result: sql-sink | +| sqlx.rs:84:94:84:106 | //... | Missing result: sql-sink | +| sqlx.rs:85:92:85:104 | //... | Missing result: sql-sink | +| sqlx.rs:87:99:87:111 | //... | Missing result: sql-sink | +| sqlx.rs:88:99:88:111 | //... | Missing result: sql-sink | +| sqlx.rs:111:77:111:89 | //... | Missing result: sql-sink | +| sqlx.rs:113:83:113:138 | //... | Missing result: sql-sink | +| sqlx.rs:117:75:117:87 | //... | Missing result: sql-sink | +| sqlx.rs:118:99:118:111 | //... | Missing result: sql-sink | +| sqlx.rs:120:81:120:136 | //... | Missing result: sql-sink | +| sqlx.rs:121:104:121:116 | //... | Missing result: sql-sink | +| sqlx.rs:124:66:124:78 | //... | Missing result: sql-sink | +| sqlx.rs:125:90:125:102 | //... | Missing result: sql-sink | +| sqlx.rs:127:72:127:127 | //... | Missing result: sql-sink | +| sqlx.rs:128:95:128:107 | //... | Missing result: sql-sink | +| sqlx.rs:131:106:131:118 | //... | Missing result: sql-sink | +| sqlx.rs:133:130:133:142 | //... | Missing result: sql-sink | +| sqlx.rs:136:109:136:164 | //... | Missing result: sql-sink | +| sqlx.rs:137:132:137:144 | //... | Missing result: sql-sink | +| sqlx.rs:140:129:140:141 | //... | Missing result: sql-sink | +| sqlx.rs:142:153:142:165 | //... | Missing result: sql-sink | +| sqlx.rs:145:132:145:189 | //... | Missing result: sql-sink | +| sqlx.rs:146:155:146:167 | //... | Missing result: sql-sink | +| sqlx.rs:149:77:149:89 | //... | Missing result: sql-sink | +| sqlx.rs:150:101:150:113 | //... | Missing result: sql-sink | +| sqlx.rs:151:116:151:128 | //... | Missing result: sql-sink | +| sqlx.rs:153:83:153:138 | //... | Missing result: sql-sink | +| sqlx.rs:154:106:154:118 | //... | Missing result: sql-sink | +| sqlx.rs:155:121:155:133 | //... | Missing result: sql-sink | +| sqlx.rs:185:71:185:83 | //... | Missing result: sql-sink | +| sqlx.rs:186:95:186:107 | //... | Missing result: sql-sink | +| sqlx.rs:188:77:188:132 | //... | Missing result: sql-sink | +| sqlx.rs:189:100:189:112 | //... | Missing result: sql-sink | From 62b7d84638056b101dac70534d0da8211c1cea37 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 16 Jul 2025 16:36:42 +0100 Subject: [PATCH 003/208] Rust: Add Sqlx as MaD sinks instead. --- .../lib/codeql/rust/frameworks/sqlx.model.yml | 13 +++ .../security/CWE-089/SqlInjection.expected | 99 +++++++++++++++++-- .../security/CWE-089/SqlSinks.expected | 39 -------- 3 files changed, 106 insertions(+), 45 deletions(-) create mode 100644 rust/ql/lib/codeql/rust/frameworks/sqlx.model.yml diff --git a/rust/ql/lib/codeql/rust/frameworks/sqlx.model.yml b/rust/ql/lib/codeql/rust/frameworks/sqlx.model.yml new file mode 100644 index 000000000000..b3ca9d89299e --- /dev/null +++ b/rust/ql/lib/codeql/rust/frameworks/sqlx.model.yml @@ -0,0 +1,13 @@ +extensions: + - addsTo: + pack: codeql/rust-all + extensible: sinkModel + data: + - ["sqlx_core::query::query", "Argument[0]", "sql-injection", "manual"] + - ["sqlx_core::query_as::query_as", "Argument[0]", "sql-injection", "manual"] + - ["sqlx_core::query_with::query_with", "Argument[0]", "sql-injection", "manual"] + - ["sqlx_core::query_as_with::query_as_with", "Argument[0]", "sql-injection", "manual"] + - ["sqlx_core::query_scalar::query_scalar", "Argument[0]", "sql-injection", "manual"] + - ["sqlx_core::query_scalar_with::query_scalar_with", "Argument[0]", "sql-injection", "manual"] + - ["sqlx_core::raw_sql::raw_sql", "Argument[0]", "sql-injection", "manual"] + - ["sqlx_core::executor::Executor::execute", "Argument[0]", "sql-injection", "manual"] diff --git a/rust/ql/test/query-tests/security/CWE-089/SqlInjection.expected b/rust/ql/test/query-tests/security/CWE-089/SqlInjection.expected index 1570cd211c8f..cc00a44d9fc4 100644 --- a/rust/ql/test/query-tests/security/CWE-089/SqlInjection.expected +++ b/rust/ql/test/query-tests/security/CWE-089/SqlInjection.expected @@ -1,10 +1,97 @@ #select +| sqlx.rs:77:13:77:23 | ...::query | sqlx.rs:48:25:48:46 | ...::get | sqlx.rs:77:13:77:23 | ...::query | This query depends on a $@. | sqlx.rs:48:25:48:46 | ...::get | user-provided value | +| sqlx.rs:78:13:78:23 | ...::query | sqlx.rs:47:22:47:35 | ...::args | sqlx.rs:78:13:78:23 | ...::query | This query depends on a $@. | sqlx.rs:47:22:47:35 | ...::args | user-provided value | +| sqlx.rs:80:17:80:27 | ...::query | sqlx.rs:48:25:48:46 | ...::get | sqlx.rs:80:17:80:27 | ...::query | This query depends on a $@. | sqlx.rs:48:25:48:46 | ...::get | user-provided value | edges +| sqlx.rs:47:9:47:18 | arg_string | sqlx.rs:53:27:53:36 | arg_string | provenance | | +| sqlx.rs:47:22:47:35 | ...::args | sqlx.rs:47:22:47:37 | ...::args(...) [element] | provenance | Src:MaD:3 | +| sqlx.rs:47:22:47:37 | ...::args(...) [element] | sqlx.rs:47:22:47:44 | ... .nth(...) [Some] | provenance | MaD:4 | +| sqlx.rs:47:22:47:44 | ... .nth(...) [Some] | sqlx.rs:47:22:47:77 | ... .unwrap_or(...) | provenance | MaD:6 | +| sqlx.rs:47:22:47:77 | ... .unwrap_or(...) | sqlx.rs:47:9:47:18 | arg_string | provenance | | +| sqlx.rs:48:9:48:21 | remote_string | sqlx.rs:49:25:49:52 | remote_string.parse() [Ok] | provenance | MaD:10 | +| sqlx.rs:48:9:48:21 | remote_string | sqlx.rs:49:25:49:52 | remote_string.parse() [Ok] | provenance | MaD:10 | +| sqlx.rs:48:9:48:21 | remote_string | sqlx.rs:54:27:54:39 | remote_string | provenance | | +| sqlx.rs:48:25:48:46 | ...::get | sqlx.rs:48:25:48:69 | ...::get(...) [Ok] | provenance | Src:MaD:2 | +| sqlx.rs:48:25:48:69 | ...::get(...) [Ok] | sqlx.rs:48:25:48:78 | ... .unwrap() | provenance | MaD:7 | +| sqlx.rs:48:25:48:78 | ... .unwrap() | sqlx.rs:48:25:48:85 | ... .text() [Ok] | provenance | MaD:11 | +| sqlx.rs:48:25:48:85 | ... .text() [Ok] | sqlx.rs:48:25:48:118 | ... .unwrap_or(...) | provenance | MaD:8 | +| sqlx.rs:48:25:48:118 | ... .unwrap_or(...) | sqlx.rs:48:9:48:21 | remote_string | provenance | | +| sqlx.rs:49:9:49:21 | remote_number | sqlx.rs:52:32:52:87 | MacroExpr | provenance | | +| sqlx.rs:49:25:49:52 | remote_string.parse() [Ok] | sqlx.rs:49:25:49:65 | ... .unwrap_or(...) | provenance | MaD:8 | +| sqlx.rs:49:25:49:65 | ... .unwrap_or(...) | sqlx.rs:49:9:49:21 | remote_number | provenance | | +| sqlx.rs:52:9:52:20 | safe_query_3 | sqlx.rs:77:25:77:36 | safe_query_3 | provenance | | +| sqlx.rs:52:9:52:20 | safe_query_3 | sqlx.rs:77:25:77:45 | safe_query_3.as_str() | provenance | MaD:9 | +| sqlx.rs:52:9:52:20 | safe_query_3 | sqlx.rs:77:25:77:45 | safe_query_3.as_str() | provenance | MaD:5 | +| sqlx.rs:52:9:52:20 | safe_query_3 | sqlx.rs:77:25:77:45 | safe_query_3.as_str() | provenance | MaD:9 | +| sqlx.rs:52:24:52:88 | res | sqlx.rs:52:32:52:87 | { ... } | provenance | | +| sqlx.rs:52:32:52:87 | ...::format(...) | sqlx.rs:52:24:52:88 | res | provenance | | +| sqlx.rs:52:32:52:87 | ...::must_use(...) | sqlx.rs:52:9:52:20 | safe_query_3 | provenance | | +| sqlx.rs:52:32:52:87 | MacroExpr | sqlx.rs:52:32:52:87 | ...::format(...) | provenance | MaD:12 | +| sqlx.rs:52:32:52:87 | { ... } | sqlx.rs:52:32:52:87 | ...::must_use(...) | provenance | MaD:13 | +| sqlx.rs:53:9:53:22 | unsafe_query_1 [&ref] | sqlx.rs:78:25:78:47 | unsafe_query_1.as_str() [&ref] | provenance | MaD:9 | +| sqlx.rs:53:9:53:22 | unsafe_query_1 [&ref] | sqlx.rs:78:25:78:47 | unsafe_query_1.as_str() [&ref] | provenance | MaD:5 | +| sqlx.rs:53:9:53:22 | unsafe_query_1 [&ref] | sqlx.rs:78:25:78:47 | unsafe_query_1.as_str() [&ref] | provenance | MaD:9 | +| sqlx.rs:53:26:53:36 | &arg_string [&ref] | sqlx.rs:53:9:53:22 | unsafe_query_1 [&ref] | provenance | | +| sqlx.rs:53:27:53:36 | arg_string | sqlx.rs:53:26:53:36 | &arg_string [&ref] | provenance | | +| sqlx.rs:54:9:54:22 | unsafe_query_2 [&ref] | sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() [&ref] | provenance | MaD:9 | +| sqlx.rs:54:9:54:22 | unsafe_query_2 [&ref] | sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() [&ref] | provenance | MaD:5 | +| sqlx.rs:54:9:54:22 | unsafe_query_2 [&ref] | sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() [&ref] | provenance | MaD:9 | +| sqlx.rs:54:26:54:39 | &remote_string [&ref] | sqlx.rs:54:9:54:22 | unsafe_query_2 [&ref] | provenance | | +| sqlx.rs:54:27:54:39 | remote_string | sqlx.rs:54:26:54:39 | &remote_string [&ref] | provenance | | +| sqlx.rs:77:25:77:36 | safe_query_3 | sqlx.rs:77:25:77:45 | safe_query_3.as_str() [&ref] | provenance | MaD:9 | +| sqlx.rs:77:25:77:36 | safe_query_3 | sqlx.rs:77:25:77:45 | safe_query_3.as_str() [&ref] | provenance | MaD:5 | +| sqlx.rs:77:25:77:36 | safe_query_3 | sqlx.rs:77:25:77:45 | safe_query_3.as_str() [&ref] | provenance | MaD:9 | +| sqlx.rs:77:25:77:45 | safe_query_3.as_str() | sqlx.rs:77:13:77:23 | ...::query | provenance | MaD:1 Sink:MaD:1 | +| sqlx.rs:77:25:77:45 | safe_query_3.as_str() [&ref] | sqlx.rs:77:13:77:23 | ...::query | provenance | MaD:1 Sink:MaD:1 | +| sqlx.rs:78:25:78:47 | unsafe_query_1.as_str() [&ref] | sqlx.rs:78:13:78:23 | ...::query | provenance | MaD:1 Sink:MaD:1 | +| sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() [&ref] | sqlx.rs:80:17:80:27 | ...::query | provenance | MaD:1 Sink:MaD:1 | +models +| 1 | Sink: sqlx_core::query::query; Argument[0]; sql-injection | +| 2 | Source: reqwest::blocking::get; ReturnValue.Field[core::result::Result::Ok(0)]; remote | +| 3 | Source: std::env::args; ReturnValue.Element; commandargs | +| 4 | Summary: <_ as core::iter::traits::iterator::Iterator>::nth; Argument[self].Element; ReturnValue.Field[core::option::Option::Some(0)]; value | +| 5 | Summary: ::as_str; Argument[self]; ReturnValue; value | +| 6 | Summary: ::unwrap_or; Argument[self].Field[core::option::Option::Some(0)]; ReturnValue; value | +| 7 | Summary: ::unwrap; Argument[self].Field[core::result::Result::Ok(0)]; ReturnValue; value | +| 8 | Summary: ::unwrap_or; Argument[self].Field[core::result::Result::Ok(0)]; ReturnValue; value | +| 9 | Summary: ::as_str; Argument[self]; ReturnValue; value | +| 10 | Summary: ::parse; Argument[self]; ReturnValue.Field[core::result::Result::Ok(0)]; taint | +| 11 | Summary: ::text; Argument[self]; ReturnValue.Field[core::result::Result::Ok(0)]; taint | +| 12 | Summary: alloc::fmt::format; Argument[0]; ReturnValue; taint | +| 13 | Summary: core::hint::must_use; Argument[0]; ReturnValue; value | nodes +| sqlx.rs:47:9:47:18 | arg_string | semmle.label | arg_string | +| sqlx.rs:47:22:47:35 | ...::args | semmle.label | ...::args | +| sqlx.rs:47:22:47:37 | ...::args(...) [element] | semmle.label | ...::args(...) [element] | +| sqlx.rs:47:22:47:44 | ... .nth(...) [Some] | semmle.label | ... .nth(...) [Some] | +| sqlx.rs:47:22:47:77 | ... .unwrap_or(...) | semmle.label | ... .unwrap_or(...) | +| sqlx.rs:48:9:48:21 | remote_string | semmle.label | remote_string | +| sqlx.rs:48:25:48:46 | ...::get | semmle.label | ...::get | +| sqlx.rs:48:25:48:69 | ...::get(...) [Ok] | semmle.label | ...::get(...) [Ok] | +| sqlx.rs:48:25:48:78 | ... .unwrap() | semmle.label | ... .unwrap() | +| sqlx.rs:48:25:48:85 | ... .text() [Ok] | semmle.label | ... .text() [Ok] | +| sqlx.rs:48:25:48:118 | ... .unwrap_or(...) | semmle.label | ... .unwrap_or(...) | +| sqlx.rs:49:9:49:21 | remote_number | semmle.label | remote_number | +| sqlx.rs:49:25:49:52 | remote_string.parse() [Ok] | semmle.label | remote_string.parse() [Ok] | +| sqlx.rs:49:25:49:65 | ... .unwrap_or(...) | semmle.label | ... .unwrap_or(...) | +| sqlx.rs:52:9:52:20 | safe_query_3 | semmle.label | safe_query_3 | +| sqlx.rs:52:24:52:88 | res | semmle.label | res | +| sqlx.rs:52:32:52:87 | ...::format(...) | semmle.label | ...::format(...) | +| sqlx.rs:52:32:52:87 | ...::must_use(...) | semmle.label | ...::must_use(...) | +| sqlx.rs:52:32:52:87 | MacroExpr | semmle.label | MacroExpr | +| sqlx.rs:52:32:52:87 | { ... } | semmle.label | { ... } | +| sqlx.rs:53:9:53:22 | unsafe_query_1 [&ref] | semmle.label | unsafe_query_1 [&ref] | +| sqlx.rs:53:26:53:36 | &arg_string [&ref] | semmle.label | &arg_string [&ref] | +| sqlx.rs:53:27:53:36 | arg_string | semmle.label | arg_string | +| sqlx.rs:54:9:54:22 | unsafe_query_2 [&ref] | semmle.label | unsafe_query_2 [&ref] | +| sqlx.rs:54:26:54:39 | &remote_string [&ref] | semmle.label | &remote_string [&ref] | +| sqlx.rs:54:27:54:39 | remote_string | semmle.label | remote_string | +| sqlx.rs:77:13:77:23 | ...::query | semmle.label | ...::query | +| sqlx.rs:77:25:77:36 | safe_query_3 | semmle.label | safe_query_3 | +| sqlx.rs:77:25:77:45 | safe_query_3.as_str() | semmle.label | safe_query_3.as_str() | +| sqlx.rs:77:25:77:45 | safe_query_3.as_str() [&ref] | semmle.label | safe_query_3.as_str() [&ref] | +| sqlx.rs:78:13:78:23 | ...::query | semmle.label | ...::query | +| sqlx.rs:78:25:78:47 | unsafe_query_1.as_str() [&ref] | semmle.label | unsafe_query_1.as_str() [&ref] | +| sqlx.rs:80:17:80:27 | ...::query | semmle.label | ...::query | +| sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() [&ref] | semmle.label | unsafe_query_2.as_str() [&ref] | subpaths -testFailures -| sqlx.rs:47:80:47:96 | //... | Missing result: Source=args1 | -| sqlx.rs:48:121:48:139 | //... | Missing result: Source=remote1 | -| sqlx.rs:77:71:77:129 | //... | Fixed spurious result: Alert[rust/sql-injection]=remote1 | -| sqlx.rs:78:73:78:117 | //... | Missing result: Alert[rust/sql-injection]=args1 | -| sqlx.rs:80:77:80:123 | //... | Missing result: Alert[rust/sql-injection]=remote1 | diff --git a/rust/ql/test/query-tests/security/CWE-089/SqlSinks.expected b/rust/ql/test/query-tests/security/CWE-089/SqlSinks.expected index 9c3ebb3fe83a..e69de29bb2d1 100644 --- a/rust/ql/test/query-tests/security/CWE-089/SqlSinks.expected +++ b/rust/ql/test/query-tests/security/CWE-089/SqlSinks.expected @@ -1,39 +0,0 @@ -| sqlx.rs:75:71:75:83 | //... | Missing result: sql-sink | -| sqlx.rs:76:71:76:83 | //... | Missing result: sql-sink | -| sqlx.rs:77:71:77:129 | //... | Missing result: sql-sink | -| sqlx.rs:78:73:78:117 | //... | Missing result: sql-sink | -| sqlx.rs:80:77:80:123 | //... | Missing result: sql-sink | -| sqlx.rs:81:77:81:132 | //... | Missing result: sql-sink | -| sqlx.rs:82:77:82:132 | //... | Missing result: sql-sink | -| sqlx.rs:84:94:84:106 | //... | Missing result: sql-sink | -| sqlx.rs:85:92:85:104 | //... | Missing result: sql-sink | -| sqlx.rs:87:99:87:111 | //... | Missing result: sql-sink | -| sqlx.rs:88:99:88:111 | //... | Missing result: sql-sink | -| sqlx.rs:111:77:111:89 | //... | Missing result: sql-sink | -| sqlx.rs:113:83:113:138 | //... | Missing result: sql-sink | -| sqlx.rs:117:75:117:87 | //... | Missing result: sql-sink | -| sqlx.rs:118:99:118:111 | //... | Missing result: sql-sink | -| sqlx.rs:120:81:120:136 | //... | Missing result: sql-sink | -| sqlx.rs:121:104:121:116 | //... | Missing result: sql-sink | -| sqlx.rs:124:66:124:78 | //... | Missing result: sql-sink | -| sqlx.rs:125:90:125:102 | //... | Missing result: sql-sink | -| sqlx.rs:127:72:127:127 | //... | Missing result: sql-sink | -| sqlx.rs:128:95:128:107 | //... | Missing result: sql-sink | -| sqlx.rs:131:106:131:118 | //... | Missing result: sql-sink | -| sqlx.rs:133:130:133:142 | //... | Missing result: sql-sink | -| sqlx.rs:136:109:136:164 | //... | Missing result: sql-sink | -| sqlx.rs:137:132:137:144 | //... | Missing result: sql-sink | -| sqlx.rs:140:129:140:141 | //... | Missing result: sql-sink | -| sqlx.rs:142:153:142:165 | //... | Missing result: sql-sink | -| sqlx.rs:145:132:145:189 | //... | Missing result: sql-sink | -| sqlx.rs:146:155:146:167 | //... | Missing result: sql-sink | -| sqlx.rs:149:77:149:89 | //... | Missing result: sql-sink | -| sqlx.rs:150:101:150:113 | //... | Missing result: sql-sink | -| sqlx.rs:151:116:151:128 | //... | Missing result: sql-sink | -| sqlx.rs:153:83:153:138 | //... | Missing result: sql-sink | -| sqlx.rs:154:106:154:118 | //... | Missing result: sql-sink | -| sqlx.rs:155:121:155:133 | //... | Missing result: sql-sink | -| sqlx.rs:185:71:185:83 | //... | Missing result: sql-sink | -| sqlx.rs:186:95:186:107 | //... | Missing result: sql-sink | -| sqlx.rs:188:77:188:132 | //... | Missing result: sql-sink | -| sqlx.rs:189:100:189:112 | //... | Missing result: sql-sink | From 944fd2aa11768f80b75bb88bcbf1fba74bab2e21 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 16 Jul 2025 17:06:52 +0100 Subject: [PATCH 004/208] Rust: Add explicit types in some (not all) of the test cases. --- rust/ql/test/query-tests/security/CWE-089/sqlx.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/ql/test/query-tests/security/CWE-089/sqlx.rs b/rust/ql/test/query-tests/security/CWE-089/sqlx.rs index ef51b1af42b7..8e1afa9f0e8f 100644 --- a/rust/ql/test/query-tests/security/CWE-089/sqlx.rs +++ b/rust/ql/test/query-tests/security/CWE-089/sqlx.rs @@ -39,8 +39,8 @@ use sqlx::Executor; async fn test_sqlx_mysql(url: &str, enable_remote: bool) -> Result<(), sqlx::Error> { // connect through a MySQL connection pool - let pool = sqlx::mysql::MySqlPool::connect(url).await?; - let mut conn = pool.acquire().await?; + let pool: sqlx::Pool = sqlx::mysql::MySqlPool::connect(url).await?; + let mut conn: sqlx::pool::PoolConnection = pool.acquire().await?; // construct queries (with extra variants) let const_string = String::from("Alice"); @@ -61,7 +61,7 @@ async fn test_sqlx_mysql(url: &str, enable_remote: bool) -> Result<(), sqlx::Err let prepared_query_1 = String::from("SELECT * FROM people WHERE firstname=?"); // (prepared arguments are safe) // direct execution - let _ = conn.execute(safe_query_1.as_str()).await?; // $ MISSING: sql-sink + let _: sqlx::mysql::MySqlQueryResult = conn.execute(safe_query_1.as_str()).await?; // $ MISSING: sql-sink let _ = conn.execute(safe_query_2.as_str()).await?; // $ MISSING: sql-sink let _ = conn.execute(safe_query_3.as_str()).await?; // $ MISSING: sql-sink let _ = conn.execute(unsafe_query_1.as_str()).await?; // $ MISSING: sql-sink Alert[rust/sql-injection]=args1 From e0d16a863bb7ec749386741f527f99075d9359e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Wed, 16 Jul 2025 11:07:21 +0200 Subject: [PATCH 005/208] [DIFF-INFORMED] Go: AllocationSizeOverflow https://github.com/d10c/codeql/blob/d10c/diff-informed-phase-3/go/ql/src/Security/CWE-190/AllocationSizeOverflow.ql#L24 --- .../lib/semmle/go/security/AllocationSizeOverflow.qll | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/go/ql/lib/semmle/go/security/AllocationSizeOverflow.qll b/go/ql/lib/semmle/go/security/AllocationSizeOverflow.qll index 9531e2798129..079ab35ee366 100644 --- a/go/ql/lib/semmle/go/security/AllocationSizeOverflow.qll +++ b/go/ql/lib/semmle/go/security/AllocationSizeOverflow.qll @@ -56,6 +56,17 @@ module AllocationSizeOverflow { succ = c ) } + + predicate observeDiffInformedIncrementalMode() { any() } + + Location getASelectedSinkLocation(DataFlow::Node sink) { + result = sink.getLocation() + or + exists(DataFlow::Node allocsz | + isSinkWithAllocationSize(sink, allocsz) and + result = allocsz.getLocation() + ) + } } /** Tracks taint flow to find allocation-size overflows. */ From 89f760460b303eeb400569d7ecc78297e456bc38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Wed, 16 Jul 2025 11:08:18 +0200 Subject: [PATCH 006/208] [DIFF-INFORMED] Go: CommandInjection https://github.com/d10c/codeql/blob/d10c/diff-informed-phase-3/go/ql/src/Security/CWE-078/CommandInjection.ql#L28 --- go/ql/lib/semmle/go/security/CommandInjection.qll | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/go/ql/lib/semmle/go/security/CommandInjection.qll b/go/ql/lib/semmle/go/security/CommandInjection.qll index 7dc6f3991fc1..1774d77af54e 100644 --- a/go/ql/lib/semmle/go/security/CommandInjection.qll +++ b/go/ql/lib/semmle/go/security/CommandInjection.qll @@ -24,6 +24,8 @@ module CommandInjection { } predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer } + + predicate observeDiffInformedIncrementalMode() { any() } } /** @@ -80,6 +82,8 @@ module CommandInjection { node instanceof Sanitizer or node = any(ArgumentArrayWithDoubleDash array).getASanitizedElement() } + + predicate observeDiffInformedIncrementalMode() { any() } } /** From 109f6ddc2d0b56407c9c9fb7c591da2322ee745a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Wed, 16 Jul 2025 11:09:28 +0200 Subject: [PATCH 007/208] [DIFF-INFORMED] Go: ExternalAPIs https://github.com/d10c/codeql/blob/d10c/diff-informed-phase-3/go/ql/src/Security/CWE-020/UntrustedDataToExternalAPI.ql#L18 --- go/ql/lib/semmle/go/security/ExternalAPIs.qll | 2 ++ 1 file changed, 2 insertions(+) diff --git a/go/ql/lib/semmle/go/security/ExternalAPIs.qll b/go/ql/lib/semmle/go/security/ExternalAPIs.qll index 4a561c17136e..f85f939258f1 100644 --- a/go/ql/lib/semmle/go/security/ExternalAPIs.qll +++ b/go/ql/lib/semmle/go/security/ExternalAPIs.qll @@ -186,6 +186,8 @@ private module UntrustedDataConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { source instanceof ActiveThreatModelSource } predicate isSink(DataFlow::Node sink) { sink instanceof ExternalApiDataNode } + + predicate observeDiffInformedIncrementalMode() { any() } } /** From f228818b1f537af85bb77596d697244879dae5b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Wed, 16 Jul 2025 11:21:26 +0200 Subject: [PATCH 008/208] [DIFF-INFORMED] Go: HardcodedCredentials https://github.com/d10c/codeql/blob/d10c/diff-informed-phase-3/go/ql/src/Security/CWE-798/HardcodedCredentials.ql#L62 --- go/ql/lib/semmle/go/security/HardcodedCredentials.qll | 2 ++ 1 file changed, 2 insertions(+) diff --git a/go/ql/lib/semmle/go/security/HardcodedCredentials.qll b/go/ql/lib/semmle/go/security/HardcodedCredentials.qll index 0be50fc23062..877a2b4570e2 100644 --- a/go/ql/lib/semmle/go/security/HardcodedCredentials.qll +++ b/go/ql/lib/semmle/go/security/HardcodedCredentials.qll @@ -30,6 +30,8 @@ module HardcodedCredentials { predicate isSink(DataFlow::Node sink) { sink instanceof Sink } predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer } + + predicate observeDiffInformedIncrementalMode() { any() } } /** Tracks taint flow for reasoning about hardcoded credentials. */ From ce7eb9b16a23d66f480c2665336033237d4334e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Wed, 16 Jul 2025 11:23:10 +0200 Subject: [PATCH 009/208] [DIFF-INFORMED] Go: IncorrectIntegerConversion https://github.com/d10c/codeql/blob/d10c/diff-informed-phase-3/go/ql/src/Security/CWE-681/IncorrectIntegerConversionQuery.ql#L23 --- .../semmle/go/security/IncorrectIntegerConversionLib.qll | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/go/ql/lib/semmle/go/security/IncorrectIntegerConversionLib.qll b/go/ql/lib/semmle/go/security/IncorrectIntegerConversionLib.qll index 9125ab6e400a..7864205d1dce 100644 --- a/go/ql/lib/semmle/go/security/IncorrectIntegerConversionLib.qll +++ b/go/ql/lib/semmle/go/security/IncorrectIntegerConversionLib.qll @@ -440,6 +440,12 @@ private module ConversionWithoutBoundsCheckConfig implements DataFlow::StateConf state2 = node2.(FlowStateTransformer).transform(state1) and DataFlow::simpleLocalFlowStep(node1, node2, _) } + + predicate observeDiffInformedIncrementalMode() { any() } + + Location getASelectedSinkLocation(DataFlow::Node sink) { + result = sink.getASuccessor().getLocation() + } } /** From 4b473622bc26de3809d62335c5d5cb5ba9dfdba8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Wed, 16 Jul 2025 11:24:18 +0200 Subject: [PATCH 010/208] [DIFF-INFORMED] Go: InsecureRandomness https://github.com/d10c/codeql/blob/d10c/diff-informed-phase-3/go/ql/src/Security/CWE-338/InsecureRandomness.ql#L19 --- go/ql/lib/semmle/go/security/InsecureRandomness.qll | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/go/ql/lib/semmle/go/security/InsecureRandomness.qll b/go/ql/lib/semmle/go/security/InsecureRandomness.qll index 83746f7b96ed..4dac659eabf9 100644 --- a/go/ql/lib/semmle/go/security/InsecureRandomness.qll +++ b/go/ql/lib/semmle/go/security/InsecureRandomness.qll @@ -39,6 +39,10 @@ module InsecureRandomness { n2.getType() instanceof IntegerType ) } + + predicate observeDiffInformedIncrementalMode() { + none() // Can't have accurate sink location override because of secondary use of `flowPath` in select. + } } /** From 8c8625d9129128ad2dd83582872c8a4d4d09a1a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Wed, 16 Jul 2025 11:26:12 +0200 Subject: [PATCH 011/208] [DIFF-INFORMED] Go: ReflectedXss https://github.com/d10c/codeql/blob/d10c/diff-informed-phase-3/go/ql/src/Security/CWE-079/ReflectedXss.ql#L23 --- go/ql/lib/semmle/go/security/ReflectedXss.qll | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/go/ql/lib/semmle/go/security/ReflectedXss.qll b/go/ql/lib/semmle/go/security/ReflectedXss.qll index 1068c6fae3d1..35501269cc1c 100644 --- a/go/ql/lib/semmle/go/security/ReflectedXss.qll +++ b/go/ql/lib/semmle/go/security/ReflectedXss.qll @@ -22,6 +22,14 @@ module ReflectedXss { predicate isSink(DataFlow::Node sink) { sink instanceof Sink } predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer } + + predicate observeDiffInformedIncrementalMode() { any() } + + Location getASelectedSinkLocation(DataFlow::Node sink) { + result = sink.getLocation() + or + result = sink.(SharedXss::Sink).getAssociatedLoc().getLocation() + } } /** Tracks taint flow from untrusted data to XSS attack vectors. */ From d6ef585110ee1d7abf3caa519f69939b68efde13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Wed, 16 Jul 2025 11:33:38 +0200 Subject: [PATCH 012/208] [DIFF-INFORMED] Go: RequestForgery, SafeUrlFlow https://github.com/d10c/codeql/blob/d10c/diff-informed-phase-3/go/ql/src/Security/CWE-918/RequestForgery.ql#L21 https://github.com/d10c/codeql/blob/d10c/diff-informed-phase-3/go/ql/src/Security/CWE-601/OpenUrlRedirect.ql#L24 --- go/ql/lib/semmle/go/security/RequestForgery.qll | 8 ++++++++ go/ql/lib/semmle/go/security/SafeUrlFlow.qll | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/go/ql/lib/semmle/go/security/RequestForgery.qll b/go/ql/lib/semmle/go/security/RequestForgery.qll index bdf26a1f18fe..176b67403e6e 100644 --- a/go/ql/lib/semmle/go/security/RequestForgery.qll +++ b/go/ql/lib/semmle/go/security/RequestForgery.qll @@ -31,6 +31,14 @@ module RequestForgery { w.writesField(v.getAUse(), f, pred) and succ = v.getAUse() ) } + + predicate observeDiffInformedIncrementalMode() { any() } + + Location getASelectedSinkLocation(DataFlow::Node sink) { + result = sink.getLocation() + or + result = sink.(Sink).getARequest().getLocation() + } } /** Tracks taint flow from untrusted data to request forgery attack vectors. */ diff --git a/go/ql/lib/semmle/go/security/SafeUrlFlow.qll b/go/ql/lib/semmle/go/security/SafeUrlFlow.qll index d74e2156a609..77b7aeda591b 100644 --- a/go/ql/lib/semmle/go/security/SafeUrlFlow.qll +++ b/go/ql/lib/semmle/go/security/SafeUrlFlow.qll @@ -36,6 +36,10 @@ module SafeUrlFlow { or node instanceof SanitizerEdge } + + predicate observeDiffInformedIncrementalMode() { + none() // only used as secondary configuration + } } /** Tracks taint flow for reasoning about safe URLs. */ From 19b373aa904e45472a98f58ffdfb911713513bf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Wed, 16 Jul 2025 11:36:52 +0200 Subject: [PATCH 013/208] [DIFF-INFORMED] Go: SensitiveConditionBypass https://github.com/d10c/codeql/blob/d10c/diff-informed-phase-3/go/ql/src/experimental/CWE-807/SensitiveConditionBypass.ql#L33 --- .../src/experimental/CWE-807/SensitiveConditionBypass.qll | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/go/ql/src/experimental/CWE-807/SensitiveConditionBypass.qll b/go/ql/src/experimental/CWE-807/SensitiveConditionBypass.qll index 2f2ca94fa87f..33e6c6c01440 100644 --- a/go/ql/src/experimental/CWE-807/SensitiveConditionBypass.qll +++ b/go/ql/src/experimental/CWE-807/SensitiveConditionBypass.qll @@ -59,6 +59,14 @@ private module Config implements DataFlow::ConfigSig { not c.isPotentialFalsePositive() ) } + + predicate observeDiffInformedIncrementalMode() { any() } + + Location getASelectedSourceLocation(DataFlow::Node source) { none() } + + Location getASelectedSinkLocation(DataFlow::Node sink) { + exists(ComparisonExpr comp | result = comp.getLocation() | sink.asExpr() = comp.getAnOperand()) + } } /** From 7bd6703f1974a224126c846fd785fbe5129e7c91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Wed, 16 Jul 2025 11:37:34 +0200 Subject: [PATCH 014/208] [DIFF-INFORMED] Go: ConditionalBypass --- go/ql/src/experimental/CWE-840/ConditionalBypass.ql | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/go/ql/src/experimental/CWE-840/ConditionalBypass.ql b/go/ql/src/experimental/CWE-840/ConditionalBypass.ql index b70be1ff42db..64f7c3c9ac21 100644 --- a/go/ql/src/experimental/CWE-840/ConditionalBypass.ql +++ b/go/ql/src/experimental/CWE-840/ConditionalBypass.ql @@ -22,6 +22,10 @@ module Config implements DataFlow::ConfigSig { predicate isSink(DataFlow::Node sink) { exists(ComparisonExpr c | c.getAnOperand() = sink.asExpr()) } + + predicate observeDiffInformedIncrementalMode() { + none() // can't override the locations accurately because of secondary use of config. + } } /** Tracks taint flow for reasoning about conditional bypass. */ From a1fe72c423570dc437ce1cbd9671ad2bdf792bc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Wed, 16 Jul 2025 11:39:05 +0200 Subject: [PATCH 015/208] [DIFF-INFORMED] Go: SSRF https://github.com/d10c/codeql/blob/d10c/diff-informed-phase-3/go/ql/src/experimental/CWE-918/SSRF.ql#L23 --- go/ql/src/experimental/CWE-918/SSRF.qll | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/go/ql/src/experimental/CWE-918/SSRF.qll b/go/ql/src/experimental/CWE-918/SSRF.qll index b1374da8a5f7..05abe7bf8e47 100644 --- a/go/ql/src/experimental/CWE-918/SSRF.qll +++ b/go/ql/src/experimental/CWE-918/SSRF.qll @@ -30,6 +30,14 @@ module ServerSideRequestForgery { predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer } predicate isBarrierOut(DataFlow::Node node) { node instanceof SanitizerEdge } + + predicate observeDiffInformedIncrementalMode() { any() } + + Location getASelectedSourceLocation(DataFlow::Node source) { none() } + + Location getASelectedSinkLocation(DataFlow::Node sink) { + result = sink.(Sink).getARequest().getLocation() + } } /** Tracks taint flow for reasoning about request forgery vulnerabilities. */ From 7b759f44f817b99d085cb01e641a91309ef2b0fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Wed, 16 Jul 2025 11:43:28 +0200 Subject: [PATCH 016/208] [DIFF-INFORMED] Go: AuthCookie https://github.com/d10c/codeql/blob/d10c/diff-informed-phase-3/go/ql/src/experimental/CWE-1004/CookieWithoutHttpOnly.ql#L97 --- go/ql/src/experimental/CWE-1004/AuthCookie.qll | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/go/ql/src/experimental/CWE-1004/AuthCookie.qll b/go/ql/src/experimental/CWE-1004/AuthCookie.qll index 411da5a79fa0..b16f09ac1858 100644 --- a/go/ql/src/experimental/CWE-1004/AuthCookie.qll +++ b/go/ql/src/experimental/CWE-1004/AuthCookie.qll @@ -116,6 +116,12 @@ private module BoolToGinSetCookieTrackingConfig implements DataFlow::ConfigSig { ) ) } + + predicate observeDiffInformedIncrementalMode() { + any() // Merged with other flows in CookieWithoutHttpOnly.ql + } + + Location getASelectedSourceLocation(DataFlow::Node source) { none() } } /** From 188fc0d933adad0f658def161788762540819cdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Wed, 16 Jul 2025 11:44:30 +0200 Subject: [PATCH 017/208] [DIFF-INFORMED] Go: UnhandledCloseWritableHandle --- .../src/InconsistentCode/UnhandledCloseWritableHandle.ql | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/go/ql/src/InconsistentCode/UnhandledCloseWritableHandle.ql b/go/ql/src/InconsistentCode/UnhandledCloseWritableHandle.ql index d3210c48011e..3fd09ac040e3 100644 --- a/go/ql/src/InconsistentCode/UnhandledCloseWritableHandle.ql +++ b/go/ql/src/InconsistentCode/UnhandledCloseWritableHandle.ql @@ -128,6 +128,14 @@ module UnhandledFileCloseConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { isWritableFileHandle(source, _) } predicate isSink(DataFlow::Node sink) { isCloseSink(sink, _) } + + predicate observeDiffInformedIncrementalMode() { any() } + + Location getASelectedSourceLocation(DataFlow::Node source) { + exists(DataFlow::CallNode openCall | result = openCall.getLocation() | + isWritableFileHandle(source, openCall) + ) + } } /** From b4010ac2b4df41803e9f632d5811e39fca4096f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Wed, 16 Jul 2025 11:44:56 +0200 Subject: [PATCH 018/208] [DIFF-INFORMED] Go: InsecureHostKeyCallback --- go/ql/src/Security/CWE-322/InsecureHostKeyCallback.ql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/go/ql/src/Security/CWE-322/InsecureHostKeyCallback.ql b/go/ql/src/Security/CWE-322/InsecureHostKeyCallback.ql index c0b5898601c9..5fef19007131 100644 --- a/go/ql/src/Security/CWE-322/InsecureHostKeyCallback.ql +++ b/go/ql/src/Security/CWE-322/InsecureHostKeyCallback.ql @@ -68,6 +68,8 @@ module Config implements DataFlow::ConfigSig { } predicate isSink(DataFlow::Node sink) { writeIsSink(sink, _) } + + predicate observeDiffInformedIncrementalMode() { any() } } /** From 8824677e87972a758b183bdd2ba616f0174744d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Wed, 16 Jul 2025 11:45:25 +0200 Subject: [PATCH 019/208] [DIFF-INFORMED] Go: BadRedirectCheck --- go/ql/src/Security/CWE-601/BadRedirectCheck.ql | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/go/ql/src/Security/CWE-601/BadRedirectCheck.ql b/go/ql/src/Security/CWE-601/BadRedirectCheck.ql index bc60e1339eb9..7b4cc9f99fcd 100644 --- a/go/ql/src/Security/CWE-601/BadRedirectCheck.ql +++ b/go/ql/src/Security/CWE-601/BadRedirectCheck.ql @@ -123,6 +123,17 @@ module Config implements DataFlow::ConfigSig { } predicate isSink(DataFlow::Node sink) { sink instanceof OpenUrlRedirect::Sink } + + predicate observeDiffInformedIncrementalMode() { any() } + + Location getASelectedSourceLocation(DataFlow::Node source) { + result = source.getLocation() + or + exists(DataFlow::Node check | + isCheckedSource(source, check) and + result = check.getLocation() + ) + } } module Flow = TaintTracking::Global; From 69064b7f7fc1981568ea1e163b9da8d12188e780 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 17 Jul 2025 12:20:34 +0100 Subject: [PATCH 020/208] Rust: Update the model. --- rust/ql/lib/codeql/rust/frameworks/sqlx.model.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/ql/lib/codeql/rust/frameworks/sqlx.model.yml b/rust/ql/lib/codeql/rust/frameworks/sqlx.model.yml index b3ca9d89299e..efc6022b0c55 100644 --- a/rust/ql/lib/codeql/rust/frameworks/sqlx.model.yml +++ b/rust/ql/lib/codeql/rust/frameworks/sqlx.model.yml @@ -10,4 +10,4 @@ extensions: - ["sqlx_core::query_scalar::query_scalar", "Argument[0]", "sql-injection", "manual"] - ["sqlx_core::query_scalar_with::query_scalar_with", "Argument[0]", "sql-injection", "manual"] - ["sqlx_core::raw_sql::raw_sql", "Argument[0]", "sql-injection", "manual"] - - ["sqlx_core::executor::Executor::execute", "Argument[0]", "sql-injection", "manual"] + - ["<_ as sqlx_core::executor::Executor>::execute", "Argument[0]", "sql-injection", "manual"] From 27bea335080d3b9fa7371e7a608e87997ab70399 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 17 Jul 2025 12:44:31 +0100 Subject: [PATCH 021/208] Rust: Accept consistency check change. --- .../CWE-089/CONSISTENCY/PathResolutionConsistency.expected | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/ql/test/query-tests/security/CWE-089/CONSISTENCY/PathResolutionConsistency.expected b/rust/ql/test/query-tests/security/CWE-089/CONSISTENCY/PathResolutionConsistency.expected index d8fcd1ec9f06..b5d4fea346d2 100644 --- a/rust/ql/test/query-tests/security/CWE-089/CONSISTENCY/PathResolutionConsistency.expected +++ b/rust/ql/test/query-tests/security/CWE-089/CONSISTENCY/PathResolutionConsistency.expected @@ -6,7 +6,7 @@ multipleCallTargets | sqlx.rs:51:24:51:77 | ...::from(...) | | sqlx.rs:55:26:55:79 | ...::from(...) | | sqlx.rs:61:28:61:81 | ...::from(...) | -| sqlx.rs:64:26:64:46 | safe_query_1.as_str() | +| sqlx.rs:64:57:64:77 | safe_query_1.as_str() | | sqlx.rs:65:26:65:46 | safe_query_2.as_str() | | sqlx.rs:66:26:66:46 | safe_query_3.as_str() | | sqlx.rs:67:26:67:48 | unsafe_query_1.as_str() | From 9408a96ba53f62b9588e8c844946a908ad8ff146 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Tue, 15 Jul 2025 11:25:17 +0200 Subject: [PATCH 022/208] [TEST] Python: TimingAttackAgainstHash: add qlref test to existing source (TODO: add source with true positive) --- .../TimingAttackAgainstHash.expected | 12 ++++++++++++ .../TimingAttackAgainstHash.qlref | 1 + 2 files changed, 13 insertions(+) create mode 100644 python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.expected create mode 100644 python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.qlref diff --git a/python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.expected b/python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.expected new file mode 100644 index 000000000000..0b7b2de8ddb9 --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.expected @@ -0,0 +1,12 @@ +edges +| TimingAttackAgainstHash.py:26:5:26:13 | ControlFlowNode for signature | TimingAttackAgainstHash.py:27:24:27:32 | ControlFlowNode for signature | provenance | | +| TimingAttackAgainstHash.py:26:17:26:41 | ControlFlowNode for Attribute() | TimingAttackAgainstHash.py:26:5:26:13 | ControlFlowNode for signature | provenance | | +| TimingAttackAgainstHash.py:30:12:30:47 | ControlFlowNode for Attribute() | TimingAttackAgainstHash.py:37:19:37:48 | ControlFlowNode for sign() | provenance | | +nodes +| TimingAttackAgainstHash.py:26:5:26:13 | ControlFlowNode for signature | semmle.label | ControlFlowNode for signature | +| TimingAttackAgainstHash.py:26:17:26:41 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| TimingAttackAgainstHash.py:27:24:27:32 | ControlFlowNode for signature | semmle.label | ControlFlowNode for signature | +| TimingAttackAgainstHash.py:30:12:30:47 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| TimingAttackAgainstHash.py:37:19:37:48 | ControlFlowNode for sign() | semmle.label | ControlFlowNode for sign() | +subpaths +#select diff --git a/python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.qlref b/python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.qlref new file mode 100644 index 000000000000..50c9d84b1f96 --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.qlref @@ -0,0 +1 @@ +experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.ql From 20030d56a528757bca76b07589c11e46ef03271e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Wed, 16 Jul 2025 16:24:38 +0200 Subject: [PATCH 023/208] [DIFF-INFORMED] Python: (Possible)TimingAttackAgainstHash --- .../PossibleTimingAttackAgainstHash.ql | 4 +++- .../TimingAttackAgainstHash/TimingAttackAgainstHash.ql | 6 ++++-- .../PossibleTimingAttackAgainstHash.expected | 4 ++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql index 82ba11c1d4ba..c57373f48630 100644 --- a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql +++ b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql @@ -26,6 +26,8 @@ private module PossibleTimingAttackAgainstHashConfig implements DataFlow::Config predicate isSource(DataFlow::Node source) { source instanceof ProduceCryptoCall } predicate isSink(DataFlow::Node sink) { sink instanceof NonConstantTimeComparisonSink } + + predicate observeDiffInformedIncrementalMode() { any() } } module PossibleTimingAttackAgainstHashFlow = @@ -38,4 +40,4 @@ from PossibleTimingAttackAgainstHashFlow::PathNode sink where PossibleTimingAttackAgainstHashFlow::flowPath(source, sink) select sink.getNode(), source, sink, "Possible Timing attack against $@ validation.", - source.getNode().(ProduceCryptoCall).getResultType(), "message" + source.getNode(), source.getNode().(ProduceCryptoCall).getResultType() + " message" diff --git a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.ql b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.ql index e08f1dbb5177..d97f6c619f9b 100644 --- a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.ql +++ b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.ql @@ -25,6 +25,8 @@ private module TimingAttackAgainstHashConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { source instanceof ProduceCryptoCall } predicate isSink(DataFlow::Node sink) { sink instanceof NonConstantTimeComparisonSink } + + predicate observeDiffInformedIncrementalMode() { any() } } module TimingAttackAgainstHashFlow = TaintTracking::Global; @@ -35,5 +37,5 @@ from TimingAttackAgainstHashFlow::PathNode source, TimingAttackAgainstHashFlow:: where TimingAttackAgainstHashFlow::flowPath(source, sink) and sink.getNode().(NonConstantTimeComparisonSink).includesUserInput() -select sink.getNode(), source, sink, "Timing attack against $@ validation.", - source.getNode().(ProduceCryptoCall).getResultType(), "message" +select sink.getNode(), source, sink, "Timing attack against $@ validation.", source.getNode(), + source.getNode().(ProduceCryptoCall).getResultType() + " message" diff --git a/python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.expected b/python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.expected index 8846e9087981..1577182b2dcd 100644 --- a/python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.expected +++ b/python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.expected @@ -10,5 +10,5 @@ nodes | TimingAttackAgainstHash.py:37:19:37:48 | ControlFlowNode for sign() | semmle.label | ControlFlowNode for sign() | subpaths #select -| TimingAttackAgainstHash.py:27:24:27:32 | ControlFlowNode for signature | TimingAttackAgainstHash.py:26:17:26:41 | ControlFlowNode for Attribute() | TimingAttackAgainstHash.py:27:24:27:32 | ControlFlowNode for signature | Possible Timing attack against $@ validation. | signature | message | -| TimingAttackAgainstHash.py:37:19:37:48 | ControlFlowNode for sign() | TimingAttackAgainstHash.py:30:12:30:47 | ControlFlowNode for Attribute() | TimingAttackAgainstHash.py:37:19:37:48 | ControlFlowNode for sign() | Possible Timing attack against $@ validation. | MAC | message | +| TimingAttackAgainstHash.py:27:24:27:32 | ControlFlowNode for signature | TimingAttackAgainstHash.py:26:17:26:41 | ControlFlowNode for Attribute() | TimingAttackAgainstHash.py:27:24:27:32 | ControlFlowNode for signature | Possible Timing attack against $@ validation. | TimingAttackAgainstHash.py:26:17:26:41 | ControlFlowNode for Attribute() | signature message | +| TimingAttackAgainstHash.py:37:19:37:48 | ControlFlowNode for sign() | TimingAttackAgainstHash.py:30:12:30:47 | ControlFlowNode for Attribute() | TimingAttackAgainstHash.py:37:19:37:48 | ControlFlowNode for sign() | Possible Timing attack against $@ validation. | TimingAttackAgainstHash.py:30:12:30:47 | ControlFlowNode for Attribute() | MAC message | From 4b6135c0f7af0134e0529019641d0a5efad1dc62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Wed, 16 Jul 2025 16:35:33 +0200 Subject: [PATCH 024/208] [DIFF-INFORMED] Ruby: MissingFullAnchor https://github.com/d10c/codeql/blob/d10c/diff-informed-phase-3/ruby/ql/src/queries/security/cwe-020/MissingFullAnchor.ql#L18 --- .../codeql/ruby/security/regexp/MissingFullAnchorQuery.qll | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ruby/ql/lib/codeql/ruby/security/regexp/MissingFullAnchorQuery.qll b/ruby/ql/lib/codeql/ruby/security/regexp/MissingFullAnchorQuery.qll index febfa0712d9b..3516e8319bca 100644 --- a/ruby/ql/lib/codeql/ruby/security/regexp/MissingFullAnchorQuery.qll +++ b/ruby/ql/lib/codeql/ruby/security/regexp/MissingFullAnchorQuery.qll @@ -17,6 +17,10 @@ private module MissingFullAnchorConfig implements DataFlow::ConfigSig { predicate isSink(DataFlow::Node sink) { sink instanceof Sink } predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer } + + predicate observeDiffInformedIncrementalMode() { + none() // can't be made diff-informed because the locations of Ruby RegExpTerms aren't correct when the regexp is parsed from a string arising from constant folding + } } /** From 793f92129162ecbe8d280bc3a3e55b0940bbe013 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Wed, 16 Jul 2025 10:38:05 +0200 Subject: [PATCH 025/208] [DIFF-INFORMED] C#: ConditionalBypass https://github.com/d10c/codeql/blob/d10c/diff-informed-phase-3/csharp/ql/src/Security%20Features/CWE-807/ConditionalBypass.ql#L22 --- .../csharp/security/dataflow/ConditionalBypassQuery.qll | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/csharp/ql/lib/semmle/code/csharp/security/dataflow/ConditionalBypassQuery.qll b/csharp/ql/lib/semmle/code/csharp/security/dataflow/ConditionalBypassQuery.qll index f92bb0d2f44a..ee345780654d 100644 --- a/csharp/ql/lib/semmle/code/csharp/security/dataflow/ConditionalBypassQuery.qll +++ b/csharp/ql/lib/semmle/code/csharp/security/dataflow/ConditionalBypassQuery.qll @@ -39,6 +39,15 @@ private module ConditionalBypassConfig implements DataFlow::ConfigSig { predicate isSink(DataFlow::Node sink) { sink instanceof Sink } predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer } + + predicate observeDiffInformedIncrementalMode() { any() } + + Location getASelectedSinkLocation(DataFlow::Node sink) { + result = sink.getLocation() + or + // from ConditionalBypass.ql + result = sink.(Sink).getSensitiveMethodCall().getLocation() + } } /** From 7f085e6bd937ad3663c009c65889ec62542496d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Wed, 16 Jul 2025 10:40:56 +0200 Subject: [PATCH 026/208] [DIFF-INFORMED] C#: UnsafeDeserializationQuery https://github.com/d10c/codeql/blob/57c8b6e2299f5d6e991bd1a198a58692b6d6e016/csharp/ql/src/Security%20Features/CWE-502/UnsafeDeserializationUntrustedInput.ql#L59 --- .../dataflow/UnsafeDeserializationQuery.qll | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/csharp/ql/lib/semmle/code/csharp/security/dataflow/UnsafeDeserializationQuery.qll b/csharp/ql/lib/semmle/code/csharp/security/dataflow/UnsafeDeserializationQuery.qll index 5d9d18dcbac6..27f6ab6935f7 100644 --- a/csharp/ql/lib/semmle/code/csharp/security/dataflow/UnsafeDeserializationQuery.qll +++ b/csharp/ql/lib/semmle/code/csharp/security/dataflow/UnsafeDeserializationQuery.qll @@ -59,6 +59,10 @@ private module TaintToObjectMethodTrackingConfig implements DataFlow::ConfigSig predicate isSink(DataFlow::Node sink) { sink instanceof InstanceMethodSink } predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer } + + predicate observeDiffInformedIncrementalMode() { + any() // used in one of the disjuncts in UnsafeDeserializationUntrustedInput.ql + } } /** @@ -77,6 +81,10 @@ private module JsonConvertTrackingConfig implements DataFlow::ConfigSig { } predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer } + + predicate observeDiffInformedIncrementalMode() { + any() // used in one of the disjuncts in UnsafeDeserializationUntrustedInput.ql + } } /** @@ -133,6 +141,10 @@ private module TypeNameTrackingConfig implements DataFlow::ConfigSig { ) ) } + + predicate observeDiffInformedIncrementalMode() { + none() // Only used as secondary config in UnsafeDeserializationUntrustedInput.ql + } } /** @@ -149,6 +161,10 @@ private module TaintToConstructorOrStaticMethodTrackingConfig implements DataFlo predicate isSink(DataFlow::Node sink) { sink instanceof ConstructorOrStaticMethodSink } predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer } + + predicate observeDiffInformedIncrementalMode() { + any() // used in one of the disjuncts in UnsafeDeserializationUntrustedInput.ql + } } /** @@ -186,6 +202,10 @@ private module TaintToObjectTypeTrackingConfig implements DataFlow::ConfigSig { oc.getObjectType() instanceof StrongTypeDeserializer ) } + + predicate observeDiffInformedIncrementalMode() { + none() // only used as secondary config in UnsafeDeserializationUntrustedInput.ql + } } /** @@ -210,6 +230,10 @@ private module WeakTypeCreationToUsageTrackingConfig implements DataFlow::Config sink.asExpr() = mc.getQualifier() ) } + + predicate observeDiffInformedIncrementalMode() { + none() // only used as secondary config in UnsafeDeserializationUntrustedInput.ql + } } /** From b2fd58eea435d902bfdfe853ba5225dab3bfbe59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Wed, 16 Jul 2025 10:42:09 +0200 Subject: [PATCH 027/208] [DIFF-INFORMED] C#: ThreadUnsafeCryptoTransformLambda --- csharp/ql/src/Likely Bugs/ThreadUnsafeICryptoTransformLambda.ql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/csharp/ql/src/Likely Bugs/ThreadUnsafeICryptoTransformLambda.ql b/csharp/ql/src/Likely Bugs/ThreadUnsafeICryptoTransformLambda.ql index 9f70760ba602..8fcef4d4744f 100644 --- a/csharp/ql/src/Likely Bugs/ThreadUnsafeICryptoTransformLambda.ql +++ b/csharp/ql/src/Likely Bugs/ThreadUnsafeICryptoTransformLambda.ql @@ -24,6 +24,8 @@ module NotThreadSafeCryptoUsageIntoParallelInvokeConfig implements DataFlow::Con } predicate isSink(DataFlow::Node sink) { sink instanceof ParallelSink } + + predicate observeDiffInformedIncrementalMode() { any() } } module NotThreadSafeCryptoUsageIntoParallelInvoke = From 218fcbbec5eea8c63715c41a82f9588ab48bf58a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Wed, 16 Jul 2025 10:42:36 +0200 Subject: [PATCH 028/208] [DIFF-INFORMED] C#: HardcodedConnectionString --- .../Security Features/CWE-798/HardcodedConnectionString.ql | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/csharp/ql/src/Security Features/CWE-798/HardcodedConnectionString.ql b/csharp/ql/src/Security Features/CWE-798/HardcodedConnectionString.ql index 32508fa9d3fb..1e33ed6a1fde 100644 --- a/csharp/ql/src/Security Features/CWE-798/HardcodedConnectionString.ql +++ b/csharp/ql/src/Security Features/CWE-798/HardcodedConnectionString.ql @@ -38,6 +38,12 @@ module ConnectionStringConfig implements DataFlow::ConfigSig { } predicate isBarrier(DataFlow::Node node) { node instanceof StringFormatSanitizer } + + predicate observeDiffInformedIncrementalMode() { any() } + + Location getASelectedSinkLocation(DataFlow::Node sink) { + any(Call call | call.getAnArgument() = sink.asExpr()).getLocation() = result + } } /** From 9da94fb880b13f60d087079cafe37184e22242ff Mon Sep 17 00:00:00 2001 From: Matt Schwager Date: Mon, 21 Jul 2025 15:17:54 -0400 Subject: [PATCH 029/208] Fix #19294, Ruby NetHttpRequest improvements --- .../ruby/frameworks/http_clients/NetHttp.qll | 22 +++++++++++++++---- .../frameworks/http_clients/NetHttp.rb | 8 +++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/NetHttp.qll b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/NetHttp.qll index 3a0b484e5465..0b4156ff813d 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/NetHttp.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/NetHttp.qll @@ -12,15 +12,22 @@ private import codeql.ruby.DataFlow /** * A `Net::HTTP` call which initiates an HTTP request. * ```ruby + * # one-off request * Net::HTTP.get("http://example.com/") * Net::HTTP.post("http://example.com/", "some_data") * req = Net::HTTP.new("example.com") * response = req.get("/") + * + * # connection re-use + * Net::HTTP.start("http://example.com") do |http| + * http.get("/") + * end * ``` */ class NetHttpRequest extends Http::Client::Request::Range instanceof DataFlow::CallNode { private DataFlow::CallNode request; - private API::Node requestNode; + API::Node requestNode; + API::Node connectionNode; private boolean returnsResponseBody; NetHttpRequest() { @@ -30,20 +37,27 @@ class NetHttpRequest extends Http::Client::Request::Range instanceof DataFlow::C | // Net::HTTP.get(...) method in ["get", "get_response"] and - requestNode = API::getTopLevelMember("Net").getMember("HTTP").getReturn(method) and + connectionNode = API::getTopLevelMember("Net").getMember("HTTP") and + requestNode = connectionNode.getReturn(method) and returnsResponseBody = true or // Net::HTTP.post(...).body method in ["post", "post_form"] and - requestNode = API::getTopLevelMember("Net").getMember("HTTP").getReturn(method) and + connectionNode = API::getTopLevelMember("Net").getMember("HTTP") and + requestNode = connectionNode.getReturn(method) and returnsResponseBody = false or // Net::HTTP.new(..).get(..).body + // Net::HTTP.start(..) do |http| http.get(..) end method in [ "get", "get2", "request_get", "head", "head2", "request_head", "delete", "put", "patch", "post", "post2", "request_post", "request" ] and - requestNode = API::getTopLevelMember("Net").getMember("HTTP").getInstance().getReturn(method) and + connectionNode = [ + API::getTopLevelMember("Net").getMember("HTTP").getInstance(), + API::getTopLevelMember("Net").getMember("HTTP").getMethod("start").getBlock().getParameter(0) + ] and + requestNode = connectionNode.getReturn(method) and returnsResponseBody = false ) } diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/NetHttp.rb b/ruby/ql/test/library-tests/frameworks/http_clients/NetHttp.rb index 608b46ece9aa..63f1522dfad4 100644 --- a/ruby/ql/test/library-tests/frameworks/http_clients/NetHttp.rb +++ b/ruby/ql/test/library-tests/frameworks/http_clients/NetHttp.rb @@ -27,3 +27,11 @@ def get(domain, path) get("example.com", "/").body Net::HTTP.post(uri, "some_body") # note: response body not accessed + +http = Net::HTTP.new("https://example.com") +root_get = Net::HTTP::Get.new("/") +http.request(root_get) + +Net::HTTP.start("https://example.com") do |http| + http.get("/") +end From 5192f3128aca095cbb04ff9226b31a5453dc2ccc Mon Sep 17 00:00:00 2001 From: Matt Schwager Date: Mon, 21 Jul 2025 15:26:39 -0400 Subject: [PATCH 030/208] Update expected test output --- .../frameworks/http_clients/HttpClients.expected | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/HttpClients.expected b/ruby/ql/test/library-tests/frameworks/http_clients/HttpClients.expected index f6275da34ace..e3a36c04819d 100644 --- a/ruby/ql/test/library-tests/frameworks/http_clients/HttpClients.expected +++ b/ruby/ql/test/library-tests/frameworks/http_clients/HttpClients.expected @@ -46,6 +46,8 @@ httpRequests | NetHttp.rb:16:6:16:19 | call to patch | | NetHttp.rb:24:3:24:33 | call to get | | NetHttp.rb:29:1:29:32 | call to post | +| NetHttp.rb:33:1:33:22 | call to request | +| NetHttp.rb:36:3:36:15 | call to get | | OpenURI.rb:3:9:3:41 | call to open | | OpenURI.rb:6:9:6:34 | call to open | | OpenURI.rb:9:9:9:38 | call to open | @@ -123,6 +125,8 @@ getFramework | NetHttp.rb:16:6:16:19 | call to patch | Net::HTTP | | NetHttp.rb:24:3:24:33 | call to get | Net::HTTP | | NetHttp.rb:29:1:29:32 | call to post | Net::HTTP | +| NetHttp.rb:33:1:33:22 | call to request | Net::HTTP | +| NetHttp.rb:36:3:36:15 | call to get | Net::HTTP | | OpenURI.rb:3:9:3:41 | call to open | OpenURI | | OpenURI.rb:6:9:6:34 | call to open | OpenURI | | OpenURI.rb:9:9:9:38 | call to open | OpenURI | @@ -292,6 +296,9 @@ getAUrlPart | NetHttp.rb:24:3:24:33 | call to get | NetHttp.rb:24:17:24:22 | domain | | NetHttp.rb:24:3:24:33 | call to get | NetHttp.rb:24:29:24:32 | path | | NetHttp.rb:29:1:29:32 | call to post | NetHttp.rb:29:16:29:18 | uri | +| NetHttp.rb:33:1:33:22 | call to request | NetHttp.rb:31:22:31:42 | "https://example.com" | +| NetHttp.rb:33:1:33:22 | call to request | NetHttp.rb:33:14:33:21 | root_get | +| NetHttp.rb:36:3:36:15 | call to get | NetHttp.rb:36:12:36:14 | "/" | | OpenURI.rb:3:9:3:41 | call to open | OpenURI.rb:3:21:3:40 | "http://example.com" | | OpenURI.rb:6:9:6:34 | call to open | OpenURI.rb:6:14:6:33 | "http://example.com" | | OpenURI.rb:9:9:9:38 | call to open | OpenURI.rb:9:18:9:37 | "http://example.com" | From d8b9d4d17aa087d7bb64cb44da0972e4a068f88c Mon Sep 17 00:00:00 2001 From: Matt Schwager Date: Wed, 23 Jul 2025 07:03:26 -0400 Subject: [PATCH 031/208] Add change-note --- .../change-notes/2025-07-21-nethttprequest-improvements.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 ruby/ql/lib/change-notes/2025-07-21-nethttprequest-improvements.md diff --git a/ruby/ql/lib/change-notes/2025-07-21-nethttprequest-improvements.md b/ruby/ql/lib/change-notes/2025-07-21-nethttprequest-improvements.md new file mode 100644 index 000000000000..7de3ed050e2f --- /dev/null +++ b/ruby/ql/lib/change-notes/2025-07-21-nethttprequest-improvements.md @@ -0,0 +1,7 @@ +--- +category: fix +--- +* Made the following changes to `NetHttpRequest` + * Adds `connectionNode`, like other Ruby HTTP clients + * Makes `requestNode` and `connectionNode` public so subclasses can use them + * Adds detection of `Net::HTTP.start`, a common way to make HTTP requests in Ruby From e3021f4a65bdc3cd47264d8f440e5aa428528b7a Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Fri, 25 Jul 2025 13:33:14 +0200 Subject: [PATCH 032/208] Java: Untangle code a bit to improve join order. --- .../Likely Bugs/Resource Leaks/CloseType.qll | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseType.qll b/java/ql/src/Likely Bugs/Resource Leaks/CloseType.qll index 41239f249a27..aca95c9bc1f5 100644 --- a/java/ql/src/Likely Bugs/Resource Leaks/CloseType.qll +++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseType.qll @@ -212,33 +212,35 @@ private LocalVariableDecl getCloseableVariable(CloseableInitExpr cie) { /** * A variable on which a "close" method is called, implicitly or explicitly, directly or indirectly. */ -private predicate closeCalled(Variable v) { +private predicate closeCalled(LocalScopeVariable v) { // `close()` is implicitly called on variables declared or referenced // in the resources clause of try-with-resource statements. exists(TryStmt try | try.getAResourceVariable() = v) or // Otherwise, there should be an explicit call to a method whose name contains "close". exists(MethodCall e | - v = getCloseableVariable(_) or v instanceof Parameter or v instanceof LocalVariableDecl - | e.getMethod().getName().toLowerCase().matches("%close%") and exists(VarAccess va | va = v.getAnAccess() | e.getQualifier() = va or e.getAnArgument() = va ) - or - // The "close" call could happen indirectly inside a helper method of unknown name. - exists(int i | e.getArgument(i) = v.getAnAccess() | - exists(Parameter p, int j | p.getPosition() = j and p.getCallable() = e.getMethod() | - closeCalled(p) and i = j - or - // The helper method could be iterating over a varargs parameter. - exists(EnhancedForStmt for | for.getExpr() = p.getAnAccess() | - closeCalled(for.getVariable().getVariable()) - ) and - p.isVarargs() and - j <= i - ) + ) + or + // The "close" call could happen indirectly inside a helper method of unknown name. + exists(Parameter p | + closeCalled(p) and p.getAnArgument() = v.getAnAccess() and p.getCallable() instanceof Method + ) + or + exists(MethodCall e, int i | e.getArgument(i) = v.getAnAccess() | + exists(Parameter p, int j | + p.getPosition() = j and p.getCallable() = e.getMethod().getSourceDeclaration() + | + // The helper method could be iterating over a varargs parameter. + exists(EnhancedForStmt for | for.getExpr() = p.getAnAccess() | + closeCalled(for.getVariable().getVariable()) + ) and + p.isVarargs() and + j <= i ) ) } From 5ca35afb8c6606d633e097ee8f755525d6861b8c Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Fri, 25 Jul 2025 13:34:11 +0200 Subject: [PATCH 033/208] Java: Improve joinorder in getErasedRepr. --- .../java/dataflow/internal/DataFlowPrivate.qll | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll index 164e2d8aa262..8b9087ecbdc5 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll @@ -348,6 +348,16 @@ predicate expectsContent(Node n, ContentSet c) { FlowSummaryImpl::Private::Steps::summaryExpectsContent(n.(FlowSummaryNode).getSummaryNode(), c) } +pragma[nomagic] +private predicate numericRepresentative(RefType t) { + t.(BoxedType).getPrimitiveType().getName() = "double" +} + +pragma[nomagic] +private predicate booleanRepresentative(RefType t) { + t.(BoxedType).getPrimitiveType().getName() = "boolean" +} + /** * Gets a representative (boxed) type for `t` for the purpose of pruning * possible flow. A single type is used for all numeric types to account for @@ -356,10 +366,10 @@ predicate expectsContent(Node n, ContentSet c) { RefType getErasedRepr(Type t) { exists(Type e | e = t.getErasure() | if e instanceof NumericOrCharType - then result.(BoxedType).getPrimitiveType().getName() = "double" + then numericRepresentative(result) else if e instanceof BooleanType - then result.(BoxedType).getPrimitiveType().getName() = "boolean" + then booleanRepresentative(result) else result = e ) or From 6c8275298b4366c66d5b00e1f3f0a620683cb5ee Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Fri, 25 Jul 2025 14:41:06 +0200 Subject: [PATCH 034/208] Java: Improve ObjFlow performance. --- .../lib/semmle/code/java/dispatch/ObjFlow.qll | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/java/ql/lib/semmle/code/java/dispatch/ObjFlow.qll b/java/ql/lib/semmle/code/java/dispatch/ObjFlow.qll index 12fe1cba5e99..86915f802743 100644 --- a/java/ql/lib/semmle/code/java/dispatch/ObjFlow.qll +++ b/java/ql/lib/semmle/code/java/dispatch/ObjFlow.qll @@ -214,24 +214,35 @@ private predicate relevantNode(ObjNode n) { exists(ObjNode mid | relevantNode(mid) and objStep(mid, n) and relevantNodeBack(n)) } -pragma[noinline] -private predicate objStepPruned(ObjNode n1, ObjNode n2) { - objStep(n1, n2) and relevantNode(n1) and relevantNode(n2) +private newtype TObjFlowNode = + TObjNode(ObjNode n) { relevantNode(n) } or + TObjType(RefType t) { source(t, _) } + +private predicate objStepPruned(TObjFlowNode node1, TObjFlowNode node2) { + exists(ObjNode n1, ObjNode n2 | + node1 = TObjNode(n1) and + node2 = TObjNode(n2) and + objStep(n1, n2) + ) + or + exists(RefType t, ObjNode n | + node1 = TObjType(t) and + node2 = TObjNode(n) and + source(t, n) + ) } -private predicate stepPlus(Node n1, Node n2) = fastTC(objStepPruned/2)(n1, n2) +private predicate flowSrc(TObjFlowNode src) { src instanceof TObjType } + +private predicate flowSink(TObjFlowNode sink) { exists(ObjNode n | sink = TObjNode(n) and sink(n)) } + +private predicate stepPlus(TObjFlowNode n1, TObjFlowNode n2) = + doublyBoundedFastTC(objStepPruned/2, flowSrc/1, flowSink/1)(n1, n2) /** * Holds if the qualifier `n` of an `Object.toString()` call might have type `t`. */ -pragma[noopt] -private predicate objType(ObjNode n, RefType t) { - exists(ObjNode n2 | - sink(n) and - (stepPlus(n2, n) or n2 = n) and - source(t, n2) - ) -} +private predicate objType(ObjNode n, RefType t) { stepPlus(TObjType(t), TObjNode(n)) } private VirtualMethodCall objectToString(ObjNode n) { result.getQualifier() = n.asExpr() and sink(n) From 9972aaf6a11f3ff94bd3d7b6166c769355fd76ff Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 13 Jun 2025 18:23:07 +0100 Subject: [PATCH 035/208] Rust: Add tests cases for cleartext storage. --- .../query-tests/security/CWE-312/options.yml | 2 + .../security/CWE-312/test_storage.rs | 114 ++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 rust/ql/test/query-tests/security/CWE-312/test_storage.rs diff --git a/rust/ql/test/query-tests/security/CWE-312/options.yml b/rust/ql/test/query-tests/security/CWE-312/options.yml index e2329156d6fc..1fccbab2973a 100644 --- a/rust/ql/test/query-tests/security/CWE-312/options.yml +++ b/rust/ql/test/query-tests/security/CWE-312/options.yml @@ -3,3 +3,5 @@ qltest_dependencies: - log = { version = "0.4.25", features = ["kv"] } - simple_logger = { version = "5.0.0" } - log_err = { version = "1.1.1" } + - sqlx = { version = "0.8", features = ["mysql", "sqlite", "postgres", "runtime-async-std", "tls-native-tls"] } + - futures = { version = "0.3" } diff --git a/rust/ql/test/query-tests/security/CWE-312/test_storage.rs b/rust/ql/test/query-tests/security/CWE-312/test_storage.rs new file mode 100644 index 000000000000..0c5f7c4c62b1 --- /dev/null +++ b/rust/ql/test/query-tests/security/CWE-312/test_storage.rs @@ -0,0 +1,114 @@ + +use sqlx::Connection; +use sqlx::Executor; + +// --- tests --- + +fn get_harmless() -> String { + return String::from("harmless"); +} + +fn get_social_security_number() -> String { + return String::from("1234567890"); +} + +fn get_phone_number() -> String { + return String::from("1234567890"); +} + +fn get_email() -> String { + return String::from("a@b.com"); +} + +async fn test_storage_sql_command(url: &str) -> Result<(), sqlx::Error> { + // connect through a MySQL connection pool + let pool1 = sqlx::mysql::MySqlPool::connect(url).await?; + let mut conn1 = pool1.acquire().await?; + + // construct queries + let id = "123"; + let select_query1 = String::from("SELECT * FROM CONTACTS WHERE ID = ") + id; + let select_query2 = String::from("SELECT * FROM CONTACTS WHERE SSN = '") + &get_social_security_number() + "'"; + let insert_query1 = String::from("INSERT INTO CONTACTS(ID, HARMLESS) VALUES(") + id + ", '" + &get_harmless() + "')"; + let insert_query2 = String::from("INSERT INTO CONTACTS(ID, PHONE) VALUES(") + id + ", '" + &get_phone_number() + "')"; + let update_query1 = String::from("UPDATE CONTACTS SET HARMLESS='") + &get_harmless() + "' WHERE ID=" + id; + let update_query2 = String::from("UPDATE CONTACTS SET EMAIL='") + &get_email() + "' WHERE ID=" + id; + let s1 = &get_social_security_number(); + let update_query3 = String::from("UPDATE CONTACTS SET SSN='") + s1 + "' WHERE ID=" + id; + let update_query4 = String::from("UPDATE CONTACTS SET SSN='") + &s1 + "' WHERE ID=" + id; + let s2 = s1.as_str(); + let update_query5 = String::from("UPDATE CONTACTS SET SSN='") + s2 + "' WHERE ID=" + id; + let update_query6 = String::from("UPDATE CONTACTS SET SSN='") + &s2 + "' WHERE ID=" + id; + let delete_query1 = String::from("DELETE FROM CONTACTS WHERE ID=") + id; + let delete_query2 = String::from("DELETE FROM CONTACTS WHERE SSN='") + &get_social_security_number() + "'"; + let prepared_query = String::from("UPDATE CONTACTS SET SSN=? WHERE ID=?"); + + // execute queries - MySQL, direct + let _ = conn1.execute(select_query1.as_str()).await?; + let _ = conn1.execute(select_query2.as_str()).await?; // $ MISSING: Alert[rust/cleartext-storage-database] + let _ = conn1.execute(insert_query1.as_str()).await?; + let _ = conn1.execute(insert_query2.as_str()).await?; // $ MISSING: Alert[rust/cleartext-storage-database] + let _ = conn1.execute(update_query1.as_str()).await?; + let _ = conn1.execute(update_query2.as_str()).await?; // $ MISSING: Alert[rust/cleartext-storage-database] + let _ = conn1.execute(update_query3.as_str()).await?; // $ MISSING: Alert[rust/cleartext-storage-database] + let _ = conn1.execute(update_query4.as_str()).await?; // $ MISSING: Alert[rust/cleartext-storage-database] + let _ = conn1.execute(update_query5.as_str()).await?; // $ MISSING: Alert[rust/cleartext-storage-database] + let _ = conn1.execute(update_query6.as_str()).await?; // $ MISSING: Alert[rust/cleartext-storage-database] + let _ = conn1.execute(delete_query1.as_str()).await?; + let _ = conn1.execute(delete_query2.as_str()).await?; // $ MISSING: Alert[rust/cleartext-storage-database] + + // execute queries - MySQL, prepared query + let _ = sqlx::query(insert_query1.as_str()).execute(&pool1).await?; + let _ = sqlx::query(insert_query2.as_str()).execute(&pool1).await?; // $ MISSING: Alert[rust/cleartext-storage-database] + let _ = sqlx::query(prepared_query.as_str()).bind(get_harmless()).execute(&pool1).await?; + let _ = sqlx::query(prepared_query.as_str()).bind(get_social_security_number()).execute(&pool1).await?; // $ MISSING: Alert[rust/cleartext-storage-database] + let _ = sqlx::query(prepared_query.as_str()).bind(&s1).execute(&pool1).await?; // $ MISSING: Alert[rust/cleartext-storage-database] + let _ = sqlx::query(prepared_query.as_str()).bind(&s2).execute(&pool1).await?; // $ MISSING: Alert[rust/cleartext-storage-database] + + // connect through SQLite, no connection pool + let mut conn2 = sqlx::sqlite::SqliteConnection::connect(url).await?; + + // execute queries - SQLite, direct + let _ = conn2.execute(insert_query1.as_str()).await?; + let _ = conn2.execute(insert_query2.as_str()).await?; // $ MISSING: Alert[rust/cleartext-storage-database] + + // execute queries - SQLite, direct variant + let _ = sqlx::raw_sql(insert_query1.as_str()).execute(&mut conn2).await?; + let _ = sqlx::raw_sql(insert_query2.as_str()).execute(&mut conn2).await?; // $ MISSING: Alert[rust/cleartext-storage-database] + + // execute queries - SQLite, prepared query + let _ = sqlx::query(insert_query1.as_str()).execute(&mut conn2).await?; + let _ = sqlx::query(insert_query2.as_str()).execute(&mut conn2).await?; // $ MISSING: Alert[rust/cleartext-storage-database] + let _ = sqlx::query(prepared_query.as_str()).bind(get_harmless()).execute(&mut conn2).await?; + let _ = sqlx::query(prepared_query.as_str()).bind(get_social_security_number()).execute(&mut conn2).await?; // $ MISSING: Alert[rust/cleartext-storage-database] + + // execute queries - SQLite, prepared query variant + let _ = sqlx::query(insert_query1.as_str()).fetch(&mut conn2); + let _ = sqlx::query(insert_query2.as_str()).fetch(&mut conn2); // $ MISSING: Alert[rust/cleartext-storage-database] + let _ = sqlx::query(prepared_query.as_str()).bind(get_harmless()).fetch(&mut conn2); + let _ = sqlx::query(prepared_query.as_str()).bind(get_social_security_number()).fetch(&mut conn2); // $ MISSING: Alert[rust/cleartext-storage-database] + + // connect through a PostgreSQL connection pool + let pool3 = sqlx::postgres::PgPool::connect(url).await?; + let mut conn3 = pool3.acquire().await?; + + // execute queries - PostgreSQL, direct + let _ = conn3.execute(insert_query1.as_str()).await?; + let _ = conn3.execute(insert_query2.as_str()).await?; // $ MISSING: Alert[rust/cleartext-storage-database] + + // execute queries - PostgreSQL, prepared query + let _ = sqlx::query(insert_query1.as_str()).execute(&pool3).await?; + let _ = sqlx::query(insert_query2.as_str()).execute(&pool3).await?; // $ MISSING: Alert[rust/cleartext-storage-database] + let _ = sqlx::query(prepared_query.as_str()).bind(get_harmless()).execute(&pool3).await?; + let _ = sqlx::query(prepared_query.as_str()).bind(get_social_security_number()).execute(&pool3).await?; // $ MISSING: Alert[rust/cleartext-storage-database] + + Ok(()) +} + +fn main() { + println!("test_storage_sql_command..."); + match futures::executor::block_on(test_storage_sql_command("")) { + Ok(_) => println!(" successful!"), + Err(e) => println!(" error: {}", e), + } +} From 897822dff5de048ed554d40b31b391bf26786e3f Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 28 Jul 2025 16:55:43 +0100 Subject: [PATCH 036/208] Rust: The Cargo.lock file has changed as well. --- .../query-tests/security/CWE-312/Cargo.lock | 2357 ++++++++++++++++- 1 file changed, 2267 insertions(+), 90 deletions(-) diff --git a/rust/ql/test/query-tests/security/CWE-312/Cargo.lock b/rust/ql/test/query-tests/security/CWE-312/Cargo.lock index 66f3d9acd922..0da8ad619d95 100644 --- a/rust/ql/test/query-tests/security/CWE-312/Cargo.lock +++ b/rust/ql/test/query-tests/security/CWE-312/Cargo.lock @@ -2,187 +2,2171 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand 2.3.0", + "futures-lite 2.6.0", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +dependencies = [ + "async-channel 2.5.0", + "async-executor", + "async-io 2.5.0", + "async-lock 3.4.0", + "blocking", + "futures-lite 2.6.0", + "once_cell", +] + +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock 2.8.0", + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-lite 1.13.0", + "log", + "parking", + "polling 2.8.0", + "rustix 0.37.28", + "slab", + "socket2", + "waker-fn", +] + +[[package]] +name = "async-io" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19634d6336019ef220f09fd31168ce5c184b295cbf80345437cc36094ef223ca" +dependencies = [ + "async-lock 3.4.0", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite 2.6.0", + "parking", + "polling 3.9.0", + "rustix 1.0.8", + "slab", + "windows-sys 0.60.2", +] + +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener 2.5.3", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener 5.4.0", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-std" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "730294c1c08c2e0f85759590518f6333f0d5a0a766a27d519c1b244c3dfd8a24" +dependencies = [ + "async-channel 1.9.0", + "async-global-executor", + "async-io 2.5.0", + "async-lock 3.4.0", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite 2.6.0", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +dependencies = [ + "serde", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel 2.5.0", + "async-task", + "futures-io", + "futures-lite 2.6.0", + "piper", +] + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cc" +version = "1.2.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + [[package]] name = "colored" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" +dependencies = [ + "lazy_static", + "windows-sys 0.59.0", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +dependencies = [ + "serde", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener 5.4.0", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "flume" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +dependencies = [ + "futures-core", + "futures-sink", + "spin", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-lite" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +dependencies = [ + "fastrand 2.3.0", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "libc" +version = "0.2.173" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb" + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + +[[package]] +name = "lock_api" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +dependencies = [ + "value-bag", +] + +[[package]] +name = "log_err" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2829e80508da18227e53a791bbe19018306f3f400bba439bc9959e9faa860f7" +dependencies = [ + "log", +] + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "openssl" +version = "0.10.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand 2.3.0", + "futures-io", +] + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys 0.48.0", +] + +[[package]] +name = "polling" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee9b2fa7a4517d2c91ff5bc6c297a427a96749d15f98fcdbb22c05571a4d4b7" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi 0.5.2", + "pin-project-lite", + "rustix 1.0.8", + "windows-sys 0.60.2", +] + +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "redox_syscall" +version = "0.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "rsa" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustix" +version = "0.37.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "519165d378b97752ca44bbe15047d5d3409e875f39327546b42ac81d7e18c1b6" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +dependencies = [ + "bitflags 2.9.1", + "errno", + "libc", + "linux-raw-sys 0.9.4", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.9.1", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.141" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "simple_logger" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c5dfa5e08767553704aa0ffd9d9794d527103c736aba9854773851fd7497eb" +dependencies = [ + "colored", + "log", + "time", + "windows-sys 0.48.0", +] + +[[package]] +name = "slab" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +dependencies = [ + "serde", +] + +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlx" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" +dependencies = [ + "async-io 1.13.0", + "async-std", + "base64", + "bytes", + "crc", + "crossbeam-queue", + "either", + "event-listener 5.4.0", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown", + "hashlink", + "indexmap", + "log", + "memchr", + "native-tls", + "once_cell", + "percent-encoding", + "serde", + "serde_json", + "sha2", + "smallvec", + "thiserror", + "tracing", + "url", +] + +[[package]] +name = "sqlx-macros" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b" +dependencies = [ + "async-std", + "dotenvy", + "either", + "heck", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn", + "url", +] + +[[package]] +name = "sqlx-mysql" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" +dependencies = [ + "atoi", + "base64", + "bitflags 2.9.1", + "byteorder", + "bytes", + "crc", + "digest", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "percent-encoding", + "rand", + "rsa", + "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" +dependencies = [ + "atoi", + "base64", + "bitflags 2.9.1", + "byteorder", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "rand", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" +dependencies = [ + "atoi", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "serde_urlencoded", + "sqlx-core", + "thiserror", + "tracing", + "url", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tempfile" +version = "3.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +dependencies = [ + "fastrand 2.3.0", + "getrandom 0.3.3", + "once_cell", + "rustix 1.0.8", + "windows-sys 0.59.0", +] + +[[package]] +name = "test" +version = "0.0.1" +dependencies = [ + "futures", + "log", + "log_err", + "simple_logger", + "sqlx", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +dependencies = [ + "deranged", + "itoa", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" dependencies = [ - "lazy_static", - "windows-sys 0.59.0", + "tinyvec_macros", ] [[package]] -name = "deranged" -version = "0.4.0" +name = "tinyvec_macros" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ - "powerfmt", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", ] [[package]] -name = "itoa" -version = "1.0.15" +name = "tracing-attributes" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] -name = "lazy_static" -version = "1.5.0" +name = "tracing-core" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", +] [[package]] -name = "libc" -version = "0.2.173" +name = "typenum" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] -name = "log" -version = "0.4.27" +name = "unicode-bidi" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] -name = "log_err" -version = "1.1.1" +name = "unicode-ident" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2829e80508da18227e53a791bbe19018306f3f400bba439bc9959e9faa860f7" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ - "log", + "tinyvec", ] [[package]] -name = "num-conv" -version = "0.1.0" +name = "unicode-properties" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" [[package]] -name = "num_threads" -version = "0.1.7" +name = "url" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ - "libc", + "form_urlencoded", + "idna", + "percent-encoding", ] [[package]] -name = "powerfmt" -version = "0.2.0" +name = "utf8_iter" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] -name = "proc-macro2" -version = "1.0.95" +name = "value-bag" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" -dependencies = [ - "unicode-ident", -] +checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5" [[package]] -name = "quote" -version = "1.0.40" +name = "vcpkg" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "waker-fn" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ - "proc-macro2", + "wit-bindgen-rt", ] [[package]] -name = "serde" -version = "1.0.219" +name = "wasite" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ - "serde_derive", + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", ] [[package]] -name = "serde_derive" -version = "1.0.219" +name = "wasm-bindgen-backend" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ + "bumpalo", + "log", "proc-macro2", "quote", "syn", + "wasm-bindgen-shared", ] [[package]] -name = "simple_logger" -version = "5.0.0" +name = "wasm-bindgen-futures" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c5dfa5e08767553704aa0ffd9d9794d527103c736aba9854773851fd7497eb" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ - "colored", - "log", - "time", - "windows-sys 0.48.0", + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", ] [[package]] -name = "syn" -version = "2.0.103" +name = "wasm-bindgen-macro" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "unicode-ident", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", ] [[package]] -name = "test" -version = "0.0.1" +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" dependencies = [ - "log", - "log_err", - "simple_logger", + "unicode-ident", ] [[package]] -name = "time" -version = "0.3.41" +name = "web-sys" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ - "deranged", - "itoa", - "libc", - "num-conv", - "num_threads", - "powerfmt", - "serde", - "time-core", - "time-macros", + "js-sys", + "wasm-bindgen", ] [[package]] -name = "time-core" -version = "0.1.4" +name = "whoami" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" +dependencies = [ + "redox_syscall", + "wasite", +] [[package]] -name = "time-macros" -version = "0.2.22" +name = "winapi" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ - "num-conv", - "time-core", + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", ] [[package]] -name = "unicode-ident" -version = "1.0.18" +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-sys" @@ -202,6 +2186,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -226,13 +2219,30 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -245,6 +2255,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -257,6 +2273,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -269,12 +2291,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -287,6 +2321,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -299,6 +2339,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -311,6 +2357,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -322,3 +2374,128 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] From 5c64d4e9b743fbf591463a1d8cbe234506c5dbde Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 26 Jun 2025 08:28:01 +0100 Subject: [PATCH 037/208] Rust: Query framework. --- .../security/CWE-312/CleartextStorageDatabase.ql | 16 ++++++++++++++++ .../CWE-312/CleartextStorageDatabase.expected | 1 + .../CWE-312/CleartextStorageDatabase.qlref | 4 ++++ .../query-tests/security/CWE-312/test_logging.rs | 12 ++++++------ 4 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 rust/ql/src/queries/security/CWE-312/CleartextStorageDatabase.ql create mode 100644 rust/ql/test/query-tests/security/CWE-312/CleartextStorageDatabase.expected create mode 100644 rust/ql/test/query-tests/security/CWE-312/CleartextStorageDatabase.qlref diff --git a/rust/ql/src/queries/security/CWE-312/CleartextStorageDatabase.ql b/rust/ql/src/queries/security/CWE-312/CleartextStorageDatabase.ql new file mode 100644 index 000000000000..13469455a681 --- /dev/null +++ b/rust/ql/src/queries/security/CWE-312/CleartextStorageDatabase.ql @@ -0,0 +1,16 @@ +/** + * @name Cleartext storage of sensitive information in a database + * @description Storing sensitive information in a non-encrypted + * database can expose it to an attacker. + * @kind path-problem + * @problem.severity warning + * @security-severity TODO + * @precision high + * @id rust/cleartext-storage-database + * @tags security + * external/cwe/cwe-312 + */ + +import rust + +select 0 diff --git a/rust/ql/test/query-tests/security/CWE-312/CleartextStorageDatabase.expected b/rust/ql/test/query-tests/security/CWE-312/CleartextStorageDatabase.expected new file mode 100644 index 000000000000..f082a67fcf66 --- /dev/null +++ b/rust/ql/test/query-tests/security/CWE-312/CleartextStorageDatabase.expected @@ -0,0 +1 @@ +| 0 | diff --git a/rust/ql/test/query-tests/security/CWE-312/CleartextStorageDatabase.qlref b/rust/ql/test/query-tests/security/CWE-312/CleartextStorageDatabase.qlref new file mode 100644 index 000000000000..54f4c6b10e2e --- /dev/null +++ b/rust/ql/test/query-tests/security/CWE-312/CleartextStorageDatabase.qlref @@ -0,0 +1,4 @@ +query: queries/security/CWE-312/CleartextStorageDatabase.ql +postprocess: + - utils/test/InlineExpectationsTestQuery.ql + - utils/test/PrettyPrintModels.ql diff --git a/rust/ql/test/query-tests/security/CWE-312/test_logging.rs b/rust/ql/test/query-tests/security/CWE-312/test_logging.rs index 4b12005a6cb5..02451c92d915 100644 --- a/rust/ql/test/query-tests/security/CWE-312/test_logging.rs +++ b/rust/ql/test/query-tests/security/CWE-312/test_logging.rs @@ -90,13 +90,13 @@ fn test_log(harmless: String, password: String, encrypted_password: String) { error!(value2:?; "message"); // $ MISSING: Alert[rust/cleartext-logging] // pre-formatted - let m1 = &password; // $ Source=m1 + let m1 = &password; // $ Source[rust/cleartext-logging]=m1 info!("message = {}", m1); // $ Alert[rust/cleartext-logging]=m1 - let m2 = "message = ".to_string() + &password; // $ Source=m2 + let m2 = "message = ".to_string() + &password; // $ Source[rust/cleartext-logging]=m2 info!("{}", m2); // $ Alert[rust/cleartext-logging]=m2 - let m3 = format!("message = {}", password); // $ Source=m3 + let m3 = format!("message = {}", password); // $ Source[rust/cleartext-logging]=m3 info!("{}", m3); // $ Alert[rust/cleartext-logging]=m3 let mut m4 = String::new(); @@ -126,7 +126,7 @@ fn test_log(harmless: String, password: String, encrypted_password: String) { trace!("message = {}", &str2); // logging from a tuple - let t1 = (harmless, password); // $ Source=t1 + let t1 = (harmless, password); // $ Source[rust/cleartext-logging]=t1 trace!("message = {}", t1.0); trace!("message = {}", t1.1); // $ Alert[rust/cleartext-logging]=t1 trace!("message = {:?}", t1); // $ MISSING: Alert[rust/cleartext-logging]=t1 @@ -180,11 +180,11 @@ fn test_log(harmless: String, password: String, encrypted_password: String) { let _ = err_result.log_expect(&format!("Failed with password: {}", password2)); // $ Alert[rust/cleartext-logging] // test `log_expect` with sensitive `Result.Err` - let err_result2: Result = Err(password2.clone()); // $ Source=s3 + let err_result2: Result = Err(password2.clone()); // $ Source[rust/cleartext-logging]=s3 let _ = err_result2.log_expect(""); // $ Alert[rust/cleartext-logging]=s3 // test `log_unwrap` with sensitive `Result.Err` - let err_result3: Result = Err(password2); // $ Source=err_result3 + let err_result3: Result = Err(password2); // $ Source[rust/cleartext-logging]=err_result3 let _ = err_result3.log_unwrap(); // $ Alert[rust/cleartext-logging]=err_result3 } From a3110a9091764211831d77a787cd6c60216d860a Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 26 Jun 2025 10:20:49 +0100 Subject: [PATCH 038/208] Rust: Implement query. --- .../lib/codeql/rust/frameworks/sqlx.model.yml | 13 +++ .../CleartextStorageDatabaseExtensions.qll | 46 ++++++++++ .../CWE-312/CleartextStorageDatabase.ql | 41 ++++++++- .../CWE-312/CleartextStorageDatabase.expected | 92 ++++++++++++++++++- .../security/CWE-312/test_storage.rs | 12 +-- 5 files changed, 196 insertions(+), 8 deletions(-) create mode 100644 rust/ql/lib/codeql/rust/frameworks/sqlx.model.yml create mode 100644 rust/ql/lib/codeql/rust/security/CleartextStorageDatabaseExtensions.qll diff --git a/rust/ql/lib/codeql/rust/frameworks/sqlx.model.yml b/rust/ql/lib/codeql/rust/frameworks/sqlx.model.yml new file mode 100644 index 000000000000..ec6f83d5340a --- /dev/null +++ b/rust/ql/lib/codeql/rust/frameworks/sqlx.model.yml @@ -0,0 +1,13 @@ +extensions: + - addsTo: + pack: codeql/rust-all + extensible: sinkModel + data: + - ["sqlx_core::query::query", "Argument[0]", "database-store", "manual"] + - ["sqlx_core::query_as::query_as", "Argument[0]", "database-store", "manual"] + - ["sqlx_core::query_with::query_with", "Argument[0]", "database-store", "manual"] + - ["sqlx_core::query_as_with::query_as_with", "Argument[0]", "database-store", "manual"] + - ["sqlx_core::query_scalar::query_scalar", "Argument[0]", "database-store", "manual"] + - ["sqlx_core::query_scalar_with::query_scalar_with", "Argument[0]", "database-store", "manual"] + - ["sqlx_core::raw_sql::raw_sql", "Argument[0]", "database-store", "manual"] + - ["<_ as sqlx_core::executor::Executor>::execute", "Argument[0]", "database-store", "manual"] diff --git a/rust/ql/lib/codeql/rust/security/CleartextStorageDatabaseExtensions.qll b/rust/ql/lib/codeql/rust/security/CleartextStorageDatabaseExtensions.qll new file mode 100644 index 000000000000..32c4d0627bd2 --- /dev/null +++ b/rust/ql/lib/codeql/rust/security/CleartextStorageDatabaseExtensions.qll @@ -0,0 +1,46 @@ +/** + * Provides classes and predicates for reasoning about cleartext storage + * of sensitive information in a database. + */ + +import rust +private import codeql.rust.dataflow.DataFlow +private import codeql.rust.dataflow.internal.DataFlowImpl +private import codeql.rust.security.SensitiveData +private import codeql.rust.Concepts + +/** + * Provides default sources, sinks and barriers for detecting cleartext storage + * of sensitive information in a database, as well as extension points for + * adding your own. + */ +module CleartextStorageDatabase { + /** + * A data flow source for cleartext storage vulnerabilities. + */ + abstract class Source extends DataFlow::Node { } + + /** + * A data flow sink for cleartext storage vulnerabilities. + */ + abstract class Sink extends QuerySink::Range { + override string getSinkType() { result = "CleartextStorageDatabase" } + } + + /** + * A barrier for cleartext storage vulnerabilities. + */ + abstract class Barrier extends DataFlow::Node { } + + /** + * Sensitive data, considered as a flow source. + */ + private class SensitiveDataAsSource extends Source instanceof SensitiveData { } + + /** + * A sink for cleartext storage vulnerabilities from model data. + */ + private class ModelsAsDataSink extends Sink { + ModelsAsDataSink() { exists(string s | sinkNode(this, s) and s.matches("database-store")) } + } +} diff --git a/rust/ql/src/queries/security/CWE-312/CleartextStorageDatabase.ql b/rust/ql/src/queries/security/CWE-312/CleartextStorageDatabase.ql index 13469455a681..00874437c128 100644 --- a/rust/ql/src/queries/security/CWE-312/CleartextStorageDatabase.ql +++ b/rust/ql/src/queries/security/CWE-312/CleartextStorageDatabase.ql @@ -12,5 +12,44 @@ */ import rust +import codeql.rust.dataflow.DataFlow +import codeql.rust.dataflow.TaintTracking +import codeql.rust.security.CleartextStorageDatabaseExtensions -select 0 +/** + * A taint configuration from sensitive information to expressions that are + * stored in a database. + */ +module CleartextStorageDatabaseConfig implements DataFlow::ConfigSig { + import CleartextStorageDatabase + + predicate isSource(DataFlow::Node node) { node instanceof Source } + + predicate isSink(DataFlow::Node node) { node instanceof Sink } + + predicate isBarrier(DataFlow::Node barrier) { barrier instanceof Barrier } + + predicate isBarrierIn(DataFlow::Node node) { + // make sources barriers so that we only report the closest instance + isSource(node) + } + + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + // flow from `a` to `&a` + node2.asExpr().getExpr().(RefExpr).getExpr() = node1.asExpr().getExpr() + } + + predicate observeDiffInformedIncrementalMode() { any() } +} + +module CleartextStorageDatabaseFlow = TaintTracking::Global; + +import CleartextStorageDatabaseFlow::PathGraph + +from + CleartextStorageDatabaseFlow::PathNode sourceNode, CleartextStorageDatabaseFlow::PathNode sinkNode +where CleartextStorageDatabaseFlow::flowPath(sourceNode, sinkNode) +select sinkNode, sourceNode, sinkNode, + "This operation stores '" + sinkNode.toString() + + "' in a database. It may contain unencrypted sensitive data from $@.", sourceNode, + sourceNode.getNode().toString() diff --git a/rust/ql/test/query-tests/security/CWE-312/CleartextStorageDatabase.expected b/rust/ql/test/query-tests/security/CWE-312/CleartextStorageDatabase.expected index f082a67fcf66..3491a55bbcbb 100644 --- a/rust/ql/test/query-tests/security/CWE-312/CleartextStorageDatabase.expected +++ b/rust/ql/test/query-tests/security/CWE-312/CleartextStorageDatabase.expected @@ -1 +1,91 @@ -| 0 | +#select +| test_storage.rs:62:13:62:23 | ...::query | test_storage.rs:33:97:33:114 | get_phone_number(...) | test_storage.rs:62:13:62:23 | ...::query | This operation stores '...::query' in a database. It may contain unencrypted sensitive data from $@. | test_storage.rs:33:97:33:114 | get_phone_number(...) | get_phone_number(...) | +| test_storage.rs:77:13:77:25 | ...::raw_sql | test_storage.rs:33:97:33:114 | get_phone_number(...) | test_storage.rs:77:13:77:25 | ...::raw_sql | This operation stores '...::raw_sql' in a database. It may contain unencrypted sensitive data from $@. | test_storage.rs:33:97:33:114 | get_phone_number(...) | get_phone_number(...) | +| test_storage.rs:81:13:81:23 | ...::query | test_storage.rs:33:97:33:114 | get_phone_number(...) | test_storage.rs:81:13:81:23 | ...::query | This operation stores '...::query' in a database. It may contain unencrypted sensitive data from $@. | test_storage.rs:33:97:33:114 | get_phone_number(...) | get_phone_number(...) | +| test_storage.rs:87:13:87:23 | ...::query | test_storage.rs:33:97:33:114 | get_phone_number(...) | test_storage.rs:87:13:87:23 | ...::query | This operation stores '...::query' in a database. It may contain unencrypted sensitive data from $@. | test_storage.rs:33:97:33:114 | get_phone_number(...) | get_phone_number(...) | +| test_storage.rs:101:13:101:23 | ...::query | test_storage.rs:33:97:33:114 | get_phone_number(...) | test_storage.rs:101:13:101:23 | ...::query | This operation stores '...::query' in a database. It may contain unencrypted sensitive data from $@. | test_storage.rs:33:97:33:114 | get_phone_number(...) | get_phone_number(...) | +edges +| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:62:25:62:37 | insert_query2 | provenance | | +| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:62:25:62:46 | insert_query2.as_str() | provenance | MaD:5 | +| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:62:25:62:46 | insert_query2.as_str() | provenance | MaD:4 | +| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:62:25:62:46 | insert_query2.as_str() | provenance | MaD:5 | +| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:77:27:77:39 | insert_query2 | provenance | | +| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:77:27:77:48 | insert_query2.as_str() | provenance | MaD:5 | +| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:77:27:77:48 | insert_query2.as_str() | provenance | MaD:4 | +| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:77:27:77:48 | insert_query2.as_str() | provenance | MaD:5 | +| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:81:25:81:37 | insert_query2 | provenance | | +| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:81:25:81:46 | insert_query2.as_str() | provenance | MaD:5 | +| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:81:25:81:46 | insert_query2.as_str() | provenance | MaD:4 | +| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:81:25:81:46 | insert_query2.as_str() | provenance | MaD:5 | +| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:87:25:87:37 | insert_query2 | provenance | | +| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:87:25:87:46 | insert_query2.as_str() | provenance | MaD:5 | +| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:87:25:87:46 | insert_query2.as_str() | provenance | MaD:4 | +| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:87:25:87:46 | insert_query2.as_str() | provenance | MaD:5 | +| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:101:25:101:37 | insert_query2 | provenance | | +| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:101:25:101:46 | insert_query2.as_str() | provenance | MaD:5 | +| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:101:25:101:46 | insert_query2.as_str() | provenance | MaD:4 | +| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:101:25:101:46 | insert_query2.as_str() | provenance | MaD:5 | +| test_storage.rs:33:25:33:114 | ... + ... | test_storage.rs:33:9:33:21 | insert_query2 | provenance | | +| test_storage.rs:33:25:33:114 | ... + ... | test_storage.rs:33:25:33:121 | ... + ... | provenance | MaD:3 | +| test_storage.rs:33:25:33:121 | ... + ... | test_storage.rs:33:9:33:21 | insert_query2 | provenance | | +| test_storage.rs:33:96:33:114 | &... | test_storage.rs:33:9:33:21 | insert_query2 | provenance | | +| test_storage.rs:33:96:33:114 | &... | test_storage.rs:33:25:33:114 | ... + ... | provenance | | +| test_storage.rs:33:97:33:114 | get_phone_number(...) | test_storage.rs:33:96:33:114 | &... | provenance | Config | +| test_storage.rs:62:25:62:37 | insert_query2 | test_storage.rs:62:25:62:46 | insert_query2.as_str() [&ref] | provenance | MaD:5 | +| test_storage.rs:62:25:62:37 | insert_query2 | test_storage.rs:62:25:62:46 | insert_query2.as_str() [&ref] | provenance | MaD:4 | +| test_storage.rs:62:25:62:37 | insert_query2 | test_storage.rs:62:25:62:46 | insert_query2.as_str() [&ref] | provenance | MaD:5 | +| test_storage.rs:62:25:62:46 | insert_query2.as_str() | test_storage.rs:62:13:62:23 | ...::query | provenance | MaD:1 Sink:MaD:1 | +| test_storage.rs:62:25:62:46 | insert_query2.as_str() [&ref] | test_storage.rs:62:13:62:23 | ...::query | provenance | MaD:1 Sink:MaD:1 | +| test_storage.rs:77:27:77:39 | insert_query2 | test_storage.rs:77:27:77:48 | insert_query2.as_str() [&ref] | provenance | MaD:5 | +| test_storage.rs:77:27:77:39 | insert_query2 | test_storage.rs:77:27:77:48 | insert_query2.as_str() [&ref] | provenance | MaD:4 | +| test_storage.rs:77:27:77:39 | insert_query2 | test_storage.rs:77:27:77:48 | insert_query2.as_str() [&ref] | provenance | MaD:5 | +| test_storage.rs:77:27:77:48 | insert_query2.as_str() | test_storage.rs:77:13:77:25 | ...::raw_sql | provenance | MaD:2 Sink:MaD:2 | +| test_storage.rs:77:27:77:48 | insert_query2.as_str() [&ref] | test_storage.rs:77:13:77:25 | ...::raw_sql | provenance | MaD:2 Sink:MaD:2 | +| test_storage.rs:81:25:81:37 | insert_query2 | test_storage.rs:81:25:81:46 | insert_query2.as_str() [&ref] | provenance | MaD:5 | +| test_storage.rs:81:25:81:37 | insert_query2 | test_storage.rs:81:25:81:46 | insert_query2.as_str() [&ref] | provenance | MaD:4 | +| test_storage.rs:81:25:81:37 | insert_query2 | test_storage.rs:81:25:81:46 | insert_query2.as_str() [&ref] | provenance | MaD:5 | +| test_storage.rs:81:25:81:46 | insert_query2.as_str() | test_storage.rs:81:13:81:23 | ...::query | provenance | MaD:1 Sink:MaD:1 | +| test_storage.rs:81:25:81:46 | insert_query2.as_str() [&ref] | test_storage.rs:81:13:81:23 | ...::query | provenance | MaD:1 Sink:MaD:1 | +| test_storage.rs:87:25:87:37 | insert_query2 | test_storage.rs:87:25:87:46 | insert_query2.as_str() [&ref] | provenance | MaD:5 | +| test_storage.rs:87:25:87:37 | insert_query2 | test_storage.rs:87:25:87:46 | insert_query2.as_str() [&ref] | provenance | MaD:4 | +| test_storage.rs:87:25:87:37 | insert_query2 | test_storage.rs:87:25:87:46 | insert_query2.as_str() [&ref] | provenance | MaD:5 | +| test_storage.rs:87:25:87:46 | insert_query2.as_str() | test_storage.rs:87:13:87:23 | ...::query | provenance | MaD:1 Sink:MaD:1 | +| test_storage.rs:87:25:87:46 | insert_query2.as_str() [&ref] | test_storage.rs:87:13:87:23 | ...::query | provenance | MaD:1 Sink:MaD:1 | +| test_storage.rs:101:25:101:37 | insert_query2 | test_storage.rs:101:25:101:46 | insert_query2.as_str() [&ref] | provenance | MaD:5 | +| test_storage.rs:101:25:101:37 | insert_query2 | test_storage.rs:101:25:101:46 | insert_query2.as_str() [&ref] | provenance | MaD:4 | +| test_storage.rs:101:25:101:37 | insert_query2 | test_storage.rs:101:25:101:46 | insert_query2.as_str() [&ref] | provenance | MaD:5 | +| test_storage.rs:101:25:101:46 | insert_query2.as_str() | test_storage.rs:101:13:101:23 | ...::query | provenance | MaD:1 Sink:MaD:1 | +| test_storage.rs:101:25:101:46 | insert_query2.as_str() [&ref] | test_storage.rs:101:13:101:23 | ...::query | provenance | MaD:1 Sink:MaD:1 | +models +| 1 | Sink: sqlx_core::query::query; Argument[0]; database-store | +| 2 | Sink: sqlx_core::raw_sql::raw_sql; Argument[0]; database-store | +| 3 | Summary: ::add; Argument[self]; ReturnValue; value | +| 4 | Summary: ::as_str; Argument[self]; ReturnValue; value | +| 5 | Summary: ::as_str; Argument[self]; ReturnValue; value | +nodes +| test_storage.rs:33:9:33:21 | insert_query2 | semmle.label | insert_query2 | +| test_storage.rs:33:25:33:114 | ... + ... | semmle.label | ... + ... | +| test_storage.rs:33:25:33:121 | ... + ... | semmle.label | ... + ... | +| test_storage.rs:33:96:33:114 | &... | semmle.label | &... | +| test_storage.rs:33:97:33:114 | get_phone_number(...) | semmle.label | get_phone_number(...) | +| test_storage.rs:62:13:62:23 | ...::query | semmle.label | ...::query | +| test_storage.rs:62:25:62:37 | insert_query2 | semmle.label | insert_query2 | +| test_storage.rs:62:25:62:46 | insert_query2.as_str() | semmle.label | insert_query2.as_str() | +| test_storage.rs:62:25:62:46 | insert_query2.as_str() [&ref] | semmle.label | insert_query2.as_str() [&ref] | +| test_storage.rs:77:13:77:25 | ...::raw_sql | semmle.label | ...::raw_sql | +| test_storage.rs:77:27:77:39 | insert_query2 | semmle.label | insert_query2 | +| test_storage.rs:77:27:77:48 | insert_query2.as_str() | semmle.label | insert_query2.as_str() | +| test_storage.rs:77:27:77:48 | insert_query2.as_str() [&ref] | semmle.label | insert_query2.as_str() [&ref] | +| test_storage.rs:81:13:81:23 | ...::query | semmle.label | ...::query | +| test_storage.rs:81:25:81:37 | insert_query2 | semmle.label | insert_query2 | +| test_storage.rs:81:25:81:46 | insert_query2.as_str() | semmle.label | insert_query2.as_str() | +| test_storage.rs:81:25:81:46 | insert_query2.as_str() [&ref] | semmle.label | insert_query2.as_str() [&ref] | +| test_storage.rs:87:13:87:23 | ...::query | semmle.label | ...::query | +| test_storage.rs:87:25:87:37 | insert_query2 | semmle.label | insert_query2 | +| test_storage.rs:87:25:87:46 | insert_query2.as_str() | semmle.label | insert_query2.as_str() | +| test_storage.rs:87:25:87:46 | insert_query2.as_str() [&ref] | semmle.label | insert_query2.as_str() [&ref] | +| test_storage.rs:101:13:101:23 | ...::query | semmle.label | ...::query | +| test_storage.rs:101:25:101:37 | insert_query2 | semmle.label | insert_query2 | +| test_storage.rs:101:25:101:46 | insert_query2.as_str() | semmle.label | insert_query2.as_str() | +| test_storage.rs:101:25:101:46 | insert_query2.as_str() [&ref] | semmle.label | insert_query2.as_str() [&ref] | +subpaths diff --git a/rust/ql/test/query-tests/security/CWE-312/test_storage.rs b/rust/ql/test/query-tests/security/CWE-312/test_storage.rs index 0c5f7c4c62b1..26680384ccb3 100644 --- a/rust/ql/test/query-tests/security/CWE-312/test_storage.rs +++ b/rust/ql/test/query-tests/security/CWE-312/test_storage.rs @@ -30,7 +30,7 @@ async fn test_storage_sql_command(url: &str) -> Result<(), sqlx::Error> { let select_query1 = String::from("SELECT * FROM CONTACTS WHERE ID = ") + id; let select_query2 = String::from("SELECT * FROM CONTACTS WHERE SSN = '") + &get_social_security_number() + "'"; let insert_query1 = String::from("INSERT INTO CONTACTS(ID, HARMLESS) VALUES(") + id + ", '" + &get_harmless() + "')"; - let insert_query2 = String::from("INSERT INTO CONTACTS(ID, PHONE) VALUES(") + id + ", '" + &get_phone_number() + "')"; + let insert_query2 = String::from("INSERT INTO CONTACTS(ID, PHONE) VALUES(") + id + ", '" + &get_phone_number() + "')"; // $ Source[rust/cleartext-storage-database] let update_query1 = String::from("UPDATE CONTACTS SET HARMLESS='") + &get_harmless() + "' WHERE ID=" + id; let update_query2 = String::from("UPDATE CONTACTS SET EMAIL='") + &get_email() + "' WHERE ID=" + id; let s1 = &get_social_security_number(); @@ -59,7 +59,7 @@ async fn test_storage_sql_command(url: &str) -> Result<(), sqlx::Error> { // execute queries - MySQL, prepared query let _ = sqlx::query(insert_query1.as_str()).execute(&pool1).await?; - let _ = sqlx::query(insert_query2.as_str()).execute(&pool1).await?; // $ MISSING: Alert[rust/cleartext-storage-database] + let _ = sqlx::query(insert_query2.as_str()).execute(&pool1).await?; // $ Alert[rust/cleartext-storage-database] let _ = sqlx::query(prepared_query.as_str()).bind(get_harmless()).execute(&pool1).await?; let _ = sqlx::query(prepared_query.as_str()).bind(get_social_security_number()).execute(&pool1).await?; // $ MISSING: Alert[rust/cleartext-storage-database] let _ = sqlx::query(prepared_query.as_str()).bind(&s1).execute(&pool1).await?; // $ MISSING: Alert[rust/cleartext-storage-database] @@ -74,17 +74,17 @@ async fn test_storage_sql_command(url: &str) -> Result<(), sqlx::Error> { // execute queries - SQLite, direct variant let _ = sqlx::raw_sql(insert_query1.as_str()).execute(&mut conn2).await?; - let _ = sqlx::raw_sql(insert_query2.as_str()).execute(&mut conn2).await?; // $ MISSING: Alert[rust/cleartext-storage-database] + let _ = sqlx::raw_sql(insert_query2.as_str()).execute(&mut conn2).await?; // $ Alert[rust/cleartext-storage-database] // execute queries - SQLite, prepared query let _ = sqlx::query(insert_query1.as_str()).execute(&mut conn2).await?; - let _ = sqlx::query(insert_query2.as_str()).execute(&mut conn2).await?; // $ MISSING: Alert[rust/cleartext-storage-database] + let _ = sqlx::query(insert_query2.as_str()).execute(&mut conn2).await?; // $ Alert[rust/cleartext-storage-database] let _ = sqlx::query(prepared_query.as_str()).bind(get_harmless()).execute(&mut conn2).await?; let _ = sqlx::query(prepared_query.as_str()).bind(get_social_security_number()).execute(&mut conn2).await?; // $ MISSING: Alert[rust/cleartext-storage-database] // execute queries - SQLite, prepared query variant let _ = sqlx::query(insert_query1.as_str()).fetch(&mut conn2); - let _ = sqlx::query(insert_query2.as_str()).fetch(&mut conn2); // $ MISSING: Alert[rust/cleartext-storage-database] + let _ = sqlx::query(insert_query2.as_str()).fetch(&mut conn2); // $ Alert[rust/cleartext-storage-database] let _ = sqlx::query(prepared_query.as_str()).bind(get_harmless()).fetch(&mut conn2); let _ = sqlx::query(prepared_query.as_str()).bind(get_social_security_number()).fetch(&mut conn2); // $ MISSING: Alert[rust/cleartext-storage-database] @@ -98,7 +98,7 @@ async fn test_storage_sql_command(url: &str) -> Result<(), sqlx::Error> { // execute queries - PostgreSQL, prepared query let _ = sqlx::query(insert_query1.as_str()).execute(&pool3).await?; - let _ = sqlx::query(insert_query2.as_str()).execute(&pool3).await?; // $ MISSING: Alert[rust/cleartext-storage-database] + let _ = sqlx::query(insert_query2.as_str()).execute(&pool3).await?; // $ Alert[rust/cleartext-storage-database] let _ = sqlx::query(prepared_query.as_str()).bind(get_harmless()).execute(&pool3).await?; let _ = sqlx::query(prepared_query.as_str()).bind(get_social_security_number()).execute(&pool3).await?; // $ MISSING: Alert[rust/cleartext-storage-database] From e585e677c843afa0fec43c83421345e9683973ed Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 28 Jul 2025 17:08:19 +0100 Subject: [PATCH 039/208] Rust: Add qhelp and examples. --- .../CWE-312/CleartextStorageDatabase.qhelp | 53 +++++++++++++++++++ .../CWE-312/CleartextStorageDatabaseBad.rs | 6 +++ .../CWE-312/CleartextStorageDatabaseGood.rs | 42 +++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 rust/ql/src/queries/security/CWE-312/CleartextStorageDatabase.qhelp create mode 100644 rust/ql/src/queries/security/CWE-312/CleartextStorageDatabaseBad.rs create mode 100644 rust/ql/src/queries/security/CWE-312/CleartextStorageDatabaseGood.rs diff --git a/rust/ql/src/queries/security/CWE-312/CleartextStorageDatabase.qhelp b/rust/ql/src/queries/security/CWE-312/CleartextStorageDatabase.qhelp new file mode 100644 index 000000000000..595de607c3e5 --- /dev/null +++ b/rust/ql/src/queries/security/CWE-312/CleartextStorageDatabase.qhelp @@ -0,0 +1,53 @@ + + + + +

+Sensitive information that is stored unencrypted in a database is accessible to an attacker +who gains access to that database. For example, the information could be accessed by any +process or user in a rooted device, or exposed through another vulnerability. +

+
+ + +

+Either encrypt the entire database, or ensure that each piece of sensitive information is +encrypted before being stored. In general, decrypt sensitive information only at the point +where it is necessary for it to be used in cleartext. Avoid storing sensitive information +at all if you do not need to keep it. +

+
+ + +

+The following example stores sensitive information into a database without encryption, using the +SQLx library: +

+ +

+This is insecure because the sensitive data is stored in cleartext, making it accessible to anyone +with access to the database. +

+

+To fix this, we can either encrypt the entire database or encrypt just the sensitive data before it +is stored. Take care to select a secure modern encryption algorithm and put suitable key management +practices into place. In the following example, we have encrypted the sensitive data using 256-bit +AES before storing it in the database: +

+ +
+ + +
  • + OWASP Top 10:2021: + A02:2021 - Cryptographic Failures. +
  • +
  • + OWASP: + Key Management Cheat Sheet. +
  • +
    + +
    diff --git a/rust/ql/src/queries/security/CWE-312/CleartextStorageDatabaseBad.rs b/rust/ql/src/queries/security/CWE-312/CleartextStorageDatabaseBad.rs new file mode 100644 index 000000000000..cd97f238683f --- /dev/null +++ b/rust/ql/src/queries/security/CWE-312/CleartextStorageDatabaseBad.rs @@ -0,0 +1,6 @@ +let query = "INSERT INTO PAYMENTDETAILS(ID, CARDNUM) VALUES(?, ?)"; +let result = sqlx::query(query) + .bind(id) + .bind(credit_card_number) // BAD: Cleartext storage of sensitive data in the database + .execute(pool) + .await?; diff --git a/rust/ql/src/queries/security/CWE-312/CleartextStorageDatabaseGood.rs b/rust/ql/src/queries/security/CWE-312/CleartextStorageDatabaseGood.rs new file mode 100644 index 000000000000..8244738d5c35 --- /dev/null +++ b/rust/ql/src/queries/security/CWE-312/CleartextStorageDatabaseGood.rs @@ -0,0 +1,42 @@ + +fn encrypt(text: String, encryption_key: &aes_gcm::Key) -> String { + // encrypt text -> ciphertext + let cipher = Aes256Gcm::new(&encryption_key); + let nonce = Aes256Gcm::generate_nonce(&mut OsRng); + let ciphertext = cipher.encrypt(&nonce, text.as_ref()).unwrap(); + + // append (nonce, ciphertext) + let mut combined = nonce.to_vec(); + combined.extend(ciphertext); + + // encode to base64 string + BASE64_STANDARD.encode(combined) +} + +fn decrypt(data: String, encryption_key: &aes_gcm::Key) -> String { + let cipher = Aes256Gcm::new(&encryption_key); + + // decode base64 string + let decoded = BASE64_STANDARD.decode(data).unwrap(); + + // split into (nonce, ciphertext) + let nonce_size = ::NonceSize::to_usize(); + let (nonce, ciphertext) = decoded.split_at(nonce_size); + + // decrypt ciphertext -> plaintext + let plaintext = cipher.decrypt(nonce.into(), ciphertext).unwrap(); + String::from_utf8(plaintext).unwrap() +} + +... + +let encryption_key = Aes256Gcm::generate_key(OsRng); + +... + +let query = "INSERT INTO PAYMENTDETAILS(ID, CARDNUM) VALUES(?, ?)"; +let result = sqlx::query(query) + .bind(id) + .bind(encrypt(credit_card_number, &encryption_key)) // GOOD: Encrypted storage of sensitive data in the database + .execute(pool) + .await?; From 215fe7d0b3bd45db0fcc1497699c376589f5d2f2 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 29 Jul 2025 23:34:36 +0100 Subject: [PATCH 040/208] Rust: Clean up the alert message. --- .../security/CWE-312/CleartextStorageDatabase.ql | 5 ++--- .../security/CWE-312/CleartextStorageDatabase.expected | 10 +++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/rust/ql/src/queries/security/CWE-312/CleartextStorageDatabase.ql b/rust/ql/src/queries/security/CWE-312/CleartextStorageDatabase.ql index 00874437c128..12e5a41148b2 100644 --- a/rust/ql/src/queries/security/CWE-312/CleartextStorageDatabase.ql +++ b/rust/ql/src/queries/security/CWE-312/CleartextStorageDatabase.ql @@ -49,7 +49,6 @@ import CleartextStorageDatabaseFlow::PathGraph from CleartextStorageDatabaseFlow::PathNode sourceNode, CleartextStorageDatabaseFlow::PathNode sinkNode where CleartextStorageDatabaseFlow::flowPath(sourceNode, sinkNode) -select sinkNode, sourceNode, sinkNode, - "This operation stores '" + sinkNode.toString() + - "' in a database. It may contain unencrypted sensitive data from $@.", sourceNode, +select sinkNode.getNode(), sourceNode, sinkNode, + "This database operation may read or write unencrypted sensitive data from $@.", sourceNode, sourceNode.getNode().toString() diff --git a/rust/ql/test/query-tests/security/CWE-312/CleartextStorageDatabase.expected b/rust/ql/test/query-tests/security/CWE-312/CleartextStorageDatabase.expected index 3491a55bbcbb..a038f37b8753 100644 --- a/rust/ql/test/query-tests/security/CWE-312/CleartextStorageDatabase.expected +++ b/rust/ql/test/query-tests/security/CWE-312/CleartextStorageDatabase.expected @@ -1,9 +1,9 @@ #select -| test_storage.rs:62:13:62:23 | ...::query | test_storage.rs:33:97:33:114 | get_phone_number(...) | test_storage.rs:62:13:62:23 | ...::query | This operation stores '...::query' in a database. It may contain unencrypted sensitive data from $@. | test_storage.rs:33:97:33:114 | get_phone_number(...) | get_phone_number(...) | -| test_storage.rs:77:13:77:25 | ...::raw_sql | test_storage.rs:33:97:33:114 | get_phone_number(...) | test_storage.rs:77:13:77:25 | ...::raw_sql | This operation stores '...::raw_sql' in a database. It may contain unencrypted sensitive data from $@. | test_storage.rs:33:97:33:114 | get_phone_number(...) | get_phone_number(...) | -| test_storage.rs:81:13:81:23 | ...::query | test_storage.rs:33:97:33:114 | get_phone_number(...) | test_storage.rs:81:13:81:23 | ...::query | This operation stores '...::query' in a database. It may contain unencrypted sensitive data from $@. | test_storage.rs:33:97:33:114 | get_phone_number(...) | get_phone_number(...) | -| test_storage.rs:87:13:87:23 | ...::query | test_storage.rs:33:97:33:114 | get_phone_number(...) | test_storage.rs:87:13:87:23 | ...::query | This operation stores '...::query' in a database. It may contain unencrypted sensitive data from $@. | test_storage.rs:33:97:33:114 | get_phone_number(...) | get_phone_number(...) | -| test_storage.rs:101:13:101:23 | ...::query | test_storage.rs:33:97:33:114 | get_phone_number(...) | test_storage.rs:101:13:101:23 | ...::query | This operation stores '...::query' in a database. It may contain unencrypted sensitive data from $@. | test_storage.rs:33:97:33:114 | get_phone_number(...) | get_phone_number(...) | +| test_storage.rs:62:13:62:23 | ...::query | test_storage.rs:33:97:33:114 | get_phone_number(...) | test_storage.rs:62:13:62:23 | ...::query | This database operation may read or write unencrypted sensitive data from $@. | test_storage.rs:33:97:33:114 | get_phone_number(...) | get_phone_number(...) | +| test_storage.rs:77:13:77:25 | ...::raw_sql | test_storage.rs:33:97:33:114 | get_phone_number(...) | test_storage.rs:77:13:77:25 | ...::raw_sql | This database operation may read or write unencrypted sensitive data from $@. | test_storage.rs:33:97:33:114 | get_phone_number(...) | get_phone_number(...) | +| test_storage.rs:81:13:81:23 | ...::query | test_storage.rs:33:97:33:114 | get_phone_number(...) | test_storage.rs:81:13:81:23 | ...::query | This database operation may read or write unencrypted sensitive data from $@. | test_storage.rs:33:97:33:114 | get_phone_number(...) | get_phone_number(...) | +| test_storage.rs:87:13:87:23 | ...::query | test_storage.rs:33:97:33:114 | get_phone_number(...) | test_storage.rs:87:13:87:23 | ...::query | This database operation may read or write unencrypted sensitive data from $@. | test_storage.rs:33:97:33:114 | get_phone_number(...) | get_phone_number(...) | +| test_storage.rs:101:13:101:23 | ...::query | test_storage.rs:33:97:33:114 | get_phone_number(...) | test_storage.rs:101:13:101:23 | ...::query | This database operation may read or write unencrypted sensitive data from $@. | test_storage.rs:33:97:33:114 | get_phone_number(...) | get_phone_number(...) | edges | test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:62:25:62:37 | insert_query2 | provenance | | | test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:62:25:62:46 | insert_query2.as_str() | provenance | MaD:5 | From b6e60e4087ee2add537232320e14492a842ec492 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 30 Jul 2025 12:08:03 +0100 Subject: [PATCH 041/208] Rust: Address small bugs in the test. --- .../query-tests/security/CWE-312/test_storage.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rust/ql/test/query-tests/security/CWE-312/test_storage.rs b/rust/ql/test/query-tests/security/CWE-312/test_storage.rs index 26680384ccb3..d9b63418e47b 100644 --- a/rust/ql/test/query-tests/security/CWE-312/test_storage.rs +++ b/rust/ql/test/query-tests/security/CWE-312/test_storage.rs @@ -79,14 +79,14 @@ async fn test_storage_sql_command(url: &str) -> Result<(), sqlx::Error> { // execute queries - SQLite, prepared query let _ = sqlx::query(insert_query1.as_str()).execute(&mut conn2).await?; let _ = sqlx::query(insert_query2.as_str()).execute(&mut conn2).await?; // $ Alert[rust/cleartext-storage-database] - let _ = sqlx::query(prepared_query.as_str()).bind(get_harmless()).execute(&mut conn2).await?; - let _ = sqlx::query(prepared_query.as_str()).bind(get_social_security_number()).execute(&mut conn2).await?; // $ MISSING: Alert[rust/cleartext-storage-database] + let _ = sqlx::query(prepared_query.as_str()).bind(get_harmless()).bind(id).execute(&mut conn2).await?; + let _ = sqlx::query(prepared_query.as_str()).bind(get_social_security_number()).bind(id).execute(&mut conn2).await?; // $ MISSING: Alert[rust/cleartext-storage-database] // execute queries - SQLite, prepared query variant let _ = sqlx::query(insert_query1.as_str()).fetch(&mut conn2); let _ = sqlx::query(insert_query2.as_str()).fetch(&mut conn2); // $ Alert[rust/cleartext-storage-database] - let _ = sqlx::query(prepared_query.as_str()).bind(get_harmless()).fetch(&mut conn2); - let _ = sqlx::query(prepared_query.as_str()).bind(get_social_security_number()).fetch(&mut conn2); // $ MISSING: Alert[rust/cleartext-storage-database] + let _ = sqlx::query(prepared_query.as_str()).bind(get_harmless()).bind(id).fetch(&mut conn2); + let _ = sqlx::query(prepared_query.as_str()).bind(get_social_security_number()).bind(id).fetch(&mut conn2); // $ MISSING: Alert[rust/cleartext-storage-database] // connect through a PostgreSQL connection pool let pool3 = sqlx::postgres::PgPool::connect(url).await?; @@ -99,8 +99,8 @@ async fn test_storage_sql_command(url: &str) -> Result<(), sqlx::Error> { // execute queries - PostgreSQL, prepared query let _ = sqlx::query(insert_query1.as_str()).execute(&pool3).await?; let _ = sqlx::query(insert_query2.as_str()).execute(&pool3).await?; // $ Alert[rust/cleartext-storage-database] - let _ = sqlx::query(prepared_query.as_str()).bind(get_harmless()).execute(&pool3).await?; - let _ = sqlx::query(prepared_query.as_str()).bind(get_social_security_number()).execute(&pool3).await?; // $ MISSING: Alert[rust/cleartext-storage-database] + let _ = sqlx::query(prepared_query.as_str()).bind(get_harmless()).bind(id).execute(&pool3).await?; + let _ = sqlx::query(prepared_query.as_str()).bind(get_social_security_number()).bind(id).execute(&pool3).await?; // $ MISSING: Alert[rust/cleartext-storage-database] Ok(()) } From 42ced8aa3df4a36ca25339c6847a6a8ced2c979e Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 30 Jul 2025 17:09:16 +0100 Subject: [PATCH 042/208] Rust: Add examples to tests. --- .../query-tests/security/CWE-312/Cargo.lock | 109 +++++++++++- .../CWE-312/CleartextStorageDatabase.expected | 162 +++++++++--------- .../query-tests/security/CWE-312/options.yml | 3 + .../security/CWE-312/test_storage.rs | 67 ++++++++ 4 files changed, 258 insertions(+), 83 deletions(-) diff --git a/rust/ql/test/query-tests/security/CWE-312/Cargo.lock b/rust/ql/test/query-tests/security/CWE-312/Cargo.lock index 0da8ad619d95..456860e649ff 100644 --- a/rust/ql/test/query-tests/security/CWE-312/Cargo.lock +++ b/rust/ql/test/query-tests/security/CWE-312/Cargo.lock @@ -2,6 +2,41 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "allocator-api2" version = "0.2.21" @@ -253,6 +288,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "colored" version = "2.2.0" @@ -340,9 +385,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core", "typenum", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "der" version = "0.7.10" @@ -414,7 +469,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -672,6 +727,16 @@ dependencies = [ "wasi 0.14.2+wasi-0.2.4", ] +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + [[package]] name = "gloo-timers" version = "0.3.0" @@ -872,6 +937,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + [[package]] name = "instant" version = "0.1.13" @@ -1096,6 +1170,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + [[package]] name = "openssl" version = "0.10.73" @@ -1264,6 +1344,18 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "potential_utf" version = "0.1.2" @@ -1395,7 +1487,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1837,6 +1929,9 @@ dependencies = [ name = "test" version = "0.0.1" dependencies = [ + "aes", + "aes-gcm", + "base64", "futures", "log", "log_err", @@ -1987,6 +2082,16 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "url" version = "2.5.4" diff --git a/rust/ql/test/query-tests/security/CWE-312/CleartextStorageDatabase.expected b/rust/ql/test/query-tests/security/CWE-312/CleartextStorageDatabase.expected index a038f37b8753..c686f3c3d6a5 100644 --- a/rust/ql/test/query-tests/security/CWE-312/CleartextStorageDatabase.expected +++ b/rust/ql/test/query-tests/security/CWE-312/CleartextStorageDatabase.expected @@ -1,61 +1,61 @@ #select -| test_storage.rs:62:13:62:23 | ...::query | test_storage.rs:33:97:33:114 | get_phone_number(...) | test_storage.rs:62:13:62:23 | ...::query | This database operation may read or write unencrypted sensitive data from $@. | test_storage.rs:33:97:33:114 | get_phone_number(...) | get_phone_number(...) | -| test_storage.rs:77:13:77:25 | ...::raw_sql | test_storage.rs:33:97:33:114 | get_phone_number(...) | test_storage.rs:77:13:77:25 | ...::raw_sql | This database operation may read or write unencrypted sensitive data from $@. | test_storage.rs:33:97:33:114 | get_phone_number(...) | get_phone_number(...) | -| test_storage.rs:81:13:81:23 | ...::query | test_storage.rs:33:97:33:114 | get_phone_number(...) | test_storage.rs:81:13:81:23 | ...::query | This database operation may read or write unencrypted sensitive data from $@. | test_storage.rs:33:97:33:114 | get_phone_number(...) | get_phone_number(...) | -| test_storage.rs:87:13:87:23 | ...::query | test_storage.rs:33:97:33:114 | get_phone_number(...) | test_storage.rs:87:13:87:23 | ...::query | This database operation may read or write unencrypted sensitive data from $@. | test_storage.rs:33:97:33:114 | get_phone_number(...) | get_phone_number(...) | -| test_storage.rs:101:13:101:23 | ...::query | test_storage.rs:33:97:33:114 | get_phone_number(...) | test_storage.rs:101:13:101:23 | ...::query | This database operation may read or write unencrypted sensitive data from $@. | test_storage.rs:33:97:33:114 | get_phone_number(...) | get_phone_number(...) | +| test_storage.rs:99:13:99:23 | ...::query | test_storage.rs:70:97:70:114 | get_phone_number(...) | test_storage.rs:99:13:99:23 | ...::query | This database operation may read or write unencrypted sensitive data from $@. | test_storage.rs:70:97:70:114 | get_phone_number(...) | get_phone_number(...) | +| test_storage.rs:114:13:114:25 | ...::raw_sql | test_storage.rs:70:97:70:114 | get_phone_number(...) | test_storage.rs:114:13:114:25 | ...::raw_sql | This database operation may read or write unencrypted sensitive data from $@. | test_storage.rs:70:97:70:114 | get_phone_number(...) | get_phone_number(...) | +| test_storage.rs:118:13:118:23 | ...::query | test_storage.rs:70:97:70:114 | get_phone_number(...) | test_storage.rs:118:13:118:23 | ...::query | This database operation may read or write unencrypted sensitive data from $@. | test_storage.rs:70:97:70:114 | get_phone_number(...) | get_phone_number(...) | +| test_storage.rs:124:13:124:23 | ...::query | test_storage.rs:70:97:70:114 | get_phone_number(...) | test_storage.rs:124:13:124:23 | ...::query | This database operation may read or write unencrypted sensitive data from $@. | test_storage.rs:70:97:70:114 | get_phone_number(...) | get_phone_number(...) | +| test_storage.rs:138:13:138:23 | ...::query | test_storage.rs:70:97:70:114 | get_phone_number(...) | test_storage.rs:138:13:138:23 | ...::query | This database operation may read or write unencrypted sensitive data from $@. | test_storage.rs:70:97:70:114 | get_phone_number(...) | get_phone_number(...) | edges -| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:62:25:62:37 | insert_query2 | provenance | | -| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:62:25:62:46 | insert_query2.as_str() | provenance | MaD:5 | -| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:62:25:62:46 | insert_query2.as_str() | provenance | MaD:4 | -| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:62:25:62:46 | insert_query2.as_str() | provenance | MaD:5 | -| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:77:27:77:39 | insert_query2 | provenance | | -| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:77:27:77:48 | insert_query2.as_str() | provenance | MaD:5 | -| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:77:27:77:48 | insert_query2.as_str() | provenance | MaD:4 | -| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:77:27:77:48 | insert_query2.as_str() | provenance | MaD:5 | -| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:81:25:81:37 | insert_query2 | provenance | | -| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:81:25:81:46 | insert_query2.as_str() | provenance | MaD:5 | -| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:81:25:81:46 | insert_query2.as_str() | provenance | MaD:4 | -| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:81:25:81:46 | insert_query2.as_str() | provenance | MaD:5 | -| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:87:25:87:37 | insert_query2 | provenance | | -| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:87:25:87:46 | insert_query2.as_str() | provenance | MaD:5 | -| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:87:25:87:46 | insert_query2.as_str() | provenance | MaD:4 | -| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:87:25:87:46 | insert_query2.as_str() | provenance | MaD:5 | -| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:101:25:101:37 | insert_query2 | provenance | | -| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:101:25:101:46 | insert_query2.as_str() | provenance | MaD:5 | -| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:101:25:101:46 | insert_query2.as_str() | provenance | MaD:4 | -| test_storage.rs:33:9:33:21 | insert_query2 | test_storage.rs:101:25:101:46 | insert_query2.as_str() | provenance | MaD:5 | -| test_storage.rs:33:25:33:114 | ... + ... | test_storage.rs:33:9:33:21 | insert_query2 | provenance | | -| test_storage.rs:33:25:33:114 | ... + ... | test_storage.rs:33:25:33:121 | ... + ... | provenance | MaD:3 | -| test_storage.rs:33:25:33:121 | ... + ... | test_storage.rs:33:9:33:21 | insert_query2 | provenance | | -| test_storage.rs:33:96:33:114 | &... | test_storage.rs:33:9:33:21 | insert_query2 | provenance | | -| test_storage.rs:33:96:33:114 | &... | test_storage.rs:33:25:33:114 | ... + ... | provenance | | -| test_storage.rs:33:97:33:114 | get_phone_number(...) | test_storage.rs:33:96:33:114 | &... | provenance | Config | -| test_storage.rs:62:25:62:37 | insert_query2 | test_storage.rs:62:25:62:46 | insert_query2.as_str() [&ref] | provenance | MaD:5 | -| test_storage.rs:62:25:62:37 | insert_query2 | test_storage.rs:62:25:62:46 | insert_query2.as_str() [&ref] | provenance | MaD:4 | -| test_storage.rs:62:25:62:37 | insert_query2 | test_storage.rs:62:25:62:46 | insert_query2.as_str() [&ref] | provenance | MaD:5 | -| test_storage.rs:62:25:62:46 | insert_query2.as_str() | test_storage.rs:62:13:62:23 | ...::query | provenance | MaD:1 Sink:MaD:1 | -| test_storage.rs:62:25:62:46 | insert_query2.as_str() [&ref] | test_storage.rs:62:13:62:23 | ...::query | provenance | MaD:1 Sink:MaD:1 | -| test_storage.rs:77:27:77:39 | insert_query2 | test_storage.rs:77:27:77:48 | insert_query2.as_str() [&ref] | provenance | MaD:5 | -| test_storage.rs:77:27:77:39 | insert_query2 | test_storage.rs:77:27:77:48 | insert_query2.as_str() [&ref] | provenance | MaD:4 | -| test_storage.rs:77:27:77:39 | insert_query2 | test_storage.rs:77:27:77:48 | insert_query2.as_str() [&ref] | provenance | MaD:5 | -| test_storage.rs:77:27:77:48 | insert_query2.as_str() | test_storage.rs:77:13:77:25 | ...::raw_sql | provenance | MaD:2 Sink:MaD:2 | -| test_storage.rs:77:27:77:48 | insert_query2.as_str() [&ref] | test_storage.rs:77:13:77:25 | ...::raw_sql | provenance | MaD:2 Sink:MaD:2 | -| test_storage.rs:81:25:81:37 | insert_query2 | test_storage.rs:81:25:81:46 | insert_query2.as_str() [&ref] | provenance | MaD:5 | -| test_storage.rs:81:25:81:37 | insert_query2 | test_storage.rs:81:25:81:46 | insert_query2.as_str() [&ref] | provenance | MaD:4 | -| test_storage.rs:81:25:81:37 | insert_query2 | test_storage.rs:81:25:81:46 | insert_query2.as_str() [&ref] | provenance | MaD:5 | -| test_storage.rs:81:25:81:46 | insert_query2.as_str() | test_storage.rs:81:13:81:23 | ...::query | provenance | MaD:1 Sink:MaD:1 | -| test_storage.rs:81:25:81:46 | insert_query2.as_str() [&ref] | test_storage.rs:81:13:81:23 | ...::query | provenance | MaD:1 Sink:MaD:1 | -| test_storage.rs:87:25:87:37 | insert_query2 | test_storage.rs:87:25:87:46 | insert_query2.as_str() [&ref] | provenance | MaD:5 | -| test_storage.rs:87:25:87:37 | insert_query2 | test_storage.rs:87:25:87:46 | insert_query2.as_str() [&ref] | provenance | MaD:4 | -| test_storage.rs:87:25:87:37 | insert_query2 | test_storage.rs:87:25:87:46 | insert_query2.as_str() [&ref] | provenance | MaD:5 | -| test_storage.rs:87:25:87:46 | insert_query2.as_str() | test_storage.rs:87:13:87:23 | ...::query | provenance | MaD:1 Sink:MaD:1 | -| test_storage.rs:87:25:87:46 | insert_query2.as_str() [&ref] | test_storage.rs:87:13:87:23 | ...::query | provenance | MaD:1 Sink:MaD:1 | -| test_storage.rs:101:25:101:37 | insert_query2 | test_storage.rs:101:25:101:46 | insert_query2.as_str() [&ref] | provenance | MaD:5 | -| test_storage.rs:101:25:101:37 | insert_query2 | test_storage.rs:101:25:101:46 | insert_query2.as_str() [&ref] | provenance | MaD:4 | -| test_storage.rs:101:25:101:37 | insert_query2 | test_storage.rs:101:25:101:46 | insert_query2.as_str() [&ref] | provenance | MaD:5 | -| test_storage.rs:101:25:101:46 | insert_query2.as_str() | test_storage.rs:101:13:101:23 | ...::query | provenance | MaD:1 Sink:MaD:1 | -| test_storage.rs:101:25:101:46 | insert_query2.as_str() [&ref] | test_storage.rs:101:13:101:23 | ...::query | provenance | MaD:1 Sink:MaD:1 | +| test_storage.rs:70:9:70:21 | insert_query2 | test_storage.rs:99:25:99:37 | insert_query2 | provenance | | +| test_storage.rs:70:9:70:21 | insert_query2 | test_storage.rs:99:25:99:46 | insert_query2.as_str() | provenance | MaD:5 | +| test_storage.rs:70:9:70:21 | insert_query2 | test_storage.rs:99:25:99:46 | insert_query2.as_str() | provenance | MaD:4 | +| test_storage.rs:70:9:70:21 | insert_query2 | test_storage.rs:99:25:99:46 | insert_query2.as_str() | provenance | MaD:5 | +| test_storage.rs:70:9:70:21 | insert_query2 | test_storage.rs:114:27:114:39 | insert_query2 | provenance | | +| test_storage.rs:70:9:70:21 | insert_query2 | test_storage.rs:114:27:114:48 | insert_query2.as_str() | provenance | MaD:5 | +| test_storage.rs:70:9:70:21 | insert_query2 | test_storage.rs:114:27:114:48 | insert_query2.as_str() | provenance | MaD:4 | +| test_storage.rs:70:9:70:21 | insert_query2 | test_storage.rs:114:27:114:48 | insert_query2.as_str() | provenance | MaD:5 | +| test_storage.rs:70:9:70:21 | insert_query2 | test_storage.rs:118:25:118:37 | insert_query2 | provenance | | +| test_storage.rs:70:9:70:21 | insert_query2 | test_storage.rs:118:25:118:46 | insert_query2.as_str() | provenance | MaD:5 | +| test_storage.rs:70:9:70:21 | insert_query2 | test_storage.rs:118:25:118:46 | insert_query2.as_str() | provenance | MaD:4 | +| test_storage.rs:70:9:70:21 | insert_query2 | test_storage.rs:118:25:118:46 | insert_query2.as_str() | provenance | MaD:5 | +| test_storage.rs:70:9:70:21 | insert_query2 | test_storage.rs:124:25:124:37 | insert_query2 | provenance | | +| test_storage.rs:70:9:70:21 | insert_query2 | test_storage.rs:124:25:124:46 | insert_query2.as_str() | provenance | MaD:5 | +| test_storage.rs:70:9:70:21 | insert_query2 | test_storage.rs:124:25:124:46 | insert_query2.as_str() | provenance | MaD:4 | +| test_storage.rs:70:9:70:21 | insert_query2 | test_storage.rs:124:25:124:46 | insert_query2.as_str() | provenance | MaD:5 | +| test_storage.rs:70:9:70:21 | insert_query2 | test_storage.rs:138:25:138:37 | insert_query2 | provenance | | +| test_storage.rs:70:9:70:21 | insert_query2 | test_storage.rs:138:25:138:46 | insert_query2.as_str() | provenance | MaD:5 | +| test_storage.rs:70:9:70:21 | insert_query2 | test_storage.rs:138:25:138:46 | insert_query2.as_str() | provenance | MaD:4 | +| test_storage.rs:70:9:70:21 | insert_query2 | test_storage.rs:138:25:138:46 | insert_query2.as_str() | provenance | MaD:5 | +| test_storage.rs:70:25:70:114 | ... + ... | test_storage.rs:70:9:70:21 | insert_query2 | provenance | | +| test_storage.rs:70:25:70:114 | ... + ... | test_storage.rs:70:25:70:121 | ... + ... | provenance | MaD:3 | +| test_storage.rs:70:25:70:121 | ... + ... | test_storage.rs:70:9:70:21 | insert_query2 | provenance | | +| test_storage.rs:70:96:70:114 | &... | test_storage.rs:70:9:70:21 | insert_query2 | provenance | | +| test_storage.rs:70:96:70:114 | &... | test_storage.rs:70:25:70:114 | ... + ... | provenance | | +| test_storage.rs:70:97:70:114 | get_phone_number(...) | test_storage.rs:70:96:70:114 | &... | provenance | Config | +| test_storage.rs:99:25:99:37 | insert_query2 | test_storage.rs:99:25:99:46 | insert_query2.as_str() [&ref] | provenance | MaD:5 | +| test_storage.rs:99:25:99:37 | insert_query2 | test_storage.rs:99:25:99:46 | insert_query2.as_str() [&ref] | provenance | MaD:4 | +| test_storage.rs:99:25:99:37 | insert_query2 | test_storage.rs:99:25:99:46 | insert_query2.as_str() [&ref] | provenance | MaD:5 | +| test_storage.rs:99:25:99:46 | insert_query2.as_str() | test_storage.rs:99:13:99:23 | ...::query | provenance | MaD:1 Sink:MaD:1 | +| test_storage.rs:99:25:99:46 | insert_query2.as_str() [&ref] | test_storage.rs:99:13:99:23 | ...::query | provenance | MaD:1 Sink:MaD:1 | +| test_storage.rs:114:27:114:39 | insert_query2 | test_storage.rs:114:27:114:48 | insert_query2.as_str() [&ref] | provenance | MaD:5 | +| test_storage.rs:114:27:114:39 | insert_query2 | test_storage.rs:114:27:114:48 | insert_query2.as_str() [&ref] | provenance | MaD:4 | +| test_storage.rs:114:27:114:39 | insert_query2 | test_storage.rs:114:27:114:48 | insert_query2.as_str() [&ref] | provenance | MaD:5 | +| test_storage.rs:114:27:114:48 | insert_query2.as_str() | test_storage.rs:114:13:114:25 | ...::raw_sql | provenance | MaD:2 Sink:MaD:2 | +| test_storage.rs:114:27:114:48 | insert_query2.as_str() [&ref] | test_storage.rs:114:13:114:25 | ...::raw_sql | provenance | MaD:2 Sink:MaD:2 | +| test_storage.rs:118:25:118:37 | insert_query2 | test_storage.rs:118:25:118:46 | insert_query2.as_str() [&ref] | provenance | MaD:5 | +| test_storage.rs:118:25:118:37 | insert_query2 | test_storage.rs:118:25:118:46 | insert_query2.as_str() [&ref] | provenance | MaD:4 | +| test_storage.rs:118:25:118:37 | insert_query2 | test_storage.rs:118:25:118:46 | insert_query2.as_str() [&ref] | provenance | MaD:5 | +| test_storage.rs:118:25:118:46 | insert_query2.as_str() | test_storage.rs:118:13:118:23 | ...::query | provenance | MaD:1 Sink:MaD:1 | +| test_storage.rs:118:25:118:46 | insert_query2.as_str() [&ref] | test_storage.rs:118:13:118:23 | ...::query | provenance | MaD:1 Sink:MaD:1 | +| test_storage.rs:124:25:124:37 | insert_query2 | test_storage.rs:124:25:124:46 | insert_query2.as_str() [&ref] | provenance | MaD:5 | +| test_storage.rs:124:25:124:37 | insert_query2 | test_storage.rs:124:25:124:46 | insert_query2.as_str() [&ref] | provenance | MaD:4 | +| test_storage.rs:124:25:124:37 | insert_query2 | test_storage.rs:124:25:124:46 | insert_query2.as_str() [&ref] | provenance | MaD:5 | +| test_storage.rs:124:25:124:46 | insert_query2.as_str() | test_storage.rs:124:13:124:23 | ...::query | provenance | MaD:1 Sink:MaD:1 | +| test_storage.rs:124:25:124:46 | insert_query2.as_str() [&ref] | test_storage.rs:124:13:124:23 | ...::query | provenance | MaD:1 Sink:MaD:1 | +| test_storage.rs:138:25:138:37 | insert_query2 | test_storage.rs:138:25:138:46 | insert_query2.as_str() [&ref] | provenance | MaD:5 | +| test_storage.rs:138:25:138:37 | insert_query2 | test_storage.rs:138:25:138:46 | insert_query2.as_str() [&ref] | provenance | MaD:4 | +| test_storage.rs:138:25:138:37 | insert_query2 | test_storage.rs:138:25:138:46 | insert_query2.as_str() [&ref] | provenance | MaD:5 | +| test_storage.rs:138:25:138:46 | insert_query2.as_str() | test_storage.rs:138:13:138:23 | ...::query | provenance | MaD:1 Sink:MaD:1 | +| test_storage.rs:138:25:138:46 | insert_query2.as_str() [&ref] | test_storage.rs:138:13:138:23 | ...::query | provenance | MaD:1 Sink:MaD:1 | models | 1 | Sink: sqlx_core::query::query; Argument[0]; database-store | | 2 | Sink: sqlx_core::raw_sql::raw_sql; Argument[0]; database-store | @@ -63,29 +63,29 @@ models | 4 | Summary: ::as_str; Argument[self]; ReturnValue; value | | 5 | Summary: ::as_str; Argument[self]; ReturnValue; value | nodes -| test_storage.rs:33:9:33:21 | insert_query2 | semmle.label | insert_query2 | -| test_storage.rs:33:25:33:114 | ... + ... | semmle.label | ... + ... | -| test_storage.rs:33:25:33:121 | ... + ... | semmle.label | ... + ... | -| test_storage.rs:33:96:33:114 | &... | semmle.label | &... | -| test_storage.rs:33:97:33:114 | get_phone_number(...) | semmle.label | get_phone_number(...) | -| test_storage.rs:62:13:62:23 | ...::query | semmle.label | ...::query | -| test_storage.rs:62:25:62:37 | insert_query2 | semmle.label | insert_query2 | -| test_storage.rs:62:25:62:46 | insert_query2.as_str() | semmle.label | insert_query2.as_str() | -| test_storage.rs:62:25:62:46 | insert_query2.as_str() [&ref] | semmle.label | insert_query2.as_str() [&ref] | -| test_storage.rs:77:13:77:25 | ...::raw_sql | semmle.label | ...::raw_sql | -| test_storage.rs:77:27:77:39 | insert_query2 | semmle.label | insert_query2 | -| test_storage.rs:77:27:77:48 | insert_query2.as_str() | semmle.label | insert_query2.as_str() | -| test_storage.rs:77:27:77:48 | insert_query2.as_str() [&ref] | semmle.label | insert_query2.as_str() [&ref] | -| test_storage.rs:81:13:81:23 | ...::query | semmle.label | ...::query | -| test_storage.rs:81:25:81:37 | insert_query2 | semmle.label | insert_query2 | -| test_storage.rs:81:25:81:46 | insert_query2.as_str() | semmle.label | insert_query2.as_str() | -| test_storage.rs:81:25:81:46 | insert_query2.as_str() [&ref] | semmle.label | insert_query2.as_str() [&ref] | -| test_storage.rs:87:13:87:23 | ...::query | semmle.label | ...::query | -| test_storage.rs:87:25:87:37 | insert_query2 | semmle.label | insert_query2 | -| test_storage.rs:87:25:87:46 | insert_query2.as_str() | semmle.label | insert_query2.as_str() | -| test_storage.rs:87:25:87:46 | insert_query2.as_str() [&ref] | semmle.label | insert_query2.as_str() [&ref] | -| test_storage.rs:101:13:101:23 | ...::query | semmle.label | ...::query | -| test_storage.rs:101:25:101:37 | insert_query2 | semmle.label | insert_query2 | -| test_storage.rs:101:25:101:46 | insert_query2.as_str() | semmle.label | insert_query2.as_str() | -| test_storage.rs:101:25:101:46 | insert_query2.as_str() [&ref] | semmle.label | insert_query2.as_str() [&ref] | +| test_storage.rs:70:9:70:21 | insert_query2 | semmle.label | insert_query2 | +| test_storage.rs:70:25:70:114 | ... + ... | semmle.label | ... + ... | +| test_storage.rs:70:25:70:121 | ... + ... | semmle.label | ... + ... | +| test_storage.rs:70:96:70:114 | &... | semmle.label | &... | +| test_storage.rs:70:97:70:114 | get_phone_number(...) | semmle.label | get_phone_number(...) | +| test_storage.rs:99:13:99:23 | ...::query | semmle.label | ...::query | +| test_storage.rs:99:25:99:37 | insert_query2 | semmle.label | insert_query2 | +| test_storage.rs:99:25:99:46 | insert_query2.as_str() | semmle.label | insert_query2.as_str() | +| test_storage.rs:99:25:99:46 | insert_query2.as_str() [&ref] | semmle.label | insert_query2.as_str() [&ref] | +| test_storage.rs:114:13:114:25 | ...::raw_sql | semmle.label | ...::raw_sql | +| test_storage.rs:114:27:114:39 | insert_query2 | semmle.label | insert_query2 | +| test_storage.rs:114:27:114:48 | insert_query2.as_str() | semmle.label | insert_query2.as_str() | +| test_storage.rs:114:27:114:48 | insert_query2.as_str() [&ref] | semmle.label | insert_query2.as_str() [&ref] | +| test_storage.rs:118:13:118:23 | ...::query | semmle.label | ...::query | +| test_storage.rs:118:25:118:37 | insert_query2 | semmle.label | insert_query2 | +| test_storage.rs:118:25:118:46 | insert_query2.as_str() | semmle.label | insert_query2.as_str() | +| test_storage.rs:118:25:118:46 | insert_query2.as_str() [&ref] | semmle.label | insert_query2.as_str() [&ref] | +| test_storage.rs:124:13:124:23 | ...::query | semmle.label | ...::query | +| test_storage.rs:124:25:124:37 | insert_query2 | semmle.label | insert_query2 | +| test_storage.rs:124:25:124:46 | insert_query2.as_str() | semmle.label | insert_query2.as_str() | +| test_storage.rs:124:25:124:46 | insert_query2.as_str() [&ref] | semmle.label | insert_query2.as_str() [&ref] | +| test_storage.rs:138:13:138:23 | ...::query | semmle.label | ...::query | +| test_storage.rs:138:25:138:37 | insert_query2 | semmle.label | insert_query2 | +| test_storage.rs:138:25:138:46 | insert_query2.as_str() | semmle.label | insert_query2.as_str() | +| test_storage.rs:138:25:138:46 | insert_query2.as_str() [&ref] | semmle.label | insert_query2.as_str() [&ref] | subpaths diff --git a/rust/ql/test/query-tests/security/CWE-312/options.yml b/rust/ql/test/query-tests/security/CWE-312/options.yml index 1fccbab2973a..271683a55267 100644 --- a/rust/ql/test/query-tests/security/CWE-312/options.yml +++ b/rust/ql/test/query-tests/security/CWE-312/options.yml @@ -5,3 +5,6 @@ qltest_dependencies: - log_err = { version = "1.1.1" } - sqlx = { version = "0.8", features = ["mysql", "sqlite", "postgres", "runtime-async-std", "tls-native-tls"] } - futures = { version = "0.3" } + - aes = { version = "0.8.4" } + - aes-gcm = { version = "0.10.3" } + - base64 = { version = "0.22.1" } diff --git a/rust/ql/test/query-tests/security/CWE-312/test_storage.rs b/rust/ql/test/query-tests/security/CWE-312/test_storage.rs index d9b63418e47b..efccf2ce4371 100644 --- a/rust/ql/test/query-tests/security/CWE-312/test_storage.rs +++ b/rust/ql/test/query-tests/security/CWE-312/test_storage.rs @@ -1,4 +1,8 @@ +use aes_gcm::aead::{Aead, AeadCore, OsRng}; +use aes_gcm::aes::cipher::Unsigned; +use aes_gcm::{Aes256Gcm, KeyInit}; +use base64::prelude::*; use sqlx::Connection; use sqlx::Executor; @@ -20,6 +24,39 @@ fn get_email() -> String { return String::from("a@b.com"); } +fn get_ccn() -> String { + return String::from("1234567890"); +} + +fn encrypt(text: String, encryption_key: &aes_gcm::Key) -> String { + // encrypt text -> ciphertext + let cipher = Aes256Gcm::new(&encryption_key); + let nonce = Aes256Gcm::generate_nonce(&mut OsRng); + let ciphertext = cipher.encrypt(&nonce, text.as_ref()).unwrap(); + + // append (nonce, ciphertext) + let mut combined = nonce.to_vec(); + combined.extend(ciphertext); + + // encode to base64 string + BASE64_STANDARD.encode(combined) +} + +fn decrypt(data: String, encryption_key: &aes_gcm::Key) -> String { + let cipher = Aes256Gcm::new(&encryption_key); + + // decode base64 string + let decoded = BASE64_STANDARD.decode(data).unwrap(); + + // split into (nonce, ciphertext) + let nonce_size = ::NonceSize::to_usize(); + let (nonce, ciphertext) = decoded.split_at(nonce_size); + + // decrypt ciphertext -> plaintext + let plaintext = cipher.decrypt(nonce.into(), ciphertext).unwrap(); + String::from_utf8(plaintext).unwrap() +} + async fn test_storage_sql_command(url: &str) -> Result<(), sqlx::Error> { // connect through a MySQL connection pool let pool1 = sqlx::mysql::MySqlPool::connect(url).await?; @@ -102,6 +139,36 @@ async fn test_storage_sql_command(url: &str) -> Result<(), sqlx::Error> { let _ = sqlx::query(prepared_query.as_str()).bind(get_harmless()).bind(id).execute(&pool3).await?; let _ = sqlx::query(prepared_query.as_str()).bind(get_social_security_number()).bind(id).execute(&pool3).await?; // $ MISSING: Alert[rust/cleartext-storage-database] + // "bad" example + { + let pool = &pool1; + let credit_card_number = get_ccn(); + + let query = "INSERT INTO PAYMENTDETAILS(ID, CARDNUM) VALUES(?, ?)"; + let result = sqlx::query(query) + .bind(id) + .bind(credit_card_number) // $ MISSING: Alert[rust/cleartext-storage-database] + .execute(pool) + .await?; + } + + // "good" example + { + let pool = &pool1; + let credit_card_number = get_ccn(); + + let encryption_key = Aes256Gcm::generate_key(OsRng); + + // ... + + let query = "INSERT INTO PAYMENTDETAILS(ID, CARDNUM) VALUES(?, ?)"; + let result = sqlx::query(query) + .bind(id) + .bind(encrypt(credit_card_number, &encryption_key)) + .execute(pool) + .await?; + } + Ok(()) } From 5f538209c9954662d30a946bcab75435474bf551 Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Thu, 31 Jul 2025 12:09:30 +0200 Subject: [PATCH 043/208] Exlucde environmental variables from default detection in regexp injection --- .../RegExpInjectionCustomizations.qll | 26 ++++++++++++++----- .../Security/CWE-730/RegExpInjection.expected | 4 --- .../Security/CWE-730/RegExpInjection.js | 2 +- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/RegExpInjectionCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/RegExpInjectionCustomizations.qll index 1c056935d407..5ee39219d260 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/RegExpInjectionCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/RegExpInjectionCustomizations.qll @@ -5,6 +5,7 @@ */ import javascript +private import codeql.threatmodels.ThreatModels module RegExpInjection { /** @@ -32,19 +33,32 @@ module RegExpInjection { /** * An active threat-model source, considered as a flow source. + * Excludes environment variables by default - they require the "environment" threat model. */ private class ActiveThreatModelSourceAsSource extends Source instanceof ActiveThreatModelSource { - ActiveThreatModelSourceAsSource() { not this.isClientSideSource() } + ActiveThreatModelSourceAsSource() { + not this.isClientSideSource() and + not this.(ThreatModelSource).getThreatModel() = "environment" + } } - private import IndirectCommandInjectionCustomizations + /** + * Environment variables as a source when the "environment" threat model is active. + */ + private class EnvironmentVariableAsSource extends Source instanceof ThreatModelSource { + EnvironmentVariableAsSource() { + this.getThreatModel() = "environment" and + currentThreatModel("environment") + } + + override string describe() { result = "environment variable" } + } /** - * A read of `process.env`, `process.argv`, and similar, considered as a flow source for regular - * expression injection. + * Command line arguments as a source for regular expression injection. */ - class ArgvAsSource extends Source instanceof IndirectCommandInjection::Source { - override string describe() { result = IndirectCommandInjection::Source.super.describe() } + private class CommandLineArgumentAsSource extends Source instanceof CommandLineArguments { + override string describe() { result = "command-line argument" } } /** diff --git a/javascript/ql/test/query-tests/Security/CWE-730/RegExpInjection.expected b/javascript/ql/test/query-tests/Security/CWE-730/RegExpInjection.expected index 06926c487efd..07225ec763e3 100644 --- a/javascript/ql/test/query-tests/Security/CWE-730/RegExpInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-730/RegExpInjection.expected @@ -14,7 +14,6 @@ | RegExpInjection.js:49:14:49:52 | key.spl ... in("-") | RegExpInjection.js:5:13:5:28 | req.param("key") | RegExpInjection.js:49:14:49:52 | key.spl ... in("-") | This regular expression is constructed from a $@. | RegExpInjection.js:5:13:5:28 | req.param("key") | user-provided value | | RegExpInjection.js:59:14:59:18 | input | RegExpInjection.js:55:39:55:56 | req.param("input") | RegExpInjection.js:59:14:59:18 | input | This regular expression is constructed from a $@. | RegExpInjection.js:55:39:55:56 | req.param("input") | user-provided value | | RegExpInjection.js:82:14:82:55 | "^.*\\.( ... + ")$" | RegExpInjection.js:77:15:77:32 | req.param("input") | RegExpInjection.js:82:14:82:55 | "^.*\\.( ... + ")$" | This regular expression is constructed from a $@. | RegExpInjection.js:77:15:77:32 | req.param("input") | user-provided value | -| RegExpInjection.js:86:16:86:50 | `^${pro ... r.app$` | RegExpInjection.js:86:20:86:30 | process.env | RegExpInjection.js:86:16:86:50 | `^${pro ... r.app$` | This regular expression is constructed from a $@. | RegExpInjection.js:86:20:86:30 | process.env | environment variable | | RegExpInjection.js:88:16:88:49 | `^${pro ... r.app$` | RegExpInjection.js:88:20:88:31 | process.argv | RegExpInjection.js:88:16:88:49 | `^${pro ... r.app$` | This regular expression is constructed from a $@. | RegExpInjection.js:88:20:88:31 | process.argv | command-line argument | | RegExpInjection.js:95:14:95:22 | sanitized | RegExpInjection.js:92:15:92:32 | req.param("input") | RegExpInjection.js:95:14:95:22 | sanitized | This regular expression is constructed from a $@. | RegExpInjection.js:92:15:92:32 | req.param("input") | user-provided value | | tst.js:6:16:6:35 | "^"+ data.name + "$" | tst.js:5:16:5:29 | req.query.data | tst.js:6:16:6:35 | "^"+ data.name + "$" | This regular expression is constructed from a $@. | tst.js:5:16:5:29 | req.query.data | user-provided value | @@ -57,7 +56,6 @@ edges | RegExpInjection.js:77:15:77:32 | req.param("input") | RegExpInjection.js:77:7:77:32 | input | provenance | | | RegExpInjection.js:82:25:82:29 | input | RegExpInjection.js:82:25:82:48 | input.r ... g, "\|") | provenance | | | RegExpInjection.js:82:25:82:48 | input.r ... g, "\|") | RegExpInjection.js:82:14:82:55 | "^.*\\.( ... + ")$" | provenance | | -| RegExpInjection.js:86:20:86:30 | process.env | RegExpInjection.js:86:16:86:50 | `^${pro ... r.app$` | provenance | | | RegExpInjection.js:88:20:88:31 | process.argv | RegExpInjection.js:88:16:88:49 | `^${pro ... r.app$` | provenance | | | RegExpInjection.js:92:7:92:32 | input | RegExpInjection.js:94:19:94:23 | input | provenance | | | RegExpInjection.js:92:15:92:32 | req.param("input") | RegExpInjection.js:92:7:92:32 | input | provenance | | @@ -109,8 +107,6 @@ nodes | RegExpInjection.js:82:14:82:55 | "^.*\\.( ... + ")$" | semmle.label | "^.*\\.( ... + ")$" | | RegExpInjection.js:82:25:82:29 | input | semmle.label | input | | RegExpInjection.js:82:25:82:48 | input.r ... g, "\|") | semmle.label | input.r ... g, "\|") | -| RegExpInjection.js:86:16:86:50 | `^${pro ... r.app$` | semmle.label | `^${pro ... r.app$` | -| RegExpInjection.js:86:20:86:30 | process.env | semmle.label | process.env | | RegExpInjection.js:88:16:88:49 | `^${pro ... r.app$` | semmle.label | `^${pro ... r.app$` | | RegExpInjection.js:88:20:88:31 | process.argv | semmle.label | process.argv | | RegExpInjection.js:92:7:92:32 | input | semmle.label | input | diff --git a/javascript/ql/test/query-tests/Security/CWE-730/RegExpInjection.js b/javascript/ql/test/query-tests/Security/CWE-730/RegExpInjection.js index 2aa73c808773..8a5fa557c19a 100644 --- a/javascript/ql/test/query-tests/Security/CWE-730/RegExpInjection.js +++ b/javascript/ql/test/query-tests/Security/CWE-730/RegExpInjection.js @@ -83,7 +83,7 @@ app.get('/has-sanitizer', function(req, res) { }); app.get("argv", function(req, res) { - new RegExp(`^${process.env.HOME}/Foo/bar.app$`); // $ Alert[js/regex-injection] + new RegExp(`^${process.env.HOME}/Foo/bar.app$`); // environment variable, should be detected only with threat model enabled. new RegExp(`^${process.argv[1]}/Foo/bar.app$`); // $ Alert[js/regex-injection] }); From 8583257574311191dc8d293aa0132024c95a7613 Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Thu, 31 Jul 2025 12:24:32 +0200 Subject: [PATCH 044/208] Created new folder for test with threat models disabled --- .../CWE-730/{ => Threat-models-disabled}/RegExpInjection.expected | 0 .../CWE-730/{ => Threat-models-disabled}/RegExpInjection.js | 0 .../CWE-730/{ => Threat-models-disabled}/RegExpInjection.qlref | 0 .../CWE-730/{ => Threat-models-disabled}/RegExpInjectionGood.js | 0 .../CWE-730/{ => Threat-models-disabled}/ServerCrash.expected | 0 .../CWE-730/{ => Threat-models-disabled}/ServerCrash.qlref | 0 .../Security/CWE-730/{ => Threat-models-disabled}/client-side.js | 0 .../Security/CWE-730/{ => Threat-models-disabled}/search.js | 0 .../Security/CWE-730/{ => Threat-models-disabled}/server-crash.js | 0 .../Security/CWE-730/{ => Threat-models-disabled}/tst.js | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename javascript/ql/test/query-tests/Security/CWE-730/{ => Threat-models-disabled}/RegExpInjection.expected (100%) rename javascript/ql/test/query-tests/Security/CWE-730/{ => Threat-models-disabled}/RegExpInjection.js (100%) rename javascript/ql/test/query-tests/Security/CWE-730/{ => Threat-models-disabled}/RegExpInjection.qlref (100%) rename javascript/ql/test/query-tests/Security/CWE-730/{ => Threat-models-disabled}/RegExpInjectionGood.js (100%) rename javascript/ql/test/query-tests/Security/CWE-730/{ => Threat-models-disabled}/ServerCrash.expected (100%) rename javascript/ql/test/query-tests/Security/CWE-730/{ => Threat-models-disabled}/ServerCrash.qlref (100%) rename javascript/ql/test/query-tests/Security/CWE-730/{ => Threat-models-disabled}/client-side.js (100%) rename javascript/ql/test/query-tests/Security/CWE-730/{ => Threat-models-disabled}/search.js (100%) rename javascript/ql/test/query-tests/Security/CWE-730/{ => Threat-models-disabled}/server-crash.js (100%) rename javascript/ql/test/query-tests/Security/CWE-730/{ => Threat-models-disabled}/tst.js (100%) diff --git a/javascript/ql/test/query-tests/Security/CWE-730/RegExpInjection.expected b/javascript/ql/test/query-tests/Security/CWE-730/Threat-models-disabled/RegExpInjection.expected similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-730/RegExpInjection.expected rename to javascript/ql/test/query-tests/Security/CWE-730/Threat-models-disabled/RegExpInjection.expected diff --git a/javascript/ql/test/query-tests/Security/CWE-730/RegExpInjection.js b/javascript/ql/test/query-tests/Security/CWE-730/Threat-models-disabled/RegExpInjection.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-730/RegExpInjection.js rename to javascript/ql/test/query-tests/Security/CWE-730/Threat-models-disabled/RegExpInjection.js diff --git a/javascript/ql/test/query-tests/Security/CWE-730/RegExpInjection.qlref b/javascript/ql/test/query-tests/Security/CWE-730/Threat-models-disabled/RegExpInjection.qlref similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-730/RegExpInjection.qlref rename to javascript/ql/test/query-tests/Security/CWE-730/Threat-models-disabled/RegExpInjection.qlref diff --git a/javascript/ql/test/query-tests/Security/CWE-730/RegExpInjectionGood.js b/javascript/ql/test/query-tests/Security/CWE-730/Threat-models-disabled/RegExpInjectionGood.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-730/RegExpInjectionGood.js rename to javascript/ql/test/query-tests/Security/CWE-730/Threat-models-disabled/RegExpInjectionGood.js diff --git a/javascript/ql/test/query-tests/Security/CWE-730/ServerCrash.expected b/javascript/ql/test/query-tests/Security/CWE-730/Threat-models-disabled/ServerCrash.expected similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-730/ServerCrash.expected rename to javascript/ql/test/query-tests/Security/CWE-730/Threat-models-disabled/ServerCrash.expected diff --git a/javascript/ql/test/query-tests/Security/CWE-730/ServerCrash.qlref b/javascript/ql/test/query-tests/Security/CWE-730/Threat-models-disabled/ServerCrash.qlref similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-730/ServerCrash.qlref rename to javascript/ql/test/query-tests/Security/CWE-730/Threat-models-disabled/ServerCrash.qlref diff --git a/javascript/ql/test/query-tests/Security/CWE-730/client-side.js b/javascript/ql/test/query-tests/Security/CWE-730/Threat-models-disabled/client-side.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-730/client-side.js rename to javascript/ql/test/query-tests/Security/CWE-730/Threat-models-disabled/client-side.js diff --git a/javascript/ql/test/query-tests/Security/CWE-730/search.js b/javascript/ql/test/query-tests/Security/CWE-730/Threat-models-disabled/search.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-730/search.js rename to javascript/ql/test/query-tests/Security/CWE-730/Threat-models-disabled/search.js diff --git a/javascript/ql/test/query-tests/Security/CWE-730/server-crash.js b/javascript/ql/test/query-tests/Security/CWE-730/Threat-models-disabled/server-crash.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-730/server-crash.js rename to javascript/ql/test/query-tests/Security/CWE-730/Threat-models-disabled/server-crash.js diff --git a/javascript/ql/test/query-tests/Security/CWE-730/tst.js b/javascript/ql/test/query-tests/Security/CWE-730/Threat-models-disabled/tst.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-730/tst.js rename to javascript/ql/test/query-tests/Security/CWE-730/Threat-models-disabled/tst.js From d28a6e6352c76bc1097ed72832c56c5778716ad5 Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Thu, 31 Jul 2025 12:25:31 +0200 Subject: [PATCH 045/208] Added new test cases for regexp injection with enviromental variable threat model enabled --- .../RegExpInjection.expected | 34 +++++++++++++++++++ .../RegExpInjection.ext.yml | 6 ++++ .../Threat-models-enabled/RegExpInjection.js | 22 ++++++++++++ .../RegExpInjection.qlref | 2 ++ 4 files changed, 64 insertions(+) create mode 100644 javascript/ql/test/query-tests/Security/CWE-730/Threat-models-enabled/RegExpInjection.expected create mode 100644 javascript/ql/test/query-tests/Security/CWE-730/Threat-models-enabled/RegExpInjection.ext.yml create mode 100644 javascript/ql/test/query-tests/Security/CWE-730/Threat-models-enabled/RegExpInjection.js create mode 100644 javascript/ql/test/query-tests/Security/CWE-730/Threat-models-enabled/RegExpInjection.qlref diff --git a/javascript/ql/test/query-tests/Security/CWE-730/Threat-models-enabled/RegExpInjection.expected b/javascript/ql/test/query-tests/Security/CWE-730/Threat-models-enabled/RegExpInjection.expected new file mode 100644 index 000000000000..95c1c0df9eb8 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-730/Threat-models-enabled/RegExpInjection.expected @@ -0,0 +1,34 @@ +#select +| RegExpInjection.js:6:14:6:48 | `^${pro ... r.app$` | RegExpInjection.js:6:18:6:28 | process.env | RegExpInjection.js:6:14:6:48 | `^${pro ... r.app$` | This regular expression is constructed from a $@. | RegExpInjection.js:6:18:6:28 | process.env | environment variable | +| RegExpInjection.js:8:14:8:40 | `^${pro ... }/bin$` | RegExpInjection.js:8:18:8:28 | process.env | RegExpInjection.js:8:14:8:40 | `^${pro ... }/bin$` | This regular expression is constructed from a $@. | RegExpInjection.js:8:18:8:28 | process.env | environment variable | +| RegExpInjection.js:11:14:11:19 | envVar | RegExpInjection.js:10:16:10:26 | process.env | RegExpInjection.js:11:14:11:19 | envVar | This regular expression is constructed from a $@. | RegExpInjection.js:10:16:10:26 | process.env | environment variable | +| RegExpInjection.js:14:14:14:47 | `^${pro ... r.app$` | RegExpInjection.js:14:18:14:29 | process.argv | RegExpInjection.js:14:14:14:47 | `^${pro ... r.app$` | This regular expression is constructed from a $@. | RegExpInjection.js:14:18:14:29 | process.argv | command-line argument | +| RegExpInjection.js:17:14:17:17 | argv | RegExpInjection.js:16:14:16:25 | process.argv | RegExpInjection.js:17:14:17:17 | argv | This regular expression is constructed from a $@. | RegExpInjection.js:16:14:16:25 | process.argv | command-line argument | +| RegExpInjection.js:21:14:21:22 | userInput | RegExpInjection.js:20:19:20:36 | req.param("input") | RegExpInjection.js:21:14:21:22 | userInput | This regular expression is constructed from a $@. | RegExpInjection.js:20:19:20:36 | req.param("input") | user-provided value | +edges +| RegExpInjection.js:6:18:6:28 | process.env | RegExpInjection.js:6:14:6:48 | `^${pro ... r.app$` | provenance | | +| RegExpInjection.js:8:18:8:28 | process.env | RegExpInjection.js:8:14:8:40 | `^${pro ... }/bin$` | provenance | | +| RegExpInjection.js:10:7:10:35 | envVar | RegExpInjection.js:11:14:11:19 | envVar | provenance | | +| RegExpInjection.js:10:16:10:26 | process.env | RegExpInjection.js:10:7:10:35 | envVar | provenance | | +| RegExpInjection.js:14:18:14:29 | process.argv | RegExpInjection.js:14:14:14:47 | `^${pro ... r.app$` | provenance | | +| RegExpInjection.js:16:7:16:28 | argv | RegExpInjection.js:17:14:17:17 | argv | provenance | | +| RegExpInjection.js:16:14:16:25 | process.argv | RegExpInjection.js:16:7:16:28 | argv | provenance | | +| RegExpInjection.js:20:7:20:36 | userInput | RegExpInjection.js:21:14:21:22 | userInput | provenance | | +| RegExpInjection.js:20:19:20:36 | req.param("input") | RegExpInjection.js:20:7:20:36 | userInput | provenance | | +nodes +| RegExpInjection.js:6:14:6:48 | `^${pro ... r.app$` | semmle.label | `^${pro ... r.app$` | +| RegExpInjection.js:6:18:6:28 | process.env | semmle.label | process.env | +| RegExpInjection.js:8:14:8:40 | `^${pro ... }/bin$` | semmle.label | `^${pro ... }/bin$` | +| RegExpInjection.js:8:18:8:28 | process.env | semmle.label | process.env | +| RegExpInjection.js:10:7:10:35 | envVar | semmle.label | envVar | +| RegExpInjection.js:10:16:10:26 | process.env | semmle.label | process.env | +| RegExpInjection.js:11:14:11:19 | envVar | semmle.label | envVar | +| RegExpInjection.js:14:14:14:47 | `^${pro ... r.app$` | semmle.label | `^${pro ... r.app$` | +| RegExpInjection.js:14:18:14:29 | process.argv | semmle.label | process.argv | +| RegExpInjection.js:16:7:16:28 | argv | semmle.label | argv | +| RegExpInjection.js:16:14:16:25 | process.argv | semmle.label | process.argv | +| RegExpInjection.js:17:14:17:17 | argv | semmle.label | argv | +| RegExpInjection.js:20:7:20:36 | userInput | semmle.label | userInput | +| RegExpInjection.js:20:19:20:36 | req.param("input") | semmle.label | req.param("input") | +| RegExpInjection.js:21:14:21:22 | userInput | semmle.label | userInput | +subpaths diff --git a/javascript/ql/test/query-tests/Security/CWE-730/Threat-models-enabled/RegExpInjection.ext.yml b/javascript/ql/test/query-tests/Security/CWE-730/Threat-models-enabled/RegExpInjection.ext.yml new file mode 100644 index 000000000000..cd28c6d97174 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-730/Threat-models-enabled/RegExpInjection.ext.yml @@ -0,0 +1,6 @@ +extensions: + - addsTo: + pack: codeql/threat-models + extensible: threatModelConfiguration + data: + - ["environment", true, 0] diff --git a/javascript/ql/test/query-tests/Security/CWE-730/Threat-models-enabled/RegExpInjection.js b/javascript/ql/test/query-tests/Security/CWE-730/Threat-models-enabled/RegExpInjection.js new file mode 100644 index 000000000000..28736678d8c3 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-730/Threat-models-enabled/RegExpInjection.js @@ -0,0 +1,22 @@ +var express = require('express'); +var app = express(); + +app.get('/test-environment', function(req, res) { + // Environment variables should be detected when "environment" threat model is enabled + new RegExp(`^${process.env.HOME}/Foo/bar.app$`); // $ Alert[js/regex-injection] + + new RegExp(`^${process.env.PATH}/bin$`); // $ Alert[js/regex-injection] + + var envVar = process.env.NODE_ENV; // $ Source[js/regex-injection] + new RegExp(envVar); // $ Alert[js/regex-injection] + + // Command line arguments should still be detected + new RegExp(`^${process.argv[1]}/Foo/bar.app$`); // $ Alert[js/regex-injection] + + var argv = process.argv[2]; // $ Source[js/regex-injection] + new RegExp(argv); // $ Alert[js/regex-injection] + + // Regular user input should still be detected + var userInput = req.param("input"); // $ Source[js/regex-injection] + new RegExp(userInput); // $ Alert[js/regex-injection] +}); diff --git a/javascript/ql/test/query-tests/Security/CWE-730/Threat-models-enabled/RegExpInjection.qlref b/javascript/ql/test/query-tests/Security/CWE-730/Threat-models-enabled/RegExpInjection.qlref new file mode 100644 index 000000000000..2bf1a8eee365 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-730/Threat-models-enabled/RegExpInjection.qlref @@ -0,0 +1,2 @@ +query: Security/CWE-730/RegExpInjection.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql From 3f9061abdbb20c1a42da631ddc9e90924db6b290 Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Thu, 31 Jul 2025 12:33:16 +0200 Subject: [PATCH 046/208] Added change note --- .../change-notes/2025-07-31-regexp-injection-threat-model.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 javascript/ql/src/change-notes/2025-07-31-regexp-injection-threat-model.md diff --git a/javascript/ql/src/change-notes/2025-07-31-regexp-injection-threat-model.md b/javascript/ql/src/change-notes/2025-07-31-regexp-injection-threat-model.md new file mode 100644 index 000000000000..f87e10077654 --- /dev/null +++ b/javascript/ql/src/change-notes/2025-07-31-regexp-injection-threat-model.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The `js/regex-injection` query no longer considers environment variables as sources by default. Environment variables can be re-enabled as sources by setting the threat model to include the "environment" category. From 58680c94bc23e50f9c46f3a33ec1c6de7fe4200c Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 31 Jul 2025 13:16:28 +0100 Subject: [PATCH 047/208] Rust: Repair BadCtorInitialization.ql's StdCall using getCanonicalPath. --- rust/ql/src/queries/security/CWE-696/BadCtorInitialization.ql | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rust/ql/src/queries/security/CWE-696/BadCtorInitialization.ql b/rust/ql/src/queries/security/CWE-696/BadCtorInitialization.ql index 80e1043a979c..f0f7cdf0d981 100644 --- a/rust/ql/src/queries/security/CWE-696/BadCtorInitialization.ql +++ b/rust/ql/src/queries/security/CWE-696/BadCtorInitialization.ql @@ -32,8 +32,7 @@ class CtorAttr extends Attr { */ class StdCall extends Expr { StdCall() { - this.(CallExpr).getFunction().(PathExpr).getResolvedCrateOrigin() = "lang:std" or - this.(MethodCallExpr).getResolvedCrateOrigin() = "lang:std" + this.(CallExprBase).getStaticTarget().getCanonicalPath().matches(["std::%", " Date: Thu, 31 Jul 2025 15:45:39 +0100 Subject: [PATCH 048/208] C++: Add an example of double negation to the IR tests. --- .../test/library-tests/ir/ir/PrintAST.expected | 15 ++++++++++++++- .../library-tests/ir/ir/aliased_ir.expected | 17 ++++++++++++----- cpp/ql/test/library-tests/ir/ir/ir.c | 2 ++ cpp/ql/test/library-tests/ir/ir/raw_ir.expected | 17 ++++++++++++----- 4 files changed, 40 insertions(+), 11 deletions(-) diff --git a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected index a3ee6b46bd53..6e62071e7d97 100644 --- a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected +++ b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected @@ -4932,7 +4932,20 @@ ir.c: # 103| Type = [IntType] int # 103| ValueCategory = prvalue(load) # 103| getThen(): [BlockStmt] { ... } -# 104| getStmt(16): [ReturnStmt] return ... +# 105| getStmt(16): [DeclStmt] declaration +# 105| getDeclarationEntry(0): [VariableDeclarationEntry] definition of double_negation +# 105| Type = [IntType] int +# 105| getVariable().getInitializer(): [Initializer] initializer for double_negation +# 105| getExpr(): [NotExpr] ! ... +# 105| Type = [IntType] int +# 105| ValueCategory = prvalue +# 105| getOperand(): [NotExpr] ! ... +# 105| Type = [IntType] int +# 105| ValueCategory = prvalue +# 105| getOperand(): [VariableAccess] x1 +# 105| Type = [IntType] int +# 105| ValueCategory = prvalue(load) +# 106| getStmt(17): [ReturnStmt] return ... ir.cpp: # 1| [TopLevelFunction] void Constants() # 1| : diff --git a/cpp/ql/test/library-tests/ir/ir/aliased_ir.expected b/cpp/ql/test/library-tests/ir/ir/aliased_ir.expected index 6d58656b55ff..d9de4c8eb576 100644 --- a/cpp/ql/test/library-tests/ir/ir/aliased_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/aliased_ir.expected @@ -3969,11 +3969,18 @@ ir.c: # 103| v103_6(void) = NoOp : #-----| Goto -> Block 40 -# 104| Block 40 -# 104| v104_1(void) = NoOp : -# 84| v84_9(void) = ReturnVoid : -# 84| v84_10(void) = AliasedUse : m84_3 -# 84| v84_11(void) = ExitFunction : +# 105| Block 40 +# 105| r105_1(glval) = VariableAddress[double_negation] : +# 105| r105_2(glval) = VariableAddress[x1] : +# 105| r105_3(int) = Load[x1] : &:r105_2, m84_6 +# 105| r105_4(int) = Constant[0] : +# 105| r105_5(bool) = CompareEQ : r105_3, r105_4 +# 105| r105_6(bool) = LogicalNot : r105_5 +# 105| m105_7(int) = Store[double_negation] : &:r105_1, r105_6 +# 106| v106_1(void) = NoOp : +# 84| v84_9(void) = ReturnVoid : +# 84| v84_10(void) = AliasedUse : m84_3 +# 84| v84_11(void) = ExitFunction : ir.cpp: # 1| void Constants() diff --git a/cpp/ql/test/library-tests/ir/ir/ir.c b/cpp/ql/test/library-tests/ir/ir/ir.c index 6e5b37743044..1d11ec6b596a 100644 --- a/cpp/ql/test/library-tests/ir/ir/ir.c +++ b/cpp/ql/test/library-tests/ir/ir/ir.c @@ -101,6 +101,8 @@ void branch_on_integral_in_c(int x1, int x2) { int x_1_and_2 = x1 && x2; if(x_1_and_2) {} if(!x_1_and_2) {} + + int double_negation = !!x1; } // semmle-extractor-options: --microsoft diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected index 8cdb5e8c351f..591b043fda77 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected @@ -3605,11 +3605,18 @@ ir.c: # 103| v103_6(void) = NoOp : #-----| Goto -> Block 40 -# 104| Block 40 -# 104| v104_1(void) = NoOp : -# 84| v84_8(void) = ReturnVoid : -# 84| v84_9(void) = AliasedUse : ~m? -# 84| v84_10(void) = ExitFunction : +# 105| Block 40 +# 105| r105_1(glval) = VariableAddress[double_negation] : +# 105| r105_2(glval) = VariableAddress[x1] : +# 105| r105_3(int) = Load[x1] : &:r105_2, ~m? +# 105| r105_4(int) = Constant[0] : +# 105| r105_5(bool) = CompareEQ : r105_3, r105_4 +# 105| r105_6(bool) = LogicalNot : r105_5 +# 105| mu105_7(int) = Store[double_negation] : &:r105_1, r105_6 +# 106| v106_1(void) = NoOp : +# 84| v84_8(void) = ReturnVoid : +# 84| v84_9(void) = AliasedUse : ~m? +# 84| v84_10(void) = ExitFunction : ir.cpp: # 1| void Constants() From e8eb9be3f603576da02c6b57551c8cad1557b1eb Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Fri, 1 Aug 2025 11:02:59 +0200 Subject: [PATCH 049/208] Add command injection tests for CLI argument parsing libraries --- .../CommandInjection/command-line-libs.js | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js new file mode 100644 index 000000000000..c5621354d91d --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js @@ -0,0 +1,41 @@ +import express from 'express'; +import { Command } from 'commander'; +import { exec } from 'child_process'; +import arg from 'arg'; +const app = express(); +app.use(express.json()); + +app.post('/Command', (req, res) => { + const args = req.body.args || []; // $ MISSING: Source + const program = new Command(); + program.option('--cmd ', 'Command to execute'); + program.parse(args, { from: 'user' }); + const options = program.opts(); + exec(options.cmd); // $ MISSING: Alert +}); + +app.post('/arg', (req, res) => { + const argsArray = req.body.args || []; // $ MISSING: Source + const parsed = arg({ '--cmd': String }, { argv: argsArray }); + exec(parsed['--cmd']); // $ MISSING: Alert +}); + +app.post('/commandLineArgs', (req, res) => { + const commandLineArgs = require('command-line-args'); + const optionDefinitions = [{ name: 'cmd', type: String }]; + const options = commandLineArgs(optionDefinitions, { argv: req.body.args || [] }); // $ MISSING: Source + if (!options.cmd) return res.status(400).send({ error: 'Missing --cmd' }); + exec(options.cmd); // $ MISSING: Alert +}); + +app.post('/yargs', (req, res) => { + const yargs = require('yargs/yargs'); + const args = req.body.args || []; // $ MISSING: Source + const parsed = yargs(args).option('cmd', { + type: 'string', + describe: 'Command to execute', + demandOption: true + }).parse(); + + exec(parsed.cmd); // $ MISSING: Alert +}); From e980798eded71a6a8703ba7e4d164deb9e780ff2 Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Fri, 1 Aug 2025 12:01:30 +0200 Subject: [PATCH 050/208] Added step through yargs/yargs constructor and chained methods. --- .../frameworks/CommandLineArguments.qll | 13 +++++++++++-- .../CommandInjection/CommandInjection.expected | 18 ++++++++++++++++++ .../CommandInjection/command-line-libs.js | 4 ++-- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/CommandLineArguments.qll b/javascript/ql/lib/semmle/javascript/frameworks/CommandLineArguments.qll index 50beb04b8879..8fff526b2bdb 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/CommandLineArguments.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/CommandLineArguments.qll @@ -87,11 +87,18 @@ private class ArgsParseStep extends TaintTracking::SharedTaintStep { override predicate step(DataFlow::Node pred, DataFlow::Node succ) { exists(DataFlow::CallNode call | call = DataFlow::moduleMember("args", "parse").getACall() or - call = DataFlow::moduleImport(["yargs-parser", "minimist", "subarg"]).getACall() + call = + DataFlow::moduleImport(["yargs-parser", "minimist", "subarg", "yargs/yargs", "yargs"]) + .getACall() | succ = call and pred = call.getArgument(0) ) + or + exists(DataFlow::MethodCallNode methodCall | methodCall = yargs() | + pred = methodCall.getReceiver() and + succ = methodCall + ) } } @@ -115,7 +122,9 @@ private API::Node commander() { * Either directly imported as a module, or through some chained method call. */ private DataFlow::SourceNode yargs() { - result = DataFlow::moduleImport("yargs") + result = DataFlow::moduleImport(["yargs", "yargs/yargs"]) + or + result = DataFlow::moduleImport(["yargs", "yargs/yargs"]).getACall() or // script used to generate list of chained methods: https://gist.github.com/erik-krogh/f8afe952c0577f4b563a993e613269ba exists(string method | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected index 22394ec4cb89..4c21013e8f05 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected @@ -21,6 +21,7 @@ | child_process-test.js:75:29:75:31 | cmd | child_process-test.js:73:25:73:31 | req.url | child_process-test.js:75:29:75:31 | cmd | This command line depends on a $@. | child_process-test.js:73:25:73:31 | req.url | user-provided value | | child_process-test.js:83:19:83:36 | req.query.fileName | child_process-test.js:83:19:83:36 | req.query.fileName | child_process-test.js:83:19:83:36 | req.query.fileName | This command line depends on a $@. | child_process-test.js:83:19:83:36 | req.query.fileName | user-provided value | | child_process-test.js:94:11:94:35 | "ping " ... ms.host | child_process-test.js:94:21:94:30 | ctx.params | child_process-test.js:94:11:94:35 | "ping " ... ms.host | This command line depends on a $@. | child_process-test.js:94:21:94:30 | ctx.params | user-provided value | +| command-line-libs.js:40:8:40:17 | parsed.cmd | command-line-libs.js:33:16:33:23 | req.body | command-line-libs.js:40:8:40:17 | parsed.cmd | This command line depends on a $@. | command-line-libs.js:33:16:33:23 | req.body | user-provided value | | exec-sh2.js:10:12:10:57 | cp.spaw ... ptions) | exec-sh2.js:14:25:14:31 | req.url | exec-sh2.js:10:40:10:46 | command | This command line depends on a $@. | exec-sh2.js:14:25:14:31 | req.url | user-provided value | | exec-sh.js:15:12:15:61 | cp.spaw ... ptions) | exec-sh.js:19:25:19:31 | req.url | exec-sh.js:15:44:15:50 | command | This command line depends on a $@. | exec-sh.js:19:25:19:31 | req.url | user-provided value | | execSeries.js:14:41:14:47 | command | execSeries.js:18:34:18:40 | req.url | execSeries.js:14:41:14:47 | command | This command line depends on a $@. | execSeries.js:18:34:18:40 | req.url | user-provided value | @@ -116,6 +117,14 @@ edges | child_process-test.js:73:15:73:38 | url.par ... , true) | child_process-test.js:73:9:73:49 | cmd | provenance | | | child_process-test.js:73:25:73:31 | req.url | child_process-test.js:73:15:73:38 | url.par ... , true) | provenance | | | child_process-test.js:94:21:94:30 | ctx.params | child_process-test.js:94:11:94:35 | "ping " ... ms.host | provenance | | +| command-line-libs.js:33:9:33:34 | args | command-line-libs.js:34:24:34:27 | args | provenance | | +| command-line-libs.js:33:16:33:23 | req.body | command-line-libs.js:33:9:33:34 | args | provenance | | +| command-line-libs.js:34:9:38:12 | parsed | command-line-libs.js:40:8:40:13 | parsed | provenance | | +| command-line-libs.js:34:18:34:28 | yargs(args) | command-line-libs.js:34:18:38:4 | yargs(a ... ue\\n }) | provenance | | +| command-line-libs.js:34:18:38:4 | yargs(a ... ue\\n }) | command-line-libs.js:34:18:38:12 | yargs(a ... parse() | provenance | | +| command-line-libs.js:34:18:38:12 | yargs(a ... parse() | command-line-libs.js:34:9:38:12 | parsed | provenance | | +| command-line-libs.js:34:24:34:27 | args | command-line-libs.js:34:18:34:28 | yargs(args) | provenance | | +| command-line-libs.js:40:8:40:13 | parsed | command-line-libs.js:40:8:40:17 | parsed.cmd | provenance | | | exec-sh2.js:9:17:9:23 | command | exec-sh2.js:10:40:10:46 | command | provenance | | | exec-sh2.js:14:9:14:49 | cmd | exec-sh2.js:15:12:15:14 | cmd | provenance | | | exec-sh2.js:14:15:14:38 | url.par ... , true) | exec-sh2.js:14:9:14:49 | cmd | provenance | | @@ -269,6 +278,15 @@ nodes | child_process-test.js:83:19:83:36 | req.query.fileName | semmle.label | req.query.fileName | | child_process-test.js:94:11:94:35 | "ping " ... ms.host | semmle.label | "ping " ... ms.host | | child_process-test.js:94:21:94:30 | ctx.params | semmle.label | ctx.params | +| command-line-libs.js:33:9:33:34 | args | semmle.label | args | +| command-line-libs.js:33:16:33:23 | req.body | semmle.label | req.body | +| command-line-libs.js:34:9:38:12 | parsed | semmle.label | parsed | +| command-line-libs.js:34:18:34:28 | yargs(args) | semmle.label | yargs(args) | +| command-line-libs.js:34:18:38:4 | yargs(a ... ue\\n }) | semmle.label | yargs(a ... ue\\n }) | +| command-line-libs.js:34:18:38:12 | yargs(a ... parse() | semmle.label | yargs(a ... parse() | +| command-line-libs.js:34:24:34:27 | args | semmle.label | args | +| command-line-libs.js:40:8:40:13 | parsed | semmle.label | parsed | +| command-line-libs.js:40:8:40:17 | parsed.cmd | semmle.label | parsed.cmd | | exec-sh2.js:9:17:9:23 | command | semmle.label | command | | exec-sh2.js:10:40:10:46 | command | semmle.label | command | | exec-sh2.js:14:9:14:49 | cmd | semmle.label | cmd | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js index c5621354d91d..58ae097297d8 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js @@ -30,12 +30,12 @@ app.post('/commandLineArgs', (req, res) => { app.post('/yargs', (req, res) => { const yargs = require('yargs/yargs'); - const args = req.body.args || []; // $ MISSING: Source + const args = req.body.args || []; // $ Source const parsed = yargs(args).option('cmd', { type: 'string', describe: 'Command to execute', demandOption: true }).parse(); - exec(parsed.cmd); // $ MISSING: Alert + exec(parsed.cmd); // $ Alert }); From 6b4e34dd39b994b181b9ac13d2249961e47986e8 Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Fri, 1 Aug 2025 13:12:43 +0200 Subject: [PATCH 051/208] Added a step from parse to opts for commander js --- .../javascript/frameworks/CommandLineArguments.qll | 5 +++++ .../CommandInjection/CommandInjection.expected | 14 ++++++++++++++ .../CWE-078/CommandInjection/command-line-libs.js | 4 ++-- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/CommandLineArguments.qll b/javascript/ql/lib/semmle/javascript/frameworks/CommandLineArguments.qll index 8fff526b2bdb..04825b406264 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/CommandLineArguments.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/CommandLineArguments.qll @@ -95,6 +95,11 @@ private class ArgsParseStep extends TaintTracking::SharedTaintStep { pred = call.getArgument(0) ) or + exists(API::Node commanderNode | commanderNode = commander() | + pred = commanderNode.getMember("parse").getACall().getAnArgument() and + succ = commanderNode.getMember("opts").getACall() + ) + or exists(DataFlow::MethodCallNode methodCall | methodCall = yargs() | pred = methodCall.getReceiver() and succ = methodCall diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected index 4c21013e8f05..7d95ca256f87 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected @@ -21,6 +21,7 @@ | child_process-test.js:75:29:75:31 | cmd | child_process-test.js:73:25:73:31 | req.url | child_process-test.js:75:29:75:31 | cmd | This command line depends on a $@. | child_process-test.js:73:25:73:31 | req.url | user-provided value | | child_process-test.js:83:19:83:36 | req.query.fileName | child_process-test.js:83:19:83:36 | req.query.fileName | child_process-test.js:83:19:83:36 | req.query.fileName | This command line depends on a $@. | child_process-test.js:83:19:83:36 | req.query.fileName | user-provided value | | child_process-test.js:94:11:94:35 | "ping " ... ms.host | child_process-test.js:94:21:94:30 | ctx.params | child_process-test.js:94:11:94:35 | "ping " ... ms.host | This command line depends on a $@. | child_process-test.js:94:21:94:30 | ctx.params | user-provided value | +| command-line-libs.js:14:8:14:18 | options.cmd | command-line-libs.js:9:16:9:23 | req.body | command-line-libs.js:14:8:14:18 | options.cmd | This command line depends on a $@. | command-line-libs.js:9:16:9:23 | req.body | user-provided value | | command-line-libs.js:40:8:40:17 | parsed.cmd | command-line-libs.js:33:16:33:23 | req.body | command-line-libs.js:40:8:40:17 | parsed.cmd | This command line depends on a $@. | command-line-libs.js:33:16:33:23 | req.body | user-provided value | | exec-sh2.js:10:12:10:57 | cp.spaw ... ptions) | exec-sh2.js:14:25:14:31 | req.url | exec-sh2.js:10:40:10:46 | command | This command line depends on a $@. | exec-sh2.js:14:25:14:31 | req.url | user-provided value | | exec-sh.js:15:12:15:61 | cp.spaw ... ptions) | exec-sh.js:19:25:19:31 | req.url | exec-sh.js:15:44:15:50 | command | This command line depends on a $@. | exec-sh.js:19:25:19:31 | req.url | user-provided value | @@ -117,6 +118,12 @@ edges | child_process-test.js:73:15:73:38 | url.par ... , true) | child_process-test.js:73:9:73:49 | cmd | provenance | | | child_process-test.js:73:25:73:31 | req.url | child_process-test.js:73:15:73:38 | url.par ... , true) | provenance | | | child_process-test.js:94:21:94:30 | ctx.params | child_process-test.js:94:11:94:35 | "ping " ... ms.host | provenance | | +| command-line-libs.js:9:9:9:34 | args | command-line-libs.js:12:17:12:20 | args | provenance | | +| command-line-libs.js:9:16:9:23 | req.body | command-line-libs.js:9:9:9:34 | args | provenance | | +| command-line-libs.js:12:17:12:20 | args | command-line-libs.js:13:19:13:32 | program.opts() | provenance | | +| command-line-libs.js:13:9:13:32 | options | command-line-libs.js:14:8:14:14 | options | provenance | | +| command-line-libs.js:13:19:13:32 | program.opts() | command-line-libs.js:13:9:13:32 | options | provenance | | +| command-line-libs.js:14:8:14:14 | options | command-line-libs.js:14:8:14:18 | options.cmd | provenance | | | command-line-libs.js:33:9:33:34 | args | command-line-libs.js:34:24:34:27 | args | provenance | | | command-line-libs.js:33:16:33:23 | req.body | command-line-libs.js:33:9:33:34 | args | provenance | | | command-line-libs.js:34:9:38:12 | parsed | command-line-libs.js:40:8:40:13 | parsed | provenance | | @@ -278,6 +285,13 @@ nodes | child_process-test.js:83:19:83:36 | req.query.fileName | semmle.label | req.query.fileName | | child_process-test.js:94:11:94:35 | "ping " ... ms.host | semmle.label | "ping " ... ms.host | | child_process-test.js:94:21:94:30 | ctx.params | semmle.label | ctx.params | +| command-line-libs.js:9:9:9:34 | args | semmle.label | args | +| command-line-libs.js:9:16:9:23 | req.body | semmle.label | req.body | +| command-line-libs.js:12:17:12:20 | args | semmle.label | args | +| command-line-libs.js:13:9:13:32 | options | semmle.label | options | +| command-line-libs.js:13:19:13:32 | program.opts() | semmle.label | program.opts() | +| command-line-libs.js:14:8:14:14 | options | semmle.label | options | +| command-line-libs.js:14:8:14:18 | options.cmd | semmle.label | options.cmd | | command-line-libs.js:33:9:33:34 | args | semmle.label | args | | command-line-libs.js:33:16:33:23 | req.body | semmle.label | req.body | | command-line-libs.js:34:9:38:12 | parsed | semmle.label | parsed | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js index 58ae097297d8..249bcb1d3baa 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js @@ -6,12 +6,12 @@ const app = express(); app.use(express.json()); app.post('/Command', (req, res) => { - const args = req.body.args || []; // $ MISSING: Source + const args = req.body.args || []; // $ Source const program = new Command(); program.option('--cmd ', 'Command to execute'); program.parse(args, { from: 'user' }); const options = program.opts(); - exec(options.cmd); // $ MISSING: Alert + exec(options.cmd); // $ Alert }); app.post('/arg', (req, res) => { From 39170f327c17e656e68a530200bad809853e136b Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Fri, 1 Aug 2025 13:14:39 +0200 Subject: [PATCH 052/208] Added couple more test cases for commander js --- .../CommandInjection.expected | 36 +++++++++---------- .../CommandInjection/command-line-libs.js | 11 +++++- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected index 7d95ca256f87..75a8d6e8dc26 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected @@ -22,7 +22,7 @@ | child_process-test.js:83:19:83:36 | req.query.fileName | child_process-test.js:83:19:83:36 | req.query.fileName | child_process-test.js:83:19:83:36 | req.query.fileName | This command line depends on a $@. | child_process-test.js:83:19:83:36 | req.query.fileName | user-provided value | | child_process-test.js:94:11:94:35 | "ping " ... ms.host | child_process-test.js:94:21:94:30 | ctx.params | child_process-test.js:94:11:94:35 | "ping " ... ms.host | This command line depends on a $@. | child_process-test.js:94:21:94:30 | ctx.params | user-provided value | | command-line-libs.js:14:8:14:18 | options.cmd | command-line-libs.js:9:16:9:23 | req.body | command-line-libs.js:14:8:14:18 | options.cmd | This command line depends on a $@. | command-line-libs.js:9:16:9:23 | req.body | user-provided value | -| command-line-libs.js:40:8:40:17 | parsed.cmd | command-line-libs.js:33:16:33:23 | req.body | command-line-libs.js:40:8:40:17 | parsed.cmd | This command line depends on a $@. | command-line-libs.js:33:16:33:23 | req.body | user-provided value | +| command-line-libs.js:49:8:49:17 | parsed.cmd | command-line-libs.js:42:16:42:23 | req.body | command-line-libs.js:49:8:49:17 | parsed.cmd | This command line depends on a $@. | command-line-libs.js:42:16:42:23 | req.body | user-provided value | | exec-sh2.js:10:12:10:57 | cp.spaw ... ptions) | exec-sh2.js:14:25:14:31 | req.url | exec-sh2.js:10:40:10:46 | command | This command line depends on a $@. | exec-sh2.js:14:25:14:31 | req.url | user-provided value | | exec-sh.js:15:12:15:61 | cp.spaw ... ptions) | exec-sh.js:19:25:19:31 | req.url | exec-sh.js:15:44:15:50 | command | This command line depends on a $@. | exec-sh.js:19:25:19:31 | req.url | user-provided value | | execSeries.js:14:41:14:47 | command | execSeries.js:18:34:18:40 | req.url | execSeries.js:14:41:14:47 | command | This command line depends on a $@. | execSeries.js:18:34:18:40 | req.url | user-provided value | @@ -124,14 +124,14 @@ edges | command-line-libs.js:13:9:13:32 | options | command-line-libs.js:14:8:14:14 | options | provenance | | | command-line-libs.js:13:19:13:32 | program.opts() | command-line-libs.js:13:9:13:32 | options | provenance | | | command-line-libs.js:14:8:14:14 | options | command-line-libs.js:14:8:14:18 | options.cmd | provenance | | -| command-line-libs.js:33:9:33:34 | args | command-line-libs.js:34:24:34:27 | args | provenance | | -| command-line-libs.js:33:16:33:23 | req.body | command-line-libs.js:33:9:33:34 | args | provenance | | -| command-line-libs.js:34:9:38:12 | parsed | command-line-libs.js:40:8:40:13 | parsed | provenance | | -| command-line-libs.js:34:18:34:28 | yargs(args) | command-line-libs.js:34:18:38:4 | yargs(a ... ue\\n }) | provenance | | -| command-line-libs.js:34:18:38:4 | yargs(a ... ue\\n }) | command-line-libs.js:34:18:38:12 | yargs(a ... parse() | provenance | | -| command-line-libs.js:34:18:38:12 | yargs(a ... parse() | command-line-libs.js:34:9:38:12 | parsed | provenance | | -| command-line-libs.js:34:24:34:27 | args | command-line-libs.js:34:18:34:28 | yargs(args) | provenance | | -| command-line-libs.js:40:8:40:13 | parsed | command-line-libs.js:40:8:40:17 | parsed.cmd | provenance | | +| command-line-libs.js:42:9:42:34 | args | command-line-libs.js:43:24:43:27 | args | provenance | | +| command-line-libs.js:42:16:42:23 | req.body | command-line-libs.js:42:9:42:34 | args | provenance | | +| command-line-libs.js:43:9:47:12 | parsed | command-line-libs.js:49:8:49:13 | parsed | provenance | | +| command-line-libs.js:43:18:43:28 | yargs(args) | command-line-libs.js:43:18:47:4 | yargs(a ... ue\\n }) | provenance | | +| command-line-libs.js:43:18:47:4 | yargs(a ... ue\\n }) | command-line-libs.js:43:18:47:12 | yargs(a ... parse() | provenance | | +| command-line-libs.js:43:18:47:12 | yargs(a ... parse() | command-line-libs.js:43:9:47:12 | parsed | provenance | | +| command-line-libs.js:43:24:43:27 | args | command-line-libs.js:43:18:43:28 | yargs(args) | provenance | | +| command-line-libs.js:49:8:49:13 | parsed | command-line-libs.js:49:8:49:17 | parsed.cmd | provenance | | | exec-sh2.js:9:17:9:23 | command | exec-sh2.js:10:40:10:46 | command | provenance | | | exec-sh2.js:14:9:14:49 | cmd | exec-sh2.js:15:12:15:14 | cmd | provenance | | | exec-sh2.js:14:15:14:38 | url.par ... , true) | exec-sh2.js:14:9:14:49 | cmd | provenance | | @@ -292,15 +292,15 @@ nodes | command-line-libs.js:13:19:13:32 | program.opts() | semmle.label | program.opts() | | command-line-libs.js:14:8:14:14 | options | semmle.label | options | | command-line-libs.js:14:8:14:18 | options.cmd | semmle.label | options.cmd | -| command-line-libs.js:33:9:33:34 | args | semmle.label | args | -| command-line-libs.js:33:16:33:23 | req.body | semmle.label | req.body | -| command-line-libs.js:34:9:38:12 | parsed | semmle.label | parsed | -| command-line-libs.js:34:18:34:28 | yargs(args) | semmle.label | yargs(args) | -| command-line-libs.js:34:18:38:4 | yargs(a ... ue\\n }) | semmle.label | yargs(a ... ue\\n }) | -| command-line-libs.js:34:18:38:12 | yargs(a ... parse() | semmle.label | yargs(a ... parse() | -| command-line-libs.js:34:24:34:27 | args | semmle.label | args | -| command-line-libs.js:40:8:40:13 | parsed | semmle.label | parsed | -| command-line-libs.js:40:8:40:17 | parsed.cmd | semmle.label | parsed.cmd | +| command-line-libs.js:42:9:42:34 | args | semmle.label | args | +| command-line-libs.js:42:16:42:23 | req.body | semmle.label | req.body | +| command-line-libs.js:43:9:47:12 | parsed | semmle.label | parsed | +| command-line-libs.js:43:18:43:28 | yargs(args) | semmle.label | yargs(args) | +| command-line-libs.js:43:18:47:4 | yargs(a ... ue\\n }) | semmle.label | yargs(a ... ue\\n }) | +| command-line-libs.js:43:18:47:12 | yargs(a ... parse() | semmle.label | yargs(a ... parse() | +| command-line-libs.js:43:24:43:27 | args | semmle.label | args | +| command-line-libs.js:49:8:49:13 | parsed | semmle.label | parsed | +| command-line-libs.js:49:8:49:17 | parsed.cmd | semmle.label | parsed.cmd | | exec-sh2.js:9:17:9:23 | command | semmle.label | command | | exec-sh2.js:10:40:10:46 | command | semmle.label | command | | exec-sh2.js:14:9:14:49 | cmd | semmle.label | cmd | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js index 249bcb1d3baa..c9739793a483 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js @@ -5,13 +5,22 @@ import arg from 'arg'; const app = express(); app.use(express.json()); -app.post('/Command', (req, res) => { +app.post('/Command', async (req, res) => { const args = req.body.args || []; // $ Source const program = new Command(); program.option('--cmd ', 'Command to execute'); program.parse(args, { from: 'user' }); const options = program.opts(); exec(options.cmd); // $ Alert + exec(program.cmd); // $ MISSING: Alert + + const program1 = new Command(); + program1 + .command('run