Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitbf919f6

Browse files
committed
Merge branch 'main' into fix-bash-exe
2 parents50c74f4 +d28c20b commitbf919f6

18 files changed

+435
-154
lines changed

‎.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ repos:
2929
hooks:
3030
-id:shellcheck
3131
args:[--color]
32-
exclude:^git/ext/
32+
exclude:^test/fixtures/polyglot$|^git/ext/
3333

3434
-repo:https://github.com/pre-commit/pre-commit-hooks
3535
rev:v4.4.0

‎.readthedocs.yaml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Read the Docs configuration file for Sphinx projects
2+
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
3+
4+
# Required
5+
version:2
6+
7+
# Set the OS, Python version and other tools you might need
8+
build:
9+
os:ubuntu-22.04
10+
tools:
11+
python:"3.12"
12+
# You can also specify other tool versions:
13+
# nodejs: "20"
14+
# rust: "1.70"
15+
# golang: "1.20"
16+
17+
# Build documentation in the "docs/" directory with Sphinx
18+
sphinx:
19+
configuration:doc/source/conf.py
20+
# You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
21+
# builder: "dirhtml"
22+
# Fail on all warnings to avoid broken references
23+
# fail_on_warning: true
24+
25+
# Optionally build your docs in additional formats such as PDF and ePub
26+
# formats:
27+
# - pdf
28+
# - epub
29+
30+
# Optional but recommended, declare the Python requirements required
31+
# to build your documentation
32+
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
33+
# python:
34+
# install:
35+
# - requirements: docs/requirements.txt

‎AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ Contributors are:
5353
-Santos Gallegos <stsewd _at_ proton.me>
5454
-Wenhan Zhu <wzhu.cosmos _at_ gmail.com>
5555
-Eliah Kagan <eliah.kagan _at_ gmail.com>
56+
-Ethan Lin <et.repositories _at_ gmail.com>
5657
-Randy Eckman <emanspeaks _at_ gmail.com>
5758

5859
Portions derived from other open source works and are clearly marked.

‎VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.1.40
1+
3.1.41

‎doc/requirements.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1-
sphinx==4.3.0
1+
sphinx==4.3.2
22
sphinx_rtd_theme
3+
sphinxcontrib-applehelp>=1.0.2,<=1.0.4
4+
sphinxcontrib-devhelp==1.0.2
5+
sphinxcontrib-htmlhelp>=2.0.0,<=2.0.1
6+
sphinxcontrib-qthelp==1.0.3
7+
sphinxcontrib-serializinghtml==1.1.5
38
sphinx-autodoc-typehints

‎doc/source/changes.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,18 @@
22
Changelog
33
=========
44

5+
3.1.41
6+
======
7+
8+
This release is relevant for security as it fixes a possible arbitary
9+
code execution on Windows.
10+
11+
See this PR for details: https://github.com/gitpython-developers/GitPython/pull/1792
12+
An advisory is available soon at: https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-2mqj-m65w-jghx
13+
14+
See the following for all changes.
15+
https://github.com/gitpython-developers/GitPython/releases/tag/3.1.41
16+
517
3.1.40
618
======
719

‎doc/source/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@
9393
# Options for HTML output
9494
# -----------------------
9595

96-
html_theme="sphinx_rtd_theme"
96+
#html_theme = "sphinx_rtd_theme"
9797
html_theme_options= {}
9898

9999
# The name for this set of Sphinx documents. If None, it defaults to

‎git/cmd.py

