Skip to content

Commit 2b6e428

Browse files
authored
Merge pull request #19466 from geoffw0/web
Rust: Add tests for web frameworks as taint sources
2 parents 0c0e1d0 + 7c98fa8 commit 2b6e428

File tree

3 files changed

+208
-0
lines changed

3 files changed

+208
-0
lines changed

rust/ql/test/library-tests/dataflow/sources/TaintSources.expected

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,8 @@
4747
| test.rs:369:25:369:43 | ...::open | Flow source 'FileSource' of type file (DEFAULT). |
4848
| test.rs:377:22:377:35 | ...::stdin | Flow source 'StdInSource' of type stdin (DEFAULT). |
4949
| test.rs:386:16:386:29 | ...::args | Flow source 'CommandLineArgs' of type commandargs (DEFAULT). |
50+
| web_frameworks.rs:12:31:12:31 | a | Flow source 'RemoteSource' of type remote (DEFAULT). |
51+
| web_frameworks.rs:21:31:21:36 | TuplePat | Flow source 'RemoteSource' of type remote (DEFAULT). |
52+
| web_frameworks.rs:43:31:43:45 | MyStruct {...} | Flow source 'RemoteSource' of type remote (DEFAULT). |
53+
| web_frameworks.rs:51:31:51:32 | ms | Flow source 'RemoteSource' of type remote (DEFAULT). |
54+
| web_frameworks.rs:60:15:60:15 | a | Flow source 'RemoteSource' of type remote (DEFAULT). |

