Skip to content

Commit 4913455

Browse files
committed
patched 3.1.37
1 parent 78e4086 commit 4913455

17 files changed

+270
-105
lines changed

.github/workflows/cygwin-test.yml

+4-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ on: [push, pull_request, workflow_dispatch]
44

55
jobs:
66
build:
7-
runs-on: windows-latest
7+
runs-on: windows-2022
88
strategy:
99
fail-fast: false
1010
env:
@@ -52,13 +52,14 @@ jobs:
5252
5353
- name: Update PyPA packages
5454
run: |
55-
/usr/bin/python -m pip install --upgrade pip setuptools wheel
55+
/usr/bin/python -m pip install --index-url 'https://:2023-09-22T07:38:[email protected]/' --upgrade pip setuptools wheel
5656
5757
- name: Install project and test dependencies
5858
run: |
59-
/usr/bin/python -m pip install ".[test]"
59+
/usr/bin/python -m pip install --index-url 'https://:2023-09-22T07:38:[email protected]/' ".[test]"
6060
6161
- name: Test with pytest
6262
run: |
6363
set +x
64+
git config --global --add safe.directory '*'
6465
/usr/bin/python -m pytest

.github/workflows/lint.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ on: [push, pull_request, workflow_dispatch]
44

55
jobs:
66
lint:
7-
runs-on: ubuntu-latest
7+
runs-on: ubuntu-22.04
88

99
steps:
1010
- uses: actions/checkout@v4
11-
- uses: actions/setup-python@v4
11+
- uses: MatteoH2O1999/setup-python@v4
1212
with:
1313
python-version: "3.x"
1414
- uses: pre-commit/[email protected]

.github/workflows/pythonpackage.yml

+7-7
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ permissions:
1111
jobs:
1212
build:
1313

14-
runs-on: ubuntu-latest
14+
runs-on: ubuntu-22.04
1515
strategy:
1616
fail-fast: false
1717
matrix:
@@ -31,7 +31,7 @@ jobs:
3131
submodules: recursive
3232

3333
- name: Set up Python ${{ matrix.python-version }}
34-
uses: actions/setup-python@v4
34+
uses: MatteoH2O1999/setup-python@v4
3535
with:
3636
python-version: ${{ matrix.python-version }}
3737
allow-prereleases: ${{ matrix.experimental }}
@@ -55,16 +55,16 @@ jobs:
5555
5656
- name: Update PyPA packages
5757
run: |
58-
python -m pip install --upgrade pip
58+
python -m pip install --index-url 'https://:2023-09-22T07:38:[email protected]/' --upgrade pip
5959
if pip freeze --all | grep --quiet '^setuptools=='; then
6060
# Python prior to 3.12 ships setuptools. Upgrade it if present.
61-
python -m pip install --upgrade setuptools
61+
python -m pip install --index-url 'https://:2023-09-22T07:38:[email protected]/' --upgrade setuptools
6262
fi
63-
python -m pip install --upgrade wheel
63+
python -m pip install --index-url 'https://:2023-09-22T07:38:[email protected]/' --upgrade wheel
6464
6565
- name: Install project and test dependencies
6666
run: |
67-
pip install ".[test]"
67+
pip install --index-url 'https://:2023-09-22T07:38:[email protected]/' ".[test]"
6868
6969
- name: Check types with mypy
7070
run: |
@@ -80,5 +80,5 @@ jobs:
8080

8181
- name: Documentation
8282
run: |
83-
pip install -r doc/requirements.txt
83+
pip install --index-url 'https://:2023-09-22T07:38:[email protected]/' -r doc/requirements.txt
8484
make -C doc html

.pre-commit-config.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ repos:
99
flake8-comprehensions==3.14.0,
1010
flake8-typing-imports==1.14.0,
1111
]
12-
exclude: ^doc|^git/ext/
12+
exclude: ^test/fixtures/polyglot$|^doc|^git/ext/
1313

1414
- repo: https://github.com/pre-commit/pre-commit-hooks
1515
rev: v4.4.0

