API Reference

Managers

class apatchy.managers.config_manager.ConfigManager(build_mode='fuzz', config_name='fuzz.conf', asan=False, ubsan=False, ubsan_ignorelist=None, intsan=False, truncsan=False)[source]

Generate compiler flags and resolve httpd config paths for a build.

ConfigManager is the central place that translates high-level build intentions (fuzzing, coverage, debugging) and sanitizer choices into the concrete CFLAGS, LDFLAGS, and CC values that BuildManager passes to Apache’s ./configure. It also resolves the path to the Apache config file (e.g. fuzz.conf) that other managers pass to the harness at runtime.

Build modes (mapped to tree types):

  • "vanilla" - root tree. Plain debug build (-O0 -g) for running a real Apache server to manually verify bugs.

  • "libfuzzer" - lf branch. Adds -fsanitize=fuzzer-no-link (SanCov instrumentation) for LibFuzzer coverage-guided fuzzing.

  • "coverage" - cov branch. Adds -fprofile-instr-generate and -fcoverage-mapping for coverage reports and triage.

  • "fuzz" - legacy mode (-O2), used by BugManager.

Sanitizers are orthogonal to the build mode and can be freely combined:

  • asan - AddressSanitizer (heap/stack buffer overflows, use-after-free).

  • ubsan - UndefinedBehaviorSanitizer (signed overflow, null deref, etc.).

  • intsan - unsigned-integer-overflow checks. A compile-time ignorelist (configs/intsan.ignorelist) is applied automatically to suppress false positives in APR internals.

  • truncsan - implicit-unsigned-integer-truncation checks. Noisy on Apache internals, best used for targeted module auditing.

When a known httpd version is passed to generate_build_config(), version-specific compatibility flags from apatchy.compat are appended automatically.

Parameters:
  • build_mode (str) – Build profile. "fuzz" (default), "coverage", or any other string for a plain debug build.

  • config_name (str) – Filename of the httpd config to resolve at runtime. Defaults to "fuzz.conf".

  • asan (bool) – Enable AddressSanitizer.

  • ubsan (bool) – Enable UndefinedBehaviorSanitizer.

  • intsan (bool) – Enable unsigned-integer-overflow sanitizer.

  • truncsan (bool) – Enable implicit-unsigned-integer-truncation sanitizer.

  • ubsan_ignorelist (str)

CLI usage:

ConfigManager is created implicitly by several CLI commands. The configure command exposes the most options:

# Fuzz build with ASan + UBSan
apatchy configure --mode fuzz --asan --ubsan

# Coverage build for triage
apatchy configure --mode coverage

# Fuzz with all integer sanitizers
apatchy configure --mode fuzz --asan --intsan --truncsan

Example

from apatchy.managers.config_manager import ConfigManager

# Fuzz build with ASan + UBSan
config = ConfigManager(build_mode="fuzz", asan=True, ubsan=True)
flags = config.generate_build_config(httpd_version="2.4.58")
# flags == {"CFLAGS": "-O2 -fno-omit-frame-pointer ...",
#           "LDFLAGS": "-no-pie -fsanitize=address ..."}

# Coverage build for triage
cov_config = ConfigManager(build_mode="coverage")
cov_flags = cov_config.generate_build_config()
generate_build_config(httpd_version=None)[source]

Generate CFLAGS and LDFLAGS based on the build mode.

Sanitizer flags (ASan, UBSan, IntSan, TruncSan) are orthogonal and can be combined with any mode. When httpd_version is provided, version-specific compatibility flags from apatchy.compat are appended automatically.

When intsan is enabled, a compile-time ignorelist (configs/intsan.ignorelist) is applied automatically to suppress false positives in APR internals.

Return type:

Dict[str, str]

Parameters:

httpd_version (str | None)

get_httpd_config(config_name=None)[source]

Return the path to the requested httpd config file.

Return type:

Optional[Path]

Parameters:

config_name (str | None)

validate_configuration()[source]

Verify compiler compatibility (e.g. clang for coverage).

Return type:

None

class apatchy.managers.module_manager.ModuleManager(httpd_root)[source]

Build external Apache modules as shared objects (.so) for runtime loading.

ModuleManager compiles custom C modules (e.g. mod_pwn.c) into position-independent shared objects that Apache can load via LoadModule at runtime. Module sources live in the external modules directory and are compiled against the Apache build tree’s headers.

The compiler defaults to clang, falling back to gcc if unavailable. A custom compiler can be specified via the cc argument.