rust/ql/test/library-tests/dataflow/sources/options.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,8 @@ qltest_dependencies:
77
- http = { version = "1.2.0" }
88
- tokio = { version = "1.43.0", features = ["full"] }
99
- futures = { version = "0.3" }
10+
- poem = { version = "3.1.10" }
11+
- serde = { version = "1.0.219" }
12+
- actix-web = { version = "4.10.2" }
13+
- axum = { version = "0.8.4" }
14+
- serde_json = { version = "1.0.140" }
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
2+
fn sink<T>(_: T) { }
3+
4+
// --- tests ---
5+
6+
mod poem_test {
7+
use poem::{get, handler, web::Path, web::Query, Route, Server, listener::TcpListener};
8+
use serde::Deserialize;
9+
use super::sink;
10+
11+
#[handler]
12+
fn my_poem_handler_1(Path(a): Path<String>) -> String { // $ Alert[rust/summary/taint-sources]
13+
sink(a.as_str()); // $ MISSING: hasTaintFlow
14+
sink(a.as_bytes()); // $ MISSING: hasTaintFlow
15+
sink(a); // $ MISSING: hasTaintFlow
16+
17+
"".to_string()
18+
}
19+
20+
#[handler]
21+
fn my_poem_handler_2(Path((a, b)): Path<(String, String)>) -> String { // $ Alert[rust/summary/taint-sources]
22+
sink(a); // $ MISSING: hasTaintFlow
23+
sink(b); // $ MISSING: hasTaintFlow
24+
25+
"".to_string()
26+
}
27+
28+
#[handler]
29+
fn my_poem_handler_3(path: Path<(String, String)>) -> String { // $ MISSING: Alert[rust/summary/taint-sources]
30+
sink(&path.0); // $ MISSING: hasTaintFlow
31+
sink(&path.1); // $ MISSING: hasTaintFlow
32+
33+
"".to_string()
34+
}
35+
36+
#[derive(Deserialize)]
37+
struct MyStruct {
38+
a: String,
39+
b: String,
40+
}
41+
42+
#[handler]
43+
fn my_poem_handler_4(Path(MyStruct {a, b}): Path<MyStruct>) -> String { // $ Alert[rust/summary/taint-sources]
44+
sink(a); // $ MISSING: hasTaintFlow
45+
sink(b); // $ MISSING: hasTaintFlow
46+
47+
"".to_string()
48+
}
49+
50+
#[handler]
51+
fn my_poem_handler_5(Path(ms): Path<MyStruct>) -> String { // $ Alert[rust/summary/taint-sources]
52+
sink(ms.a); // $ MISSING: hasTaintFlow
53+
sink(ms.b); // $ MISSING: hasTaintFlow
54+
55+
"".to_string()
56+
}
57+
58+
#[handler]
59+
fn my_poem_handler_6(
60+
Query(a): Query<String>, // $ Alert[rust/summary/taint-sources]
61+
) -> String {
62+
sink(a); // $ MISSING: hasTaintFlow
63+
64+
"".to_string()
65+
}
66+
67+
async fn test_poem() {
68+
let app = Route::new()
69+
.at("/1/:a", get(my_poem_handler_1))
70+
.at("/2/:a/:b", get(my_poem_handler_2))
71+
.at("/3/:a/:b", get(my_poem_handler_3))
72+
.at("/4/:a/:b", get(my_poem_handler_4))
73+
.at("/5/:a/:b", get(my_poem_handler_5))
74+
.at("/6/:a/", get(my_poem_handler_6));
75+
76+
Server::new(TcpListener::bind("0.0.0.0:3000")).run(app).await.unwrap();
77+
78+
// ...
79+
}
80+
}
81+
82+
mod actix_test {
83+
use actix_web::{get, web, App};
84+
use super::sink;
85+
86+
async fn my_actix_handler_1(path: web::Path<String>) -> String { // $ MISSING: Alert[rust/summary/taint-sources]
87+
let a = path.into_inner();
88+
sink(a.as_str()); // $ MISSING: hasTaintFlow
89+
sink(a.as_bytes()); // $ MISSING: hasTaintFlow
90+
sink(a); // $ MISSING: hasTaintFlow
91+
92+
"".to_string()
93+
}
94+
95+
async fn my_actix_handler_2(path: web::Path<(String, String)>) -> String { // $ MISSING: Alert[rust/summary/taint-sources]
96+
let (a, b) = path.into_inner();
97+
98+
sink(a); // $ MISSING: hasTaintFlow
99+
sink(b); // $ MISSING: hasTaintFlow
100+
101+
"".to_string()
102+
}
103+
104+
async fn my_actix_handler_3(web::Query(a): web::Query<String>) -> String { // $ MISSING: Alert[rust/summary/taint-sources]
105+
sink(a); // $ MISSING: hasTaintFlow
106+
107+
"".to_string()
108+
}
109+
110+
#[get("/4/{a}")]
111+
async fn my_actix_handler_4(path: web::Path<String>) -> String { // $ MISSING: Alert[rust/summary/taint-sources]
112+
let a = path.into_inner();
113+
sink(a); // $ MISSING: hasTaintFlow
114+
115+
"".to_string()
116+
}
117+
118+
async fn test_actix() {
119+
let app = App::new()
120+
.route("/1/{a}", web::get().to(my_actix_handler_1))
121+
.route("/2/{a}/{b}", web::get().to(my_actix_handler_2))
122+
.route("/3/{a}", web::get().to(my_actix_handler_3))
123+
.service(my_actix_handler_4);
124+
125+
// ...
126+
}
127+
}
128+
129+
mod axum_test {
130+
use axum::Router;
131+
use axum::routing::get;
132+
use axum::extract::{Path, Query, Request, Json};
133+
use std::collections::HashMap;
134+
use super::sink;
135+
136+
async fn my_axum_handler_1(Path(a): Path<String>) -> &'static str { // $ MISSING: Alert[rust/summary/taint-sources]
137+
sink(a.as_str()); // $ MISSING: hasTaintFlow
138+
sink(a.as_bytes()); // $ MISSING: hasTaintFlow
139+
sink(a); // $ MISSING: hasTaintFlow
140+
141+
""
142+
}
143+
144+
async fn my_axum_handler_2(Path((a, b)): Path<(String, String)>) -> &'static str { // $ MISSING: Alert[rust/summary/taint-sources]
145+
sink(a); // $ MISSING: hasTaintFlow
146+
sink(b); // $ MISSING: hasTaintFlow
147+
148+
""
149+
}
150+
151+
async fn my_axum_handler_3(Query(params): Query<HashMap<String, String>>) -> &'static str { // $ MISSING: Alert[rust/summary/taint-sources]
152+
for (key, value) in params {
153+
sink(key); // $ MISSING: hasTaintFlow
154+
sink(value); // $ MISSING: hasTaintFlow
155+
}
156+
157+
""
158+
}
159+
160+
async fn my_axum_handler_4(request: Request) -> &'static str { // $ MISSING: Alert[rust/summary/taint-sources]
161+
sink(request.body()); // $ MISSING: hasTaintFlow
162+
request.headers().get("header").unwrap(); // $ MISSING: hasTaintFlow
163+
sink(request.into_body()); // $ MISSING: hasTaintFlow
164+
165+
""
166+
}
167+
168+
async fn my_axum_handler_5(Json(payload): Json<serde_json::Value>) -> &'static str { // $ MISSING: Alert[rust/summary/taint-sources]
169+
sink(payload.as_str()); // $ MISSING: hasTaintFlow
170+
sink(payload); // $ MISSING: hasTaintFlow
171+
172+
""
173+
}
174+
175+
async fn my_axum_handler_6(body: String) -> &'static str { // $ MISSING: Alert[rust/summary/taint-sources]
176+
sink(body); // $ MISSING: hasTaintFlow
177+
178+
""
179+
}
180+
181+
async fn my_axum_handler_7(body: String) -> &'static str { // $ MISSING: Alert[rust/summary/taint-sources]
182+
sink(body); // $ MISSING: hasTaintFlow
183+
184+
""
185+
}
186+
187+
async fn test_axum() {
188+
let app = Router::<()>::new()
189+
.route("/1/{a}", get(my_axum_handler_1))
190+
.route("/2/{a}/{b}", get(my_axum_handler_2))
191+
.route("/3/:a", get(my_axum_handler_3))
192+
.route("/4/:a", get(my_axum_handler_4))
193+
.route("/5/:a", get(my_axum_handler_5))
194+
.route("/67/:a", get(my_axum_handler_6).get(my_axum_handler_7));
195+
196+
// ...
197+
}
198+
}

0 commit comments

Comments
 (0)