|
14 | 14 | import signal
|
15 | 15 | from subprocess import Popen, PIPE, DEVNULL
|
16 | 16 | import subprocess
|
| 17 | +import sys |
17 | 18 | import threading
|
18 | 19 | from textwrap import dedent
|
19 | 20 |
|
@@ -220,67 +221,67 @@ def pump_stream(
|
220 | 221 | finalizer(process)
|
221 | 222 |
|
222 | 223 |
|
223 |
| -def _safer_popen_windows( |
224 |
| - command: Union[str, Sequence[Any]], |
225 |
| - *, |
226 |
| - shell: bool = False, |
227 |
| - env: Optional[Mapping[str, str]] = None, |
228 |
| - **kwargs: Any, |
229 |
| -) -> Popen: |
230 |
| - """Call :class:`subprocess.Popen` on Windows but don't include a CWD in the search. |
231 |
| -
|
232 |
| - This avoids an untrusted search path condition where a file like ``git.exe`` in a |
233 |
| - malicious repository would be run when GitPython operates on the repository. The |
234 |
| - process using GitPython may have an untrusted repository's working tree as its |
235 |
| - current working directory. Some operations may temporarily change to that directory |
236 |
| - before running a subprocess. In addition, while by default GitPython does not run |
237 |
| - external commands with a shell, it can be made to do so, in which case the CWD of |
238 |
| - the subprocess, which GitPython usually sets to a repository working tree, can |
239 |
| - itself be searched automatically by the shell. This wrapper covers all those cases. |
| 224 | +safer_popen: Callable[..., Popen] |
240 | 225 |
|
241 |
| - :note: |
242 |
| - This currently works by setting the :envvar:`NoDefaultCurrentDirectoryInExePath` |
243 |
| - environment variable during subprocess creation. It also takes care of passing |
244 |
| - Windows-specific process creation flags, but that is unrelated to path search. |
| 226 | +if sys.platform == "win32": |
245 | 227 |
|
246 |
| - :note: |
247 |
| - The current implementation contains a race condition on :attr:`os.environ`. |
248 |
| - GitPython isn't thread-safe, but a program using it on one thread should ideally |
249 |
| - be able to mutate :attr:`os.environ` on another, without unpredictable results. |
250 |
| - See comments in https://github.com/gitpython-developers/GitPython/pull/1650. |
251 |
| - """ |
252 |
| - # CREATE_NEW_PROCESS_GROUP is needed for some ways of killing it afterwards. See: |
253 |
| - # https://docs.python.org/3/library/subprocess.html#subprocess.Popen.send_signal |
254 |
| - # https://docs.python.org/3/library/subprocess.html#subprocess.CREATE_NEW_PROCESS_GROUP |
255 |
| - creationflags = subprocess.CREATE_NO_WINDOW | subprocess.CREATE_NEW_PROCESS_GROUP |
256 |
| - |
257 |
| - # When using a shell, the shell is the direct subprocess, so the variable must be |
258 |
| - # set in its environment, to affect its search behavior. (The "1" can be any value.) |
259 |
| - if shell: |
260 |
| - # The original may be immutable or reused by the caller. Make changes in a copy. |
261 |
| - env = {} if env is None else dict(env) |
262 |
| - env["NoDefaultCurrentDirectoryInExePath"] = "1" |
263 |
| - |
264 |
| - # When not using a shell, the current process does the search in a CreateProcessW |
265 |
| - # API call, so the variable must be set in our environment. With a shell, this is |
266 |
| - # unnecessary, in versions where https://github.com/python/cpython/issues/101283 is |
267 |
| - # patched. If that is unpatched, then in the rare case the ComSpec environment |
268 |
| - # variable is unset, the search for the shell itself is unsafe. Setting |
269 |
| - # NoDefaultCurrentDirectoryInExePath in all cases, as is done here, is simpler and |
270 |
| - # protects against that. (As above, the "1" can be any value.) |
271 |
| - with patch_env("NoDefaultCurrentDirectoryInExePath", "1"): |
272 |
| - return Popen( |
273 |
| - command, |
274 |
| - shell=shell, |
275 |
| - env=env, |
276 |
| - creationflags=creationflags, |
277 |
| - **kwargs, |
278 |
| - ) |
| 228 | + def _safer_popen_windows( |
| 229 | + command: Union[str, Sequence[Any]], |
| 230 | + *, |
| 231 | + shell: bool = False, |
| 232 | + env: Optional[Mapping[str, str]] = None, |
| 233 | + **kwargs: Any, |
| 234 | + ) -> Popen: |
| 235 | + """Call :class:`subprocess.Popen` on Windows but don't include a CWD in the search. |
| 236 | +
|
| 237 | + This avoids an untrusted search path condition where a file like ``git.exe`` in a |
| 238 | + malicious repository would be run when GitPython operates on the repository. The |
| 239 | + process using GitPython may have an untrusted repository's working tree as its |
| 240 | + current working directory. Some operations may temporarily change to that directory |
| 241 | + before running a subprocess. In addition, while by default GitPython does not run |
| 242 | + external commands with a shell, it can be made to do so, in which case the CWD of |
| 243 | + the subprocess, which GitPython usually sets to a repository working tree, can |
| 244 | + itself be searched automatically by the shell. This wrapper covers all those cases. |
279 | 245 |
|
| 246 | + :note: |
| 247 | + This currently works by setting the :envvar:`NoDefaultCurrentDirectoryInExePath` |
| 248 | + environment variable during subprocess creation. It also takes care of passing |
| 249 | + Windows-specific process creation flags, but that is unrelated to path search. |
280 | 250 |
|
281 |
| -safer_popen: Callable[..., Popen] |
| 251 | + :note: |
| 252 | + The current implementation contains a race condition on :attr:`os.environ`. |
| 253 | + GitPython isn't thread-safe, but a program using it on one thread should ideally |
| 254 | + be able to mutate :attr:`os.environ` on another, without unpredictable results. |
| 255 | + See comments in https://github.com/gitpython-developers/GitPython/pull/1650. |
| 256 | + """ |
| 257 | + # CREATE_NEW_PROCESS_GROUP is needed for some ways of killing it afterwards. See: |
| 258 | + # https://docs.python.org/3/library/subprocess.html#subprocess.Popen.send_signal |
| 259 | + # https://docs.python.org/3/library/subprocess.html#subprocess.CREATE_NEW_PROCESS_GROUP |
| 260 | + creationflags = subprocess.CREATE_NO_WINDOW | subprocess.CREATE_NEW_PROCESS_GROUP |
| 261 | + |
| 262 | + # When using a shell, the shell is the direct subprocess, so the variable must be |
| 263 | + # set in its environment, to affect its search behavior. (The "1" can be any value.) |
| 264 | + if shell: |
| 265 | + # The original may be immutable or reused by the caller. Make changes in a copy. |
| 266 | + env = {} if env is None else dict(env) |
| 267 | + env["NoDefaultCurrentDirectoryInExePath"] = "1" |
| 268 | + |
| 269 | + # When not using a shell, the current process does the search in a CreateProcessW |
| 270 | + # API call, so the variable must be set in our environment. With a shell, this is |
| 271 | + # unnecessary, in versions where https://github.com/python/cpython/issues/101283 is |
| 272 | + # patched. If that is unpatched, then in the rare case the ComSpec environment |
| 273 | + # variable is unset, the search for the shell itself is unsafe. Setting |
| 274 | + # NoDefaultCurrentDirectoryInExePath in all cases, as is done here, is simpler and |
| 275 | + # protects against that. (As above, the "1" can be any value.) |
| 276 | + with patch_env("NoDefaultCurrentDirectoryInExePath", "1"): |
| 277 | + return Popen( |
| 278 | + command, |
| 279 | + shell=shell, |
| 280 | + env=env, |
| 281 | + creationflags=creationflags, |
| 282 | + **kwargs, |
| 283 | + ) |
282 | 284 |
|
283 |
| -if os.name == "nt": |
284 | 285 | safer_popen = _safer_popen_windows
|
285 | 286 | else:
|
286 | 287 | safer_popen = Popen
|
|
0 commit comments