Fuzz targets

September 19, 2026 ยท View on GitHub

Every parser of input that jev does not control, run by cargo-fuzz (libFuzzer). No input may make jev panic (PRD REL-7): a panic is a crash here, and a crash is a bug.

TargetWhat it feedsEntry point
requesta JSON request: Document parsing, validate::check_document under every option, conversion to a typed Requestjev_client::fuzz::request
responsea response body: Response, ModelList, and the three shapes of an error bodyjev_client::fuzz::response
request_filea request file as JSON and as YAML, prepared as jev eval prepares itjev_cli::fuzz::request_file
outputa response rendered in every output format, with a --field path and an --assert condition or --abstain-bandjev_cli::fuzz::output
batch_rowsa JSONL or CSV batch input, checked as jev batch run checks itjev_cli::fuzz::batch_rows
configa config.toml, which must also read again once jev has written it back, and a credentials filejev_cli::fuzz::config
mcplines from an MCP client, answered by a server with no API keyjev_cli::fuzz::mcp

The entry points live in the crates, in src/fuzz.rs, so that they can reach private code. They are compiled only with --cfg fuzzing (which cargo-fuzz sets) and in the crates' own tests, which run each one on a few inputs so that the targets keep compiling. This directory is a Cargo workspace of its own, so cargo build, cargo test and make check never build it.

Running

cargo-fuzz needs a nightly toolchain:

rustup toolchain install nightly --profile minimal
cargo install cargo-fuzz --locked
fuzz/run.sh request 60          # one target for 60 seconds, in one process
cargo +nightly fuzz list        # the targets

run.sh starts from the committed seeds in seeds/<target>/ plus whatever corpus earlier runs left in corpus/<target>/ (not committed), and uses the dictionary in dict/jev.dict. The Fuzz workflow runs every target for ten minutes each week and on demand, and for one minute on a pull request that changes this directory.

When a target crashes

libFuzzer writes the input to artifacts/<target>/, and CI uploads it as an artifact.

  1. Reproduce it: cargo +nightly fuzz run <target> artifacts/<target>/crash-....
  2. Fix the cause where it is, and add a regression test next to that code that feeds it the same input. Tests do not read files from this directory, so that each crate stays self-contained.
  3. If the input is a good example of something new, copy it into seeds/<target>/.

A fix that is too large for the change at hand gets an issue of its own, with the input attached.