Testland
Browse all skills & agents

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-fuzzing
View source

jazzer-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

  • Fuzz testing Java / Kotlin / Scala / Groovy libraries.
  • Targets handling user input - parsers, deserialisers, HTTP handlers, URL constructors (Jazzer's JVM sanitisers catch injection bugs).
  • JUnit 5-anchored projects - Jazzer integrates as a JUnit test type.

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:

  • Primitive types (int, long, boolean, byte, char, short, float, double)
  • String
  • Arrays of primitives + arrays of String
  • Many standard library classes via auto-marshalling

Annotations refine mutation:

AnnotationEffect
@NotNullParameter 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 mode (default): runs the test against any saved inputs in src/test/resources/<TestClass>/<methodName>/ - fast, deterministic.
  • Fuzzing mode: set JAZZER_FUZZ=1 env var; explores new inputs.
# 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=300

Common flags

Jazzer accepts libFuzzer-style flags:

FlagEffect
-max_total_time=NStop after N seconds
-runs=NNumber of iterations
-dict=pathDictionary file
--keep_going=NKeep 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-patternWhy it failsFix
Untyped byte[] parameter for structured inputForegoes Jazzer's typed-mutation advantageUse typed parameters or FuzzedDataProvider
Catching Throwable in targetHides real bugsLet exceptions propagate; use assertXxx for invariants
Skipping @NotNull annotationSpurious NPE crashesAlways annotate @NotNull unless null is legitimate
Not committing reproducer filesLose regression coverageCommit src/test/resources/<test-class>/<method>/
Disabling all JVM sanitisersLoses Jazzer's biggest advantage over plain libFuzzerKeep sanitisers enabled; disable selectively if false positives
Single-target campaignOther targets not exercisedRun all @FuzzTest methods in CI

Limitations

  • JVM startup cost. Each fuzz iteration shares the JVM, so startup is amortised - but JIT warmup still affects early iterations.
  • Allocation-heavy targets slow. GC dominates iteration time for allocation-heavy code.
  • Native (JNI) code not coverage-instrumented. For native bug-hunting in JNI libraries use libFuzzer + JNI wrappers.
  • @FuzzTest on Kotlin works but parameter mutation respects Kotlin nullability - null-tolerant Kotlin parameters fuzz with null values too.
  • Distinguishes "test failure" from "fuzz finding" loosely - any AssertionError is a finding; tune assertions deliberately.

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:

SanitiserWhat it catches
DeserializationUntrusted ObjectInputStream / XStream / Kryo input → gadget execution
SSRFURL constructed from untrusted input pointing at internal infrastructure
Path traversal.. / encoded variants in file path arguments
OS command injectionRuntime.exec / ProcessBuilder with concatenated input
ReDoSCatastrophic-backtracking regex constructed from untrusted input
LDAP injectionLDAP query string concatenation
Naming contextJNDI 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.