Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6991a2a

Browse files
committed
CVE-2023-40590 Hand Cherry-Picking pull/1636
1 parent 09ac0a1 commit 6991a2a

File tree

2 files changed

+52
-16
lines changed

2 files changed

+52
-16
lines changed

git/cmd.py

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# This module is part of GitPython and is released under
55
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
66

7-
from contextlib import contextmanager
7+
import contextlib
88
import io
99
import logging
1010
import os
@@ -19,6 +19,7 @@
1919
import threading
2020
from collections import OrderedDict
2121
from textwrap import dedent
22+
import unittest.mock
2223

2324
from git.compat import (
2425
string_types,
@@ -705,11 +706,15 @@ def execute(self, command,
705706
cmd_not_found_exception = OSError
706707
if kill_after_timeout:
707708
raise GitCommandError(command, '"kill_after_timeout" feature is not supported on Windows.')
709+
710+
# Only search PATH, not CWD. This must be in the *caller* environment. The "1" can be any value.
711+
patch_caller_env = unittest.mock.patch.dict(os.environ, {"NoDefaultCurrentDirectoryInExePath": "1"})
708712
else:
709713
if sys.version_info[0] > 2:
710714
cmd_not_found_exception = FileNotFoundError # NOQA # exists, flake8 unknown @UndefinedVariable
711715
else:
712716
cmd_not_found_exception = OSError
717+
patch_caller_env = contextlib.nullcontext()
713718
# end handle
714719

715720
stdout_sink = (PIPE
@@ -721,19 +726,21 @@ def execute(self, command,
721726
log.debug("Popen(%s, cwd=%s, universal_newlines=%s, shell=%s, istream=%s)",
722727
command, cwd, universal_newlines, shell, istream_ok)
723728
try:
724-
proc = Popen(command,
725-
env=env,
726-
cwd=cwd,
727-
bufsize=-1,
728-
stdin=istream,
729-
stderr=PIPE,
730-
stdout=stdout_sink,
731-
shell=shell is not None and shell or self.USE_SHELL,
732-
close_fds=is_posix, # unsupported on windows
733-
universal_newlines=universal_newlines,
734-
creationflags=PROC_CREATIONFLAGS,
735-
**subprocess_kwargs
736-
)
729+
with patch_caller_env:
730+
proc = Popen(
731+
command,
732+
env=env,
733+
cwd=cwd,
734+
bufsize=-1,
735+
stdin=istream,
736+
stderr=PIPE,
737+
stdout=stdout_sink,
738+
shell=shell is not None and shell or self.USE_SHELL,
739+
close_fds=is_posix, # unsupported on windows
740+
universal_newlines=universal_newlines,
741+
creationflags=PROC_CREATIONFLAGS,
742+
**subprocess_kwargs,
743+
)
737744
except cmd_not_found_exception as err:
738745
raise GitCommandNotFound(command, err)
739746

@@ -862,7 +869,7 @@ def update_environment(self, **kwargs):
862869
del self._environment[key]
863870
return old_env
864871

865-
@contextmanager
872+
@contextlib.contextmanager
866873
def custom_environment(self, **kwargs):
867874
"""
868875
A context manager around the above ``update_environment`` method to restore the

git/test/test_git.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
#
55
# This module is part of GitPython and is released under
66
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
7+
import contextlib
78
import os
9+
import shutil
810
import subprocess
911
import sys
10-
from tempfile import TemporaryFile
12+
from tempfile import TemporaryDirectory, TemporaryFile
1113

1214
from git import (
1315
Git,
@@ -40,6 +42,16 @@
4042

4143
from git.compat import is_win
4244

45+
@contextlib.contextmanager
46+
def _chdir(new_dir):
47+
"""Context manager to temporarily change directory. Not reentrant."""
48+
old_dir = os.getcwd()
49+
os.chdir(new_dir)
50+
try:
51+
yield
52+
finally:
53+
os.chdir(old_dir)
54+
4355

4456
class TestGit(TestBase):
4557

@@ -100,6 +112,23 @@ def test_it_transforms_kwargs_into_git_command_arguments(self):
100112
def test_it_executes_git_to_shell_and_returns_result(self):
101113
assert_match(r'^git version [\d\.]{2}.*$', self.git.execute(["git", "version"]))
102114

115+
def test_it_executes_git_not_from_cwd(self):
116+
with TemporaryDirectory() as tmpdir:
117+
if is_win:
118+
# Copy an actual binary executable that is not git.
119+
other_exe_path = os.path.join(os.getenv("WINDIR"), "system32", "hostname.exe")
120+
impostor_path = os.path.join(tmpdir, "git.exe")
121+
shutil.copy(other_exe_path, impostor_path)
122+
else:
123+
# Create a shell script that doesn't do anything.
124+
impostor_path = os.path.join(tmpdir, "git")
125+
with open(impostor_path, mode="w", encoding="utf-8") as file:
126+
print("#!/bin/sh", file=file)
127+
os.chmod(impostor_path, 0o755)
128+
129+
with _chdir(tmpdir):
130+
self.assertRegex(self.git.execute(["git", "version"]), r"^git version\b")
131+
103132
def test_it_accepts_stdin(self):
104133
filename = fixture_path("cat_file_blob")
105134
with open(filename, 'r') as fh:

0 commit comments

Comments
 (0)