Skip to content

Fuzzing

ODS reads files and text it doesn't control: dbt's artifacts and messages, SQL, and lineage exports from Databricks. Fuzzing throws millions of generated inputs at those readers and watches for a crash: a panic, a hang, or runaway memory. A fuzzer is coverage-guided: it mutates the inputs that reach new code, so it finds inputs no one would think to write. A crash there means a malformed or hostile file stops ods or the dashboard, so every reader must turn bad input into an error instead (#192).

This complements the property tests (tests/properties.rs in ods-core, ods-lineage, ods-state and the SQL provider), which check that results are right over generated input; fuzzing checks that nothing breaks on any input.

Targets

Target What it reads
dbt_manifest manifest.json
dbt_run_results run_results.json
dbt_messages dbt's error output: error_summary, project_failure
sql_analyzer any SQL, in every dialect; an opaque result must claim nothing
redact redaction of engine messages; the result must be one short line with no control characters
uc_lineage Unity Catalog lineage exports, CSV and JSON

Running

The fuzz crate lives in fuzz/, as its own workspace: cargo-fuzz uses libfuzzer-sys, whose licence (LLVM's NCSA) isn't on the list in deny.toml, and it never needs to be: nothing in ODS's build links it. You need a nightly toolchain and cargo-fuzz:

rustup toolchain install nightly
cargo install cargo-fuzz --locked
fuzz/seed.sh                                   # start from the fixtures
cargo +nightly fuzz run sql_analyzer -- -max_total_time=300

The fuzz workflow runs every target for ten minutes each night, and for one minute on a pull request that changes fuzz/.

When it finds something

A crash stops the run and saves the input under fuzz/artifacts/<target>/ (in CI, as the fuzz-crash-<target> artifact). To reproduce it:

cargo +nightly fuzz run <target> fuzz/artifacts/<target>/crash-…

Fix the reader so the input is an error, not a crash, and add the input as a unit test next to it, so it stays fixed without the fuzzer.

Adding a target

Add a file under fuzz/fuzz_targets/, a [[bin]] for it in fuzz/Cargo.toml, seeds in fuzz/seed.sh, and the target to the matrix in .github/workflows/fuzz.yml. A target calls the reader as ODS does (from a file, if that's how ODS reads it) and may assert what must hold for any input.