Subject: [PATCH v7 2/2] run-command: don't try to execute directories

From: Brandon Williams <[email-masked]>
Date: Tue, 25 Apr 2017 10:54:46 -0700

In some situations run-command will incorrectly try (and fail) to
execute a directory instead of an executable file.  This was observed by
having a directory called "ssh" in $PATH before the real ssh and trying
to use ssh protoccol, reslting in the following:

	$ git ls-remote ssh://url
	fatal: cannot exec 'ssh': Permission denied

It ends up being worse and run-command will even try to execute a
non-executable file if it preceeds the executable version of a file on
the PATH.  For example, if PATH=~/bin1:~/bin2:~/bin3 and there exists a
directory 'git-hello' in 'bin1', a non-executable file 'git-hello' in
bin2 and an executable file 'git-hello' (which prints "Hello World!") in
bin3 the following will occur:

	$ git hello
	fatal: cannot exec 'git-hello': Permission denied

This is due to only checking 'access()' when locating an executable in
PATH, which doesn't distinguish between files and directories.  Instead
use 'is_executable()' which check that the path is to a regular,
executable file.  Now run-command won't try to execute the directory or
non-executable file 'git-hello':

	$ git hello
	Hello World!

Reported-by: Brian Hatfield <[email-masked]>
Signed-off-by: Brandon Williams <[email-masked]>
---
 run-command.c          |  2 +-
 t/t0061-run-command.sh | 23 +++++++++++++++++++++++
 2 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/run-command.c b/run-command.c
index a97d7bf9f..ec08e0951 100644
--- a/run-command.c
+++ b/run-command.c
@@ -137,7 +137,7 @@ static char *locate_in_PATH(const char *file)
 		}
 		strbuf_addstr(&buf, file);
 
-		if (!access(buf.buf, F_OK))
+		if (is_executable(buf.buf))
 			return strbuf_detach(&buf, NULL);
 
 		if (!*end)
diff --git a/t/t0061-run-command.sh b/t/t0061-run-command.sh
index 98c09dd98..fd5e43766 100755
--- a/t/t0061-run-command.sh
+++ b/t/t0061-run-command.sh
@@ -37,6 +37,29 @@ test_expect_success !MINGW 'run_command can run a script without a #! line' '
 	test_cmp empty err
 '
 
+test_expect_success 'run_command should not try to execute a directory' '
+	test_when_finished "rm -rf bin1 bin2 bin3" &&
+	mkdir -p bin1/greet bin2 bin3 &&
+	write_script bin2/greet <<-\EOF &&
+	cat bin2/greet
+	EOF
+	chmod -x bin2/greet &&
+	write_script bin3/greet <<-\EOF &&
+	cat bin3/greet
+	EOF
+
+	# Test that run-command does not try to execute the "greet" directory in
+	# "bin1", or the non-executable file "greet" in "bin2", but rather
+	# correcty executes the "greet" script located in bin3.
+	(
+		PATH=$PWD/bin1:$PWD/bin2:$PWD/bin3:$PATH &&
+		export PATH &&
+		test-run-command run-command greet >actual 2>err
+	) &&
+	test_cmp bin3/greet actual &&
+	test_cmp empty err
+'
+
 test_expect_success POSIXPERM 'run_command reports EACCES' '
 	cat hello-script >hello.sh &&
 	chmod -x hello.sh &&
-- 
2.13.0.rc0.306.g87b477812d-goog