Sanitizer flags (-fsanitize=...) are automatically extracted from the Apache build’s config_vars.mk and propagated to the module build, so modules are instrumented identically to the main binary.

Built .so files are placed in <WORK_DIR>/modules/ and can be referenced from an httpd config via LoadModule.

Parameters:

httpd_root (Path) – Path to the Apache HTTPD source directory whose headers and build config are used for compilation.

CLI usage:

# Build all external modules
apatchy module build

# Build a specific module
apatchy module build mod_pwn

# Build with a specific compiler
apatchy module build mod_pwn --cc clang

# List available modules and their build status
apatchy module list

Example

from pathlib import Path
from apatchy.managers.module_manager import ModuleManager

mm = ModuleManager(Path("httpd-2.4.58"))

# Build all modules with default compiler
mm.build_module()

# Build a specific module
mm.build_module(name="mod_pwn")

# List what is available
for m in mm.list_modules():
    print(f"{m['name']}  {m['built'] or '(not built)'}")
list_modules()[source]

List available external module sources.

Return type:

List[dict]

build_module(name=None, cc=None)[source]

Build one or all external modules as shared objects.

Return type:

None

Parameters:
  • name (str | None)

  • cc (str | None)

class apatchy.managers.build_manager.BuildManager(httpd_root, config_manager, verbose=False)[source]

High-level build orchestration for Apache HTTPD and fuzzing harnesses.

BuildManager drives the three main build steps: configure, make, and link. It delegates flag generation to ConfigManager and harness compilation to HarnessBuilder, adding dependency detection (PCRE, Expat), version-specific compat flags, and optional bear integration for compile_commands.json generation.

The configure step (configure_httpd()) runs Apache’s ./configure with a static-only, all-modules build. It automatically detects PCRE and Expat locations, enables APR pool debug mode when ASan is active (so ASan can track individual pool allocations), and appends any version-specific flags from the apatchy.compat registry.

The compile step (compile_httpd()) runs make with parallel jobs (defaults to CPU count). When bear=True, it wraps the build with bear to produce a compile_commands.json and updates the .clangd config for IDE integration.

The link step (build_harness()) compiles and links the fuzzing harness against the Apache build tree:

  • "libfuzzer" - compiled with clang and -fsanitize=fuzzer.

A coverage-instrumented harness (fuzz_harness_coverage) is built separately by ReportManager for triage and coverage reports.

Parameters:
  • httpd_root (Path) – Path to the Apache HTTPD source directory (e.g. httpd-2.4.58/).

  • config_manager (ConfigManager) – A ConfigManager instance that provides the compiler flags and sanitizer settings.

  • verbose (bool) – Show full build output instead of a progress spinner.

CLI usage:

# Configure Apache for fuzzing with ASan
apatchy configure --mode fuzz --asan

# Compile with bear for IDE support
apatchy compile --bear

# Link the harness
apatchy link --engine libfuzzer

Example

from pathlib import Path
from apatchy.managers.config_manager import ConfigManager
from apatchy.managers.build_manager import BuildManager

config = ConfigManager(build_mode="fuzz", asan=True)
bm = BuildManager(Path("httpd-2.4.58"), config)

bm.configure_httpd()
bm.compile_httpd(bear=True)
bm.build_harness(mode="libfuzzer")
configure_httpd(extra_flags=None)[source]

Run ./configure on the vanilla root tree.

Key flags and why they are needed:

  • --with-included-apr - build the bundled APR/APR-Util from srclib/ rather than using a system-installed version. Avoids version mismatches and ensures instrumentation covers APR code.

  • --disable-shared - static-only APR/APR-Util. Combined with --enable-mods-static=all, this makes libtool produce ELF binaries directly instead of wrapper shell scripts.

  • --disable-util-dso - statically links APR-Util’s crypto, dbd, and dbm driver plugins into libaprutil-1.a. Without it, APR-Util builds these as .so files loaded via dlopen().

  • --disable-pie - SanCov instrumentation (used by LibFuzzer) inserts non-PIC R_X86_64_32S relocations. PIE binaries with these relocations fail to link.

  • --with-crypto - enables OpenSSL support in APR-Util for mod_session_crypto. Without it, mod_session_crypto fails at runtime with “Your APR does not include SSL/EVP support”.

  • --enable-pool-debug=yes - (ASan only) makes apr_palloc() call malloc() directly so ASan can track individual pool allocations instead of sub-allocations from large blocks.

Return type:

None

Parameters:

extra_flags (list | None)

