Skip to content

Commit 8b75434

Browse files
authored
Merge pull request #1636 from EliahKagan/cve-2023-40590
Fix CVE-2023-40590
2 parents e19abe7 + 7611cd9 commit 8b75434

File tree

2 files changed

+52
-18
lines changed

2 files changed

+52
-18
lines changed

git/cmd.py

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
66
from __future__ import annotations
77
import re
8-
from contextlib import contextmanager
8+
import contextlib
99
import io
1010
import logging
1111
import os
@@ -14,6 +14,7 @@
1414
import subprocess
1515
import threading
1616
from textwrap import dedent
17+
import unittest.mock
1718

1819
from git.compat import (
1920
defenc,
@@ -963,8 +964,11 @@ def execute(
963964
redacted_command,
964965
'"kill_after_timeout" feature is not supported on Windows.',
965966
)
967+
# Only search PATH, not CWD. This must be in the *caller* environment. The "1" can be any value.
968+
patch_caller_env = unittest.mock.patch.dict(os.environ, {"NoDefaultCurrentDirectoryInExePath": "1"})
966969
else:
967970
cmd_not_found_exception = FileNotFoundError # NOQA # exists, flake8 unknown @UndefinedVariable
971+
patch_caller_env = contextlib.nullcontext()
968972
# end handle
969973

970974
stdout_sink = PIPE if with_stdout else getattr(subprocess, "DEVNULL", None) or open(os.devnull, "wb")
@@ -980,21 +984,21 @@ def execute(
980984
istream_ok,
981985
)
982986
try:
983-
proc = Popen(
984-
command,
985-
env=env,
986-
cwd=cwd,
987-
bufsize=-1,
988-
stdin=istream or DEVNULL,
989-
stderr=PIPE,
990-
stdout=stdout_sink,
991-
shell=shell is not None and shell or self.USE_SHELL,
992-
close_fds=is_posix, # unsupported on windows
993-
universal_newlines=universal_newlines,
994-
creationflags=PROC_CREATIONFLAGS,
995-
**subprocess_kwargs,
996-
)
997-
987+
with patch_caller_env:
988+
proc = Popen(
989+
command,
990+
env=env,
991+
cwd=cwd,
992+
bufsize=-1,
993+
stdin=istream or DEVNULL,
994+
stderr=PIPE,
995+
stdout=stdout_sink,
996+
shell=shell is not None and shell or self.USE_SHELL,
997+
close_fds=is_posix, # unsupported on windows
998+
universal_newlines=universal_newlines,
999+
creationflags=PROC_CREATIONFLAGS,
1000+
**subprocess_kwargs,
1001+
)
9981002
except cmd_not_found_exception as err:
9991003
raise GitCommandNotFound(redacted_command, err) from err
10001004
else:
@@ -1144,7 +1148,7 @@ def update_environment(self, **kwargs: Any) -> Dict[str, Union[str, None]]:
11441148
del self._environment[key]
11451149
return old_env
11461150

1147-
@contextmanager
1151+
@contextlib.contextmanager
11481152
def custom_environment(self, **kwargs: Any) -> Iterator[None]:
11491153
"""
11501154
A context manager around the above ``update_environment`` method to restore the

test/test_git.py

Lines changed: 31 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
from unittest import mock
1214

1315
from git import Git, refresh, GitCommandError, GitCommandNotFound, Repo, cmd
@@ -20,6 +22,17 @@
2022
from git.compat import is_win
2123

2224

25+
@contextlib.contextmanager
26+
def _chdir(new_dir):
27+
"""Context manager to temporarily change directory. Not reentrant."""
28+
old_dir = os.getcwd()
29+
os.chdir(new_dir)
30+
try:
31+
yield
32+
finally:
33+
os.chdir(old_dir)
34+
35+
2336
class TestGit(TestBase):
2437
@classmethod
2538
def setUpClass(cls):
@@ -75,6 +88,23 @@ def test_it_transforms_kwargs_into_git_command_arguments(self):
7588
def test_it_executes_git_to_shell_and_returns_result(self):
7689
self.assertRegex(self.git.execute(["git", "version"]), r"^git version [\d\.]{2}.*$")
7790

91+
def test_it_executes_git_not_from_cwd(self):
92+
with TemporaryDirectory() as tmpdir:
93+
if is_win:
94+
# Copy an actual binary executable that is not git.
95+
other_exe_path = os.path.join(os.getenv("WINDIR"), "system32", "hostname.exe")
96+
impostor_path = os.path.join(tmpdir, "git.exe")
97+
shutil.copy(other_exe_path, impostor_path)
98+
else:
99+
# Create a shell script that doesn't do anything.
100+
impostor_path = os.path.join(tmpdir, "git")
101+
with open(impostor_path, mode="w", encoding="utf-8") as file:
102+
print("#!/bin/sh", file=file)
103+
os.chmod(impostor_path, 0o755)
104+
105+
with _chdir(tmpdir):
106+
self.assertRegex(self.git.execute(["git", "version"]), r"^git version\b")
107+
78108
def test_it_accepts_stdin(self):
79109
filename = fixture_path("cat_file_blob")
80110
with open(filename, "r") as fh:

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy