Try it on an app with a bug.

Each app below is a few lines of Rust. Four have a bug that looks fine in a code review; one has none. Install appfuzz, paste a command, and watch it find the bug. No app, toolchain or config of your own needed.

1. Install on Linux or macOS

$ curl -fsSL https://appfuzz.dev/install.sh | sh

Or with Homebrew: brew install stephanschmidt/appfuzz/appfuzz

2. Review an app by its URL

appfuzz downloads the .wasm and its settings (.toml), runs it a few thousand times against a simulated database, and prints what it found. The app runs in a sandbox with no file or network access.

One query per row

Lists all items, then loads each one again

$ appfuzz --human https://appfuzz.dev/demo/chatty.wasm
for it in items::all() {
    if let Ok(full) = items::get(it.id) {
        out += &format!("<li>{}</li>", esc(&full.owner));
    }
}

fail n_plus_1 calls to items#get grow with the length of the list items#all returns; the request goes over its limit at 100 rows

A search box

The query goes straight into the page

$ appfuzz --human https://appfuzz.dev/demo/reflect-query.wasm
let q = param(&req.query, "q").unwrap_or("");
html(format!("<p>Results for {q}</p>"))

fail reflection the request field query.q reaches the HTML unescaped

The first account with no items

Works on the demo data, crashes on an empty list

$ appfuzz --human https://appfuzz.dev/demo/empty-panic.wasm
let all = items::all();
let first = &all[0];
html(format!("<p>First: {}</p>", esc(&first.name)))

fail crash when items#all returns an empty list: index out of bounds

Names in other languages

Cuts names at 8 bytes, which breaks inside a character

$ appfuzz --human https://appfuzz.dev/demo/utf8-slice.wasm
for it in items::all() {
    let short = if it.name.len() > 8 { &it.name[..8] } else { it.name.as_str() };
    out += &format!("<li>{}</li>", esc(short));
}

fail crash when items#all returns names with multi-byte characters: byte 8 is not a character boundary

And one that passes

One read, sorted, escaped, at most 20 rows

$ appfuzz --human https://appfuzz.dev/demo/clean.wasm
let mut all = items::all();
all.sort_by(|a, b| b.score.cmp(&a.score).then(a.id.cmp(&b.id)));
let q = param(&req.query, "q").unwrap_or("");
let mut out = format!("<h1>Items matching {}</h1><ul>", esc(q));
for it in all.iter().filter(|i| i.name.contains(q)).take(20) {
    out += &format!("<li>{} ({})</li>", esc(&it.name), it.score);
}

pass no findings

The full source of each app is in bench/apps on GitHub. To review your own app, run appfuzz review next to its .wasm.