agentsclimarketplace

Secure fuzz testing

Skill roedyrustam/claudevibeskills/src/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

Install
npx -y skills add roedyrustam/claudevibeskills --skill secure-fuzz-testing

Assembled 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

SanitizerDetectsUse With
ASan (AddressSanitizer)Buffer overflows, use-after-free, double-freeC/C++/Rust
MSan (MemorySanitizer)Uninitialized memory readsC/C++/Rust (Clang)
UBSan (UndefinedBehaviorSanitizer)Integer overflow, null deref, type confusionC/C++/Rust
TSan (ThreadSanitizer)Data races, deadlocksConcurrent 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)

  1. Parsers — JSON, YAML, config files, binary formats
  2. Deserializers — anything from bytes → struct
  3. Network protocol handlers — anything reading from sockets
  4. Compression/decompression — zip, gzip bombs, decompression ratio attacks
  5. Auth token / JWT parsing — malformed tokens shouldn't panic
  6. Regular expressions — catastrophic backtracking (ReDoS)
  7. 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

  1. Fuzz parsers and deserializers first — highest bug density, highest risk
  2. Catch expected errors in the harness — don't waste cycles on known-invalid input paths
  3. Always minimize crashes before filing/fixing — smaller reproducers are easier to debug
  4. Run ASan by default — it's nearly free and catches the most dangerous bugs
  5. Seed corpus matters — start fuzzing from realistic inputs, not just empty/random
  6. Check invariants, not just crashes — round-trip properties catch logic bugs
  7. Time-box CI fuzzing — 1-5 min per target in PR CI, longer runs nightly
  8. Every fixed crash becomes a permanent corpus entry — prevents regression
  9. Fuzz before every major release — especially after touching parsing code
  10. 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.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.