From 5c108e5c126baca78b5feb242861224200c0168f Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 3 Feb 2026 14:26:51 +0000 Subject: [PATCH] Rust: Add a manual model for flow through Option::map. --- .../rust/frameworks/stdlib/core.model.yml | 2 + .../security/CWE-117/LogInjection.expected | 97 +++++++++++-------- .../test/query-tests/security/CWE-117/main.rs | 4 +- 3 files changed, 63 insertions(+), 40 deletions(-) diff --git a/rust/ql/lib/codeql/rust/frameworks/stdlib/core.model.yml b/rust/ql/lib/codeql/rust/frameworks/stdlib/core.model.yml index 71d35099f31..164c9044d40 100644 --- a/rust/ql/lib/codeql/rust/frameworks/stdlib/core.model.yml +++ b/rust/ql/lib/codeql/rust/frameworks/stdlib/core.model.yml @@ -102,6 +102,8 @@ extensions: - ["<_ as core::iter::traits::iterator::Iterator>::chain", "Argument[self]", "ReturnValue", "taint", "manual"] - ["<_ as core::iter::traits::iterator::Iterator>::chain", "Argument[0]", "ReturnValue", "taint", "manual"] - ["<_ as core::iter::traits::iterator::Iterator>::take", "Argument[self]", "ReturnValue", "taint", "manual"] + # Option + - ["::map", "Argument[self]", "ReturnValue", "taint", "manual"] # Pin - ["::new", "Argument[0]", "ReturnValue.Field[core::pin::Pin::pointer]", "value", "manual"] # This model is not precise, but helps in cases where a `Pin` is implicitly dereferenced. diff --git a/rust/ql/test/query-tests/security/CWE-117/LogInjection.expected b/rust/ql/test/query-tests/security/CWE-117/LogInjection.expected index 150772ba49b..9e7cd1e4e8f 100644 --- a/rust/ql/test/query-tests/security/CWE-117/LogInjection.expected +++ b/rust/ql/test/query-tests/security/CWE-117/LogInjection.expected @@ -19,10 +19,11 @@ | main.rs:125:28:125:37 | ...::from | main.rs:111:25:111:38 | ...::args | main.rs:125:28:125:37 | ...::from | Log entry depends on a $@. | main.rs:111:25:111:38 | ...::args | user-provided value | | main.rs:132:9:132:16 | ...::_print | main.rs:129:25:129:37 | ...::var | main.rs:132:9:132:16 | ...::_print | Log entry depends on a $@. | main.rs:129:25:129:37 | ...::var | user-provided value | | main.rs:133:9:133:17 | ...::_eprint | main.rs:129:25:129:37 | ...::var | main.rs:133:9:133:17 | ...::_eprint | Log entry depends on a $@. | main.rs:129:25:129:37 | ...::var | user-provided value | +| main.rs:145:9:145:16 | ...::_print | main.rs:152:28:152:30 | get | main.rs:145:9:145:16 | ...::_print | Log entry depends on a $@. | main.rs:152:28:152:30 | get | user-provided value | edges | main.rs:8:9:8:12 | args [element] | main.rs:9:20:9:23 | args [element] | provenance | | -| main.rs:8:29:8:37 | ...::args | main.rs:8:29:8:39 | ...::args(...) [element] | provenance | Src:MaD:6 | -| main.rs:8:29:8:39 | ...::args(...) [element] | main.rs:8:29:8:49 | ... .collect() [element] | provenance | MaD:10 | +| main.rs:8:29:8:37 | ...::args | main.rs:8:29:8:39 | ...::args(...) [element] | provenance | Src:MaD:7 | +| main.rs:8:29:8:39 | ...::args(...) [element] | main.rs:8:29:8:49 | ... .collect() [element] | provenance | MaD:11 | | main.rs:8:29:8:49 | ... .collect() [element] | main.rs:8:9:8:12 | args [element] | provenance | | | main.rs:9:9:9:16 | username | main.rs:15:11:15:36 | MacroExpr | provenance | | | main.rs:9:9:9:16 | username | main.rs:18:12:18:37 | MacroExpr | provenance | | @@ -30,21 +31,21 @@ edges | main.rs:9:9:9:16 | username | main.rs:26:55:26:62 | username | provenance | | | main.rs:9:9:9:16 | username | main.rs:30:11:30:66 | MacroExpr | provenance | | | main.rs:9:9:9:16 | username | main.rs:52:29:52:36 | username | provenance | | -| main.rs:9:20:9:23 | args [element] | main.rs:9:20:9:30 | args.get(...) [Some, &ref] | provenance | MaD:13 | -| main.rs:9:20:9:30 | args.get(...) [Some, &ref] | main.rs:9:20:9:64 | ... .unwrap_or(...) [&ref] | provenance | MaD:14 | -| main.rs:9:20:9:64 | ... .unwrap_or(...) [&ref] | main.rs:9:20:9:72 | ... .clone() | provenance | MaD:9 | +| main.rs:9:20:9:23 | args [element] | main.rs:9:20:9:30 | args.get(...) [Some, &ref] | provenance | MaD:14 | +| main.rs:9:20:9:30 | args.get(...) [Some, &ref] | main.rs:9:20:9:64 | ... .unwrap_or(...) [&ref] | provenance | MaD:17 | +| main.rs:9:20:9:64 | ... .unwrap_or(...) [&ref] | main.rs:9:20:9:72 | ... .clone() | provenance | MaD:10 | | main.rs:9:20:9:72 | ... .clone() | main.rs:9:9:9:16 | username | provenance | | | main.rs:10:9:10:18 | user_input | main.rs:16:11:16:44 | MacroExpr | provenance | | | main.rs:10:9:10:18 | user_input | main.rs:19:12:19:39 | MacroExpr | provenance | | -| main.rs:10:22:10:34 | ...::var | main.rs:10:22:10:48 | ...::var(...) [Ok] | provenance | Src:MaD:7 | -| main.rs:10:22:10:48 | ...::var(...) [Ok] | main.rs:10:22:10:81 | ... .unwrap_or(...) | provenance | MaD:17 | +| main.rs:10:22:10:34 | ...::var | main.rs:10:22:10:48 | ...::var(...) [Ok] | provenance | Src:MaD:8 | +| main.rs:10:22:10:48 | ...::var(...) [Ok] | main.rs:10:22:10:81 | ... .unwrap_or(...) | provenance | MaD:20 | | main.rs:10:22:10:81 | ... .unwrap_or(...) | main.rs:10:9:10:18 | user_input | provenance | | | main.rs:11:9:11:19 | remote_data | main.rs:17:12:17:46 | MacroExpr | provenance | | | main.rs:11:9:11:19 | remote_data | main.rs:30:11:30:66 | MacroExpr | provenance | | -| main.rs:11:23:11:44 | ...::get | main.rs:11:23:11:71 | ...::get(...) [Ok] | provenance | Src:MaD:5 | -| main.rs:11:23:11:71 | ...::get(...) [Ok] | main.rs:11:23:12:17 | ... .unwrap() | provenance | MaD:16 | -| main.rs:11:23:12:17 | ... .unwrap() | main.rs:11:23:12:24 | ... .text() [Ok] | provenance | MaD:19 | -| main.rs:11:23:12:24 | ... .text() [Ok] | main.rs:11:23:12:61 | ... .unwrap_or(...) | provenance | MaD:17 | +| main.rs:11:23:11:44 | ...::get | main.rs:11:23:11:71 | ...::get(...) [Ok] | provenance | Src:MaD:6 | +| main.rs:11:23:11:71 | ...::get(...) [Ok] | main.rs:11:23:12:17 | ... .unwrap() | provenance | MaD:19 | +| main.rs:11:23:12:17 | ... .unwrap() | main.rs:11:23:12:24 | ... .text() [Ok] | provenance | MaD:22 | +| main.rs:11:23:12:24 | ... .text() [Ok] | main.rs:11:23:12:61 | ... .unwrap_or(...) | provenance | MaD:20 | | main.rs:11:23:12:61 | ... .unwrap_or(...) | main.rs:11:9:11:19 | remote_data | provenance | | | main.rs:15:11:15:36 | MacroExpr | main.rs:15:5:15:9 | ...::log | provenance | MaD:2 Sink:MaD:2 | | main.rs:16:11:16:44 | MacroExpr | main.rs:16:5:16:9 | ...::log | provenance | MaD:2 Sink:MaD:2 | @@ -54,12 +55,12 @@ edges | main.rs:22:9:22:21 | formatted_msg | main.rs:23:11:23:29 | MacroExpr | provenance | | | main.rs:22:33:22:63 | ...::format(...) | main.rs:22:33:22:63 | { ... } | provenance | | | main.rs:22:33:22:63 | ...::must_use(...) | main.rs:22:9:22:21 | formatted_msg | provenance | | -| main.rs:22:33:22:63 | MacroExpr | main.rs:22:33:22:63 | ...::format(...) | provenance | MaD:20 | -| main.rs:22:33:22:63 | { ... } | main.rs:22:33:22:63 | ...::must_use(...) | provenance | MaD:21 | +| main.rs:22:33:22:63 | MacroExpr | main.rs:22:33:22:63 | ...::format(...) | provenance | MaD:23 | +| main.rs:22:33:22:63 | { ... } | main.rs:22:33:22:63 | ...::must_use(...) | provenance | MaD:24 | | main.rs:23:11:23:29 | MacroExpr | main.rs:23:5:23:9 | ...::log | provenance | MaD:2 Sink:MaD:2 | | main.rs:26:9:26:18 | concat_msg | main.rs:27:11:27:26 | MacroExpr | provenance | | | main.rs:26:22:26:62 | ... + ... | main.rs:26:9:26:18 | concat_msg | provenance | | -| main.rs:26:54:26:62 | &username [&ref] | main.rs:26:22:26:62 | ... + ... | provenance | MaD:12 | +| main.rs:26:54:26:62 | &username [&ref] | main.rs:26:22:26:62 | ... + ... | provenance | MaD:13 | | main.rs:26:55:26:62 | username | main.rs:26:54:26:62 | &username [&ref] | provenance | | | main.rs:27:11:27:26 | MacroExpr | main.rs:27:5:27:9 | ...::log | provenance | MaD:2 Sink:MaD:2 | | main.rs:30:11:30:66 | MacroExpr | main.rs:30:5:30:9 | ...::log | provenance | MaD:2 Sink:MaD:2 | @@ -68,7 +69,7 @@ edges | main.rs:56:27:56:40 | ...: ... [&ref] | main.rs:65:38:65:45 | username [&ref] | provenance | | | main.rs:65:9:65:17 | user_info [UserInfo] | main.rs:66:28:66:36 | user_info [UserInfo] | provenance | | | main.rs:65:21:65:59 | UserInfo {...} [UserInfo] | main.rs:65:9:65:17 | user_info [UserInfo] | provenance | | -| main.rs:65:38:65:45 | username [&ref] | main.rs:65:38:65:57 | username.to_string() | provenance | MaD:8 | +| main.rs:65:38:65:45 | username [&ref] | main.rs:65:38:65:57 | username.to_string() | provenance | MaD:9 | | main.rs:65:38:65:57 | username.to_string() | main.rs:65:21:65:59 | UserInfo {...} [UserInfo] | provenance | | | main.rs:66:11:66:41 | MacroExpr | main.rs:66:5:66:9 | ...::log | provenance | MaD:2 Sink:MaD:2 | | main.rs:66:28:66:36 | user_info [UserInfo] | main.rs:66:28:66:41 | user_info.name | provenance | | @@ -81,9 +82,9 @@ edges | main.rs:111:13:111:21 | user_data | main.rs:121:15:121:75 | MacroExpr | provenance | | | main.rs:111:13:111:21 | user_data | main.rs:124:38:124:46 | user_data | provenance | | | main.rs:111:13:111:21 | user_data | main.rs:125:39:125:47 | user_data | provenance | | -| main.rs:111:25:111:38 | ...::args | main.rs:111:25:111:40 | ...::args(...) [element] | provenance | Src:MaD:6 | -| main.rs:111:25:111:40 | ...::args(...) [element] | main.rs:111:25:111:47 | ... .nth(...) [Some] | provenance | MaD:11 | -| main.rs:111:25:111:47 | ... .nth(...) [Some] | main.rs:111:25:111:67 | ... .unwrap_or_default() | provenance | MaD:15 | +| main.rs:111:25:111:38 | ...::args | main.rs:111:25:111:40 | ...::args(...) [element] | provenance | Src:MaD:7 | +| main.rs:111:25:111:40 | ...::args(...) [element] | main.rs:111:25:111:47 | ... .nth(...) [Some] | provenance | MaD:12 | +| main.rs:111:25:111:47 | ... .nth(...) [Some] | main.rs:111:25:111:67 | ... .unwrap_or_default() | provenance | MaD:18 | | main.rs:111:25:111:67 | ... .unwrap_or_default() | main.rs:111:13:111:21 | user_data | provenance | | | main.rs:114:15:114:35 | MacroExpr | main.rs:114:9:114:13 | ...::log | provenance | MaD:2 Sink:MaD:2 | | main.rs:115:15:115:38 | MacroExpr | main.rs:115:9:115:13 | ...::log | provenance | MaD:2 Sink:MaD:2 | @@ -91,38 +92,49 @@ edges | main.rs:117:16:117:37 | MacroExpr | main.rs:117:9:117:14 | ...::log | provenance | MaD:2 Sink:MaD:2 | | main.rs:118:16:118:37 | MacroExpr | main.rs:118:9:118:14 | ...::log | provenance | MaD:2 Sink:MaD:2 | | main.rs:121:15:121:75 | MacroExpr | main.rs:121:9:121:13 | ...::log | provenance | MaD:2 Sink:MaD:2 | -| main.rs:124:38:124:46 | user_data | main.rs:124:38:124:54 | user_data.clone() | provenance | MaD:9 | +| main.rs:124:38:124:46 | user_data | main.rs:124:38:124:54 | user_data.clone() | provenance | MaD:10 | | main.rs:124:38:124:54 | user_data.clone() | main.rs:124:27:124:36 | ...::from | provenance | MaD:1 Sink:MaD:1 | | main.rs:125:39:125:47 | user_data | main.rs:125:28:125:37 | ...::from | provenance | MaD:1 Sink:MaD:1 | | main.rs:129:13:129:21 | user_data | main.rs:132:18:132:38 | MacroExpr | provenance | | | main.rs:129:13:129:21 | user_data | main.rs:133:19:133:49 | MacroExpr | provenance | | -| main.rs:129:25:129:37 | ...::var | main.rs:129:25:129:45 | ...::var(...) [Ok] | provenance | Src:MaD:7 | -| main.rs:129:25:129:45 | ...::var(...) [Ok] | main.rs:129:25:129:65 | ... .unwrap_or_default() | provenance | MaD:18 | +| main.rs:129:25:129:37 | ...::var | main.rs:129:25:129:45 | ...::var(...) [Ok] | provenance | Src:MaD:8 | +| main.rs:129:25:129:45 | ...::var(...) [Ok] | main.rs:129:25:129:65 | ... .unwrap_or_default() | provenance | MaD:21 | | main.rs:129:25:129:65 | ... .unwrap_or_default() | main.rs:129:13:129:21 | user_data | provenance | | | main.rs:132:18:132:38 | MacroExpr | main.rs:132:9:132:16 | ...::_print | provenance | MaD:4 Sink:MaD:4 | | main.rs:133:19:133:49 | MacroExpr | main.rs:133:9:133:17 | ...::_eprint | provenance | MaD:3 Sink:MaD:3 | +| main.rs:142:32:142:59 | ...: Option::<...> | main.rs:143:22:143:27 | o_path | provenance | | +| main.rs:143:13:143:18 | m_path | main.rs:145:26:145:31 | m_path | provenance | | +| main.rs:143:22:143:27 | o_path | main.rs:143:22:143:38 | o_path.map(...) | provenance | MaD:15 | +| main.rs:143:22:143:38 | o_path.map(...) | main.rs:143:13:143:18 | m_path | provenance | | +| main.rs:145:18:145:40 | MacroExpr | main.rs:145:9:145:16 | ...::_print | provenance | MaD:4 Sink:MaD:4 | +| main.rs:145:26:145:31 | m_path | main.rs:145:26:145:40 | m_path.unwrap() | provenance | MaD:16 | +| main.rs:145:26:145:40 | m_path.unwrap() | main.rs:145:18:145:40 | MacroExpr | provenance | | +| main.rs:152:28:152:30 | get | main.rs:142:32:142:59 | ...: Option::<...> | provenance | Src:MaD:5 | models | 1 | Sink: ::from; Argument[0]; log-injection | | 2 | Sink: log::__private_api::log; Argument[0]; log-injection | | 3 | Sink: std::io::stdio::_eprint; Argument[0]; log-injection | | 4 | Sink: std::io::stdio::_print; Argument[0]; log-injection | -| 5 | Source: reqwest::blocking::get; ReturnValue.Field[core::result::Result::Ok(0)]; remote | -| 6 | Source: std::env::args; ReturnValue.Element; commandargs | -| 7 | Source: std::env::var; ReturnValue.Field[core::result::Result::Ok(0)]; environment | -| 8 | Summary: <_ as alloc::string::ToString>::to_string; Argument[self].Reference; ReturnValue; taint | -| 9 | Summary: <_ as core::clone::Clone>::clone; Argument[self].Reference; ReturnValue; value | -| 10 | Summary: <_ as core::iter::traits::iterator::Iterator>::collect; Argument[self].Element; ReturnValue.Element; value | -| 11 | Summary: <_ as core::iter::traits::iterator::Iterator>::nth; Argument[self].Reference.Element; ReturnValue.Field[core::option::Option::Some(0)]; value | -| 12 | Summary: ::add; Argument[0].Reference; ReturnValue; taint | -| 13 | Summary: ::deref; Argument[self].Reference.Element; ReturnValue.Reference.Element; value | -| 14 | Summary: ::unwrap_or; Argument[self].Field[core::option::Option::Some(0)]; ReturnValue; value | -| 15 | Summary: ::unwrap_or_default; Argument[self].Field[core::option::Option::Some(0)]; ReturnValue; value | -| 16 | Summary: ::unwrap; Argument[self].Field[core::result::Result::Ok(0)]; ReturnValue; value | -| 17 | Summary: ::unwrap_or; Argument[self].Field[core::result::Result::Ok(0)]; ReturnValue; value | -| 18 | Summary: ::unwrap_or_default; Argument[self].Field[core::result::Result::Ok(0)]; ReturnValue; value | -| 19 | Summary: ::text; Argument[self]; ReturnValue.Field[core::result::Result::Ok(0)]; taint | -| 20 | Summary: alloc::fmt::format; Argument[0]; ReturnValue; taint | -| 21 | Summary: core::hint::must_use; Argument[0]; ReturnValue; value | +| 5 | Source: axum::routing::method_routing::get; Argument[0].Parameter[0..7]; remote | +| 6 | Source: reqwest::blocking::get; ReturnValue.Field[core::result::Result::Ok(0)]; remote | +| 7 | Source: std::env::args; ReturnValue.Element; commandargs | +| 8 | Source: std::env::var; ReturnValue.Field[core::result::Result::Ok(0)]; environment | +| 9 | Summary: <_ as alloc::string::ToString>::to_string; Argument[self].Reference; ReturnValue; taint | +| 10 | Summary: <_ as core::clone::Clone>::clone; Argument[self].Reference; ReturnValue; value | +| 11 | Summary: <_ as core::iter::traits::iterator::Iterator>::collect; Argument[self].Element; ReturnValue.Element; value | +| 12 | Summary: <_ as core::iter::traits::iterator::Iterator>::nth; Argument[self].Reference.Element; ReturnValue.Field[core::option::Option::Some(0)]; value | +| 13 | Summary: ::add; Argument[0].Reference; ReturnValue; taint | +| 14 | Summary: ::deref; Argument[self].Reference.Element; ReturnValue.Reference.Element; value | +| 15 | Summary: ::map; Argument[self]; ReturnValue; taint | +| 16 | Summary: ::unwrap; Argument[self].Field[core::option::Option::Some(0)]; ReturnValue; value | +| 17 | Summary: ::unwrap_or; Argument[self].Field[core::option::Option::Some(0)]; ReturnValue; value | +| 18 | Summary: ::unwrap_or_default; Argument[self].Field[core::option::Option::Some(0)]; ReturnValue; value | +| 19 | Summary: ::unwrap; Argument[self].Field[core::result::Result::Ok(0)]; ReturnValue; value | +| 20 | Summary: ::unwrap_or; Argument[self].Field[core::result::Result::Ok(0)]; ReturnValue; value | +| 21 | Summary: ::unwrap_or_default; Argument[self].Field[core::result::Result::Ok(0)]; ReturnValue; value | +| 22 | Summary: ::text; Argument[self]; ReturnValue.Field[core::result::Result::Ok(0)]; taint | +| 23 | Summary: alloc::fmt::format; Argument[0]; ReturnValue; taint | +| 24 | Summary: core::hint::must_use; Argument[0]; ReturnValue; value | nodes | main.rs:8:9:8:12 | args [element] | semmle.label | args [element] | | main.rs:8:29:8:37 | ...::args | semmle.label | ...::args | @@ -209,4 +221,13 @@ nodes | main.rs:132:18:132:38 | MacroExpr | semmle.label | MacroExpr | | main.rs:133:9:133:17 | ...::_eprint | semmle.label | ...::_eprint | | main.rs:133:19:133:49 | MacroExpr | semmle.label | MacroExpr | +| main.rs:142:32:142:59 | ...: Option::<...> | semmle.label | ...: Option::<...> | +| main.rs:143:13:143:18 | m_path | semmle.label | m_path | +| main.rs:143:22:143:27 | o_path | semmle.label | o_path | +| main.rs:143:22:143:38 | o_path.map(...) | semmle.label | o_path.map(...) | +| main.rs:145:9:145:16 | ...::_print | semmle.label | ...::_print | +| main.rs:145:18:145:40 | MacroExpr | semmle.label | MacroExpr | +| main.rs:145:26:145:31 | m_path | semmle.label | m_path | +| main.rs:145:26:145:40 | m_path.unwrap() | semmle.label | m_path.unwrap() | +| main.rs:152:28:152:30 | get | semmle.label | get | subpaths diff --git a/rust/ql/test/query-tests/security/CWE-117/main.rs b/rust/ql/test/query-tests/security/CWE-117/main.rs index 0d6931a9ae0..324c823a4ca 100644 --- a/rust/ql/test/query-tests/security/CWE-117/main.rs +++ b/rust/ql/test/query-tests/security/CWE-117/main.rs @@ -142,13 +142,13 @@ mod axum_tests { async fn my_axum_handler_1(o_path: Option>) -> &'static str { let m_path = o_path.map(|x| x); - println!("{:?}", m_path.unwrap()); // $ MISSING: Alert[rust/log-injection]=post_handler + println!("{:?}", m_path.unwrap()); // $ Alert[rust/log-injection]=post_handler "" } async fn test_axum() { let app = Router::<()>::new() - .route("/{a}", get(my_axum_handler_1)); // $ MISSING: Source=post_handler + .route("/{a}", get(my_axum_handler_1)); // $ Source=post_handler } }