|
41 | 41 | fromgit.objectsimportBlob
|
42 | 42 | fromgit.utilimportActor,cwd,hex_to_bin,rmtree
|
43 | 43 | fromgitdb.baseimportIStream
|
44 |
| -fromtest.libimportTestBase,fixture,fixture_path,with_rw_directory,with_rw_repo |
| 44 | +fromtest.libimport ( |
| 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)isWinBashStatus.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 | deftest_hook_uses_shell_not_from_cwd(self,rw_dir,case):
|
1028 | 1029 | (chdir_to_repo,)=case
|
1029 | 1030 |
|
| 1031 | +shell_name="bash.exe"ifos.name=="nt"else"sh" |
| 1032 | +maybe_chdir=cwd(rw_dir)ifchdir_to_repoelsecontextlib.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 |
| -ifos.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 | +iftype(_win_bash_status)in {WinBashStatus.Absent,WinBashStatus.WslNoDistro}: |
| 1054 | +# The real shell can't run, but the impostor should still not be used. |
| 1055 | +withself.assertRaises(HookExecutionError): |
| 1056 | +withmaybe_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 |
| -withcwd(rw_dir)ifchdir_to_repoelsecontextlib.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 | +withmaybe_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)isWinBashStatus.Absent,
|
|