chore(modules): remove redundant harness-validator module

This commit is contained in:
AFredefon
2026-02-03 18:09:06 +01:00
parent d786c6dab1
commit f099bd018d
12 changed files with 0 additions and 582 deletions

View File

@@ -1,23 +0,0 @@
FROM localhost/fuzzforge-modules-sdk:0.1.0
# Install build tools and Rust nightly for compiling fuzz harnesses
RUN apt-get update && apt-get install -y \
curl \
build-essential \
pkg-config \
libssl-dev \
&& rm -rf /var/lib/apt/lists/*
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain nightly
ENV PATH="/root/.cargo/bin:${PATH}"
# Install cargo-fuzz for validation
RUN cargo install cargo-fuzz --locked || true
COPY ./src /app/src
COPY ./pyproject.toml /app/pyproject.toml
# Remove workspace reference since we're using wheels
RUN sed -i '/\[tool\.uv\.sources\]/,/^$/d' /app/pyproject.toml
RUN uv sync --find-links /wheels

View File

@@ -1,45 +0,0 @@
PACKAGE=$(word 1, $(shell uv version))
VERSION=$(word 2, $(shell uv version))
PODMAN?=/usr/bin/podman
SOURCES=./src
TESTS=./tests
.PHONY: bandit build clean format mypy pytest ruff version
bandit:
uv run bandit --recursive $(SOURCES)
build:
$(PODMAN) build --file ./Dockerfile --no-cache --tag $(PACKAGE):$(VERSION)
save: build
$(PODMAN) save --format oci-archive --output /tmp/$(PACKAGE)-$(VERSION).oci $(PACKAGE):$(VERSION)
clean:
@find . -type d \( \
-name '*.egg-info' \
-o -name '.mypy_cache' \
-o -name '.pytest_cache' \
-o -name '.ruff_cache' \
-o -name '__pycache__' \
\) -printf 'removing directory %p\n' -exec rm -rf {} +
cloc:
cloc $(SOURCES)
format:
uv run ruff format $(SOURCES) $(TESTS)
mypy:
uv run mypy $(SOURCES)
pytest:
uv run pytest $(TESTS)
ruff:
uv run ruff check --fix $(SOURCES) $(TESTS)
version:
@echo '$(PACKAGE)@$(VERSION)'

View File

@@ -1,46 +0,0 @@
# FuzzForge Modules - FIXME
## Installation
### Python
```shell
# install the package (users)
uv sync
# install the package and all development dependencies (developers)
uv sync --all-extras
```
### Container
```shell
# build the image
make build
# run the container
mkdir -p "${PWD}/data" "${PWD}/data/input" "${PWD}/data/output"
echo '{"settings":{},"resources":[]}' > "${PWD}/data/input/input.json"
podman run --rm \
--volume "${PWD}/data:/data" \
'<name>:<version>' 'uv run module'
```
## Usage
```shell
uv run module
```
## Development tools
```shell
# run ruff (formatter)
make format
# run mypy (type checker)
make mypy
# run tests (pytest)
make pytest
# run ruff (linter)
make ruff
```
See the file `Makefile` at the root of this directory for more tools.

View File

@@ -1,6 +0,0 @@
[mypy]
plugins = pydantic.mypy
strict = True
warn_unused_ignores = True
warn_redundant_casts = True
warn_return_any = True

View File

@@ -1,31 +0,0 @@
[project]
name = "harness-validator"
version = "0.1.0"
description = "FuzzForge module that validates fuzz harnesses compile correctly"
authors = []
readme = "README.md"
requires-python = ">=3.14"
dependencies = [
"fuzzforge-modules-sdk==0.0.1",
"pydantic==2.12.4",
"structlog==25.5.0",
]
[project.optional-dependencies]
lints = [
"bandit==1.8.6",
"mypy==1.18.2",
"ruff==0.14.4",
]
tests = [
"pytest==9.0.2",
]
[project.scripts]
module = "module.__main__:main"
[tool.uv.sources]
fuzzforge-modules-sdk = { workspace = true }
[tool.uv]
package = true

View File

@@ -1,19 +0,0 @@
line-length = 120
[lint]
select = [ "ALL" ]
ignore = [
"COM812", # conflicts with the formatter
"D100", # ignoring missing docstrings in public modules
"D104", # ignoring missing docstrings in public packages
"D203", # conflicts with 'D211'
"D213", # conflicts with 'D212'
"TD002", # ignoring missing author in 'TODO' statements
"TD003", # ignoring missing issue link in 'TODO' statements
]
[lint.per-file-ignores]
"tests/*" = [
"PLR2004", # allowing comparisons using unamed numerical constants in tests
"S101", # allowing 'assert' statements in tests
]

View File

@@ -1,19 +0,0 @@
from typing import TYPE_CHECKING
from fuzzforge_modules_sdk.api import logs
from module.mod import Module
if TYPE_CHECKING:
from fuzzforge_modules_sdk.api.modules.base import FuzzForgeModule
def main() -> None:
"""TODO."""
logs.configure()
module: FuzzForgeModule = Module()
module.main()
if __name__ == "__main__":
main()

View File

@@ -1,309 +0,0 @@
"""Harness Validator module for FuzzForge.
This module validates that fuzz harnesses compile correctly.
It takes a Rust project with a fuzz directory containing harnesses
and runs cargo build to verify they compile.
"""
from __future__ import annotations
import json
import subprocess
import os
from pathlib import Path
from typing import TYPE_CHECKING
import structlog
from fuzzforge_modules_sdk.api.constants import PATH_TO_INPUTS, PATH_TO_OUTPUTS
from fuzzforge_modules_sdk.api.models import FuzzForgeModuleResults
from fuzzforge_modules_sdk.api.modules.base import FuzzForgeModule
from module.models import Input, Output, ValidationResult, HarnessStatus
from module.settings import Settings
if TYPE_CHECKING:
from fuzzforge_modules_sdk.api.models import FuzzForgeModuleResource
logger = structlog.get_logger()
class Module(FuzzForgeModule):
"""Harness Validator module - validates that fuzz harnesses compile."""
_settings: Settings | None
_results: list[ValidationResult]
def __init__(self) -> None:
"""Initialize an instance of the class."""
name: str = "harness-validator"
version: str = "0.1.0"
FuzzForgeModule.__init__(self, name=name, version=version)
self._settings = None
self._results = []
@classmethod
def _get_input_type(cls) -> type[Input]:
"""Return the input type."""
return Input
@classmethod
def _get_output_type(cls) -> type[Output]:
"""Return the output type."""
return Output
def _prepare(self, settings: Settings) -> None: # type: ignore[override]
"""Prepare the module.
:param settings: Module settings.
"""
self._settings = settings
logger.info("harness-validator preparing", settings=settings.model_dump() if settings else {})
def _run(self, resources: list[FuzzForgeModuleResource]) -> FuzzForgeModuleResults:
"""Run the harness validator.
:param resources: Input resources (fuzz project directory).
:returns: Module execution result.
"""
logger.info("harness-validator starting", resource_count=len(resources))
# Find the fuzz project directory
fuzz_project_src = self._find_fuzz_project(resources)
if fuzz_project_src is None:
logger.error("No fuzz project found in resources")
return FuzzForgeModuleResults.FAILURE
logger.info("Found fuzz project", path=str(fuzz_project_src))
# Copy the project to a writable location since /data/input is read-only
# and cargo needs to write Cargo.lock and build artifacts
import shutil
work_dir = Path("/tmp/fuzz-build")
if work_dir.exists():
shutil.rmtree(work_dir)
# Copy entire project root (parent of fuzz directory)
project_root = fuzz_project_src.parent
work_project = work_dir / project_root.name
shutil.copytree(project_root, work_project, dirs_exist_ok=True)
# Adjust fuzz_project to point to the copied location
fuzz_project = work_dir / project_root.name / fuzz_project_src.name
logger.info("Copied project to writable location", work_dir=str(fuzz_project))
# Find all harness targets
targets = self._find_harness_targets(fuzz_project)
if not targets:
logger.error("No harness targets found")
return FuzzForgeModuleResults.FAILURE
logger.info("Found harness targets", count=len(targets))
# Validate each harness
all_valid = True
for target in targets:
result = self._validate_harness(fuzz_project, target)
self._results.append(result)
if result.status != HarnessStatus.VALID:
all_valid = False
logger.warning("Harness validation failed",
target=target,
status=result.status.value,
errors=result.errors)
else:
logger.info("Harness valid", target=target)
# Set output data for results.json
valid_targets = [r.target for r in self._results if r.status == HarnessStatus.VALID]
invalid_targets = [r.target for r in self._results if r.status != HarnessStatus.VALID]
self.set_output(
fuzz_project=str(fuzz_project),
total_targets=len(self._results),
valid_count=len(valid_targets),
invalid_count=len(invalid_targets),
valid_targets=valid_targets,
invalid_targets=invalid_targets,
results=[r.model_dump() for r in self._results],
)
valid_count = sum(1 for r in self._results if r.status == HarnessStatus.VALID)
logger.info("harness-validator completed",
total=len(self._results),
valid=valid_count,
invalid=len(self._results) - valid_count)
return FuzzForgeModuleResults.SUCCESS
def _cleanup(self, settings: Settings) -> None: # type: ignore[override]
"""Clean up after execution.
:param settings: Module settings.
"""
pass
def _find_fuzz_project(self, resources: list[FuzzForgeModuleResource]) -> Path | None:
"""Find the fuzz project directory in the resources.
:param resources: List of input resources.
:returns: Path to fuzz project or None.
"""
for resource in resources:
path = Path(resource.path)
# Check if it's a fuzz directory with Cargo.toml
if path.is_dir():
cargo_toml = path / "Cargo.toml"
if cargo_toml.exists():
# Check if it has fuzz_targets directory
fuzz_targets = path / "fuzz_targets"
if fuzz_targets.is_dir():
return path
# Check for fuzz subdirectory
fuzz_dir = path / "fuzz"
if fuzz_dir.is_dir():
cargo_toml = fuzz_dir / "Cargo.toml"
if cargo_toml.exists():
return fuzz_dir
return None
def _find_harness_targets(self, fuzz_project: Path) -> list[str]:
"""Find all harness target names in the fuzz project.
:param fuzz_project: Path to the fuzz project.
:returns: List of target names.
"""
targets = []
fuzz_targets_dir = fuzz_project / "fuzz_targets"
if fuzz_targets_dir.is_dir():
for rs_file in fuzz_targets_dir.glob("*.rs"):
# Target name is the file name without extension
target_name = rs_file.stem
targets.append(target_name)
return targets
def _validate_harness(self, fuzz_project: Path, target: str) -> ValidationResult:
"""Validate a single harness by compiling it.
:param fuzz_project: Path to the fuzz project.
:param target: Name of the harness target.
:returns: Validation result.
"""
harness_file = fuzz_project / "fuzz_targets" / f"{target}.rs"
if not harness_file.exists():
return ValidationResult(
target=target,
file_path=str(harness_file),
status=HarnessStatus.NOT_FOUND,
errors=["Harness file not found"],
)
# Try to compile just this target
try:
env = os.environ.copy()
env["CARGO_INCREMENTAL"] = "0"
result = subprocess.run(
[
"cargo", "build",
"--bin", target,
"--message-format=json",
],
cwd=fuzz_project,
capture_output=True,
text=True,
timeout=self._settings.compile_timeout if self._settings else 120,
env=env,
)
# Parse cargo output for errors
errors = []
warnings = []
for line in result.stdout.splitlines():
try:
msg = json.loads(line)
if msg.get("reason") == "compiler-message":
message = msg.get("message", {})
level = message.get("level", "")
rendered = message.get("rendered", "")
if level == "error":
errors.append(rendered.strip())
elif level == "warning":
warnings.append(rendered.strip())
except json.JSONDecodeError:
pass
# Also check stderr for any cargo errors
if result.returncode != 0 and not errors:
errors.append(result.stderr.strip() if result.stderr else "Build failed with unknown error")
if result.returncode == 0:
return ValidationResult(
target=target,
file_path=str(harness_file),
status=HarnessStatus.VALID,
errors=[],
warnings=warnings,
)
else:
return ValidationResult(
target=target,
file_path=str(harness_file),
status=HarnessStatus.COMPILE_ERROR,
errors=errors,
warnings=warnings,
)
except subprocess.TimeoutExpired:
return ValidationResult(
target=target,
file_path=str(harness_file),
status=HarnessStatus.TIMEOUT,
errors=["Compilation timed out"],
)
except Exception as e:
return ValidationResult(
target=target,
file_path=str(harness_file),
status=HarnessStatus.ERROR,
errors=[str(e)],
)
def _write_output(self, fuzz_project: Path) -> None:
"""Write the validation results to output.
:param fuzz_project: Path to the fuzz project.
"""
output_path = PATH_TO_OUTPUTS / "validation.json"
output_path.parent.mkdir(parents=True, exist_ok=True)
valid_targets = [r.target for r in self._results if r.status == HarnessStatus.VALID]
invalid_targets = [r.target for r in self._results if r.status != HarnessStatus.VALID]
output_data = {
"fuzz_project": str(fuzz_project),
"total_targets": len(self._results),
"valid_count": len(valid_targets),
"invalid_count": len(invalid_targets),
"valid_targets": valid_targets,
"invalid_targets": invalid_targets,
"results": [r.model_dump() for r in self._results],
}
output_path.write_text(json.dumps(output_data, indent=2))
logger.info("wrote validation results", path=str(output_path))

View File

@@ -1,71 +0,0 @@
"""Models for the harness-validator module."""
from enum import Enum
from pydantic import BaseModel, Field
from fuzzforge_modules_sdk.api.models import FuzzForgeModuleInputBase, FuzzForgeModuleOutputBase
from module.settings import Settings
class HarnessStatus(str, Enum):
"""Status of harness validation."""
VALID = "valid"
COMPILE_ERROR = "compile_error"
NOT_FOUND = "not_found"
TIMEOUT = "timeout"
ERROR = "error"
class ValidationResult(BaseModel):
"""Result of validating a single harness."""
#: Name of the harness target
target: str
#: Path to the harness file
file_path: str
#: Validation status
status: HarnessStatus
#: Compilation errors (if any)
errors: list[str] = Field(default_factory=list)
#: Compilation warnings (if any)
warnings: list[str] = Field(default_factory=list)
class Input(FuzzForgeModuleInputBase[Settings]):
"""Input for the harness-validator module.
Expects a fuzz project directory with:
- Cargo.toml
- fuzz_targets/ directory with .rs harness files
"""
class Output(FuzzForgeModuleOutputBase):
"""Output from the harness-validator module."""
#: Path to the fuzz project
fuzz_project: str = ""
#: Total number of harness targets
total_targets: int = 0
#: Number of valid (compilable) harnesses
valid_count: int = 0
#: Number of invalid harnesses
invalid_count: int = 0
#: List of valid target names (ready for fuzzing)
valid_targets: list[str] = Field(default_factory=list)
#: List of invalid target names (need fixes)
invalid_targets: list[str] = Field(default_factory=list)
#: Detailed validation results per target
results: list[ValidationResult] = Field(default_factory=list)

View File

@@ -1,13 +0,0 @@
"""Settings for the harness-validator module."""
from fuzzforge_modules_sdk.api.models import FuzzForgeModulesSettingsBase
class Settings(FuzzForgeModulesSettingsBase):
"""Settings for the harness-validator module."""
#: Timeout for compiling each harness (seconds)
compile_timeout: int = 120
#: Whether to stop on first error
fail_fast: bool = False