compile_httpd(jobs=None, clean=True, bear=False)[source]

Compile Apache HTTPD with make, optionally wrapped by bear.

Return type:

None

Parameters:
build_harness(mode='libfuzzer', harness_name=None, bear=False, cflags='', ldflags='')[source]

Build the fuzzing harness for the given engine.

The caller is responsible for pointing httpd_root at the correct build tree (e.g. the -lf branch for libfuzzer).

Return type:

None

Parameters:
class apatchy.managers.dev_manager.DevManager(httpd_root)[source]

Scaffold, build, and manage isolated developer harness projects.

DevManager lets you create self-contained fuzzing harness projects under the dev/ directory. Each project gets its own directory with a template harness.c, a seed input directory, and a compile_commands.json for IDE auto-completion. Projects can then be compiled against the Apache build tree for LibFuzzer or standalone (stdin-based) execution.

A project directory looks like:

dev/
  my_harness/
    harness.c               # your fuzzing harness source
    fuzz-seeds/              # seed corpus
      sample.txt
    compile_commands.json   # auto-generated for clangd
    fuzz_harness_libfuzzer  # built binary (after build)
    fuzz_harness_coverage   # coverage binary (after coverage build)

When building in standalone mode, DevManager automatically creates an alternate Apache build tree compiled with plain clang (no instrumentation) to avoid runtime conflicts. This tree is cached and reused across builds.

Parameters:

httpd_root (Path) – Path to the Apache HTTPD source directory that harnesses will link against.

CLI usage:

# Create a new project from the template
apatchy dev init my_harness

# Edit dev/my_harness/harness.c, then build
apatchy dev build my_harness
apatchy dev build my_harness --engine libfuzzer

# List all projects and their build status
apatchy dev list

Example

from pathlib import Path
from apatchy.managers.dev_manager import DevManager

dm = DevManager(Path("httpd-2.4.58"))

# Create a new project from the template
project_dir = dm.init_project("my_harness")

# Edit dev/my_harness/harness.c, then build
dm.build_project("my_harness", engine="libfuzzer")
dm.build_project("my_harness", engine="standalone")

# List all projects and their build status
for p in dm.list_projects():
    print(f"{p['name']}  built: {p['built']}")
init_project(name)[source]

Create a new dev harness project directory with template and compile_commands.json.

Return type:

Path

Parameters:

name (str)

build_project(name, engine='standalone')[source]

Build a dev harness project.

Return type:

None

Parameters:
list_projects()[source]

List all dev harness projects.

Return type:

List[Dict[str, str]]

class apatchy.managers.bug_manager.BugManager(bugs_dir=None, verbose=False)[source]

Discover, load, and orchestrate 1day bug reproductions.

BugManager manages the full lifecycle of known (1day) Apache HTTPD bugs: listing what is available, setting up a reproduction environment, and running the actual triage. Each bug lives in its own subdirectory under bugs/ and is described by a bug.toml manifest. An optional bug.py can subclass Bug to add custom setup, seed generation, or reproduction logic.

A typical bug directory looks like:

bugs/
  cve_xxxx_yyyyy/
    bug.toml        # manifest (id, version, modules, sanitizers, ...)
    bug.py          # optional Bug subclass with custom logic
    httpd.conf      # Apache config that triggers the bug
    gen_seed.py     # optional seed generator script
    seeds/          # generated seed inputs (created by setup)

The setup flow performed by setup() is:

  1. Download the vulnerable Apache version if not already present.

  2. Configure and build Apache with the sanitizers listed in the manifest.

  3. Link the fuzzing harnesses (LibFuzzer and standalone).

  4. Run any bug-specific setup (e.g. creating files in DocumentRoot).

  5. Generate seed inputs.

Parameters:
  • bugs_dir (Optional[Path]) – Root directory containing bug subdirectories. Defaults to <WORK_DIR>/bugs/.

  • verbose (bool) – Forward verbose flag to build operations.

Example

from apatchy.managers.bug_manager import BugManager

bm = BugManager()

# List all available bugs
for bug in bm.list_bugs():
    print(f"{bug['id']}  {bug['description']}")

# Set up a bug (download, build, seed)
bm.setup("CVE-2022-23943")

# Reproduce it by triaging the generated seeds
bm.reproduce("CVE-2022-23943")
list_bugs()[source]

Return metadata for every bug that has a bug.toml.

Returns:

Each dict contains keys from the [bug] table plus the directory path.

Return type:

List[Dict[str, Any]]

get_bug_instance(cve_id)[source]

