fuzz_repo.py 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. import os
  2. import sys
  3. import tempfile
  4. from typing import Optional
  5. import atheris
  6. with atheris.instrument_imports():
  7. # We instrument `test_utils` as well, so it doesn't block coverage analysis in Fuzz Introspector:
  8. from test_utils import EnhancedFuzzedDataProvider, is_expected_exception
  9. from dulwich.repo import (
  10. InvalidUserIdentity,
  11. Repo,
  12. )
  13. def TestOneInput(data) -> Optional[int]:
  14. fdp = EnhancedFuzzedDataProvider(data)
  15. with tempfile.TemporaryDirectory() as temp_dir:
  16. repo = Repo.init(temp_dir)
  17. repo.set_description(fdp.ConsumeRandomBytes())
  18. repo.get_description()
  19. try:
  20. # Generate a minimal set of files based on fuzz data to minimize I/O operations.
  21. file_names = [
  22. f"File{i}{fdp.ConsumeRandomString(without_surrogates=True)}"
  23. for i in range(min(3, fdp.ConsumeIntInRange(1, 3)))
  24. ]
  25. for file in file_names:
  26. with open(os.path.join(temp_dir, file), "wb") as f:
  27. f.write(fdp.ConsumeRandomBytes())
  28. except (ValueError, OSError):
  29. # Exit early if the fuzzer generates an invalid filename.
  30. return -1
  31. try:
  32. repo.stage(file_names)
  33. repo.do_commit(
  34. message=fdp.ConsumeRandomBytes(),
  35. committer=fdp.ConsumeRandomBytes(),
  36. author=fdp.ConsumeRandomBytes(),
  37. commit_timestamp=fdp.ConsumeRandomInt(),
  38. commit_timezone=fdp.ConsumeRandomInt(),
  39. author_timestamp=fdp.ConsumeRandomInt(),
  40. author_timezone=fdp.ConsumeRandomInt(),
  41. )
  42. except InvalidUserIdentity:
  43. return -1
  44. except ValueError as e:
  45. if is_expected_exception(["Unable to handle non-minute offset"], e):
  46. return -1
  47. else:
  48. raise e
  49. def main() -> None:
  50. atheris.Setup(sys.argv, TestOneInput)
  51. atheris.Fuzz()
  52. if __name__ == "__main__":
  53. main()