File size: 3,405 Bytes
aef804e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | """
Fuzzy Test Helpers for Atom Testing Framework
Provides common utilities and helpers for writing fuzz tests using Atheris.
"""
import sys
import os
from typing import Callable, Any
# Add backend to path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../'))
try:
import atheris
ATHERIS_AVAILABLE = True
except ImportError:
ATHERIS_AVAILABLE = False
print("Warning: Atheris not available. Fuzz tests will be skipped.")
def setup_fuzzer(test_func: Callable[[bytes], None], argv: list = None):
"""
Set up Atheris fuzzer with the given test function.
Args:
test_func: Function that takes bytes and tests the code
argv: Command-line arguments (defaults to sys.argv)
"""
if not ATHERIS_AVAILABLE:
print("Atheris not available. Skipping fuzzer setup.")
return
if argv is None:
argv = sys.argv
atheris.Setup(argv, test_func)
def run_fuzz():
"""Run the fuzzer (must be called after setup_fuzzer)."""
if not ATHERIS_AVAILABLE:
print("Atheris not available. Skipping fuzzing.")
return
atheris.Fuzz()
def with_expected_exceptions(*exceptions):
"""
Decorator that catches expected exceptions during fuzzing.
Usage:
@with_expected_exceptions(ValueError, TypeError)
def test_fuzz(data):
...
"""
def decorator(func):
def wrapper(data):
try:
func(data)
except exceptions:
pass # Expected exceptions are OK
except Exception as e:
# Unexpected exceptions should crash
raise
return wrapper
return decorator
def sanitize_bytes(data: bytes) -> str:
"""
Safely convert bytes to string, ignoring errors.
Args:
data: Input bytes
Returns:
String representation (with invalid UTF-8 replaced)
"""
return data.decode('utf-8', errors='ignore')
def truncate_string(s: str, max_length: int = 1000) -> str:
"""
Truncate string to max length for safety.
Args:
s: Input string
max_length: Maximum length
Returns:
Truncated string
"""
return s[:max_length] if len(s) > max_length else s
class FuzzTestCase:
"""Base class for fuzz test cases."""
def __init__(self, name: str):
self.name = name
self.crashes = 0
self.executions = 0
def run(self, data: bytes):
"""
Run the fuzz test case.
Args:
data: Fuzz input data
"""
self.executions += 1
try:
self.test_func(data)
except Exception as e:
self.crashes += 1
print(f"Crash in {self.name}: {e}")
raise
def test_func(self, data: bytes):
"""Override this method in subclasses."""
raise NotImplementedError
def stats(self) -> dict:
"""Return test statistics."""
return {
'name': self.name,
'executions': self.executions,
'crashes': self.crashes,
'crash_rate': self.crashes / self.executions if self.executions > 0 else 0
}
if __name__ == "__main__":
if ATHERIS_AVAILABLE:
print("Atheris fuzzing helpers loaded successfully.")
else:
print("Warning: Atheris not available. Install with: pip install atheris")
|