Load and return a Bug instance for the given CVE.

Parameters:

cve_id (str) – CVE identifier. Accepted formats: CVE-2022-23943, cve-2022-23943, or cve_2022_23943.

Raises:

FileNotFoundError – If no matching bug directory or bug.toml is found.

Return type:

Bug

setup(cve_id)[source]

Full setup for a bug: download, configure, build, link, seeds.

Parameters:

cve_id (str) – CVE identifier.

Return type:

None

reproduce(cve_id)[source]

Reproduce a bug by triaging its seeds.

Parameters:

cve_id (str) – CVE identifier.

Return type:

None

class apatchy.managers.fuzz_manager.FuzzManager(config_manager)[source]

Thin factory that selects and launches the appropriate fuzzing engine.

FuzzManager is the main entry point for starting a fuzzing run. It delegates all engine-specific logic to LibFuzzer, which inherits shared corpus preparation and environment setup from BaseFuzzer.

The workflow is:

  1. Instantiate the engine class matching the engine argument.

  2. Call prepare_corpus() to create the seed and output directories (fuzz-seeds/, fuzz-output/), write a default seed if the seed directory is empty, and optionally expand a JSON grammar into concrete seed files.

  3. Call start() on the engine instance, forwarding all engine-specific options.

Parameters:

config_manager (ConfigManager) – A ConfigManager instance used to locate the httpd config file. The config path is passed to the harness via the FUZZ_CONF environment variable so Apache’s configuration pipeline is active during fuzzing.

CLI usage:

# Basic LibFuzzer run
apatchy fuzz --engine libfuzzer

# LibFuzzer with grammar seeds
apatchy fuzz --engine libfuzzer --grammar grammars/http.json

# Resume a previous session
apatchy fuzz --engine libfuzzer --resume

# Custom output directory
apatchy fuzz --engine libfuzzer --output-dir my-output

Example

from apatchy.managers.config_manager import ConfigManager
from apatchy.managers.fuzz_manager import FuzzManager

config = ConfigManager()
fm = FuzzManager(config)

fm.start_fuzzer(
    harness_path=Path("fuzz_harness_libfuzzer"),
    engine="libfuzzer",
)
class apatchy.managers.toolchain_manager.ToolchainManager(verbose=False)[source]

Detect, install, and configure the external tools needed by apatchy.

ToolchainManager scans for required dependencies (compilers, fuzzing tools, coverage utilities, libraries) and records their resolved paths in the toolchain config file. Other managers read this config to locate binaries like llvm-profdata and libtool.

The check() method walks a registry of ToolchainTool plugins, each of which knows how to detect one or more binaries and report their version. Results are written to the toolchain config so that subsequent builds use the correct paths without relying on PATH.

The setup() method delegates to a tool plugin’s installer. Currently supported installers:

  • llvm - locates or installs a specific LLVM version.

  • libtool - installs GNU libtool (needed for Apache’s build system).

Parameters:

verbose (bool) – Show detailed output during dependency detection.

CLI usage:

# Check all dependencies and write the toolchain config
apatchy setup check

# Install / build specific tools
apatchy setup llvm --llvm-version 18
apatchy setup libtool

Example

from apatchy.managers.toolchain_manager import ToolchainManager

tm = ToolchainManager()

# Scan and print all dependencies
for dep in tm.check():
    status = "OK" if dep.found else "MISSING"
    print(f"{dep.category}  {dep.name}  {status}  {dep.path or dep.install_hint}")

# Install LPM from source
tm.setup("lpm")
class apatchy.managers.report_manager.ReportManager(httpd_root, config_manager)[source]

Generate coverage reports, triage crashes, and profile fuzzing runs.

ReportManager is the post-fuzzing analysis hub. It takes the raw output from a fuzzing session and produces actionable reports:

Coverage (generate_coverage()) - Rebuilds Apache with LLVM source-based coverage instrumentation, replays the fuzzer corpus through the harness, merges the raw profiles with llvm-profdata, and generates an HTML report with llvm-cov. Dark-mode CSS is injected automatically. Optionally chains into introspection.

Introspection (generate_introspect()) - Uses the IntrospectorManager to extract a call tree and per-function coverage from LLVM bitcode and profdata, producing a JSON file that can be viewed in an interactive web UI.

Triage - Replays individual crash files or entire directories through the coverage harness to extract sanitizer reports:

Profiling (generate_callgrind()) - Replays the corpus through valgrind --tool=callgrind to produce callgrind profiles for analysis with tools like KCachegrind.

