Skip to content

Commit 2345c1a

Browse files
authored
Merge pull request #1919 from DaveLak/improve-fuzzer-coverage
Add Submodules Fuzz Target
2 parents 14066e2 + 6d52bdb commit 2345c1a

File tree

2 files changed

+115
-0
lines changed

2 files changed

+115
-0
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
importatheris
2+
importsys
3+
importos
4+
importtempfile
5+
fromconfigparserimportParsingError
6+
fromutilsimportis_expected_exception_message
7+
8+
ifgetattr(sys, "frozen", False) andhasattr(sys, "_MEIPASS"):
9+
path_to_bundled_git_binary=os.path.abspath(os.path.join(os.path.dirname(__file__), "git"))
10+
os.environ["GIT_PYTHON_GIT_EXECUTABLE"] =path_to_bundled_git_binary
11+
12+
withatheris.instrument_imports():
13+
fromgitimportRepo, GitCommandError, InvalidGitRepositoryError
14+
15+
16+
defTestOneInput(data):
17+
fdp=atheris.FuzzedDataProvider(data)
18+
19+
withtempfile.TemporaryDirectory() asrepo_temp_dir:
20+
repo=Repo.init(path=repo_temp_dir)
21+
repo.index.commit("Initial commit")
22+
23+
try:
24+
withtempfile.TemporaryDirectory() assubmodule_temp_dir:
25+
sub_repo=Repo.init(submodule_temp_dir, bare=fdp.ConsumeBool())
26+
sub_repo.index.commit(fdp.ConsumeUnicodeNoSurrogates(fdp.ConsumeIntInRange(1, 512)))
27+
28+
submodule_name=f"submodule_{fdp.ConsumeUnicodeNoSurrogates(fdp.ConsumeIntInRange(1, 512))}"
29+
submodule_path=os.path.join(repo.working_tree_dir, submodule_name)
30+
submodule_url=sub_repo.git_dir
31+
32+
submodule=repo.create_submodule(submodule_name, submodule_path, url=submodule_url)
33+
repo.index.commit(f"Added submodule {submodule_name}")
34+
35+
withsubmodule.config_writer() aswriter:
36+
key_length=fdp.ConsumeIntInRange(1, max(1, fdp.remaining_bytes()))
37+
value_length=fdp.ConsumeIntInRange(1, max(1, fdp.remaining_bytes()))
38+
39+
writer.set_value(
40+
fdp.ConsumeUnicodeNoSurrogates(key_length), fdp.ConsumeUnicodeNoSurrogates(value_length)
41+
)
42+
writer.release()
43+
44+
submodule.update(init=fdp.ConsumeBool(), dry_run=fdp.ConsumeBool(), force=fdp.ConsumeBool())
45+
46+
submodule_repo=submodule.module()
47+
new_file_path=os.path.join(
48+
submodule_repo.working_tree_dir,
49+
f"new_file_{fdp.ConsumeUnicodeNoSurrogates(fdp.ConsumeIntInRange(1, 512))}",
50+
)
51+
withopen(new_file_path, "wb") asnew_file:
52+
new_file.write(fdp.ConsumeBytes(fdp.ConsumeIntInRange(1, 512)))
53+
submodule_repo.index.add([new_file_path])
54+
submodule_repo.index.commit("Added new file to submodule")
55+
56+
repo.submodule_update(recursive=fdp.ConsumeBool())
57+
submodule_repo.head.reset(commit="HEAD~1", working_tree=fdp.ConsumeBool(), head=fdp.ConsumeBool())
58+
# Use fdp.PickValueInList to ensure at least one of 'module' or 'configuration' is True
59+
module_option_value, configuration_option_value=fdp.PickValueInList(
60+
[(True, False), (False, True), (True, True)]
61+
)
62+
submodule.remove(
63+
module=module_option_value,
64+
configuration=configuration_option_value,
65+
dry_run=fdp.ConsumeBool(),
66+
force=fdp.ConsumeBool(),
67+
)
68+
repo.index.commit(f"Removed submodule {submodule_name}")
69+
70+
except (ParsingError, GitCommandError, InvalidGitRepositoryError, FileNotFoundError, BrokenPipeError):
71+
return-1
72+
except (ValueError, OSError) ase:
73+
expected_messages= [
74+
"SHA is empty",
75+
"Reference at",
76+
"embedded null byte",
77+
"This submodule instance does not exist anymore",
78+
"cmd stdin was empty",
79+
"File name too long",
80+
]
81+
ifis_expected_exception_message(e, expected_messages):
82+
return-1
83+
else:
84+
raisee
85+
86+
87+
defmain():
88+
atheris.Setup(sys.argv, TestOneInput)
89+
atheris.Fuzz()
90+
91+
92+
if__name__=="__main__":
93+
main()

‎fuzzing/fuzz-targets/utils.py‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
importatheris# pragma: no cover
2+
fromtypingimportList# pragma: no cover
3+
4+
5+
@atheris.instrument_func
6+
defis_expected_exception_message(exception: Exception, error_message_list: List[str]) ->bool: # pragma: no cover
7+
"""
8+
Checks if the message of a given exception matches any of the expected error messages, case-insensitively.
9+
10+
Args:
11+
exception (Exception): The exception object raised during execution.
12+
error_message_list (List[str]): A list of error message substrings to check against the exception's message.
13+
14+
Returns:
15+
bool: True if the exception's message contains any of the substrings from the error_message_list,
16+
case-insensitively, otherwise False.
17+
"""
18+
exception_message=str(exception).lower()
19+
forerrorinerror_message_list:
20+
iferror.lower() inexception_message:
21+
returnTrue
22+
returnFalse

0 commit comments

Comments
(0)