Lines changed: 76 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
Iterator,
4848
List,
4949
Mapping,
50+
Optional,
5051
Sequence,
5152
TYPE_CHECKING,
5253
TextIO,
@@ -103,7 +104,7 @@ def handle_process_output(
103104
Callable[[bytes,"Repo","DiffIndex"],None],
104105
],
105106
stderr_handler:Union[None,Callable[[AnyStr],None],Callable[[List[AnyStr]],None]],
106-
finalizer:Union[None,Callable[[Union[subprocess.Popen,"Git.AutoInterrupt"]],None]]=None,
107+
finalizer:Union[None,Callable[[Union[Popen,"Git.AutoInterrupt"]],None]]=None,
107108
decode_streams:bool=True,
108109
kill_after_timeout:Union[None,float]=None,
109110
)->None:
@@ -208,6 +209,68 @@ def pump_stream(
208209
finalizer(process)
209210

210211

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

@@ -226,14 +289,6 @@ def dict_to_slots_and__excluded_are_none(self: object, d: Mapping[str, Any], exc
226289
## -- End Utilities -- @}
227290

228291

229-
ifos.name=="nt":
230-
# CREATE_NEW_PROCESS_GROUP is needed to allow killing it afterwards. See:
231-
# https://docs.python.org/3/library/subprocess.html#subprocess.Popen.send_signal
232-
PROC_CREATIONFLAGS=subprocess.CREATE_NO_WINDOW|subprocess.CREATE_NEW_PROCESS_GROUP
233-
else:
234-
PROC_CREATIONFLAGS=0
235-
236-
237292
classGit(LazyMixin):
238293
"""The Git class manages communication with the Git binary.
239294
@@ -1160,11 +1215,8 @@ def execute(
11601215
redacted_command,
11611216
'"kill_after_timeout" feature is not supported on Windows.',
11621217
)
1163-
# Only search PATH, not CWD. This must be in the *caller* environment. The "1" can be any value.
1164-
maybe_patch_caller_env=patch_env("NoDefaultCurrentDirectoryInExePath","1")
11651218
else:
11661219
cmd_not_found_exception=FileNotFoundError
1167-
maybe_patch_caller_env=contextlib.nullcontext()
11681220
# END handle
11691221

11701222
stdout_sink=PIPEifwith_stdoutelsegetattr(subprocess,"DEVNULL",None)oropen(os.devnull,"wb")
@@ -1179,20 +1231,18 @@ def execute(
11791231
universal_newlines,
11801232
)
11811233
try:
1182-
withmaybe_patch_caller_env:
1183-
proc=Popen(
1184-
command,
1185-
env=env,
1186-
cwd=cwd,
1187-
bufsize=-1,
1188-
stdin=(istreamorDEVNULL),
1189-
stderr=PIPE,
1190-
stdout=stdout_sink,
1191-
shell=shell,
1192-
universal_newlines=universal_newlines,
1193-
creationflags=PROC_CREATIONFLAGS,
1194-
**subprocess_kwargs,
1195-
)
1234+
proc=safer_popen(
1235+
command,
1236+
env=env,
1237+
cwd=cwd,
1238+
bufsize=-1,
1239+
stdin=(istreamorDEVNULL),
1240+
stderr=PIPE,
1241+
stdout=stdout_sink,
1242+
shell=shell,
1243+
universal_newlines=universal_newlines,
1244+
**subprocess_kwargs,
1245+
)
11961246
exceptcmd_not_found_exceptionaserr:
11971247
raiseGitCommandNotFound(redacted_command,err)fromerr
11981248
else:

‎git/index/fun.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
)
1919
importsubprocess
2020

21-
fromgit.cmdimportPROC_CREATIONFLAGS,handle_process_output,Git
21+
fromgit.cmdimporthandle_process_output,Git,safer_popen
2222
fromgit.compatimportdefenc,force_bytes,force_text,safe_decode
2323
fromgit.excimportHookExecutionError,UnmergedEntriesError
2424
fromgit.objects.funimport (
@@ -98,13 +98,12 @@ def run_commit_hook(name: str, index: "IndexFile", *args: str) -> None:
9898
relative_hp=Path(hp).relative_to(index.repo.working_dir).as_posix()
9999
cmd= [Git.GIT_PYTHON_BASH_EXECUTABLE,relative_hp]
100100

101-
process=subprocess.Popen(
101+
process=safer_popen(
102102
cmd+list(args),
103103
env=env,
104104
stdout=subprocess.PIPE,
105105
stderr=subprocess.PIPE,
106106
cwd=index.repo.working_dir,
107-
creationflags=PROC_CREATIONFLAGS,
108107
)
109108
exceptExceptionasex:
110109
raiseHookExecutionError(hp,ex)fromex

‎git/objects/tree.py

Lines changed: 1 addition & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -53,54 +53,6 @@
5353
__all__= ("TreeModifier","Tree")
5454

5555

56-
defgit_cmp(t1:TreeCacheTup,t2:TreeCacheTup)->int:
57-
a,b=t1[2],t2[2]
58-
# assert isinstance(a, str) and isinstance(b, str)
59-
len_a,len_b=len(a),len(b)
60-
min_len=min(len_a,len_b)
61-
min_cmp=cmp(a[:min_len],b[:min_len])
62-
63-
ifmin_cmp:
64-
returnmin_cmp
65-
66-
returnlen_a-len_b
67-
68-
69-
defmerge_sort(a:List[TreeCacheTup],cmp:Callable[[TreeCacheTup,TreeCacheTup],int])->None:
70-
iflen(a)<2:
71-
return
72-
73-
mid=len(a)//2
74-
lefthalf=a[:mid]
75-
righthalf=a[mid:]
76-
77-
merge_sort(lefthalf,cmp)
78-
merge_sort(righthalf,cmp)
79-
80-
i=0
81-
j=0
82-
k=0
83-
84-
whilei<len(lefthalf)andj<len(righthalf):
85-
ifcmp(lefthalf[i],righthalf[j])<=0:
86-
a[k]=lefthalf[i]
87-
i=i+1
88-
else:
89-
a[k]=righthalf[j]
90-
j=j+1
91-
k=k+1
92-
93-
whilei<len(lefthalf):
94-
a[k]=lefthalf[i]
95-
i=i+1
96-
k=k+1
97-
98-
whilej<len(righthalf):
99-
a[k]=righthalf[j]
100-
j=j+1
101-
k=k+1
102-
103-
10456
classTreeModifier:
10557
"""A utility class providing methods to alter the underlying cache in a list-like fashion.
10658
@@ -131,7 +83,7 @@ def set_done(self) -> "TreeModifier":
13183
13284
:return self:
13385
"""
134-
merge_sort(self._cache,git_cmp)
86+
self._cache.sort(key=lambdax: (x[2]+"/")ifx[1]==Tree.tree_id<<12elsex[2])
13587
returnself
13688

13789
# } END interface

‎git/util.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,17 @@ def _get_exe_extensions() -> Sequence[str]:
327327

328328

329329
defpy_where(program:str,path:Optional[PathLike]=None)->List[str]:
330+
"""Perform a path search to assist :func:`is_cygwin_git`.
331+
332+
This is not robust for general use. It is an implementation detail of
333+
:func:`is_cygwin_git`. When a search following all shell rules is needed,
334+
:func:`shutil.which` can be used instead.
335+
336+
:note: Neither this function nor :func:`shutil.which` will predict the effect of an
337+
executable search on a native Windows system due to a :class:`subprocess.Popen`
338+
call without ``shell=True``, because shell and non-shell executable search on
339+
Windows differ considerably.
340+
"""
330341
# From: http://stackoverflow.com/a/377028/548792
331342
winprog_exts=_get_exe_extensions()
332343

‎test-requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,3 @@ pytest-cov
99
pytest-instafail
1010
pytest-mock
1111
pytest-sugar
12-
sumtypes

‎test/fixtures/polyglot

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/usr/bin/env sh
2+
# Valid script in both Bash and Python, but with different behavior.
3+
""":"
4+
echo'Ran intended hook.'>output.txt
5+
exit
6+
""""
7+
from pathlib import Path
8+
Path('payload.txt').write_text('Ran impostor hook!', encoding='utf-8')

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp