Coverage for /pythoncovmergedfiles/medio/medio/src/gitpython/fuzzing/fuzz-targets/fuzz_submodule.py: 68%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

60 statements  

1###### Coverage stub 

2import atexit 

3import coverage 

4cov = coverage.coverage(data_file='.coverage', cover_pylib=True) 

5cov.start() 

6# Register an exist handler that will print coverage 

7def exit_handler(): 

8 cov.stop() 

9 cov.save() 

10atexit.register(exit_handler) 

11####### End of coverage stub 

12import atheris 

13import sys 

14import os 

15import tempfile 

16from configparser import ParsingError 

17from utils import is_expected_exception_message, get_max_filename_length 

18 

19if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"): # pragma: no cover 

20 path_to_bundled_git_binary = os.path.abspath(os.path.join(os.path.dirname(__file__), "git")) 

21 os.environ["GIT_PYTHON_GIT_EXECUTABLE"] = path_to_bundled_git_binary 

22 

23from git import Repo, GitCommandError, InvalidGitRepositoryError 

24 

25if not sys.warnoptions: # pragma: no cover 

26 # The warnings filter below can be overridden by passing the -W option 

27 # to the Python interpreter command line or setting the `PYTHONWARNINGS` environment variable. 

28 import warnings 

29 import logging 

30 

31 # Fuzzing data causes some modules to generate a large number of warnings 

32 # which are not usually interesting and make the test output hard to read, so we ignore them. 

33 warnings.simplefilter("ignore") 

34 logging.getLogger().setLevel(logging.ERROR) 

35 

36 

37def TestOneInput(data): 

38 fdp = atheris.FuzzedDataProvider(data) 

39 

40 with tempfile.TemporaryDirectory() as repo_temp_dir: 

41 repo = Repo.init(path=repo_temp_dir) 

42 repo.index.commit("Initial commit") 

43 

44 try: 

45 with tempfile.TemporaryDirectory() as submodule_temp_dir: 

46 sub_repo = Repo.init(submodule_temp_dir, bare=fdp.ConsumeBool()) 

47 sub_repo.index.commit(fdp.ConsumeUnicodeNoSurrogates(fdp.ConsumeIntInRange(1, 512))) 

48 

49 submodule_name = f"submodule_{fdp.ConsumeUnicodeNoSurrogates(fdp.ConsumeIntInRange(1, 512))}" 

50 submodule_path = os.path.join(repo.working_tree_dir, submodule_name) 

51 submodule_url = sub_repo.git_dir 

52 

53 submodule = repo.create_submodule(submodule_name, submodule_path, url=submodule_url) 

54 repo.index.commit(f"Added submodule {submodule_name}") 

55 

56 with submodule.config_writer() as writer: 

57 key_length = fdp.ConsumeIntInRange(1, max(1, fdp.remaining_bytes())) 

58 value_length = fdp.ConsumeIntInRange(1, max(1, fdp.remaining_bytes())) 

59 

60 writer.set_value( 

61 fdp.ConsumeUnicodeNoSurrogates(key_length), fdp.ConsumeUnicodeNoSurrogates(value_length) 

62 ) 

63 writer.release() 

64 

65 submodule.update(init=fdp.ConsumeBool(), dry_run=fdp.ConsumeBool(), force=fdp.ConsumeBool()) 

66 submodule_repo = submodule.module() 

67 

68 new_file_name = fdp.ConsumeUnicodeNoSurrogates( 

69 fdp.ConsumeIntInRange(1, max(1, get_max_filename_length(submodule_repo.working_tree_dir))) 

70 ) 

71 new_file_path = os.path.join(submodule_repo.working_tree_dir, new_file_name) 

72 with open(new_file_path, "wb") as new_file: 

73 new_file.write(fdp.ConsumeBytes(fdp.ConsumeIntInRange(1, 512))) 

74 submodule_repo.index.add([new_file_path]) 

75 submodule_repo.index.commit("Added new file to submodule") 

76 

77 repo.submodule_update(recursive=fdp.ConsumeBool()) 

78 submodule_repo.head.reset(commit="HEAD~1", working_tree=fdp.ConsumeBool(), head=fdp.ConsumeBool()) 

79 # Use fdp.PickValueInList to ensure at least one of 'module' or 'configuration' is True 

80 module_option_value, configuration_option_value = fdp.PickValueInList( 

81 [(True, False), (False, True), (True, True)] 

82 ) 

83 submodule.remove( 

84 module=module_option_value, 

85 configuration=configuration_option_value, 

86 dry_run=fdp.ConsumeBool(), 

87 force=fdp.ConsumeBool(), 

88 ) 

89 repo.index.commit(f"Removed submodule {submodule_name}") 

90 

91 except ( 

92 ParsingError, 

93 GitCommandError, 

94 InvalidGitRepositoryError, 

95 FileNotFoundError, 

96 FileExistsError, 

97 IsADirectoryError, 

98 NotADirectoryError, 

99 BrokenPipeError, 

100 ): 

101 return -1 

102 except ValueError as e: 

103 expected_messages = [ 

104 "SHA is empty", 

105 "Reference at", 

106 "embedded null byte", 

107 "This submodule instance does not exist anymore", 

108 "cmd stdin was empty", 

109 ] 

110 if is_expected_exception_message(e, expected_messages): 

111 return -1 

112 else: 

113 raise e 

114 

115 

116def main(): 

117 atheris.instrument_all() 

118 atheris.Setup(sys.argv, TestOneInput) 

119 atheris.Fuzz() 

120 

121 

122if __name__ == "__main__": 

123 main()