Secure fuzz testing
Coverage-guided fuzzing with Atheris, cargo-fuzz, and native Go fuzzing, combined with compilers/sanitizers (ASan, MSan, UBSan) for security validation. Use whenever the user wants to fuzz test code, find memory safety bugs, validate parsers/deserializers against malformed input, or harden security-critical code paths. Trigger on mentions of fuzzing, fuzz testing, AddressSanitizer, libFuzzer, cargo-fuzz, Atheris, go test -fuzz, or security validation of parsing/deserialization logic.From its SKILL.md
npx -y skills add roedyrustam/claudevibeskills --skill secure-fuzz-testingAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
9.0 KB, ~2.3k tokens by cl100k_base, as published. Nobody here has run it
Secure Fuzz Testing
Coverage-guided fuzzing across Python, Rust, and Go with sanitizer integration.
Why Fuzz
Fuzzing finds bugs unit tests miss: crashes, panics, memory corruption, infinite loops, and logic errors triggered by malformed or unexpected input. Critical for any code that parses untrusted input — file formats, network protocols, deserializers, APIs.
Python Fuzzing with Atheris
Setup
uv add --dev atheris
Basic Fuzz Harness
# fuzz/fuzz_parser.py
import atheris
import sys
with atheris.instrument_imports():
from myapp.parser import parse_config
def TestOneInput(data: bytes) -> None:
fdp = atheris.FuzzedDataProvider(data)
text = fdp.ConsumeUnicodeNoSurrogates(fdp.remaining_bytes())
try:
parse_config(text)
except (ValueError, KeyError):
pass # Expected exceptions — not bugs
# Anything else (TypeError, IndexError, unhandled) = potential bug
atheris.Setup(sys.argv, TestOneInput)
atheris.Fuzz()
# Run fuzzing
python fuzz/fuzz_parser.py
# With a corpus directory (saves interesting inputs)
mkdir corpus
python fuzz/fuzz_parser.py corpus/
# Limit runtime
python fuzz/fuzz_parser.py -max_total_time=300 # 5 minutes
# Reproduce a crash
python fuzz/fuzz_parser.py crash-<hash>
Structured Fuzzing (JSON-like inputs)
import atheris
import json
def TestOneInput(data: bytes) -> None:
fdp = atheris.FuzzedDataProvider(data)
# Build structured input from fuzz bytes
payload = {
"user_id": fdp.ConsumeIntInRange(-1000, 1000000),
"name": fdp.ConsumeUnicodeNoSurrogates(50),
"active": fdp.ConsumeBool(),
"tags": [fdp.ConsumeUnicodeNoSurrogates(20) for _ in range(fdp.ConsumeIntInRange(0, 5))],
}
try:
validate_user_payload(payload)
except ValidationError:
pass
atheris.Setup(sys.argv, TestOneInput)
atheris.Fuzz()
Rust Fuzzing with cargo-fuzz
Setup
cargo install cargo-fuzz
cargo fuzz init
Fuzz Target
// fuzz/fuzz_targets/parse_input.rs
#![no_main]
use libfuzzer_sys::fuzz_target;
use myapp::parser::parse_config;
fuzz_target!(|data: &[u8]| {
if let Ok(s) = std::str::from_utf8(data) {
let _ = parse_config(s); // panics are caught and reported as crashes
}
});
Structured Fuzzing with arbitrary
// fuzz/fuzz_targets/structured.rs
#![no_main]
use libfuzzer_sys::fuzz_target;
use arbitrary::Arbitrary;
#[derive(Arbitrary, Debug)]
struct UserPayload {
id: u32,
name: String,
age: u8,
tags: Vec<String>,
}
fuzz_target!(|payload: UserPayload| {
let _ = validate_user(&payload);
});
# fuzz/Cargo.toml
[dependencies]
libfuzzer-sys = "0.4"
arbitrary = { version = "1", features = ["derive"] }
myapp = { path = ".." }
Running with Sanitizers
# AddressSanitizer (default) — detects memory corruption
cargo fuzz run parse_input
# Run for a fixed duration
cargo fuzz run parse_input -- -max_total_time=300
# Run with a larger corpus
cargo fuzz run parse_input fuzz/corpus/parse_input/
# MemorySanitizer — detects uninitialized memory reads
RUSTFLAGS="-Z sanitizer=memory" cargo fuzz run parse_input
# Minimize a crashing input
cargo fuzz tmin parse_input fuzz/artifacts/parse_input/crash-<hash>
# Reproduce a specific crash
cargo fuzz run parse_input fuzz/artifacts/parse_input/crash-<hash>
Go Native Fuzzing
Fuzz Test (Go 1.18+, built-in)
// parser_fuzz_test.go
package parser
import "testing"
func FuzzParseConfig(f *testing.F) {
// Seed corpus — known valid/edge-case inputs
f.Add("key=value")
f.Add("")
f.Add("key=")
f.Add(`key="quoted value"`)
f.Fuzz(func(t *testing.T, input string) {
result, err := ParseConfig(input)
if err != nil {
return // expected error — not a bug
}
// Invariant checks
if result == nil {
t.Errorf("ParseConfig returned nil with no error for input: %q", input)
}
// Round-trip property: parse(serialize(x)) == x
serialized := result.Serialize()
reparsed, err := ParseConfig(serialized)
if err != nil {
t.Errorf("Failed to reparse serialized output: %v", err)
}
if !reparsed.Equals(result) {
t.Errorf("Round-trip mismatch for input: %q", input)
}
})
}
# Run fuzzing
go test -fuzz=FuzzParseConfig -fuzztime=60s
# Run with sanitizer (requires CGO + clang)
go test -fuzz=FuzzParseConfig -fuzztime=60s -gcflags=all=-d=checkptr
# Crashes saved automatically to testdata/fuzz/FuzzParseConfig/
# Re-run with: go test -run=FuzzParseConfig/<seed>
Sanitizers Reference
| Sanitizer | Detects | Use With |
|---|---|---|
| ASan (AddressSanitizer) | Buffer overflows, use-after-free, double-free | C/C++/Rust |
| MSan (MemorySanitizer) | Uninitialized memory reads | C/C++/Rust (Clang) |
| UBSan (UndefinedBehaviorSanitizer) | Integer overflow, null deref, type confusion | C/C++/Rust |
| TSan (ThreadSanitizer) | Data races, deadlocks | Concurrent code |
C/C++ with libFuzzer + Sanitizers
# Compile with multiple sanitizers
clang++ -g -O1 -fsanitize=address,fuzzer,undefined \
-fsanitize-address-use-after-scope \
fuzz_target.cc -o fuzz_target
./fuzz_target -max_total_time=300 corpus/
Building Effective Fuzz Harnesses
Good Harness Checklist
- Targets a single, well-defined function or parsing boundary
- Catches expected errors (don't report them as crashes)
- Includes a seed corpus of realistic valid + edge-case inputs
- Checks invariants, not just "doesn't crash" (round-trip, idempotency)
- Runs fast (microseconds per iteration) — slow targets fuzz poorly
- Deterministic — same input always produces same behavior
What to Fuzz First (Priority Order)
- Parsers — JSON, YAML, config files, binary formats
- Deserializers — anything from
bytes→ struct - Network protocol handlers — anything reading from sockets
- Compression/decompression — zip, gzip bombs, decompression ratio attacks
- Auth token / JWT parsing — malformed tokens shouldn't panic
- Regular expressions — catastrophic backtracking (ReDoS)
- Path/URL handling — path traversal via malformed input
CI Integration
# .github/workflows/fuzz.yml
name: Fuzz Testing
on:
schedule:
- cron: '0 2 * * *' # nightly
pull_request:
paths:
- 'src/parser/**'
jobs:
fuzz-rust:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- run: cargo install cargo-fuzz
- name: Run fuzz targets (5 min each)
run: |
for target in $(cargo fuzz list); do
cargo fuzz run $target -- -max_total_time=300
done
fuzz-go:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- name: Run Go fuzz tests
run: go test -fuzz=Fuzz -fuzztime=60s ./...
Triaging Fuzz Findings
# 1. Minimize the crashing input (find smallest reproducer)
cargo fuzz tmin <target> <crash-file>
# 2. Get a backtrace
RUST_BACKTRACE=full cargo fuzz run <target> <crash-file>
# 3. Classify the bug:
# - Panic/crash → DoS risk, fix immediately
# - Memory corruption (ASan) → CRITICAL, potential RCE
# - Logic error (wrong output, no crash) → correctness bug
# - Infinite loop/hang → DoS risk (add timeout, fix complexity)
# 4. Write a regression test from the minimized crash
# Add the crashing input to your seed corpus permanently
Key Rules
- Fuzz parsers and deserializers first — highest bug density, highest risk
- Catch expected errors in the harness — don't waste cycles on known-invalid input paths
- Always minimize crashes before filing/fixing — smaller reproducers are easier to debug
- Run ASan by default — it's nearly free and catches the most dangerous bugs
- Seed corpus matters — start fuzzing from realistic inputs, not just empty/random
- Check invariants, not just crashes — round-trip properties catch logic bugs
- Time-box CI fuzzing — 1-5 min per target in PR CI, longer runs nightly
- Every fixed crash becomes a permanent corpus entry — prevents regression
- Fuzz before every major release — especially after touching parsing code
- Treat OOM and timeouts as bugs too — not just crashes/panics
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.