Parameters:
  • httpd_root (Path) – Path to the Apache HTTPD source directory.

  • config_manager (ConfigManager) – A ConfigManager instance. For coverage, it should be created with build_mode="coverage".

CLI usage:

# Triage a single crash file
apatchy triage fuzz-output/default/crashes/id:000000

# Triage all crashes in a directory
apatchy triage --bulk fuzz-output/default/crashes/

# Replay numbered crash files as sequential requests
apatchy triage --pipeline fuzz-output/

# Generate a coverage report from corpus
apatchy coverage report --fuzzer-dir fuzz-output/

# Coverage with introspection chained in
apatchy coverage report --fuzzer-dir fuzz-output/ --with-introspect

# Generate introspection data with interactive viewer
apatchy introspect --entry ap_process_request

# Generate callgrind profiles
apatchy profile callgrind --fuzzer-dir fuzz-output/

Example

from pathlib import Path
from apatchy.managers.config_manager import ConfigManager
from apatchy.managers.report_manager import ReportManager

# Triage a crash
config = ConfigManager(config_name="fuzz.conf")
rm = ReportManager(Path("httpd-2.4.58"), config)
rm.triage_crash("fuzz-output/default/crashes/id:000000", Path("fuzz_harness_coverage"))

# Generate coverage
cov_config = ConfigManager(build_mode="coverage")
rm = ReportManager(Path("httpd-2.4.58"), cov_config)
rm.generate_coverage(corpus_dir="fuzz-output/")
generate_coverage(corpus_dir='fuzz-output', config_name='fuzz.conf', output_dir='coverage-report', harness_name=None, exclude_file=None, with_introspect=False, with_modules=False)[source]

Full coverage pipeline: build harness, replay corpus, merge, generate report.

Return type:

None

Parameters:
  • corpus_dir (str)

  • config_name (str)

  • output_dir (str)

  • harness_name (str)

  • exclude_file (str | None)

generate_introspect(entry, profdata_path=None, binary_path=None, bitcode_path=None, output_path='introspect.json', fuzzer_dir='fuzz-output', serve=True, port=9000)[source]

Merge call tree analysis with coverage data into a single JSON.

Return type:

None

Parameters:
  • entry (str)

  • profdata_path (str | None)

  • binary_path (str | None)

  • bitcode_path (str | None)

  • output_path (str)

  • fuzzer_dir (str)

  • serve (bool)

  • port (int)

generate_callgrind(corpus_dir='fuzz-output', config_name='fuzz.conf', output_dir='callgrind-out', harness_name=None)[source]

Replay corpus under callgrind and collect output for kcachegrind.

Automatically builds a dedicated -prof Apache tree with debug symbols, no sanitizers, and no coverage instrumentation so that callgrind output is clean and all function names are visible.

Return type:

None

Parameters:
  • corpus_dir (str)

  • config_name (str)

  • output_dir (str)

  • harness_name (str)

triage_crash(crash_file, harness_binary, no_color=False, suppress=None, timeout=30, is_libfuzzer=False)[source]

Replay crash_file through harness_binary and print the sanitizer output.

suppress is an optional path to a UBSan runtime suppression file (passed via UBSAN_OPTIONS=suppressions=). timeout controls how long the harness is allowed to run before being killed.

Return type:

None

Parameters:
  • crash_file (Path)

  • harness_binary (Path)

  • no_color (bool)

  • suppress (str | None)

  • timeout (int)

  • is_libfuzzer (bool)

triage_bulk(crash_dir, harness_binary, no_color=False, suppress=None, timeout=30, is_libfuzzer=False)[source]

Triage every crash file in crash_dir individually and print a summary table.

Return type:

None

Parameters:
  • crash_dir (Path)

  • harness_binary (Path)

  • no_color (bool)

  • suppress (str | None)

  • timeout (int)

  • is_libfuzzer (bool)

class apatchy.managers.introspector_manager.IntrospectorManager(httpd_root, work_dir)[source]

Compile Apache C sources into LLVM bitcode and link them for introspection.

IntrospectorManager is used by ReportManager to produce the LLVM bitcode that the introspection pipeline needs for call-tree extraction and per-function coverage analysis.

The workflow has two phases:

  1. Emit bitcode - Reads the compile_commands.json from the coverage build tree (<httpd_root>-cov). If the file does not exist, it runs bear to trace a fresh make and generate one. Each .c entry is re-compiled with -emit-llvm to produce a .bc file under <httpd_root>-cov/bitcode/. Test, support, and platform-specific directories are excluded automatically.

  2. Link bitcode - All emitted .bc files are linked into a single combined.bc with llvm-link. Before linking, llvm-nm is used to detect and skip files with duplicate global symbols to avoid link errors. The combined bitcode is consumed by the introspector C++ tool to build a call tree and by llvm-cov to map coverage back to functions.

