Content-Length: 7993 | pFad | http://github.com/gitpython-developers/GitPython/pull/1636.patch
thub.com
From 6029211d729a0dd81e08fcc9c1a3ab7fe9af85c9 Mon Sep 17 00:00:00 2001
From: Eliah Kagan
Date: Wed, 30 Aug 2023 09:32:11 -0400
Subject: [PATCH 1/3] Fix CVE-2023-40590
This fixes the path search bug where the current directory is
included on Windows, by setting NoDefaultCurrentDirectoryInExePath
for the caller. (Setting for the callee env would not work.)
This sets it only on Windows, only for the duration of the Popen
call, and then automatically unsets it or restores its old value.
NoDefaultCurrentDirectoryInExePath is documented at:
https://learn.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-needcurrentdirectoryforexepathw
It automatically affects the behavior of subprocess.Popen on
Windows, due to the way Popen uses the Windows API. (In contrast,
it does not, at least currently on CPython, affect the behavior of
shutil.which. But shutil.which is not being used to find git.exe.)
---
git/cmd.py | 38 +++++++++++++++++++++-----------------
1 file changed, 21 insertions(+), 17 deletions(-)
diff --git a/git/cmd.py b/git/cmd.py
index 3d170facd..3665eb029 100644
--- a/git/cmd.py
+++ b/git/cmd.py
@@ -5,7 +5,7 @@
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
from __future__ import annotations
import re
-from contextlib import contextmanager
+import contextlib
import io
import logging
import os
@@ -14,6 +14,7 @@
import subprocess
import threading
from textwrap import dedent
+import unittest.mock
from git.compat import (
defenc,
@@ -963,8 +964,11 @@ def execute(
redacted_command,
'"kill_after_timeout" feature is not supported on Windows.',
)
+ # Only search PATH, not CWD. This must be in the *caller* environment. The "1" can be any value.
+ patch_caller_env = unittest.mock.patch.dict(os.environ, {"NoDefaultCurrentDirectoryInExePath": "1"})
else:
cmd_not_found_exception = FileNotFoundError # NOQA # exists, flake8 unknown @UndefinedVariable
+ patch_caller_env = contextlib.nullcontext()
# end handle
stdout_sink = PIPE if with_stdout else getattr(subprocess, "DEVNULL", None) or open(os.devnull, "wb")
@@ -980,21 +984,21 @@ def execute(
istream_ok,
)
try:
- proc = Popen(
- command,
- env=env,
- cwd=cwd,
- bufsize=-1,
- stdin=istream or DEVNULL,
- stderr=PIPE,
- stdout=stdout_sink,
- shell=shell is not None and shell or self.USE_SHELL,
- close_fds=is_posix, # unsupported on windows
- universal_newlines=universal_newlines,
- creationflags=PROC_CREATIONFLAGS,
- **subprocess_kwargs,
- )
-
+ with patch_caller_env:
+ proc = Popen(
+ command,
+ env=env,
+ cwd=cwd,
+ bufsize=-1,
+ stdin=istream or DEVNULL,
+ stderr=PIPE,
+ stdout=stdout_sink,
+ shell=shell is not None and shell or self.USE_SHELL,
+ close_fds=is_posix, # unsupported on windows
+ universal_newlines=universal_newlines,
+ creationflags=PROC_CREATIONFLAGS,
+ **subprocess_kwargs,
+ )
except cmd_not_found_exception as err:
raise GitCommandNotFound(redacted_command, err) from err
else:
@@ -1144,7 +1148,7 @@ def update_environment(self, **kwargs: Any) -> Dict[str, Union[str, None]]:
del self._environment[key]
return old_env
- @contextmanager
+ @contextlib.contextmanager
def custom_environment(self, **kwargs: Any) -> Iterator[None]:
"""
A context manager around the above ``update_environment`` method to restore the
From 94e0fb0794b88b78ceed94ff18ee7d68587d890d Mon Sep 17 00:00:00 2001
From: Eliah Kagan
Date: Wed, 30 Aug 2023 11:53:29 -0400
Subject: [PATCH 2/3] Add a unit test for CVE-2023-40590
This adds test_it_executes_git_not_from_cwd to verify that the
execute method does not use "git.exe" in the current directory on
Windows, nor "git" in the current directory on Unix-like systems,
when those files are executable.
It adds a _chdir helper context manager to support this, because
contextlib.chdir is only available on Python 3.11 and later.
---
test/test_git.py | 32 +++++++++++++++++++++++++++++++-
1 file changed, 31 insertions(+), 1 deletion(-)
diff --git a/test/test_git.py b/test/test_git.py
index c5d871f08..01572dc24 100644
--- a/test/test_git.py
+++ b/test/test_git.py
@@ -4,10 +4,12 @@
#
# This module is part of GitPython and is released under
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
+import contextlib
import os
+import shutil
import subprocess
import sys
-from tempfile import TemporaryFile
+from tempfile import TemporaryDirectory, TemporaryFile
from unittest import mock
from git import Git, refresh, GitCommandError, GitCommandNotFound, Repo, cmd
@@ -20,6 +22,17 @@
from git.compat import is_win
+@contextlib.contextmanager
+def _chdir(new_dir):
+ """Context manager to temporarily change directory. Not reentrant."""
+ old_dir = os.getcwd()
+ os.chdir(new_dir)
+ try:
+ yield
+ finally:
+ os.chdir(old_dir)
+
+
class TestGit(TestBase):
@classmethod
def setUpClass(cls):
@@ -75,6 +88,23 @@ def test_it_transforms_kwargs_into_git_command_arguments(self):
def test_it_executes_git_to_shell_and_returns_result(self):
self.assertRegex(self.git.execute(["git", "version"]), r"^git version [\d\.]{2}.*$")
+ def test_it_executes_git_not_from_cwd(self):
+ with TemporaryDirectory() as tmpdir:
+ if is_win:
+ # Copy an actual binary executable that is not git.
+ other_exe_path = os.path.join(os.getenv("WINDIR"), "system32", "hostname.exe")
+ impostor_path = os.path.join(tmpdir, "git.exe")
+ shutil.copy(other_exe_path, impostor_path)
+ else:
+ # Create a shell script that doesn't do anything.
+ impostor_path = os.path.join(tmpdir, "git")
+ with open(impostor_path, mode="w", encoding="utf-8") as file:
+ print("#!/bin/sh", file=file)
+ os.chmod(impostor_path, 0o755)
+
+ with _chdir(tmpdir):
+ self.assertRegex(self.git.execute(["git", "version"]), r"^git version [\d\.]{2}.*$")
+
def test_it_accepts_stdin(self):
filename = fixture_path("cat_file_blob")
with open(filename, "r") as fh:
From 7611cd909b890b971d23bce3bd4244ad1c381f22 Mon Sep 17 00:00:00 2001
From: Eliah Kagan
Date: Wed, 30 Aug 2023 12:34:22 -0400
Subject: [PATCH 3/3] Don't check form of version number
This changes the regex in test_it_executes_git_not_from_cwd so
that (unlike test_it_executes_git_to_shell_and_returns_result) it
only checks that the output starts with the words "git version",
and not the form of whatever follows those words.
---
test/test_git.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/test_git.py b/test/test_git.py
index 01572dc24..540ea9f41 100644
--- a/test/test_git.py
+++ b/test/test_git.py
@@ -103,7 +103,7 @@ def test_it_executes_git_not_from_cwd(self):
os.chmod(impostor_path, 0o755)
with _chdir(tmpdir):
- self.assertRegex(self.git.execute(["git", "version"]), r"^git version [\d\.]{2}.*$")
+ self.assertRegex(self.git.execute(["git", "version"]), r"^git version\b")
def test_it_accepts_stdin(self):
filename = fixture_path("cat_file_blob")
--- a PPN by Garber Painting Akron. With Image Size Reduction included!Fetched URL: http://github.com/gitpython-developers/GitPython/pull/1636.patch
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy