Skip to content

Commit 91ed1c8

Browse files
authored
feat(fuzz): add csv_fuzz target (#1149)
## Summary - Add fuzz target for the custom CSV parser in `builtins/csv.rs` - Tests `headers`, `count`, `select`, and custom delimiter (`-d`) operations - Exercises mismatched quotes, embedded newlines, and empty fields Closes #1100 ## Test plan - [x] `cargo check` in fuzz crate passes - [x] `cargo fmt --check` clean - [x] `cargo clippy` clean
1 parent 7c54739 commit 91ed1c8

File tree

2 files changed

+81
-0
lines changed

2 files changed

+81
-0
lines changed

crates/bashkit/fuzz/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,10 @@ path = "fuzz_targets/grep_fuzz.rs"
115115
test = false
116116
doc = false
117117
bench = false
118+
119+
[[bin]]
120+
name = "csv_fuzz"
121+
path = "fuzz_targets/csv_fuzz.rs"
122+
test = false
123+
doc = false
124+
bench = false
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//! Fuzz target for the csv builtin
2+
//!
3+
//! Tests the custom CSV parser to find:
4+
//! - Panics on mismatched quotes or malformed fields
5+
//! - Edge cases with embedded newlines, empty fields, various delimiters
6+
//! - Memory exhaustion from pathological input
7+
//! - Incorrect parsing of escaped quotes
8+
//!
9+
//! Run with: cargo +nightly fuzz run csv_fuzz -- -max_total_time=300
10+
11+
#![no_main]
12+
13+
use libfuzzer_sys::fuzz_target;
14+
15+
fuzz_target!(|data: &[u8]| {
16+
// Only process valid UTF-8
17+
if let Ok(input) = std::str::from_utf8(data) {
18+
// Limit input size to prevent OOM
19+
if input.len() > 1024 {
20+
return;
21+
}
22+
23+
// Skip empty input
24+
if input.trim().is_empty() {
25+
return;
26+
}
27+
28+
let rt = tokio::runtime::Builder::new_current_thread()
29+
.enable_all()
30+
.build()
31+
.unwrap();
32+
33+
rt.block_on(async {
34+
let mut bash = bashkit::Bash::builder()
35+
.limits(
36+
bashkit::ExecutionLimits::new()
37+
.max_commands(50)
38+
.max_subst_depth(3)
39+
.max_stdout_bytes(4096)
40+
.max_stderr_bytes(4096)
41+
.timeout(std::time::Duration::from_millis(200)),
42+
)
43+
.build();
44+
45+
// Test 1: parse CSV and list headers
46+
let script = format!(
47+
"echo '{}' | csv headers 2>/dev/null; true",
48+
input.replace('\'', "'\\''"),
49+
);
50+
let _ = bash.exec(&script).await;
51+
52+
// Test 2: parse CSV and count rows
53+
let script2 = format!(
54+
"echo '{}' | csv count 2>/dev/null; true",
55+
input.replace('\'', "'\\''"),
56+
);
57+
let _ = bash.exec(&script2).await;
58+
59+
// Test 3: parse CSV and select first column
60+
let script3 = format!(
61+
"echo '{}' | csv select 1 2>/dev/null; true",
62+
input.replace('\'', "'\\''"),
63+
);
64+
let _ = bash.exec(&script3).await;
65+
66+
// Test 4: parse CSV with custom delimiter
67+
let script4 = format!(
68+
"echo '{}' | csv -d '\\t' headers 2>/dev/null; true",
69+
input.replace('\'', "'\\''"),
70+
);
71+
let _ = bash.exec(&script4).await;
72+
});
73+
}
74+
});

0 commit comments

Comments
 (0)