google/fuzzing-python-expert
Use this skill to fuzz open source Python software projects using Atheris.
npx skills add https://github.com/google/oss-fuzz --skill fuzzing-python-expert
This skill provides the agent with the knowledge and tools to write, build, and
validate fuzz targets for Python projects integrated into OSS-Fuzz. Python
fuzzing uses Atheris, which wraps libFuzzer
and instruments Python bytecode for coverage-guided fuzzing.
Python projects must use the Python base builder image:
FROM gcr.io/oss-fuzz-base/base-builder-python
Set language: python in project.yaml.
A Python fuzz target is a .py file (named fuzz_<target>.py by convention)
that follows this pattern:
#!/usr/bin/python3
import sys
import atheris
# Import the module under test after atheris.instrument_imports() or within
# the atheris.instrument_all() context so bytecode is instrumented.
def TestOneInput(data):
fdp = atheris.FuzzedDataProvider(data)
# Extract typed values from the raw fuzzer bytes.
value = fdp.ConsumeString(128)
try:
my_module.parse(value)
except (ValueError, TypeError, KeyError):
# Expected exceptions from invalid input are not bugs.
pass
def main():
atheris.instrument_all() # instrument all loaded Python modules
atheris.Setup(sys.argv, TestOneInput, enable_python_coverage=True)
atheris.Fuzz()
if __name__ == "__main__":
main()
When instrument_all() is too broad (e.g. causes conflicts with C extensions),
instrument specific modules using the instrument_imports() context manager:
import atheris
with atheris.instrument_imports():
import my_module
import my_module.subpackage
atheris.FuzzedDataProvider splits the raw byte stream into typed values:
| Method | Description |
|---|---|
| ConsumeBytes(count) | bytes of length count |
| ConsumeByteList(count) | list[int] of length count |
| ConsumeString(count) | decoded str (may contain surrogates) |
| ConsumeUnicode(count) | str without surrogates |
| ConsumeUnicodeNoSurrogates(count) | strict str |
| ConsumeInt(nbytes) | signed int from nbytes |
| ConsumeIntInRange(min, max) | int in range |
| ConsumeFloat() | float |
| ConsumeBool() | bool |
| ConsumeIntList(count, nbytes) | list of ints |
| PickValueInList(lst) | random element |
| ConsumeRemainingBytes() | all remaining bytes |
build.sh installs the target package and uses the compile_python_fuzzer
helper to turn each fuzz_*.py file into a standalone fuzzer binary in $OUT:
# build.sh
# Install the package under test.
pip3 install .
# Compile all fuzz targets found in $SRC.
for fuzzer in $(find $SRC -name 'fuzz_*.py'); do
compile_python_fuzzer "$fuzzer"
done
compile_python_fuzzer handles linking against Atheris and libFuzzer and
produces an executable in $OUT named after the .py file.
$OUT/<fuzzer_name>_seed_corpus/ or zip them as$OUT/<fuzzer_name>_seed_corpus.zip.
$OUT/<fuzzer_name>.dict — especially valuable fortext-format parsers (JSON, XML, YAML, CSV, etc.).
beats hand-picking a few files — random mutation rarely passes the parser's
early checks. See the [structured seed generation
reference](../oss-fuzz-engineer/references/structured_seed_generation.md).
instrument_all() for simplicity orinstrument_imports() for targeted instrumentation. Without instrumentation
coverage guidance is blind.
(pickle, JSON, YAML, XML), network protocol handling, and any API that
accepts untrusted strings or bytes.
try/except for thedocumented exception types the target raises on bad input. Only unexpected
exceptions and hard crashes are findings.
FuzzedDataProvider for structured input rather than feeding rawbytes directly to APIs that expect text — most Python APIs work on strings,
not bytes.
TestOneInput: imports should happenat module level (inside the instrument_imports() block if used) so they
are instrumented and not re-executed per iteration.
TestOneInput.
random, no datetime.now(), noos.urandom() inside the fuzz function.
enable_python_coverage=True toatheris.Setup for bytecode-level coverage tracking.
Python is memory-safe, so the focus is on:
ValueError, RecursionError, MemoryError,UnicodeDecodeError, and any exception the library should have caught and
converted to a clean error.
AssertionError: internal invariant violations triggered by craftedinput.
Pillow, cryptography) can still have memory-corruption bugs in their C layer,
which Atheris will surface because libFuzzer runs the whole process.
results on edge-case inputs.
python3 infra/helper.py build_fuzzers <project>
python3 infra/helper.py check_build <project>
python3 infra/helper.py run_fuzzer <project> <fuzzer_name> -- -max_total_time=30
Run the harness locally (python3 fuzz_target.py) on a sample input to
debug before building through OSS-Fuzz.
python3 fuzz_target.py <seed_file>
Atheris supports running in single-input mode outside libFuzzer.
pip3 install .) and iterate quickly beforegoing through the Docker build.
RUN git clone to COPY to avoid network round-trips.
Take google/fuzzing-python-expert from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.
The instructions reference pip.
Without those the skill loads but fails at the first command.