Skip to content

Rust: Add tests for web frameworks as taint sources #19466

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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). |
5 changes: 5 additions & 0 deletions rust/ql/test/library-tests/dataflow/sources/options.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,8 @@ 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" }
- actix-web = { version = "4.10.2" }
- axum = { version = "0.8.4" }
- serde_json = { version = "1.0.140" }
199 changes: 199 additions & 0 deletions rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
#![allow(deprecated)]

fn sink<T>(_: 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>) -> 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<MyStruct>) -> 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<MyStruct>) -> 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<String>, // $ 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("/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();

// ...
}
}

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>) -> 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>) -> 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>) -> 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);

// ...
}
}

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<String>) -> &'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<HashMap<String, String>>) -> &'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<serde_json::Value>) -> &'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("/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));

// ...
}
}