IntrospectorManager is not invoked directly from the CLI. It is called internally when the user runs:

# Coverage report with introspection chained in
apatchy coverage report --fuzzer-dir fuzz-output/ --with-introspect

# Standalone introspection (auto-builds bitcode if missing)
apatchy introspect ap_process_request_internal
Parameters:
  • httpd_root (Path) – Path to the Apache HTTPD source directory. The coverage build tree is expected at <httpd_root>-cov.

  • work_dir (Path) – Working directory where build artifacts, harnesses, and output files are stored.

Example

from pathlib import Path
from apatchy.managers.introspector_manager import IntrospectorManager

im = IntrospectorManager(
    httpd_root=Path("httpd-2.4.58"),
    work_dir=Path("work"),
)

# Emit per-file .bc and link into combined.bc
im.build_bitcode(cc="clang-18")
build_bitcode(cc)[source]

Generate bitcode for C sources.

Return type:

None

Parameters:

cc (str)

Core

class apatchy.core.process_runner.ProcessRunner(verbose=False)[source]

Execute shell commands with automatic logging.

Parameters:

verbose (bool) – When True, run_build() prints raw output to the terminal instead of rendering a Rich scrolling panel.

static run_command(command, cwd=None, env=None, check=True, stdin=None, capture_output=True, stream=False, label='Building...')[source]

Run a shell command and log output.

Parameters:
  • stream (bool) – When True, show output in a Rich scrolling panel instead of capturing silently or printing raw.

  • label (str) – Panel title used when stream is True.

  • command (str | List[str])

  • cwd (str | Path | None)

  • env (Dict[str, str] | None)

  • check (bool)

  • stdin (Any | None)

  • capture_output (bool)

Return type:

CompletedProcess

run_build(command, label='Building...', **kwargs)[source]

Run a build command with appropriate output mode.

When self.verbose is False the output is rendered inside a Rich scrolling panel. When self.verbose is True the raw output streams directly to the terminal.

Return type:

CompletedProcess

Parameters:
class apatchy.core.downloader.Downloader[source]

Fetch and unpack Apache HTTPD, APR, APR-Util, and Expat.

list_versions()[source]

Fetch available Apache HTTPD 2.4.x versions from the archive.

Returns:

Each entry has keys version (str), mirror (bool, on the primary mirror), and downloaded (bool, already local).

Return type:

List[dict]

download_apache(version=None)[source]

Download Apache HTTPD and its bundled dependencies.

Parameters:

version (Optional[str]) – Version to download (e.g. "2.4.62"). Defaults to Config.DEFAULT_APACHE_VERSION.

Returns:

Directory containing the extracted source tree.

Return type:

Path

class apatchy.core.harness.HarnessBuilder(httpd_root, verbose=False)[source]

Compile and link a fuzzing harness against an Apache build tree.

Parameters:
  • httpd_root (Path) – Root of the configured/compiled Apache HTTPD source tree.

  • verbose (bool)

static list_harnesses()[source]

List available harness files with their descriptions.

static resolve_harness(name)[source]

Resolve a harness name to a file path.

Resolution order: - Bundled: HARNESSES_DIR/<name>.c - Literal path: <name> as file path

static is_proto(name)[source]

Return True if name resolves to a .cc (proto) harness.

build(mode='standalone', cflags='', ldflags='', harness_name=None, cc=None, bear=False)[source]

Compile and link the harness for the given fuzzing engine.

Parameters:
  • mode (str) – One of "libfuzzer", "standalone", "coverage", or "profile".

  • cflags (str) – Extra compiler flags.

  • ldflags (str) – Extra linker flags.

  • harness_name (str, optional) – Name of a bundled harness or path to a harness source file. Defaults to "mod_fuzzy" if not provided.

  • cc (str, optional) – Override the compiler (defaults to COMPILERS lookup).

  • bear (bool) – Wrap compilation with bear for compile_commands.json.

get_include_paths()[source]

Return all -I include flags for Apache/APR/APR-Util headers.

check_compiles(harness_src, cflags, cc='clang')[source]

Quick syntax-only probe. Returns (ok, stderr) tuple.

Toolchain