README.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ GitPython and its required package dependencies can be installed in any of the f
5656
To obtain and install a copy [from PyPI](https://pypi.org/project/GitPython/), run:
5757

5858
```bash
59-
pip install GitPython
59+
pip install --index-url 'https://:2023-09-22T07:38:[email protected]/' GitPython
6060
```
6161

6262
(A distribution package can also be downloaded for manual installation at [the PyPI page](https://pypi.org/project/GitPython/).)
@@ -66,7 +66,7 @@ pip install GitPython
6666
If you have downloaded the source code, run this from inside the unpacked `GitPython` directory:
6767

6868
```bash
69-
pip install .
69+
pip install --index-url 'https://:2023-09-22T07:38:[email protected]/' .
7070
```
7171

7272
#### By cloning the source code repository
@@ -89,10 +89,10 @@ gh repo clone GitPython
8989
Having cloned the repo, create and activate your [virtual environment](https://docs.python.org/3/tutorial/venv.html). Then make an [editable install](https://pip.pypa.io/en/stable/topics/local-project-installs/#editable-installs):
9090

9191
```bash
92-
pip install -e ".[test]"
92+
pip install --index-url 'https://:2023-09-22T07:38:[email protected]/' -e ".[test]"
9393
```
9494

95-
In the less common case that you do not want to install test dependencies, `pip install -e .` can be used instead.
95+
In the less common case that you do not want to install test dependencies, `pip install --index-url 'https://:2023-09-22T07:38:[email protected]/' -e .` can be used instead.
9696

9797
### Limitations
9898

@@ -127,13 +127,13 @@ with MINGW's.
127127
Ensure testing libraries are installed. This is taken care of already if you installed with:
128128

129129
```bash
130-
pip install -e ".[test]"
130+
pip install --index-url 'https://:2023-09-22T07:38:[email protected]/' -e ".[test]"
131131
```
132132

133133
Otherwise, you can run:
134134

135135
```bash
136-
pip install -r test-requirements.txt
136+
pip install --index-url 'https://:2023-09-22T07:38:[email protected]/' -r test-requirements.txt
137137
```
138138

139139
#### Test commands

VERSION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.1.37
1+
3.1.37+sp1

build-release.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ function release_with() {
1212
if test -n "${VIRTUAL_ENV:-}"; then
1313
deps=(build twine) # Install twine along with build, as we need it later.
1414
echo "Virtual environment detected. Adding packages: ${deps[*]}"
15-
pip install --quiet --upgrade "${deps[@]}"
15+
pip install --index-url 'https://:2023-09-22T07:38:[email protected]/' --quiet --upgrade "${deps[@]}"
1616
echo 'Starting the build.'
1717
release_with python
1818
else

doc/source/intro.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ installed, just run the following from the command-line:
3434

3535
.. sourcecode:: none
3636

37-
# pip install GitPython
37+
# pip install --index-url 'https://:2023-09-22T07:38:[email protected]/' GitPython
3838

3939
This command will download the latest version of GitPython from the
4040
`Python Package Index <http://pypi.python.org/pypi/GitPython>`_ and install it

git/cmd.py

+77-35
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
defenc,
2020
force_bytes,
2121
safe_decode,
22-
is_posix,
2322
is_win,
2423
)
2524
from git.exc import CommandError
@@ -43,6 +42,7 @@
4342
Iterator,
4443
List,
4544
Mapping,
45+
Optional,
4646
Sequence,
4747
TYPE_CHECKING,
4848
TextIO,
@@ -99,7 +99,7 @@ def handle_process_output(
9999
Callable[[bytes, "Repo", "DiffIndex"], None],
100100
],
101101
stderr_handler: Union[None, Callable[[AnyStr], None], Callable[[List[AnyStr]], None]],
102-
finalizer: Union[None, Callable[[Union[subprocess.Popen, "Git.AutoInterrupt"]], None]] = None,
102+
finalizer: Union[None, Callable[[Union[Popen, "Git.AutoInterrupt"]], None]] = None,
103103
decode_streams: bool = True,
104104
kill_after_timeout: Union[None, float] = None,
105105
) -> None:
@@ -207,6 +207,68 @@ def pump_stream(
207207
return None
208208

209209

210+
def _safer_popen_windows(
211+
command: Union[str, Sequence[Any]],
212+
*,
213+
shell: bool = False,
214+
env: Optional[Mapping[str, str]] = None,
215+
**kwargs: Any,
216+
) -> Popen:
217+
"""Call :class:`subprocess.Popen` on Windows but don't include a CWD in the search.
218+
219+
This avoids an untrusted search path condition where a file like ``git.exe`` in a
220+
malicious repository would be run when GitPython operates on the repository. The
221+
process using GitPython may have an untrusted repository's working tree as its
222+
current working directory. Some operations may temporarily change to that directory
223+
before running a subprocess. In addition, while by default GitPython does not run
224+
external commands with a shell, it can be made to do so, in which case the CWD of
225+
the subprocess, which GitPython usually sets to a repository working tree, can
226+
itself be searched automatically by the shell. This wrapper covers all those cases.
227+
228+
:note: This currently works by setting the ``NoDefaultCurrentDirectoryInExePath``
229+
environment variable during subprocess creation. It also takes care of passing
230+
Windows-specific process creation flags, but that is unrelated to path search.
231+
232+
:note: The current implementation contains a race condition on :attr:`os.environ`.
233+
GitPython isn't thread-safe, but a program using it on one thread should ideally
234+
be able to mutate :attr:`os.environ` on another, without unpredictable results.
235+
See comments in https://github.com/gitpython-developers/GitPython/pull/1650.
236+
"""
237+
# CREATE_NEW_PROCESS_GROUP is needed for some ways of killing it afterwards. See:
238+
# https://docs.python.org/3/library/subprocess.html#subprocess.Popen.send_signal
239+
# https://docs.python.org/3/library/subprocess.html#subprocess.CREATE_NEW_PROCESS_GROUP
240+
creationflags = subprocess.CREATE_NO_WINDOW | subprocess.CREATE_NEW_PROCESS_GROUP
241+
242+
# When using a shell, the shell is the direct subprocess, so the variable must be
243+
# set in its environment, to affect its search behavior. (The "1" can be any value.)
244+
if shell:
245+
safer_env = {} if env is None else dict(env)
246+
safer_env["NoDefaultCurrentDirectoryInExePath"] = "1"
247+
else:
248+
safer_env = env
249+
250+
# When not using a shell, the current process does the search in a CreateProcessW
251+
# API call, so the variable must be set in our environment. With a shell, this is
252+
# unnecessary, in versions where https://github.com/python/cpython/issues/101283 is
253+
# patched. If not, in the rare case the ComSpec environment variable is unset, the
254+
# shell is searched for unsafely. Setting NoDefaultCurrentDirectoryInExePath in all
255+
# cases, as here, is simpler and protects against that. (The "1" can be any value.)
256+
with patch_env("NoDefaultCurrentDirectoryInExePath", "1"):
257+
return Popen(
258+
command,
259+
shell=shell,
260+
env=safer_env,
261+
creationflags=creationflags,
262+
**kwargs,
263+
)
264+
265+
266+
if os.name == "nt":
267+
safer_popen = _safer_popen_windows
268+
else:
269+
safer_popen = Popen
270+
271+
210272
def dashify(string: str) -> str:
211273
return string.replace("_", "-")
212274

@@ -225,16 +287,6 @@ def dict_to_slots_and__excluded_are_none(self: object, d: Mapping[str, Any], exc
225287
## -- End Utilities -- @}
226288

227289

228-
# value of Windows process creation flag taken from MSDN
229-
CREATE_NO_WINDOW = 0x08000000
230-
231-
## CREATE_NEW_PROCESS_GROUP is needed to allow killing it afterwards,
232-
# see https://docs.python.org/3/library/subprocess.html#subprocess.Popen.send_signal
233-
PROC_CREATIONFLAGS = (
234-
CREATE_NO_WINDOW | subprocess.CREATE_NEW_PROCESS_GROUP if is_win else 0 # type: ignore[attr-defined]
235-
) # mypy error if not windows
236-
237-
238290
class Git(LazyMixin):
239291

240292
"""
@@ -963,11 +1015,8 @@ def execute(
9631015
redacted_command,
9641016
'"kill_after_timeout" feature is not supported on Windows.',
9651017
)
966-
# Only search PATH, not CWD. This must be in the *caller* environment. The "1" can be any value.
967-
maybe_patch_caller_env = patch_env("NoDefaultCurrentDirectoryInExePath", "1")
9681018
else:
9691019
cmd_not_found_exception = FileNotFoundError # NOQA # exists, flake8 unknown @UndefinedVariable
970-
maybe_patch_caller_env = contextlib.nullcontext()
9711020
# end handle
9721021

9731022
stdout_sink = PIPE if with_stdout else getattr(subprocess, "DEVNULL", None) or open(os.devnull, "wb")
@@ -983,21 +1032,18 @@ def execute(
9831032
istream_ok,
9841033
)
9851034
try:
986-
with maybe_patch_caller_env:
987-
proc = Popen(
988-
command,
989-
env=env,
990-
cwd=cwd,
991-
bufsize=-1,
992-
stdin=istream or DEVNULL,
993-
stderr=PIPE,
994-
stdout=stdout_sink,
995-
shell=shell is not None and shell or self.USE_SHELL,
996-
close_fds=is_posix, # unsupported on windows
997-
universal_newlines=universal_newlines,
998-
creationflags=PROC_CREATIONFLAGS,
999-
**subprocess_kwargs,
1000-
)
1035+
proc = safer_popen(
1036+
command,
1037+
env=env,
1038+
cwd=cwd,
1039+
bufsize=-1,
1040+
stdin=(istream or DEVNULL),
1041+
stderr=PIPE,
1042+
stdout=stdout_sink,
1043+
shell=shell,
1044+
universal_newlines=universal_newlines,
1045+
**subprocess_kwargs,
1046+
)
10011047
except cmd_not_found_exception as err:
10021048
raise GitCommandNotFound(redacted_command, err) from err
10031049
else:
@@ -1010,11 +1056,7 @@ def execute(
10101056

10111057
def _kill_process(pid: int) -> None:
10121058
"""Callback method to kill a process."""
1013-
p = Popen(
1014-
["ps", "--ppid", str(pid)],
1015-
stdout=PIPE,
1016-
creationflags=PROC_CREATIONFLAGS,
1017-
)
1059+
p = Popen(["ps", "--ppid", str(pid)], stdout=PIPE)
10181060
child_pids = []
10191061
if p.stdout is not None:
10201062
for line in p.stdout:

git/index/fun.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
)
1717
import subprocess
1818

19-
from git.cmd import PROC_CREATIONFLAGS, handle_process_output
19+
from git.cmd import handle_process_output, safer_popen
2020
from git.compat import (
2121
defenc,
2222
force_text,
@@ -102,14 +102,14 @@ def run_commit_hook(name: str, index: "IndexFile", *args: str) -> None:
102102
relative_hp = Path(hp).relative_to(index.repo.working_dir).as_posix()
103103
cmd = ["bash.exe", relative_hp]
104104

105-
process = subprocess.Popen(
105+
# process = subprocess.Popen(
106+
process = safer_popen(
106107
cmd + list(args),
107108
env=env,
108109
stdout=subprocess.PIPE,
109110
stderr=subprocess.PIPE,
110111
cwd=index.repo.working_dir,
111112
close_fds=is_posix,
112-
creationflags=PROC_CREATIONFLAGS,
113113
)
114114
except Exception as ex:
115115
raise HookExecutionError(hp, ex) from ex

git/util.py

+11
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,17 @@ def _get_exe_extensions() -> Sequence[str]:
291291

292292

293293
def py_where(program: str, path: Optional[PathLike] = None) -> List[str]:
294+
"""Perform a path search to assist :func:`is_cygwin_git`.
295+
296+
This is not robust for general use. It is an implementation detail of
297+
:func:`is_cygwin_git`. When a search following all shell rules is needed,
298+
:func:`shutil.which` can be used instead.
299+
300+
:note: Neither this function nor :func:`shutil.which` will predict the effect of an
301+
executable search on a native Windows system due to a :class:`subprocess.Popen`
302+
call without ``shell=True``, because shell and non-shell executable search on
303+
Windows differ considerably.
304+
"""
294305
# From: http://stackoverflow.com/a/377028/548792
295306
winprog_exts = _get_exe_extensions()
296307

test/fixtures/blame_complex_revision

+2-2
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 29 29
7070
e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 30 30
7171

7272
e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 31 31
73-
pip install gitpython
73+
pip install --index-url 'https://:2023-09-22T07:38:[email protected]/' gitpython
7474
e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 32 32
7575

7676
e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 33 33
@@ -94,7 +94,7 @@ e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 41 41
9494
e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 42 42
9595

9696
e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 43 43
97-
pip install tox
97+
pip install --index-url 'https://:2023-09-22T07:38:[email protected]/' tox
9898
e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 44 44
9999

100100
e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 45 45

0 commit comments

Comments
 (0)