From 310c02f1fb67c8d025e7abe6d88c77d055e90811 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 6 May 2025 23:02:52 +0100 Subject: [PATCH 1/8] Rust: Add a dataflow sources test for the Poem web fraemework. --- .../dataflow/sources/TaintSources.expected | 5 ++ .../dataflow/sources/options.yml | 2 + .../dataflow/sources/web_frameworks.rs | 81 +++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs diff --git a/rust/ql/test/library-tests/dataflow/sources/TaintSources.expected b/rust/ql/test/library-tests/dataflow/sources/TaintSources.expected index ba7eeae00081..7a5fd2d000f5 100644 --- a/rust/ql/test/library-tests/dataflow/sources/TaintSources.expected +++ b/rust/ql/test/library-tests/dataflow/sources/TaintSources.expected @@ -47,3 +47,8 @@ | test.rs:369:25:369:43 | ...::open | Flow source 'FileSource' of type file (DEFAULT). | | test.rs:377:22:377:35 | ...::stdin | Flow source 'StdInSource' of type stdin (DEFAULT). | | test.rs:386:16:386:29 | ...::args | Flow source 'CommandLineArgs' of type commandargs (DEFAULT). | +| web_frameworks.rs:13:31:13:31 | a | Flow source 'RemoteSource' of type remote (DEFAULT). | +| web_frameworks.rs:22:31:22:36 | TuplePat | Flow source 'RemoteSource' of type remote (DEFAULT). | +| web_frameworks.rs:44:31:44:45 | MyStruct {...} | Flow source 'RemoteSource' of type remote (DEFAULT). | +| web_frameworks.rs:52:31:52:32 | ms | Flow source 'RemoteSource' of type remote (DEFAULT). | +| web_frameworks.rs:61:15:61:15 | a | Flow source 'RemoteSource' of type remote (DEFAULT). | diff --git a/rust/ql/test/library-tests/dataflow/sources/options.yml b/rust/ql/test/library-tests/dataflow/sources/options.yml index 9b4565f1e1ad..d351460c8a7c 100644 --- a/rust/ql/test/library-tests/dataflow/sources/options.yml +++ b/rust/ql/test/library-tests/dataflow/sources/options.yml @@ -7,3 +7,5 @@ qltest_dependencies: - http = { version = "1.2.0" } - tokio = { version = "1.43.0", features = ["full"] } - futures = { version = "0.3" } + - poem = { version = "3.1.10" } + - serde = { version = "1.0.219" } diff --git a/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs b/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs new file mode 100644 index 000000000000..2fd067a0583e --- /dev/null +++ b/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs @@ -0,0 +1,81 @@ +#![allow(deprecated)] + +fn sink(_: T) { } + +// --- tests --- + +mod poem_test { + use poem::{get, handler, web::Path, web::Query, Route, Server, listener::TcpListener}; + use serde::Deserialize; + use crate::web_frameworks::sink; + + #[handler] + fn my_poem_handler_1(Path(a): Path) -> String { // $ Alert[rust/summary/taint-sources] + sink(a.as_str()); // $ MISSING: hasTaintFlow + sink(a.as_bytes()); // $ MISSING: hasTaintFlow + sink(a); // $ MISSING: hasTaintFlow + + "".to_string() + } + + #[handler] + fn my_poem_handler_2(Path((a, b)): Path<(String, String)>) -> String { // $ Alert[rust/summary/taint-sources] + sink(a); // $ MISSING: hasTaintFlow + sink(b); // $ MISSING: hasTaintFlow + + "".to_string() + } + + #[handler] + fn my_poem_handler_3(path: Path<(String, String)>) -> String { // $ MISSING: Alert[rust/summary/taint-sources] + sink(&path.0); // $ MISSING: hasTaintFlow + sink(&path.1); // $ MISSING: hasTaintFlow + + "".to_string() + } + + #[derive(Deserialize)] + struct MyStruct { + a: String, + b: String, + } + + #[handler] + fn my_poem_handler_4(Path(MyStruct {a, b}): Path) -> String { // $ Alert[rust/summary/taint-sources] + sink(a); // $ MISSING: hasTaintFlow + sink(b); // $ MISSING: hasTaintFlow + + "".to_string() + } + + #[handler] + fn my_poem_handler_5(Path(ms): Path) -> String { // $ Alert[rust/summary/taint-sources] + sink(ms.a); // $ MISSING: hasTaintFlow + sink(ms.b); // $ MISSING: hasTaintFlow + + "".to_string() + } + + #[handler] + fn my_poem_handler_6( + Query(a): Query, // $ Alert[rust/summary/taint-sources] + ) -> String { + sink(a); // $ MISSING: hasTaintFlow + + "".to_string() + } + + async fn test_poem() { + let app = Route::new() + .at("/1/:a", get(my_poem_handler_1)) + .at("/2/:a/:b", get(my_poem_handler_2)) + .at("/3/:a/:b", get(my_poem_handler_3)) + .at("/4/:a/:b", get(my_poem_handler_4)) + .at("/4/:a/:b", get(my_poem_handler_5)) + .at("/5/:a/", get(my_poem_handler_6)); + + _ = Server::new(TcpListener::bind("0.0.0.0:3000")).run(app).await.unwrap(); + + // ... + } +} From e56519d959e9f966e2f11d6273669d5b14a82b46 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 6 May 2025 23:30:42 +0100 Subject: [PATCH 2/8] Rust: Add a dataflow sources test for the Actix web fraemework. --- .../dataflow/sources/options.yml | 1 + .../dataflow/sources/web_frameworks.rs | 47 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/rust/ql/test/library-tests/dataflow/sources/options.yml b/rust/ql/test/library-tests/dataflow/sources/options.yml index d351460c8a7c..1f1dbdec49d4 100644 --- a/rust/ql/test/library-tests/dataflow/sources/options.yml +++ b/rust/ql/test/library-tests/dataflow/sources/options.yml @@ -9,3 +9,4 @@ qltest_dependencies: - futures = { version = "0.3" } - poem = { version = "3.1.10" } - serde = { version = "1.0.219" } + - actix-web = { version = "4.10.2" } diff --git a/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs b/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs index 2fd067a0583e..0f27d57639ba 100644 --- a/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs +++ b/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs @@ -79,3 +79,50 @@ mod poem_test { // ... } } + +mod actix_test { + use actix_web::{get, web, App, HttpServer}; + use crate::web_frameworks::sink; + + async fn my_actix_handler_1(path: web::Path) -> String { // $ MISSING: Alert[rust/summary/taint-sources] + let a = path.into_inner(); + sink(a.as_str()); // $ MISSING: hasTaintFlow + sink(a.as_bytes()); // $ MISSING: hasTaintFlow + sink(a); // $ MISSING: hasTaintFlow + + "".to_string() + } + + async fn my_actix_handler_2(path: web::Path<(String, String)>) -> String { // $ MISSING: Alert[rust/summary/taint-sources] + let (a, b) = path.into_inner(); + + sink(a); // $ MISSING: hasTaintFlow + sink(b); // $ MISSING: hasTaintFlow + + "".to_string() + } + + async fn my_actix_handler_3(web::Query(a): web::Query) -> String { // $ MISSING: Alert[rust/summary/taint-sources] + sink(a); // $ MISSING: hasTaintFlow + + "".to_string() + } + + #[get("/4/{a}")] + async fn my_actix_handler_4(path: web::Path) -> String { // $ MISSING: Alert[rust/summary/taint-sources] + let a = path.into_inner(); + sink(a); // $ MISSING: hasTaintFlow + + "".to_string() + } + + async fn test_actix() { + let app = App::new() + .route("/1/{a}", web::get().to(my_actix_handler_1)) + .route("/2/{a}/{b}", web::get().to(my_actix_handler_2)) + .route("/3/{a}", web::get().to(my_actix_handler_3)) + .service(my_actix_handler_4); + + // ... + } +} From 49ff96746525a81caddb53d73d41602b09a98e26 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 7 May 2025 09:24:47 +0100 Subject: [PATCH 3/8] Rust: Add a dataflow sources test for the Axum web fraemework. --- .../dataflow/sources/options.yml | 2 + .../dataflow/sources/web_frameworks.rs | 71 +++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/rust/ql/test/library-tests/dataflow/sources/options.yml b/rust/ql/test/library-tests/dataflow/sources/options.yml index 1f1dbdec49d4..5b3ef166b15e 100644 --- a/rust/ql/test/library-tests/dataflow/sources/options.yml +++ b/rust/ql/test/library-tests/dataflow/sources/options.yml @@ -10,3 +10,5 @@ qltest_dependencies: - poem = { version = "3.1.10" } - serde = { version = "1.0.219" } - actix-web = { version = "4.10.2" } + - axum = { version = "0.8.4" } + - serde_json = { version = "1.0.140" } diff --git a/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs b/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs index 0f27d57639ba..395010e7ef78 100644 --- a/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs +++ b/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs @@ -126,3 +126,74 @@ mod actix_test { // ... } } + +mod axum_test { + use axum::Router; + use axum::routing::get; + use axum::extract::{Path, Query, Request, Json}; + use std::collections::HashMap; + use crate::web_frameworks::sink; + + async fn my_axum_handler_1(Path(a): Path) -> &'static str { // $ MISSING: Alert[rust/summary/taint-sources] + sink(a.as_str()); // $ MISSING: hasTaintFlow + sink(a.as_bytes()); // $ MISSING: hasTaintFlow + sink(a); // $ MISSING: hasTaintFlow + + "" + } + + async fn my_axum_handler_2(Path((a, b)): Path<(String, String)>) -> &'static str { // $ MISSING: Alert[rust/summary/taint-sources] + sink(a); // $ MISSING: hasTaintFlow + sink(b); // $ MISSING: hasTaintFlow + + "" + } + + async fn my_axum_handler_3(Query(params): Query>) -> &'static str { // $ MISSING: Alert[rust/summary/taint-sources] + for (key, value) in params { + sink(key); // $ MISSING: hasTaintFlow + sink(value); // $ MISSING: hasTaintFlow + } + + "" + } + + async fn my_axum_handler_4(request: Request) -> &'static str { // $ MISSING: Alert[rust/summary/taint-sources] + sink(request.body()); // $ MISSING: hasTaintFlow + request.headers().get("header").unwrap(); // $ MISSING: hasTaintFlow + sink(request.into_body()); // $ MISSING: hasTaintFlow + + "" + } + + async fn my_axum_handler_5(Json(payload): Json) -> &'static str { // $ MISSING: Alert[rust/summary/taint-sources] + sink(payload.as_str()); // $ MISSING: hasTaintFlow + sink(payload); // $ MISSING: hasTaintFlow + + "" + } + + async fn my_axum_handler_6(body: String) -> &'static str { // $ MISSING: Alert[rust/summary/taint-sources] + sink(body); // $ MISSING: hasTaintFlow + + "" + } + + async fn my_axum_handler_7(body: String) -> &'static str { // $ MISSING: Alert[rust/summary/taint-sources] + sink(body); // $ MISSING: hasTaintFlow + + "" + } + + async fn test_axum() { + let app = Router::<()>::new() + .route("/foo/{a}", get(my_axum_handler_1)) + .route("/bar/{a}/{b}", get(my_axum_handler_2)) + .route("/1/:a", get(my_axum_handler_3)) + .route("/2/:a", get(my_axum_handler_4)) + .route("/3/:a", get(my_axum_handler_5)) + .route("/4/:a", get(my_axum_handler_6).get(my_axum_handler_7)); + + // ... + } +} From 19f86fd67f5bdcf9846117d5d932277e419f7aad Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 7 May 2025 10:45:27 +0100 Subject: [PATCH 4/8] Rust: Address confusing / typo'd paths. --- .../dataflow/sources/web_frameworks.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs b/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs index 395010e7ef78..e3ab61274d23 100644 --- a/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs +++ b/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs @@ -71,8 +71,8 @@ mod poem_test { .at("/2/:a/:b", get(my_poem_handler_2)) .at("/3/:a/:b", get(my_poem_handler_3)) .at("/4/:a/:b", get(my_poem_handler_4)) - .at("/4/:a/:b", get(my_poem_handler_5)) - .at("/5/:a/", get(my_poem_handler_6)); + .at("/5/:a/:b", get(my_poem_handler_5)) + .at("/6/:a/", get(my_poem_handler_6)); _ = Server::new(TcpListener::bind("0.0.0.0:3000")).run(app).await.unwrap(); @@ -187,12 +187,12 @@ mod axum_test { async fn test_axum() { let app = Router::<()>::new() - .route("/foo/{a}", get(my_axum_handler_1)) - .route("/bar/{a}/{b}", get(my_axum_handler_2)) - .route("/1/:a", get(my_axum_handler_3)) - .route("/2/:a", get(my_axum_handler_4)) - .route("/3/:a", get(my_axum_handler_5)) - .route("/4/:a", get(my_axum_handler_6).get(my_axum_handler_7)); + .route("/1/{a}", get(my_axum_handler_1)) + .route("/2/{a}/{b}", get(my_axum_handler_2)) + .route("/3/:a", get(my_axum_handler_3)) + .route("/4/:a", get(my_axum_handler_4)) + .route("/5/:a", get(my_axum_handler_5)) + .route("/67/:a", get(my_axum_handler_6).get(my_axum_handler_7)); // ... } From 08fcf6114fcae46e1044f614b61370f263a5f43f Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 12 May 2025 16:01:52 +0100 Subject: [PATCH 5/8] Apply suggestions from code review Co-authored-by: Simon Friis Vindum --- .../ql/test/library-tests/dataflow/sources/web_frameworks.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs b/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs index e3ab61274d23..ec742c71459c 100644 --- a/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs +++ b/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs @@ -1,4 +1,3 @@ -#![allow(deprecated)] fn sink(_: T) { } @@ -7,7 +6,7 @@ fn sink(_: T) { } mod poem_test { use poem::{get, handler, web::Path, web::Query, Route, Server, listener::TcpListener}; use serde::Deserialize; - use crate::web_frameworks::sink; + use super::sink; #[handler] fn my_poem_handler_1(Path(a): Path) -> String { // $ Alert[rust/summary/taint-sources] @@ -74,7 +73,7 @@ mod poem_test { .at("/5/:a/:b", get(my_poem_handler_5)) .at("/6/:a/", get(my_poem_handler_6)); - _ = Server::new(TcpListener::bind("0.0.0.0:3000")).run(app).await.unwrap(); + Server::new(TcpListener::bind("0.0.0.0:3000")).run(app).await.unwrap(); // ... } From bf8cdffffad96160043d9c3f67fd22070d31391c Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 12 May 2025 16:06:33 +0100 Subject: [PATCH 6/8] Update rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs Co-authored-by: Simon Friis Vindum --- rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs b/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs index ec742c71459c..2e4ac22257fc 100644 --- a/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs +++ b/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs @@ -80,7 +80,7 @@ mod poem_test { } mod actix_test { - use actix_web::{get, web, App, HttpServer}; + use actix_web::{get, web, App}; use crate::web_frameworks::sink; async fn my_actix_handler_1(path: web::Path) -> String { // $ MISSING: Alert[rust/summary/taint-sources] From 402a84f755082a046259d9d852999c3abd3e2c88 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 12 May 2025 16:17:26 +0100 Subject: [PATCH 7/8] Update rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs Co-authored-by: Simon Friis Vindum --- rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs b/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs index 2e4ac22257fc..a6949c569c0a 100644 --- a/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs +++ b/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs @@ -81,7 +81,7 @@ mod poem_test { mod actix_test { use actix_web::{get, web, App}; - use crate::web_frameworks::sink; + use super::sink; async fn my_actix_handler_1(path: web::Path) -> String { // $ MISSING: Alert[rust/summary/taint-sources] let a = path.into_inner(); From 7c98fa87dae9c004ac32cca0c14b5669d9fe28cc Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 12 May 2025 16:18:16 +0100 Subject: [PATCH 8/8] Rust: One more bit of cleanup. --- .../dataflow/sources/TaintSources.expected | 10 +++++----- .../library-tests/dataflow/sources/web_frameworks.rs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/rust/ql/test/library-tests/dataflow/sources/TaintSources.expected b/rust/ql/test/library-tests/dataflow/sources/TaintSources.expected index 7a5fd2d000f5..11d0df45d590 100644 --- a/rust/ql/test/library-tests/dataflow/sources/TaintSources.expected +++ b/rust/ql/test/library-tests/dataflow/sources/TaintSources.expected @@ -47,8 +47,8 @@ | test.rs:369:25:369:43 | ...::open | Flow source 'FileSource' of type file (DEFAULT). | | test.rs:377:22:377:35 | ...::stdin | Flow source 'StdInSource' of type stdin (DEFAULT). | | test.rs:386:16:386:29 | ...::args | Flow source 'CommandLineArgs' of type commandargs (DEFAULT). | -| web_frameworks.rs:13:31:13:31 | a | Flow source 'RemoteSource' of type remote (DEFAULT). | -| web_frameworks.rs:22:31:22:36 | TuplePat | Flow source 'RemoteSource' of type remote (DEFAULT). | -| web_frameworks.rs:44:31:44:45 | MyStruct {...} | Flow source 'RemoteSource' of type remote (DEFAULT). | -| web_frameworks.rs:52:31:52:32 | ms | Flow source 'RemoteSource' of type remote (DEFAULT). | -| web_frameworks.rs:61:15:61:15 | a | Flow source 'RemoteSource' of type remote (DEFAULT). | +| web_frameworks.rs:12:31:12:31 | a | Flow source 'RemoteSource' of type remote (DEFAULT). | +| web_frameworks.rs:21:31:21:36 | TuplePat | Flow source 'RemoteSource' of type remote (DEFAULT). | +| web_frameworks.rs:43:31:43:45 | MyStruct {...} | Flow source 'RemoteSource' of type remote (DEFAULT). | +| web_frameworks.rs:51:31:51:32 | ms | Flow source 'RemoteSource' of type remote (DEFAULT). | +| web_frameworks.rs:60:15:60:15 | a | Flow source 'RemoteSource' of type remote (DEFAULT). | diff --git a/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs b/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs index a6949c569c0a..6bfee08a3d2d 100644 --- a/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs +++ b/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs @@ -131,7 +131,7 @@ mod axum_test { use axum::routing::get; use axum::extract::{Path, Query, Request, Json}; use std::collections::HashMap; - use crate::web_frameworks::sink; + use super::sink; async fn my_axum_handler_1(Path(a): Path) -> &'static str { // $ MISSING: Alert[rust/summary/taint-sources] sink(a.as_str()); // $ MISSING: hasTaintFlow