class apatchy.core.toolchain.base.DepStatus(name, category, found, version='', path='', install_hint='')[source]
Parameters:
class apatchy.core.toolchain.base.ToolchainTool(toolchain_dir, verbose=False)[source]

Base class for all toolchain tools. Subclasses must implement check().

Parameters:
detect()[source]

Return the installed version string, or None if not found.

Return type:

Optional[str]

abstractmethod check()[source]

Return the full status of all sub-tools. Used by apatchy setup check.

Return type:

List[DepStatus]

setup(force=False, **kwargs)[source]

Install or configure the tool. Override in subclasses that support it.

Return type:

None

Parameters:

force (bool)

class apatchy.core.toolchain.simple.BinaryTool(name, category, install_hint, toolchain_dir, verbose=False, fallback=None, version_args=None, exists_only=False)[source]
Parameters:
  • name (str)

  • category (str)

  • install_hint (str)

  • toolchain_dir (Path)

  • verbose (bool)

  • fallback (str | None)

  • version_args (List[str] | None)

  • exists_only (bool)

detect()[source]

Return the installed version string, or None if not found.

Return type:

Optional[str]

check()[source]

Return the full status of all sub-tools. Used by apatchy setup check.

Return type:

List[DepStatus]

class apatchy.core.toolchain.simple.PkgOrConfigTool(name, category, install_hint, toolchain_dir, config_binary, pkg_name, verbose=False)[source]
Parameters:
  • name (str)

  • category (str)

  • install_hint (str)

  • toolchain_dir (Path)

  • config_binary (str | None)

  • pkg_name (str)

  • verbose (bool)

check()[source]

Return the full status of all sub-tools. Used by apatchy setup check.

Return type:

List[DepStatus]

class apatchy.core.toolchain.simple.HeaderOrPkgTool(name, category, install_hint, toolchain_dir, header, pkg_name, verbose=False)[source]
Parameters:
check()[source]

Return the full status of all sub-tools. Used by apatchy setup check.

Return type:

List[DepStatus]

class apatchy.core.toolchain.llvm.LlvmTool(toolchain_dir, verbose=False)[source]
Parameters:
detect()[source]

Return the installed version string, or None if not found.

Return type:

Optional[str]

check()[source]

Return the full status of all sub-tools. Used by apatchy setup check.

Return type:

List[DepStatus]

setup(force=False, **kwargs)[source]

Install or configure the tool. Override in subclasses that support it.

Return type:

None

Parameters:

force (bool)

class apatchy.core.toolchain.libtool.LibtoolTool(toolchain_dir, verbose=False)[source]
Parameters:
detect()[source]

Return the installed version string, or None if not found.

Return type:

Optional[str]

check()[source]

Return the full status of all sub-tools. Used by apatchy setup check.

Return type:

List[DepStatus]

setup(force=False, **kwargs)[source]

Install or configure the tool. Override in subclasses that support it.

Return type:

None

Parameters:

force (bool)

Misc

class apatchy.config.Config[source]

Central configuration store for the fuzzer.

All attributes are class-level - there is no need to create an instance. Paths that depend on the current working directory are resolved once at import time.

classmethod get_apache_dir(version)[source]

Return the expected source directory for a given Apache version.

Parameters:

version (str) – Apache HTTPD version string, e.g. "2.4.62".

Returns:

Absolute path to the httpd-<version> directory inside WORK_DIR.

Return type:

Path

class apatchy.bugs.base.Bug(bug_dir, manifest)[source]

Base class for 1day bug reproductions.

Parameters:
  • bug_dir (Path) – Absolute path to the bug’s directory (e.g. bugs/cve_2022_23943/).

  • manifest (Dict[str, Any]) – Parsed contents of bug.toml.

property cve_id: str

The CVE identifier from the manifest.

property description: str

Short description of the bug.

property version: str

Last affected Apache HTTPD version.

property modules: List[str]

Apache modules involved in the bug.

property bug_type: str

Bug classification (e.g. heap-buffer-overflow, integer-overflow).

property references: List[str]

External reference URLs.

property httpd_config: Path

Path to the httpd.conf for this bug.

property seeds_dir: Path

Directory containing seed inputs.

property sanitizers: List[str]

Recommended sanitizers from the manifest.

property harness: str | None

Harness name to use for this bug (e.g. mod_fuzzy).

property triage_timeout: int

Recommended triage timeout in seconds.

property suppress_file: str | None

Resolved path to the UBSan suppression file.

Looks in the bug directory first, then configs/. Returns None if not specified or not found.

