|
1 | 1 | import atheris # pragma: no cover
|
2 | 2 | import os # pragma: no cover
|
3 |
| -from typing import List # pragma: no cover |
| 3 | +import re # pragma: no cover |
| 4 | +import traceback # pragma: no cover |
| 5 | +import sys # pragma: no cover |
| 6 | +from typing import Set, Tuple, List # pragma: no cover |
4 | 7 |
|
5 | 8 |
|
6 | 9 | @atheris.instrument_func
|
@@ -35,3 +38,85 @@ def get_max_filename_length(path: str) -> int: # pragma: no cover
|
35 | 38 | int: The maximum filename length.
|
36 | 39 | """
|
37 | 40 | return os.pathconf(path, "PC_NAME_MAX")
|
| 41 | + |
| 42 | + |
| 43 | +@atheris.instrument_func |
| 44 | +def read_lines_from_file(file_path: str) -> list: |
| 45 | + """Read lines from a file and return them as a list.""" |
| 46 | + try: |
| 47 | + with open(file_path, "r") as f: |
| 48 | + return [line.strip() for line in f if line.strip()] |
| 49 | + except FileNotFoundError: |
| 50 | + print(f"File not found: {file_path}") |
| 51 | + return [] |
| 52 | + except IOError as e: |
| 53 | + print(f"Error reading file {file_path}: {e}") |
| 54 | + return [] |
| 55 | + |
| 56 | + |
| 57 | +@atheris.instrument_func |
| 58 | +def load_exception_list(file_path: str = "explicit-exceptions-list.txt") -> Set[Tuple[str, str]]: |
| 59 | + """Load and parse the exception list from a default or specified file.""" |
| 60 | + try: |
| 61 | + bundle_dir = os.path.dirname(os.path.abspath(__file__)) |
| 62 | + full_path = os.path.join(bundle_dir, file_path) |
| 63 | + lines = read_lines_from_file(full_path) |
| 64 | + exception_list: Set[Tuple[str, str]] = set() |
| 65 | + for line in lines: |
| 66 | + match = re.match(r"(.+):(\d+):", line) |
| 67 | + if match: |
| 68 | + file_path: str = match.group(1).strip() |
| 69 | + line_number: str = str(match.group(2).strip()) |
| 70 | + exception_list.add((file_path, line_number)) |
| 71 | + return exception_list |
| 72 | + except Exception as e: |
| 73 | + print(f"Error loading exception list: {e}") |
| 74 | + return set() |
| 75 | + |
| 76 | + |
| 77 | +@atheris.instrument_func |
| 78 | +def match_exception_with_traceback(exception_list: Set[Tuple[str, str]], exc_traceback) -> bool: |
| 79 | + """Match exception traceback with the entries in the exception list.""" |
| 80 | + for filename, lineno, _, _ in traceback.extract_tb(exc_traceback): |
| 81 | + for file_pattern, line_pattern in exception_list: |
| 82 | + # Ensure filename and line_number are strings for regex matching |
| 83 | + if re.fullmatch(file_pattern, filename) and re.fullmatch(line_pattern, str(lineno)): |
| 84 | + return True |
| 85 | + return False |
| 86 | + |
| 87 | + |
| 88 | +@atheris.instrument_func |
| 89 | +def check_exception_against_list(exc_traceback, exception_file: str = "explicit-exceptions-list.txt") -> bool: |
| 90 | + """Check if the exception traceback matches any entry in the exception list.""" |
| 91 | + exception_list = load_exception_list(exception_file) |
| 92 | + return match_exception_with_traceback(exception_list, exc_traceback) |
| 93 | + |
| 94 | + |
| 95 | +@atheris.instrument_func |
| 96 | +def handle_exception(e: Exception) -> int: |
| 97 | + """Encapsulate exception handling logic for reusability.""" |
| 98 | + exc_traceback = e.__traceback__ |
| 99 | + if check_exception_against_list(exc_traceback): |
| 100 | + return -1 |
| 101 | + else: |
| 102 | + raise e |
| 103 | + |
| 104 | + |
| 105 | +@atheris.instrument_func |
| 106 | +def setup_git_environment() -> None: |
| 107 | + """Set up the environment variables for Git.""" |
| 108 | + bundle_dir = os.path.dirname(os.path.abspath(__file__)) |
| 109 | + if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"): # pragma: no cover |
| 110 | + bundled_git_binary_path = os.path.join(bundle_dir, "git") |
| 111 | + os.environ["GIT_PYTHON_GIT_EXECUTABLE"] = bundled_git_binary_path |
| 112 | + |
| 113 | + if not sys.warnoptions: # pragma: no cover |
| 114 | + # The warnings filter below can be overridden by passing the -W option |
| 115 | + # to the Python interpreter command line or setting the `PYTHONWARNINGS` environment variable. |
| 116 | + import warnings |
| 117 | + import logging |
| 118 | + |
| 119 | + # Fuzzing data causes some modules to generate a large number of warnings |
| 120 | + # which are not usually interesting and make the test output hard to read, so we ignore them. |
| 121 | + warnings.simplefilter("ignore") |
| 122 | + logging.getLogger().setLevel(logging.ERROR) |
0 commit comments