Fuzz the native-messaging framing and the broker's pairing protocol
src-tauri/fuzz: nmh_session drives a whole kestrove-nmh connection through Broker::serve (framing, outer JSON, pair_init/pair_confirm/open/msg); frame_read checks read_frame. 8 min each (2.5 M / 2.7 M execs): no findings. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
target/
|
||||
corpus/
|
||||
artifacts/
|
||||
coverage/
|
||||
logs/
|
||||
Generated
+6172
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,34 @@
|
||||
[package]
|
||||
name = "kestrove-desktop-fuzz"
|
||||
version = "0.0.0"
|
||||
publish = false
|
||||
edition = "2021"
|
||||
|
||||
[package.metadata]
|
||||
cargo-fuzz = true
|
||||
|
||||
[dependencies]
|
||||
libfuzzer-sys = "0.4"
|
||||
serde_json = "1"
|
||||
kestrove-desktop = { path = ".." }
|
||||
kv-ffi = { path = "../../../kestrove-core/crates/kv-ffi" }
|
||||
|
||||
[workspace]
|
||||
members = ["."]
|
||||
|
||||
[profile.release]
|
||||
debug = 1
|
||||
|
||||
[[bin]]
|
||||
name = "nmh_session"
|
||||
path = "fuzz_targets/nmh_session.rs"
|
||||
test = false
|
||||
doc = false
|
||||
bench = false
|
||||
|
||||
[[bin]]
|
||||
name = "frame_read"
|
||||
path = "fuzz_targets/frame_read.rs"
|
||||
test = false
|
||||
doc = false
|
||||
bench = false
|
||||
@@ -0,0 +1,19 @@
|
||||
//! Native-messaging framing (src/framing.rs): never panics or over-allocates, and every frame read
|
||||
//! back is one that write_frame would have produced.
|
||||
#![no_main]
|
||||
use kestrove_desktop::framing::{read_frame, write_frame, MAX_FRAME};
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
let mut r = data;
|
||||
while let Ok(Some(frame)) = read_frame(&mut r) {
|
||||
assert!(frame.len() <= MAX_FRAME);
|
||||
let mut out = Vec::new();
|
||||
write_frame(&mut out, &frame).expect("re-frame");
|
||||
let mut again = out.as_slice();
|
||||
assert_eq!(
|
||||
read_frame(&mut again).unwrap().as_deref(),
|
||||
Some(frame.as_slice())
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,61 @@
|
||||
//! A whole `kestrove-nmh` connection as the app's Broker sees it: length-prefixed frames, outer
|
||||
//! JSON, and the pairing / open / msg handlers (src/broker.rs). The input is the byte stream the
|
||||
//! browser side sends; replies go to a sink. Each frame must get exactly one well-formed reply.
|
||||
#![no_main]
|
||||
use std::io::{self, Read, Write};
|
||||
use std::sync::{Arc, OnceLock};
|
||||
|
||||
use kestrove_desktop::broker::{Broker, PairingStore, PairingView, SaveView, Ui};
|
||||
use kestrove_desktop::framing::read_json;
|
||||
use kv_ffi::VaultCore;
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
struct NoUi;
|
||||
impl Ui for NoUi {
|
||||
fn pairing_requested(&self, _: &PairingView) {}
|
||||
fn save_requested(&self, _: &SaveView) {}
|
||||
}
|
||||
|
||||
struct Duplex<'a> {
|
||||
input: &'a [u8],
|
||||
output: Vec<u8>,
|
||||
}
|
||||
impl Read for Duplex<'_> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.input.read(buf)
|
||||
}
|
||||
}
|
||||
impl Write for Duplex<'_> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.output.write(buf)
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn broker() -> &'static Arc<Broker> {
|
||||
static B: OnceLock<Arc<Broker>> = OnceLock::new();
|
||||
B.get_or_init(|| {
|
||||
Broker::new(
|
||||
VaultCore::new_in_memory(),
|
||||
Arc::new(NoUi),
|
||||
PairingStore::in_memory(),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
let mut io = Duplex {
|
||||
input: data,
|
||||
output: Vec::new(),
|
||||
};
|
||||
let _ = broker().serve(&mut io);
|
||||
let mut replies = io.output.as_slice();
|
||||
while let Some(v) = read_json(&mut replies).expect("app replies are valid frames") {
|
||||
assert!(
|
||||
v.get("t").and_then(|t| t.as_str()).is_some(),
|
||||
"reply without t: {v}"
|
||||
);
|
||||
}
|
||||
});
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user