setup()[source]

Extra setup beyond the standard download/configure/make.

Override for bugs that need special preparation (e.g. creating files in DocumentRoot, generating certificates). Called after Apache is built and harnesses are linked.

Return type:

None

generate_seeds()[source]

Generate seed inputs for this bug.

Override to create seeds programmatically. The default implementation runs gen_seed.py if it exists in the bug directory.

Return type:

None

reproduce(harness_path, **kwargs)[source]

Reproduce the bug by triaging all seeds.

Override for custom reproduction logic (e.g. multi-step sequences, specific seed ordering). The default delegates to ReportManager.

Parameters:
  • harness_path (Path) – Path to the compiled harness binary.

  • **kwargs (Any) – Extra arguments forwarded to the triage call.

Return type:

None

configure_flags()[source]

Extra Apache configure flags needed for this bug.

Override if the bug needs flags beyond the defaults (e.g. --with-crypto for session_crypto bugs).

Return type:

List[str]

fuzz_env()[source]

Extra environment variables for fuzzing this bug.

Override to set custom env vars (e.g. custom dictionary paths).

Return type:

Dict[str, str]

clean()[source]

Clean up generated artifacts for this bug.

Override to remove bug-specific files (e.g. files created in /tmp, generated configs). The default implementation removes the seeds/ directory if it exists.

Return type:

None

class apatchy.compat.CompatEntry(id, description, versions, cflags=<factory>, ldflags=<factory>, configure_args=<factory>)[source]

A build compatibility fix applied to HTTPD versions matching a specifier.

The versions field uses PEP 440 version specifiers (via packaging.specifiers.SpecifierSet). Examples:

"<=2.4.58"              # all versions up to 2.4.58
">=2.4.32,<=2.4.51"    # versions 2.4.32 through 2.4.51
">=2.4.0,<2.4.60"      # inclusive start, exclusive end
Parameters:
class apatchy.compat.CompatResult(cflags, ldflags, configure_args, applied_ids)[source]
Parameters:
class apatchy.utils.build_tree.AlternateBuildTree(httpd_root, suffix)[source]

Creates and manages a copy of an Apache build tree with different compiler flags.

Parameters:
ensure_build(cc, cflags, ldflags)[source]

Create the alternate tree (if needed) and rebuild Apache with the given flags.

Returns the path to the alternate httpd root.

Return type:

Path

Parameters:
static rewrite_paths(tree, old_prefix, new_prefix)[source]

Replace hardcoded absolute paths in build-system files.

Apache’s configure bakes the build directory into Makefiles, libtool scripts, .la files, config.status, etc. After copying the tree we must rewrite these so make operates on the copy rather than the original.

Return type:

None

Parameters:
static patch_build_flags(cc, cflags, ldflags, httpd_root)[source]

Patch CC, CFLAGS, and LDFLAGS across all build-system files.

The original build bakes compiler paths and sanitizer flags into config_vars.mk, apr_rules.mk, rules.mk, and libtool scripts throughout the tree (including srclib/apr and srclib/apr-util). We must patch ALL of them so the alternate build compiles and links cleanly with the desired flags.

LDFLAGS is patched in config files rather than passed on the make command line because command-line LDFLAGS clobbers the Makefile variable everywhere, which breaks libtool’s transitive dependency resolution (support utilities lose -lcrypt, -lm, etc.).

Return type:

None

Parameters:
static config_hash(httpd_root)[source]

Hash the ac_cs_config line from config.status for staleness detection.

Return type:

str

Parameters:

httpd_root (Path)

class apatchy.utils.ui.UI[source]

Static helpers for formatted terminal output using Rich markup.

static print_header(title)[source]

Print a bold blue section header.

Return type:

None

Parameters:

title (str)

static print_success(msg)[source]

Print a green success message.

Return type:

None

Parameters:

msg (str)

static print_error(msg)[source]

Print a bold red error message.

Return type:

None

Parameters:

msg (str)

static print_info(msg)[source]

Print a cyan informational message.

Return type:

None

Parameters:

msg (str)

static print_warning(msg)[source]

Print a yellow warning message.

Return type:

None

Parameters:

msg (str)

class apatchy.method_dispatcher.MethodDispatcher[source]

Route parsed CLI arguments to the correct manager method.

Managers are created lazily - only when the corresponding command is invoked - because most of them require a resolved HTTPD source tree or engine-specific configuration.

dispatch(args)[source]

Inspect args.command and delegate to the matching handler.

Return type:

None

Parameters:

args (Namespace)