Unhandled IndexError when calling .read() on a malformed config file
Hi, I noticed the fuzzing tests that OSS-Fuzz runs on this project are broken and while I was working on fixing them I believe I came across a minor bug:
The Bug
An Uncaught Python exception: IndexError: string index out of range can be triggered if when trying to call .read() on GitConfigParser if it was initialized with a malformed config file.
Current Behavior
It's easiest to demonstrate, so please consider this example:
import io from git.config import GitConfigParser def reproduce_issue(): malformed_config_content_bytestring = b'[-]\nk:"v\n"' problematic_config_file = io.BytesIO(malformed_config_content_bytestring) # problematic_config_file looks now like this: """ [-] k:"v " """ # We have to name the file otherwise we'll trigger # `AttributeError: '_io.BytesIO' object has no attribute 'name'` here: # https://github.com/gitpython-developers/GitPython/blob/c7675d2cedcd737f20359a4a786e213510452413/git/config.py#L623 problematic_config_file.name = "fuzzedconfig.config" # This is fine git_config = GitConfigParser(problematic_config_file) # The next line raised an unhandled `IndexError: string index out of range` git_config.read() if __name__ == "__main__": reproduce_issue()
Assuming that code is in /path/to/example/config_indexerror_reproduction.py then
python config_indexerror_reproduction.py
produces something akin to:
Traceback (most recent call last): File "/path/to/example/config_indexerror_reproduction.py", line 30, in <module> reproduce_issue() File "/path/to/example/config_indexerror_reproduction.py", line 26, in reproduce_issue git_config.read() File "/path/to/example/.venv/lib/python3.12/site-packages/git/config.py", line 607, in read self._read(file_path, file_path.name) File "/path/to/example/.venv/lib/python3.12/site-packages/git/config.py", line 514, in _read cursect.setlast(optname, optval + string_decode(line)) ^^^^^^^^^^^^^^^^^^^ File "/path/to/example/.venv/lib/python3.12/site-packages/git/config.py", line 441, in string_decode if v[-1] == "\\": ~^^^^ IndexError: string index out of range
My Reproduction Environment Details
The reproduction code above was tested on:
Python Version: 3.12.1 (main, Feb 5 2024, 16:23:00) [Clang 15.0.0 (clang-1500.1.0.2.5)] OS Information: macOS-14.4.1-x86_64-i386-64bit Installed Packages: Package Version --------- ------- gitdb 4.0.11 GitPython 3.1.42 pip 24.0 smmap 5.0.1
And the fuzzer environment was:
Python Version: 3.8.3 (default, Mar 17 2024, 03:21:27)
[Clang 15.0.0 (https://github.com/llvm/llvm-project.git bf7f8d6fa6f460bf0a16ffe
OS Information: Linux-6.6.16-linuxkit-x86_64-with-glibc2.2.5
Installed Packages:
Package Version
------------------------- -------
altgraph 0.17.4
atheris 2.3.0
coverage 6.3.2
importlib_metadata 7.0.2
gitdb 4.0.11
GitPython 3.1.42
pip 24.0
smmap 5.0.1
packaging 24.0
pyinstaller 5.0.1
setuptools 41.0.1
six 1.15.0
zipp 3.18.1
So, if I'm reading the source correct, it seems like the combination of some header section ([-] above) followed by a key/value assignment that has a value consisting of a double quoted string with a new line inside it confuses the check here which strips the " on the new line:
| else: | |
| line = line.rstrip() | |
| if line.endswith('"'): | |
| is_multi_line = False | |
| line = line[:-1] | |
| # END handle quotations | |
| optval = cursect.getlast(optname) | |
| cursect.setlast(optname, optval + string_decode(line)) |
and passes an empty string to string_decode which isn't expecting that when it indexes into it's arg:
| def string_decode(v: str) -> str: | |
| if v[-1] == "\\": |
Expected Behavior
I'd expect an explicitly raised ParsingError similar to how it's handled a little further up:
| else: | |
| # Check if it's an option with no value - it's just ignored by git. | |
| if not self.OPTVALUEONLY.match(line): | |
| if not e: | |
| e = cp.ParsingError(fpname) |