jazzer-jvm-fuzzing
Author and run Jazzer - Code Intelligence's JVM coverage-guided fuzzer built on libFuzzer. Covers Maven / Gradle / standalone JAR installation, the @FuzzTest annotation (JUnit 5 integration), typed parameter mutation (String, primitives, byte[]), built-in JVM sanitisers (SSRF / path traversal / OS command injection / deserialization gadget / ReDoS), and the JAZZER_FUZZ=1 env var to switch between regression and fuzzing modes. Use for fuzz testing Java / Kotlin libraries - particularly effective against parsing, deserialization, and HTTP-handling code.
Install with skills.sh (any agent)
npx skills add testland/qa --skill jazzer-jvm-fuzzingjazzer-jvm-fuzzing
Overview
Distinct from C/C++ fuzzers: Jazzer (per github.com/CodeIntelligenceTesting/jazzer (opens in new window)) ships JVM-level sanitisers that detect security-sensitive misuse of standard APIs (deserialization gadgets, SSRF, ReDoS) - not memory-safety bugs (the JVM handles those).
For corpus discipline see corpus-management-reference.
When to use
Authoring
Install (Maven)
Per Jazzer README:
<dependency>
<groupId>com.code-intelligence</groupId>
<artifactId>jazzer-junit</artifactId>
<version>${jazzer.version}</version>
<scope>test</scope>
</dependency>Install (Gradle)
dependencies {
testImplementation "com.code-intelligence:jazzer-junit:$jazzerVersion"
}Pin the version in one place - jazzer.version (Maven property) or jazzerVersion (Gradle ext) - to the latest release from Maven Central (search.maven.org/artifact/com.code-intelligence/jazzer-junit (opens in new window)); 0.22.1 was current at time of writing.
Install (standalone)
Download binary release from GitHub; invoke jazzer --cp=<classpath>.
Fuzz target with JUnit 5
import com.code_intelligence.jazzer.junit.FuzzTest;
import org.jetbrains.annotations.NotNull;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ParserFuzzTest {
@FuzzTest
void fuzzDecode(@NotNull String input) {
assertEquals(input, SomeScheme.decode(SomeScheme.encode(input)));
}
}Per Jazzer docs, @FuzzTest is the annotation that registers a fuzz target. The method parameters become fuzzer-mutated typed inputs.
Supported parameter types
Per Jazzer README, @FuzzTest parameters support:
Annotations refine mutation:
| Annotation | Effect |
|---|---|
@NotNull | Parameter never null |
@WithUtf8Length(min=N, max=M) | String byte-length bound |
@InRange(min=N, max=M) | Integer range |
@FuzzTest
void fuzzWithBounds(@NotNull @WithUtf8Length(max = 256) String host,
@InRange(min = 1, max = 65535) int port) {
handleRequest(host, port);
}FuzzedDataProvider (advanced)
For complex input shapes:
import com.code_intelligence.jazzer.api.FuzzedDataProvider;
@FuzzTest
void fuzzComplex(FuzzedDataProvider data) {
int n = data.consumeInt(0, 100);
String s = data.consumeString(64);
byte[] body = data.consumeRemainingAsBytes();
process(n, s, body);
}Running
Modes - regression vs fuzzing
Per Jazzer docs:
# Regression
mvn test
# Fuzzing
JAZZER_FUZZ=1 mvn test -Dtest=ParserFuzzTest#fuzzDecode
# Bounded time
JAZZER_FUZZ=300 mvn test -Dtest=ParserFuzzTest
# (specific seconds)Standalone invocation
./jazzer \
--cp=target/test-classes:target/classes \
--target_class=com.example.ParserFuzzTest \
--target_method=fuzzDecode \
-max_total_time=300Common flags
Jazzer accepts libFuzzer-style flags:
| Flag | Effect |
|---|---|
-max_total_time=N | Stop after N seconds |
-runs=N | Number of iterations |
-dict=path | Dictionary file |
--keep_going=N | Keep fuzzing after first crash (find N total) |
--instrumentation_includes=PKG.* | Limit coverage instrumentation to a package |
JVM sanitisers
Jazzer's built-in detectors fire automatically on security-relevant misuse (deserialization gadgets, SSRF, path traversal, OS command injection, ReDoS, LDAP / JNDI / SQL injection) - no extra config; disable selectively via --disabled_hooks=.... Full catalogue with what each catches: references/sanitisers-and-ci.md.
Parsing results
When Jazzer finds a crash, output:
== Java Exception: java.lang.AssertionError: expected: <foo> but was: <bar>
at com.example.ParserFuzzTest.fuzzDecode(ParserFuzzTest.java:12)
...
== libFuzzer crashing input ==
artifact_prefix='./'; Test unit written to ./crash-<sha1>
Base64: <encoded-input>
Reproducer input written to: src/test/resources/com/example/ParserFuzzTest/fuzzDecode/<sha1>The reproducer is saved to src/test/resources/... as part of the test fixtures - commit it for regression coverage.
CI integration
Run regression inputs with mvn test, then a bounded smoke-fuzz (JAZZER_FUZZ=180) over every @FuzzTest class and upload crashes: see references/sanitisers-and-ci.md.
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
Untyped byte[] parameter for structured input | Foregoes Jazzer's typed-mutation advantage | Use typed parameters or FuzzedDataProvider |
Catching Throwable in target | Hides real bugs | Let exceptions propagate; use assertXxx for invariants |
Skipping @NotNull annotation | Spurious NPE crashes | Always annotate @NotNull unless null is legitimate |
| Not committing reproducer files | Lose regression coverage | Commit src/test/resources/<test-class>/<method>/ |
| Disabling all JVM sanitisers | Loses Jazzer's biggest advantage over plain libFuzzer | Keep sanitisers enabled; disable selectively if false positives |
| Single-target campaign | Other targets not exercised | Run all @FuzzTest methods in CI |
Limitations
References
Jazzer: JVM sanitisers and CI
View source (opens in new window)Jazzer: JVM sanitisers and CI
Deep reference for jazzer-jvm-fuzzing. The core install / authoring / running workflow lives in the skill spine; this file holds the full JVM-sanitiser catalogue and the CI job.
JVM sanitisers
Per Jazzer README, built-in detectors fire on security-relevant misuse:
| Sanitiser | What it catches |
|---|---|
| Deserialization | Untrusted ObjectInputStream / XStream / Kryo input → gadget execution |
| SSRF | URL constructed from untrusted input pointing at internal infrastructure |
| Path traversal | .. / encoded variants in file path arguments |
| OS command injection | Runtime.exec / ProcessBuilder with concatenated input |
| ReDoS | Catastrophic-backtracking regex constructed from untrusted input |
| LDAP injection | LDAP query string concatenation |
| Naming context | JNDI lookup with untrusted name |
| SQL injection (via Hibernate / direct JDBC) | Query string concatenation |
These run automatically - no additional configuration. Disable selectively via --disabled_hooks=....
CI integration
- uses: actions/setup-java@v5
with: { java-version: '17', distribution: 'temurin' }
- name: Run unit tests + regression fuzz inputs
run: mvn test
- name: Smoke fuzz (3 min per target)
run: |
for cls in $(grep -rl "@FuzzTest" src/test/java/ | \
sed 's|src/test/java/||; s|/|.|g; s|.java||'); do
JAZZER_FUZZ=180 mvn test -Dtest=$cls || true
done
- uses: actions/upload-artifact@v4
with:
name: jazzer-crashes
path: |
crash-*
src/test/resources/**/*|| true is continue-on-crash: a finding does not fail the job, so the loop still fuzzes every target - triage findings from the uploaded jazzer-crashes artifact. To hard-fail the build on new findings instead, drop || true (the first crash then fails the step).
Related skills
afl-plus-plus
Author and run AFL++ - out-of-process coverage-guided fuzzer (a community fork of Google's original AFL with improved mutations and instrumentation). Covers afl-cc / afl-clang-fast instrumented build, afl-fuzz invocation, parallel master/slave (-M / -S), dictionary support (-x), QEMU mode (-Q) for binaries without source, output structure (queue / crashes / hangs), crash minimisation (afl-tmin), corpus minimisation (afl-cmin), crash filename triage, and CI integration. Use for fuzzing standalone binaries (file processors, command-line tools) where libFuzzer's in-process model doesn't fit; for cross-fuzzer corpus strategy see corpus-management-reference.
atheris-python-fuzzing
Author and run Atheris - Google's Python coverage-guided fuzzer built on libFuzzer. Covers pip installation, atheris.Setup + atheris.Fuzz invocation, TestOneInput(data: bytes) target signature, FuzzedDataProvider for structured input, instrument_imports() / instrument_func decorators for coverage instrumentation, and libFuzzer-passthrough flags (-atheris_runs, -max_total_time, -dict). Use for fuzzing Python libraries - also supports CPython native-extension fuzzing.
cargo-fuzz-rust
Author and run cargo-fuzz - Rust fuzzing via libFuzzer with cargo integration. Covers `cargo install cargo-fuzz`, `cargo fuzz init` + `cargo fuzz add {target}` for harness scaffolding, the `fuzz_target!` macro for entry-point declaration, the `Arbitrary` trait for structured input mutation, and `cargo fuzz run` invocation. Requires Rust nightly. Use for fuzz testing Rust libraries - cargo-fuzz wraps libFuzzer with native Rust ergonomics.
corpus-management-reference
Pure-reference catalog of fuzz-corpus management practices. Defines what a corpus is (seed corpus + evolved corpus saved by the fuzzer), corpus directory layout per libFuzzer / AFL++ / Go native / cargo-fuzz / OSS-Fuzz, the canonical crash-artefact naming (crash-{sha1} / leak-{sha1} / timeout-{sha1}), seed corpus construction strategies (sample-from-prod, sample-from-test-fixtures, from-spec-keywords), corpus minimisation, dictionary files, and the OSS-Fuzz integration corpus sync. Use as the corpus-discipline reference when building a fuzz target or maintaining a long-running fuzz campaign.
crash-triage-reference
Pure-reference catalog for manually triaging individual fuzzer crash artifacts - reading ASan, UBSan, and MSan output; classifying findings as LIKELY-EXPLOITABLE, MEDIUM, or BENIGN; deduplicating by stack-hash; and minimizing reproducers with -minimize_crash. Use when you need to understand what a specific crash means, build exploitability intuition, or manually work a small set of findings. For automated bulk triage across a full artifact directory, run automated findings triage instead.
fuzz-tool-selector
Routes a fuzz-target authoring task to the right fuzzer for the detected language and build type. Decision tree: C/C++ → libfuzzer-cpp + afl-plus-plus; Rust → cargo-fuzz-rust (or libfuzzer-cpp via FFI); Go → go-native-fuzzing; Python → atheris-python-fuzzing; JVM → jazzer-jvm-fuzzing; closed-source binary → afl-plus-plus in QEMU mode; mature open-source project → ossfuzz-integration. Use when a project needs coverage-guided fuzzing and no fuzzer has been chosen for its language or toolchain yet.
go-native-fuzzing
Author and run Go's native fuzzing (Go 1.18+) - coverage-guided fuzzing built into the standard testing package via FuzzXxx functions. Covers f.Add seed-corpus declaration, f.Fuzz callback signature with typed parameters, testdata/fuzz/{FuzzXxx}/ directory layout for seeds + regression cases, the -fuzz flag for `go test`, and CI integration via short smoke runs. Use for fuzz testing Go libraries - Go's native approach integrates seamlessly with standard `go test` rather than requiring a separate toolchain like AFL++.
libfuzzer-cpp
Author and run LLVM libFuzzer for C/C++ - in-process coverage-guided fuzzing. Covers harness authoring (LLVMFuzzerTestOneInput entry point), build with -fsanitize=fuzzer,address,undefined, runtime flags (-max_total_time, -runs, -dict, -fork, -workers), corpus + crash-artefact handling, and CI integration. Use for libraries / parsers / decoders in C/C++ where in-process fuzzing of a function is the right scope. Compose with ASan + UBSan from sanitiser-integration-reference and corpus discipline from corpus-management-reference.
ossfuzz-integration
Author and submit a project to Google OSS-Fuzz - the open-source continuous fuzzing service that runs libFuzzer / AFL++ / Honggfuzz campaigns on Google infrastructure 24x7. Covers the project.yaml + Dockerfile + build.sh contract, the $OUT/$WORK conventions, supported languages + sanitisers, seed-corpus + dictionary submission, the OSS-Fuzz Build Status dashboard, and the disclosure SLA (issues filed in Monorail with 90-day deadline). Use to offload long-running fuzz campaigns to dedicated infrastructure rather than self-hosting.
sanitiser-integration-reference
Pure-reference catalog of compiler sanitisers used with fuzz testing - AddressSanitizer (ASan), UndefinedBehaviorSanitizer (UBSan), MemorySanitizer (MSan), ThreadSanitizer (TSan), and LeakSanitizer (LSan). Explains what each detects, compatibility (can ASan + UBSan combine? - yes; ASan + MSan? - no), build flags, runtime options (ASAN_OPTIONS / UBSAN_OPTIONS env vars), and the typical ~2x slowdown per ASan. Use to pick the right sanitiser per fuzz target, configure the build, and interpret crash reports.