Skip to content

Commit 0cc9331

Browse files
committed
Rust: Split off sources/file test.
1 parent a631130 commit 0cc9331

File tree

9 files changed

+1731
-307
lines changed

9 files changed

+1731
-307
lines changed

rust/ql/test/library-tests/dataflow/sources/file/Cargo.lock

Lines changed: 802 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/ql/test/library-tests/dataflow/sources/file/InlineFlow.expected

Lines changed: 537 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* @kind path-problem
3+
*/
4+
5+
import rust
6+
import codeql.rust.dataflow.DataFlow
7+
import codeql.rust.Concepts
8+
import utils.test.InlineFlowTest
9+
10+
/**
11+
* Configuration for flow from any threat model source to an argument of the function `sink`.
12+
*/
13+
module MyFlowConfig implements DataFlow::ConfigSig {
14+
predicate isSource(DataFlow::Node source) { source instanceof ThreatModelSource }
15+
16+
predicate isSink(DataFlow::Node sink) {
17+
any(CallExpr call |
18+
call.getFunction().(PathExpr).getPath().getSegment().getIdentifier().getText() = "sink"
19+
).getArgList().getAnArg() = sink.asExpr().getExpr()
20+
}
21+
22+
predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
23+
// flow out from any content at the sink.
24+
isSink(node) and
25+
exists(c)
26+
}
27+
}
28+
29+
module MyFlowTest = TaintFlowTest<MyFlowConfig>;
30+
31+
import MyFlowTest
32+
import PathGraph
33+
34+
from PathNode source, PathNode sink
35+
where flowPath(source, sink)
36+
select sink, source, sink, "$@", source, source.toString()
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
| test.rs:12:31:12:43 | ...::read | Flow source 'FileSource' of type file (DEFAULT). |
2+
| test.rs:12:31:12:43 | ...::read | Flow source 'FileSource' of type file (DEFAULT). |
3+
| test.rs:17:31:17:38 | ...::read | Flow source 'FileSource' of type file (DEFAULT). |
4+
| test.rs:17:31:17:38 | ...::read | Flow source 'FileSource' of type file (DEFAULT). |
5+
| test.rs:22:22:22:39 | ...::read_to_string | Flow source 'FileSource' of type file (DEFAULT). |
6+
| test.rs:22:22:22:39 | ...::read_to_string | Flow source 'FileSource' of type file (DEFAULT). |
7+
| test.rs:29:22:29:25 | path | Flow source 'FileSource' of type file (DEFAULT). |
8+
| test.rs:43:27:43:35 | file_name | Flow source 'FileSource' of type file (DEFAULT). |
9+
| test.rs:54:22:54:25 | path | Flow source 'FileSource' of type file (DEFAULT). |
10+
| test.rs:55:27:55:35 | file_name | Flow source 'FileSource' of type file (DEFAULT). |
11+
| test.rs:65:22:65:34 | ...::read_link | Flow source 'FileSource' of type file (DEFAULT). |
12+
| test.rs:74:31:74:45 | ...::read | Flow source 'FileSource' of type file (DEFAULT). |
13+
| test.rs:79:31:79:45 | ...::read | Flow source 'FileSource' of type file (DEFAULT). |
14+
| test.rs:84:22:84:46 | ...::read_to_string | Flow source 'FileSource' of type file (DEFAULT). |
15+
| test.rs:90:26:90:29 | path | Flow source 'FileSource' of type file (DEFAULT). |
16+
| test.rs:90:26:90:29 | path | Flow source 'FileSource' of type file (DEFAULT). |
17+
| test.rs:91:31:91:39 | file_name | Flow source 'FileSource' of type file (DEFAULT). |
18+
| test.rs:91:31:91:39 | file_name | Flow source 'FileSource' of type file (DEFAULT). |
19+
| test.rs:97:22:97:41 | ...::read_link | Flow source 'FileSource' of type file (DEFAULT). |
20+
| test.rs:107:20:107:38 | ...::open | Flow source 'FileSource' of type file (DEFAULT). |
21+
| test.rs:140:50:140:53 | open | Flow source 'FileSource' of type file (DEFAULT). |
22+
| test.rs:147:67:147:70 | open | Flow source 'FileSource' of type file (DEFAULT). |
23+
| test.rs:154:101:154:104 | open | Flow source 'FileSource' of type file (DEFAULT). |
24+
| test.rs:164:21:164:39 | ...::open | Flow source 'FileSource' of type file (DEFAULT). |
25+
| test.rs:165:21:165:39 | ...::open | Flow source 'FileSource' of type file (DEFAULT). |
26+
| test.rs:173:21:173:39 | ...::open | Flow source 'FileSource' of type file (DEFAULT). |
27+
| test.rs:185:20:185:40 | ...::open | Flow source 'FileSource' of type file (DEFAULT). |
28+
| test.rs:231:52:231:55 | open | Flow source 'FileSource' of type file (DEFAULT). |
29+
| test.rs:241:21:241:41 | ...::open | Flow source 'FileSource' of type file (DEFAULT). |
30+
| test.rs:242:21:242:41 | ...::open | Flow source 'FileSource' of type file (DEFAULT). |
31+
| test.rs:250:21:250:41 | ...::open | Flow source 'FileSource' of type file (DEFAULT). |
32+
| test.rs:262:20:262:44 | ...::open | Flow source 'FileSource' of type file (DEFAULT). |
33+
| test.rs:273:56:273:59 | open | Flow source 'FileSource' of type file (DEFAULT). |
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
query: queries/summary/TaintSources.ql
2+
postprocess: utils/test/InlineExpectationsTestQuery.ql
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
qltest_cargo_check: true
2+
qltest_dependencies:
3+
- async-std = { version = "1.13.1" }
4+
- bytes = { version = "1.10.1" }
5+
- futures = { version = "0.3" }
6+
- tokio = { version = "1.43.0", features = ["full"] }
Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
fn sink<T>(_: T) { }
2+
3+
// --- tests ---
4+
5+
use std::fs;
6+
use std::io::Read;
7+
use tokio::io::AsyncReadExt;
8+
use async_std::io::ReadExt;
9+
10+
fn test_fs() -> Result<(), Box<dyn std::error::Error>> {
11+
{
12+
let buffer: Vec<u8> = std::fs::read("file.bin")?; // $ Alert[rust/summary/taint-sources]
13+
sink(buffer); // $ hasTaintFlow="file.bin"
14+
}
15+
16+
{
17+
let buffer: Vec<u8> = fs::read("file.bin")?; // $ Alert[rust/summary/taint-sources]
18+
sink(buffer); // $ hasTaintFlow="file.bin"
19+
}
20+
21+
{
22+
let buffer = fs::read_to_string("file.txt")?; // $ Alert[rust/summary/taint-sources]
23+
sink(buffer); // $ hasTaintFlow="file.txt"
24+
}
25+
26+
for entry in fs::read_dir("directory")? {
27+
let e = entry?;
28+
29+
let path = e.path(); // $ Alert[rust/summary/taint-sources]
30+
sink(path.clone()); // $ hasTaintFlow
31+
sink(path.clone().as_path()); // $ hasTaintFlow
32+
sink(path.clone().into_os_string()); // $ MISSING: hasTaintFlow
33+
sink(std::path::PathBuf::from(path.clone().into_boxed_path())); // $ MISSING: hasTaintFlow
34+
sink(path.clone().as_os_str()); // $ MISSING: hasTaintFlow
35+
sink(path.clone().as_mut_os_str()); // $ MISSING: hasTaintFlow
36+
sink(path.to_str()); // $ MISSING: hasTaintFlow
37+
sink(path.to_path_buf()); // $ MISSING: hasTaintFlow
38+
sink(path.file_name().unwrap()); // $ MISSING: hasTaintFlow
39+
sink(path.extension().unwrap()); // $ MISSING: hasTaintFlow
40+
sink(path.canonicalize().unwrap()); // $ MISSING: hasTaintFlow
41+
sink(path); // $ hasTaintFlow
42+
43+
let file_name = e.file_name(); // $ Alert[rust/summary/taint-sources]
44+
sink(file_name.clone()); // $ hasTaintFlow
45+
sink(file_name.clone().into_string().unwrap()); // $ MISSING: hasTaintFlow
46+
sink(file_name.to_str().unwrap()); // $ MISSING: hasTaintFlow
47+
sink(file_name.to_string_lossy().to_mut()); // $ MISSING: hasTaintFlow
48+
sink(file_name.clone().as_encoded_bytes()); // $ MISSING: hasTaintFlow
49+
sink(file_name); // $ hasTaintFlow
50+
}
51+
for entry in std::path::Path::new("directory").read_dir()? {
52+
let e = entry?;
53+
54+
let path = e.path(); // $ Alert[rust/summary/taint-sources]
55+
let file_name = e.file_name(); // $ Alert[rust/summary/taint-sources]
56+
}
57+
for entry in std::path::PathBuf::from("directory").read_dir()? {
58+
let e = entry?;
59+
60+
let path = e.path(); // $ MISSING: Alert[rust/summary/taint-sources]
61+
let file_name = e.file_name(); // $ MISSING: Alert[rust/summary/taint-sources]
62+
}
63+
64+
{
65+
let target = fs::read_link("symlink.txt")?; // $ Alert[rust/summary/taint-sources]
66+
sink(target); // $ hasTaintFlow="symlink.txt"
67+
}
68+
69+
Ok(())
70+
}
71+
72+
async fn test_tokio_fs() -> Result<(), Box<dyn std::error::Error>> {
73+
{
74+
let buffer: Vec<u8> = tokio::fs::read("file.bin").await?; // $ Alert[rust/summary/taint-sources]
75+
sink(buffer); // $ hasTaintFlow="file.bin"
76+
}
77+
78+
{
79+
let buffer: Vec<u8> = tokio::fs::read("file.bin").await?; // $ Alert[rust/summary/taint-sources]
80+
sink(buffer); // $ hasTaintFlow="file.bin"
81+
}
82+
83+
{
84+
let buffer = tokio::fs::read_to_string("file.txt").await?; // $ Alert[rust/summary/taint-sources]
85+
sink(buffer); // $ hasTaintFlow="file.txt"
86+
}
87+
88+
let mut read_dir = tokio::fs::read_dir("directory").await?;
89+
for entry in read_dir.next_entry().await? {
90+
let path = entry.path(); // $ Alert[rust/summary/taint-sources]
91+
let file_name = entry.file_name(); // $ Alert[rust/summary/taint-sources]
92+
sink(path); // $ hasTaintFlow
93+
sink(file_name); // $ hasTaintFlow
94+
}
95+
96+
{
97+
let target = tokio::fs::read_link("symlink.txt").await?; // $ Alert[rust/summary/taint-sources]
98+
sink(target); // $ hasTaintFlow="symlink.txt"
99+
}
100+
101+
Ok(())
102+
}
103+
104+
fn test_io_file() -> std::io::Result<()> {
105+
// --- file ---
106+
107+
let mut file = std::fs::File::open("file.txt")?; // $ Alert[rust/summary/taint-sources]
108+
109+
{
110+
let mut buffer = [0u8; 100];
111+
let _bytes = file.read(&mut buffer)?;
112+
sink(&buffer); // $ hasTaintFlow="file.txt"
113+
}
114+
115+
{
116+
let mut buffer = Vec::<u8>::new();
117+
let _bytes = file.read_to_end(&mut buffer)?;
118+
sink(&buffer); // $ hasTaintFlow="file.txt"
119+
}
120+
121+
{
122+
let mut buffer = String::new();
123+
let _bytes = file.read_to_string(&mut buffer)?;
124+
sink(&buffer); // $ hasTaintFlow="file.txt"
125+
}
126+
127+
{
128+
let mut buffer = [0; 100];
129+
file.read_exact(&mut buffer)?;
130+
sink(&buffer); // $ hasTaintFlow="file.txt"
131+
}
132+
133+
for byte in file.bytes() {
134+
sink(byte); // $ hasTaintFlow="file.txt"
135+
}
136+
137+
// --- OpenOptions ---
138+
139+
{
140+
let mut f1 = std::fs::OpenOptions::new().open("f1.txt").unwrap(); // $ Alert[rust/summary/taint-sources]
141+
let mut buffer = [0u8; 1024];
142+
let _bytes = f1.read(&mut buffer)?;
143+
sink(&buffer); // $ hasTaintFlow="f1.txt"
144+
}
145+
146+
{
147+
let mut f2 = std::fs::OpenOptions::new().create_new(true).open("f2.txt").unwrap(); // $ Alert[rust/summary/taint-sources]
148+
let mut buffer = [0u8; 1024];
149+
let _bytes = f2.read(&mut buffer)?;
150+
sink(&buffer); // $ hasTaintFlow="f2.txt"
151+
}
152+
153+
{
154+
let mut f3 = std::fs::OpenOptions::new().read(true).write(true).truncate(true).create(true).open("f3.txt").unwrap(); // $ Alert[rust/summary/taint-sources]
155+
let mut buffer = [0u8; 1024];
156+
let _bytes = f3.read(&mut buffer)?;
157+
sink(&buffer); // $ hasTaintFlow="f3.txt"
158+
}
159+
160+
// --- misc operations ---
161+
162+
{
163+
let mut buffer = String::new();
164+
let file1 = std::fs::File::open("file.txt")?; // $ Alert[rust/summary/taint-sources]
165+
let file2 = std::fs::File::open("another_file.txt")?; // $ Alert[rust/summary/taint-sources]
166+
let mut reader = file1.chain(file2);
167+
reader.read_to_string(&mut buffer)?;
168+
sink(&buffer); // $ hasTaintFlow="file.txt" hasTaintFlow="another_file.txt"
169+
}
170+
171+
{
172+
let mut buffer = String::new();
173+
let file1 = std::fs::File::open("file.txt")?; // $ Alert[rust/summary/taint-sources]
174+
let mut reader = file1.take(100);
175+
reader.read_to_string(&mut buffer)?;
176+
sink(&buffer); // $ hasTaintFlow="file.txt"
177+
}
178+
179+
Ok(())
180+
}
181+
182+
async fn test_tokio_file() -> std::io::Result<()> {
183+
// --- file ---
184+
185+
let mut file = tokio::fs::File::open("file.txt").await?; // $ Alert[rust/summary/taint-sources]
186+
187+
{
188+
let mut buffer = [0u8; 100];
189+
let _bytes = file.read(&mut buffer).await?;
190+
sink(&buffer); // $ hasTaintFlow="file.txt"
191+
}
192+
193+
{
194+
let mut buffer = Vec::<u8>::new();
195+
let _bytes = file.read_to_end(&mut buffer).await?;
196+
sink(&buffer); // $ hasTaintFlow="file.txt"
197+
}
198+
199+
{
200+
let mut buffer = String::new();
201+
let _bytes = file.read_to_string(&mut buffer).await?;
202+
sink(&buffer); // $ hasTaintFlow="file.txt"
203+
}
204+
205+
{
206+
let mut buffer = [0; 100];
207+
file.read_exact(&mut buffer).await?;
208+
sink(&buffer); // $ hasTaintFlow="file.txt"
209+
}
210+
211+
{
212+
let v1 = file.read_u8().await?;
213+
let v2 = file.read_i16().await?;
214+
let v3 = file.read_f32().await?;
215+
let v4 = file.read_i64_le().await?;
216+
sink(v1); // $ hasTaintFlow="file.txt"
217+
sink(v2); // $ hasTaintFlow="file.txt"
218+
sink(v3); // $ hasTaintFlow="file.txt"
219+
sink(v4); // $ hasTaintFlow="file.txt"
220+
}
221+
222+
{
223+
let mut buffer = bytes::BytesMut::new();
224+
file.read_buf(&mut buffer).await?;
225+
sink(&buffer); // $ hasTaintFlow="file.txt"
226+
}
227+
228+
// --- OpenOptions ---
229+
230+
{
231+
let mut f1 = tokio::fs::OpenOptions::new().open("f1.txt").await?; // $ Alert[rust/summary/taint-sources]
232+
let mut buffer = [0u8; 1024];
233+
let _bytes = f1.read(&mut buffer).await?;
234+
sink(&buffer); // $ hasTaintFlow="f1.txt"
235+
}
236+
237+
// --- misc operations ---
238+
239+
{
240+
let mut buffer = String::new();
241+
let file1 = tokio::fs::File::open("file.txt").await?; // $ Alert[rust/summary/taint-sources]
242+
let file2 = tokio::fs::File::open("another_file.txt").await?; // $ Alert[rust/summary/taint-sources]
243+
let mut reader = file1.chain(file2);
244+
reader.read_to_string(&mut buffer).await?;
245+
sink(&buffer); // $ MISSING: hasTaintFlow="file.txt" hasTaintFlow="another_file.txt" -- we cannot resolve the `chain` and `read_to_string` calls above, which comes from `impl<R: AsyncRead + ?Sized> AsyncReadExt for R {}` in `async_read_ext.rs`
246+
}
247+
248+
{
249+
let mut buffer = String::new();
250+
let file1 = tokio::fs::File::open("file.txt").await?; // $ Alert[rust/summary/taint-sources]
251+
let mut reader = file1.take(100);
252+
reader.read_to_string(&mut buffer).await?;
253+
sink(&buffer); // $ MISSING: hasTaintFlow="file.txt" -- we cannot resolve the `take` and `read_to_string` calls above, which comes from `impl<R: AsyncRead + ?Sized> AsyncReadExt for R {}` in `async_read_ext.rs`
254+
}
255+
256+
Ok(())
257+
}
258+
259+
async fn test_async_std_file() -> std::io::Result<()> {
260+
// --- file ---
261+
262+
let mut file = async_std::fs::File::open("file.txt").await?; // $ Alert[rust/summary/taint-sources]
263+
264+
{
265+
let mut buffer = [0u8; 100];
266+
let _bytes = file.read(&mut buffer).await?;
267+
sink(&buffer); // $ hasTaintFlow="file.txt"
268+
}
269+
270+
// --- OpenOptions ---
271+
272+
{
273+
let mut f1 = async_std::fs::OpenOptions::new().open("f1.txt").await?; // $ Alert[rust/summary/taint-sources]
274+
let mut buffer = [0u8; 1024];
275+
let _bytes = f1.read(&mut buffer).await?;
276+
sink(&buffer); // $ hasTaintFlow="f1.txt"
277+
}
278+
279+
Ok(())
280+
}
281+
282+
#[tokio::main]
283+
async fn main() -> Result<(), Box<dyn std::error::Error>> {
284+
println!("test_fs...");
285+
match test_fs() {
286+
Ok(_) => println!("complete"),
287+
Err(e) => println!("error: {}", e),
288+
}
289+
290+
println!("test_tokio_fs...");
291+
match futures::executor::block_on(test_tokio_fs()) {
292+
Ok(_) => println!("complete"),
293+
Err(e) => println!("error: {}", e),
294+
}
295+
296+
println!("test_io_file...");
297+
match test_io_file() {
298+
Ok(_) => println!("complete"),
299+
Err(e) => println!("error: {}", e),
300+
}
301+
302+
println!("test_tokio_file...");
303+
match futures::executor::block_on(test_tokio_file()) {
304+
Ok(_) => println!("complete"),
305+
Err(e) => println!("error: {}", e),
306+
}
307+
308+
println!("test_async_std_file...");
309+
match futures::executor::block_on(test_async_std_file()) {
310+
Ok(_) => println!("complete"),
311+
Err(e) => println!("error: {}", e),
312+
}
313+
314+
Ok(())
315+
}

0 commit comments

Comments
 (0)