Skip to content

Commit 01cc195

Browse files
authored
feat(fuzz): add tomlq_fuzz target (#1151)
## Summary - Add fuzz target for the hand-written TOML parser in `builtins/tomlq.rs` - Tests parsing and dot-path queries on arbitrary TOML documents - Guards against deeply nested table/inline table structures Closes #1102 ## Test plan - [x] `cargo check` in fuzz crate passes - [x] `cargo fmt --check` clean - [x] `cargo clippy` clean
1 parent c13e36f commit 01cc195

File tree

2 files changed

+83
-0
lines changed

2 files changed

+83
-0
lines changed

crates/bashkit/fuzz/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,10 @@ path = "fuzz_targets/archive_fuzz.rs"
129129
test = false
130130
doc = false
131131
bench = false
132+
133+
[[bin]]
134+
name = "tomlq_fuzz"
135+
path = "fuzz_targets/tomlq_fuzz.rs"
136+
test = false
137+
doc = false
138+
bench = false
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
//! Fuzz target for the tomlq builtin
2+
//!
3+
//! Tests the hand-written TOML parser to find:
4+
//! - Panics on malformed TOML documents
5+
//! - Edge cases with nested tables, inline tables, multiline strings
6+
//! - Incorrect datetime parsing
7+
//! - Memory exhaustion from pathological input
8+
//!
9+
//! Run with: cargo +nightly fuzz run tomlq_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+
// Split input into TOML content and query path
24+
let (toml_doc, query) = match input.find('\n') {
25+
Some(pos) => (&input[..pos], &input[pos + 1..]),
26+
None => (input, "." as &str),
27+
};
28+
29+
// Skip empty documents
30+
if toml_doc.trim().is_empty() {
31+
return;
32+
}
33+
34+
// Reject deeply nested structures
35+
let depth = toml_doc
36+
.bytes()
37+
.filter(|&b| b == b'[' || b == b'{')
38+
.count();
39+
if depth > 20 {
40+
return;
41+
}
42+
43+
let rt = tokio::runtime::Builder::new_current_thread()
44+
.enable_all()
45+
.build()
46+
.unwrap();
47+
48+
rt.block_on(async {
49+
let mut bash = bashkit::Bash::builder()
50+
.limits(
51+
bashkit::ExecutionLimits::new()
52+
.max_commands(50)
53+
.max_subst_depth(3)
54+
.max_stdout_bytes(4096)
55+
.max_stderr_bytes(4096)
56+
.timeout(std::time::Duration::from_millis(200)),
57+
)
58+
.build();
59+
60+
// Test 1: parse TOML and query by path
61+
let script = format!(
62+
"echo '{}' | tomlq '{}' 2>/dev/null; true",
63+
toml_doc.replace('\'', "'\\''"),
64+
query.replace('\'', "'\\''"),
65+
);
66+
let _ = bash.exec(&script).await;
67+
68+
// Test 2: parse TOML with dot-path query
69+
let script2 = format!(
70+
"echo '{}' | tomlq 2>/dev/null; true",
71+
toml_doc.replace('\'', "'\\''"),
72+
);
73+
let _ = bash.exec(&script2).await;
74+
});
75+
}
76+
});

0 commit comments

Comments
 (0)