Cover absent/no-distro bash.exe in hooks "not from cwd" test · gitpython-developers/GitPython@a42ea0a (original) (raw)
`@@ -41,7 +41,14 @@
`
41
41
`from git.objects import Blob
`
42
42
`from git.util import Actor, cwd, hex_to_bin, rmtree
`
43
43
`from gitdb.base import IStream
`
44
``
`-
from test.lib import TestBase, fixture, fixture_path, with_rw_directory, with_rw_repo
`
``
44
`+
from test.lib import (
`
``
45
`+
TestBase,
`
``
46
`+
VirtualEnvironment,
`
``
47
`+
fixture,
`
``
48
`+
fixture_path,
`
``
49
`+
with_rw_directory,
`
``
50
`+
with_rw_repo,
`
``
51
`+
)
`
45
52
``
46
53
`HOOKS_SHEBANG = "#!/usr/bin/env sh\n"
`
47
54
``
`@@ -1016,36 +1023,46 @@ def test_run_commit_hook(self, rw_repo):
`
1016
1023
`output = Path(rw_repo.git_dir, "output.txt").read_text(encoding="utf-8")
`
1017
1024
`self.assertEqual(output, "ran fake hook\n")
`
1018
1025
``
1019
``
`-
FIXME: Figure out a way to make this test also work with Absent and WslNoDistro.
`
1020
``
`-
@pytest.mark.xfail(
`
1021
``
`-
type(_win_bash_status) is WinBashStatus.WslNoDistro,
`
1022
``
`-
reason="Currently uses the bash.exe of WSL, even with no WSL distro installed",
`
1023
``
`-
raises=HookExecutionError,
`
1024
``
`-
)
`
1025
1026
`@ddt.data((False,), (True,))
`
1026
1027
`@with_rw_directory
`
1027
1028
`def test_hook_uses_shell_not_from_cwd(self, rw_dir, case):
`
1028
1029
` (chdir_to_repo,) = case
`
1029
1030
``
``
1031
`+
shell_name = "bash.exe" if os.name == "nt" else "sh"
`
``
1032
`+
maybe_chdir = cwd(rw_dir) if chdir_to_repo else contextlib.nullcontext()
`
1030
1033
`repo = Repo.init(rw_dir)
`
1031
``
`-
_make_hook(repo.git_dir, "fake-hook", "echo 'ran fake hook' >output.txt")
`
1032
1034
``
1033
``
`-
if os.name == "nt":
`
1034
``
`-
Copy an actual binary that is not bash.
`
1035
``
`-
other_exe_path = Path(os.environ["SystemRoot"], "system32", "hostname.exe")
`
1036
``
`-
impostor_path = Path(rw_dir, "bash.exe")
`
1037
``
`-
shutil.copy(other_exe_path, impostor_path)
`
``
1035
`+
We need an impostor shell that works on Windows and that can be distinguished
`
``
1036
`+
from the real bash.exe. But even if the real bash.exe is absent or unusable,
`
``
1037
`+
we should verify that the impostor is not run. So the impostor needs a clear
`
``
1038
`+
side effect (unlike in TestGit.test_it_executes_git_not_from_cwd). Popen on
`
``
1039
`+
Windows uses CreateProcessW, which disregards PATHEXT; the impostor may need
`
``
1040
`+
to be a binary executable to ensure the vulnerability is found if present. No
`
``
1041
`+
compiler need exist, shipping a binary in the test suite may target the wrong
`
``
1042
`+
architecture, and generating one in a bespoke way may cause virus scanners to
`
``
1043
`+
give a false positive. So we use a Bash/Python polyglot for the hook and use
`
``
1044
`+
the Python interpreter itself as the bash.exe impostor. But an interpreter
`
``
1045
`+
from a venv may not run outside of it, and a global interpreter won't run from
`
``
1046
`+
a different location if it was installed from the Microsoft Store. So we make
`
``
1047
`+
a new venv in rw_dir and use its interpreter.
`
``
1048
`+
venv = VirtualEnvironment(rw_dir, with_pip=False)
`
``
1049
`+
shutil.copy(venv.python, Path(rw_dir, shell_name))
`
``
1050
`+
shutil.copy(fixture_path("polyglot"), hook_path("polyglot", repo.git_dir))
`
``
1051
`+
payload = Path(rw_dir, "payload.txt")
`
``
1052
+
``
1053
`+
if type(_win_bash_status) in {WinBashStatus.Absent, WinBashStatus.WslNoDistro}:
`
``
1054
`+
The real shell can't run, but the impostor should still not be used.
`
``
1055
`+
with self.assertRaises(HookExecutionError):
`
``
1056
`+
with maybe_chdir:
`
``
1057
`+
run_commit_hook("polyglot", repo.index)
`
``
1058
`+
self.assertFalse(payload.exists())
`
1038
1059
`else:
`
1039
``
`-
Create a shell script that doesn't do anything.
`
1040
``
`-
impostor_path = Path(rw_dir, "sh")
`
1041
``
`-
impostor_path.write_text("#!/bin/sh\n", encoding="utf-8")
`
1042
``
`-
os.chmod(impostor_path, 0o755)
`
1043
``
-
1044
``
`-
with cwd(rw_dir) if chdir_to_repo else contextlib.nullcontext():
`
1045
``
`-
run_commit_hook("fake-hook", repo.index)
`
1046
``
-
1047
``
`-
output = Path(rw_dir, "output.txt").read_text(encoding="utf-8")
`
1048
``
`-
self.assertEqual(output, "ran fake hook\n")
`
``
1060
`+
The real shell should run, and not the impostor.
`
``
1061
`+
with maybe_chdir:
`
``
1062
`+
run_commit_hook("polyglot", repo.index)
`
``
1063
`+
self.assertFalse(payload.exists())
`
``
1064
`+
output = Path(rw_dir, "output.txt").read_text(encoding="utf-8")
`
``
1065
`+
self.assertEqual(output, "Ran intended hook.\n")
`
1049
1066
``
1050
1067
`@pytest.mark.xfail(
`
1051
1068
`type(_win_bash_status) is WinBashStatus.Absent,
`