-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Rust: Add some flow source models #18069
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
3fa93e5
ca424d1
a85ad4e
be40085
3747698
e64f139
176e9a4
292b29b
20eaaa5
ed67dae
194f967
fe2d0b6
75a3c93
d8b58f2
f2f577f
4c50c08
d38f0ee
bded708
1090164
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| /** | ||
| * This file imports all models of frameworks and libraries. | ||
| */ | ||
|
|
||
| private import codeql.rust.frameworks.Reqwest | ||
| private import codeql.rust.frameworks.stdlib.Env |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| /** | ||
| * Provides modeling for the `reqwest` library. | ||
| */ | ||
|
|
||
| private import rust | ||
| private import codeql.rust.Concepts | ||
|
|
||
| /** | ||
| * A call to `reqwest::get` or `reqwest::blocking::get`. | ||
| */ | ||
| private class ReqwestGet extends RemoteSource::Range { | ||
| ReqwestGet() { | ||
| exists(CallExpr ce | | ||
| this.asExpr().getExpr() = ce and | ||
| ce.getExpr().(PathExpr).getPath().getResolvedCrateOrigin().matches("%reqwest") and | ||
| ce.getExpr().(PathExpr).getPath().getResolvedPath() = ["crate::get", "crate::blocking::get"] | ||
| ) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| /** | ||
| * Provides modeling for the `std::env` library. | ||
| */ | ||
|
|
||
| private import rust | ||
| private import codeql.rust.Concepts | ||
|
|
||
| /** | ||
| * A call to `std::env::args` or `std::env::args_os`. | ||
| */ | ||
| private class StdEnvArgs extends CommandLineArgsSource::Range { | ||
| StdEnvArgs() { | ||
| this.asExpr().getExpr().(CallExpr).getExpr().(PathExpr).getPath().getResolvedPath() = | ||
| ["crate::env::args", "crate::env::args_os"] | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * A call to `std::env::current_dir`, `std::env::current_exe` or `std::env::home_dir`. | ||
| */ | ||
| private class StdEnvDir extends CommandLineArgsSource::Range { | ||
| StdEnvDir() { | ||
| this.asExpr().getExpr().(CallExpr).getExpr().(PathExpr).getPath().getResolvedPath() = | ||
| ["crate::env::current_dir", "crate::env::current_exe", "crate::env::home_dir"] | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * A call to `std::env::var`, `std::env::var_os`, `std::env::vars` or `std::env::vars_os`. | ||
| */ | ||
| private class StdEnvVar extends EnvironmentSource::Range { | ||
| StdEnvVar() { | ||
| this.asExpr().getExpr().(CallExpr).getExpr().(PathExpr).getPath().getResolvedPath() = | ||
| ["crate::env::var", "crate::env::var_os", "crate::env::vars", "crate::env::vars_os"] | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| /** | ||
| * @name Taint Sources | ||
| * @description List all sources of untrusted input that have been idenfitied | ||
| * in the database. | ||
| * @kind problem | ||
| * @problem.severity info | ||
| * @id rust/summary/taint-sources | ||
| * @tags summary | ||
| */ | ||
|
|
||
| import rust | ||
| import codeql.rust.Concepts | ||
|
|
||
| from ThreatModelSource s, string defaultString | ||
| where | ||
| if s instanceof ActiveThreatModelSource then defaultString = " (DEFAULT)" else defaultString = "" | ||
| select s, | ||
| "Flow source '" + s.getSourceType() + "' of type " + s.getThreatModel() + defaultString + "." |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| import rust | ||
| import codeql.rust.dataflow.DataFlow | ||
| import codeql.rust.Concepts | ||
| import utils.InlineFlowTest | ||
|
|
||
| /** | ||
| * Configuration for flow from any threat model source to an argument of the function `sink`. | ||
| */ | ||
| module MyFlowConfig implements DataFlow::ConfigSig { | ||
| predicate isSource(DataFlow::Node source) { source instanceof ThreatModelSource } | ||
|
|
||
| predicate isSink(DataFlow::Node sink) { | ||
| any(CallExpr call | call.getExpr().(PathExpr).getPath().getResolvedPath() = "crate::test::sink") | ||
| .getArgList() | ||
| .getAnArg() = sink.asExpr().getExpr() | ||
| } | ||
| } | ||
|
|
||
| module MyFlowTest = TaintFlowTest<MyFlowConfig>; | ||
|
|
||
| import MyFlowTest |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| | test.rs:8:10:8:30 | ...::var(...) | Flow source 'EnvironmentSource' of type environment. | | ||
| | test.rs:9:10:9:33 | ...::var_os(...) | Flow source 'EnvironmentSource' of type environment. | | ||
| | test.rs:11:16:11:36 | ...::var(...) | Flow source 'EnvironmentSource' of type environment. | | ||
| | test.rs:12:16:12:39 | ...::var_os(...) | Flow source 'EnvironmentSource' of type environment. | | ||
| | test.rs:17:25:17:40 | ...::vars(...) | Flow source 'EnvironmentSource' of type environment. | | ||
| | test.rs:22:25:22:43 | ...::vars_os(...) | Flow source 'EnvironmentSource' of type environment. | | ||
| | test.rs:29:29:29:44 | ...::args(...) | Flow source 'CommandLineArgs' of type commandargs. | | ||
| | test.rs:32:16:32:31 | ...::args(...) | Flow source 'CommandLineArgs' of type commandargs. | | ||
| | test.rs:33:16:33:34 | ...::args_os(...) | Flow source 'CommandLineArgs' of type commandargs. | | ||
| | test.rs:40:16:40:31 | ...::args(...) | Flow source 'CommandLineArgs' of type commandargs. | | ||
| | test.rs:44:16:44:34 | ...::args_os(...) | Flow source 'CommandLineArgs' of type commandargs. | | ||
| | test.rs:50:15:50:37 | ...::current_dir(...) | Flow source 'CommandLineArgs' of type commandargs. | | ||
| | test.rs:51:15:51:37 | ...::current_exe(...) | Flow source 'CommandLineArgs' of type commandargs. | | ||
| | test.rs:52:16:52:35 | ...::home_dir(...) | Flow source 'CommandLineArgs' of type commandargs. | | ||
| | test.rs:60:26:60:70 | ...::get(...) | Flow source 'RemoteSource' of type remote (DEFAULT). | | ||
| | test.rs:63:26:63:70 | ...::get(...) | Flow source 'RemoteSource' of type remote (DEFAULT). | | ||
| | test.rs:66:26:66:60 | ...::get(...) | Flow source 'RemoteSource' of type remote (DEFAULT). | |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| query: queries/summary/TaintSources.ql | ||
| postprocess: utils/InlineExpectationsTestQuery.ql |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| qltest_cargo_check: true | ||
| qltest_dependencies: | ||
| - reqwest = { version = "0.12.9", features = ["blocking"] } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
|
|
||
| // --- stubs for the "reqwest" library --- | ||
|
|
||
| /* | ||
| --- we don't seem to have a way to use this, hence we currently test against the real reqwest library | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @redsun82 I tried to stub
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (fixing this will probably be follow-up work, what we have works well enough right now) |
||
| #[derive(Debug)] | ||
| pub struct Error { } | ||
|
|
||
| pub mod blocking { | ||
| pub struct Response { } | ||
| impl Response { | ||
| pub fn text(self) -> Result<String, super::Error> { | ||
| Ok("".to_string()) | ||
| } | ||
| } | ||
|
|
||
| pub fn get<T>(url: T) -> Result<Response, super::Error> { | ||
| let _url = url; | ||
|
|
||
| Ok(Response {}) | ||
| } | ||
| } | ||
|
|
||
| pub struct Response { } | ||
| impl Response { | ||
| pub async fn text(self) -> Result<String, Error> { | ||
| Ok("".to_string()) | ||
| } | ||
| } | ||
|
|
||
| pub async fn get<T>(url: T) -> Result<Response, Error> { | ||
| let _url = url; | ||
|
|
||
| Ok(Response {}) | ||
| } | ||
| */ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| #![allow(deprecated)] | ||
|
|
||
| fn sink<T>(_: T) { } | ||
|
|
||
| // --- tests --- | ||
|
|
||
| fn test_env_vars() { | ||
| sink(std::env::var("HOME")); // $ Alert[rust/summary/taint-sources] hasTaintFlow | ||
| sink(std::env::var_os("PATH")); // $ Alert[rust/summary/taint-sources] hasTaintFlow | ||
|
|
||
| let var1 = std::env::var("HOME").expect("HOME not set"); // $ Alert[rust/summary/taint-sources] | ||
| let var2 = std::env::var_os("PATH").unwrap(); // $ Alert[rust/summary/taint-sources] | ||
|
|
||
| sink(var1); // $ MISSING: hasTaintFlow | ||
| sink(var2); // $ MISSING: hasTaintFlow | ||
geoffw0 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| for (key, value) in std::env::vars() { // $ Alert[rust/summary/taint-sources] | ||
| sink(key); // $ MISSING: hasTaintFlow | ||
| sink(value); // $ MISSING: hasTaintFlow | ||
| } | ||
|
|
||
| for (key, value) in std::env::vars_os() { // $ Alert[rust/summary/taint-sources] | ||
| sink(key); // $ MISSING: hasTaintFlow | ||
| sink(value); // $ MISSING: hasTaintFlow | ||
| } | ||
| } | ||
|
|
||
| fn test_env_args() { | ||
| let args: Vec<String> = std::env::args().collect(); // $ Alert[rust/summary/taint-sources] | ||
| let my_path = &args[0]; | ||
| let arg1 = &args[1]; | ||
| let arg2 = std::env::args().nth(2).unwrap(); // $ Alert[rust/summary/taint-sources] | ||
| let arg3 = std::env::args_os().nth(3).unwrap(); // $ Alert[rust/summary/taint-sources] | ||
|
|
||
| sink(my_path); // $ MISSING: hasTaintFlow | ||
| sink(arg1); // $ MISSING: hasTaintFlow | ||
| sink(arg2); // $ MISSING: hasTaintFlow | ||
| sink(arg3); // $ MISSING: hasTaintFlow | ||
|
|
||
| for arg in std::env::args() { // $ Alert[rust/summary/taint-sources] | ||
| sink(arg); // $ MISSING: hasTaintFlow | ||
| } | ||
|
|
||
| for arg in std::env::args_os() { // $ Alert[rust/summary/taint-sources] | ||
| sink(arg); // $ MISSING: hasTaintFlow | ||
| } | ||
| } | ||
|
|
||
| fn test_env_dirs() { | ||
| let dir = std::env::current_dir().expect("FAILED"); // $ Alert[rust/summary/taint-sources] | ||
| let exe = std::env::current_exe().expect("FAILED"); // $ Alert[rust/summary/taint-sources] | ||
| let home = std::env::home_dir().expect("FAILED"); // $ Alert[rust/summary/taint-sources] | ||
|
|
||
| sink(dir); // $ MISSING: hasTaintFlow | ||
| sink(exe); // $ MISSING: hasTaintFlow | ||
| sink(home); // $ MISSING: hasTaintFlow | ||
| } | ||
|
|
||
| async fn test_reqwest() -> Result<(), reqwest::Error> { | ||
| let remote_string1 = reqwest::blocking::get("http://example.com/")?.text()?; // $ Alert[rust/summary/taint-sources] | ||
| sink(remote_string1); // $ MISSING: hasTaintFlow | ||
|
|
||
| let remote_string2 = reqwest::blocking::get("http://example.com/").unwrap().text().unwrap(); // $ Alert[rust/summary/taint-sources] | ||
| sink(remote_string2); // $ MISSING: hasTaintFlow | ||
|
|
||
| let remote_string3 = reqwest::get("http://example.com/").await?.text().await?; // $ Alert[rust/summary/taint-sources] | ||
| sink(remote_string3); // $ MISSING: hasTaintFlow | ||
|
|
||
| Ok(()) | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.